diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-05-21 12:45:18 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-05-21 12:45:18 +0200 |
commit | 775b2449bdba7c97dda9f274c92bf7a83dac4142 (patch) | |
tree | b4bee45c13762ea93642b1e38c62de454e51cf5d | |
parent | 21363cf0ca5c9c62e34e37422fb1d13d70d3de3c (diff) | |
parent | 5fb86e5d4a951ddb0474cdfd809380c8e2a8d101 (diff) | |
download | linux-775b2449bdba7c97dda9f274c92bf7a83dac4142.tar.bz2 |
Merge branch 'topic/asoc' into for-linus
1765 files changed, 36950 insertions, 15719 deletions
diff --git a/Documentation/ABI/stable/sysfs-driver-usb-usbtmc b/Documentation/ABI/stable/sysfs-driver-usb-usbtmc index 2a7f9a00cb0a..e960cd027e1e 100644 --- a/Documentation/ABI/stable/sysfs-driver-usb-usbtmc +++ b/Documentation/ABI/stable/sysfs-driver-usb-usbtmc @@ -1,5 +1,5 @@ -What: /sys/bus/usb/drivers/usbtmc/devices/*/interface_capabilities -What: /sys/bus/usb/drivers/usbtmc/devices/*/device_capabilities +What: /sys/bus/usb/drivers/usbtmc/*/interface_capabilities +What: /sys/bus/usb/drivers/usbtmc/*/device_capabilities Date: August 2008 Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Description: @@ -12,8 +12,8 @@ Description: The files are read only. -What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_interface_capabilities -What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_device_capabilities +What: /sys/bus/usb/drivers/usbtmc/*/usb488_interface_capabilities +What: /sys/bus/usb/drivers/usbtmc/*/usb488_device_capabilities Date: August 2008 Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Description: @@ -27,7 +27,7 @@ Description: The files are read only. -What: /sys/bus/usb/drivers/usbtmc/devices/*/TermChar +What: /sys/bus/usb/drivers/usbtmc/*/TermChar Date: August 2008 Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Description: @@ -40,7 +40,7 @@ Description: sent to the device or not. -What: /sys/bus/usb/drivers/usbtmc/devices/*/TermCharEnabled +What: /sys/bus/usb/drivers/usbtmc/*/TermCharEnabled Date: August 2008 Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Description: @@ -51,7 +51,7 @@ Description: published by the USB-IF. -What: /sys/bus/usb/drivers/usbtmc/devices/*/auto_abort +What: /sys/bus/usb/drivers/usbtmc/*/auto_abort Date: August 2008 Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Description: diff --git a/Documentation/ABI/testing/sysfs-block-rssd b/Documentation/ABI/testing/sysfs-block-rssd new file mode 100644 index 000000000000..d535757799fe --- /dev/null +++ b/Documentation/ABI/testing/sysfs-block-rssd @@ -0,0 +1,18 @@ +What: /sys/block/rssd*/registers +Date: March 2012 +KernelVersion: 3.3 +Contact: Asai Thambi S P <asamymuthupa@micron.com> +Description: This is a read-only file. Dumps below driver information and + hardware registers. + - S ACTive + - Command Issue + - Allocated + - Completed + - PORT IRQ STAT + - HOST IRQ STAT + +What: /sys/block/rssd*/status +Date: April 2012 +KernelVersion: 3.4 +Contact: Asai Thambi S P <asamymuthupa@micron.com> +Description: This is a read-only file. Indicates the status of the device. diff --git a/Documentation/ABI/testing/sysfs-bus-hsi b/Documentation/ABI/testing/sysfs-bus-hsi new file mode 100644 index 000000000000..1b1b282a99e1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-hsi @@ -0,0 +1,19 @@ +What: /sys/bus/hsi +Date: April 2012 +KernelVersion: 3.4 +Contact: Carlos Chinea <carlos.chinea@nokia.com> +Description: + High Speed Synchronous Serial Interface (HSI) is a + serial interface mainly used for connecting application + engines (APE) with cellular modem engines (CMT) in cellular + handsets. + The bus will be populated with devices (hsi_clients) representing + the protocols available in the system. Bus drivers implement + those protocols. + +What: /sys/bus/hsi/devices/.../modalias +Date: April 2012 +KernelVersion: 3.4 +Contact: Carlos Chinea <carlos.chinea@nokia.com> +Description: Stores the same MODALIAS value emitted by uevent + Format: hsi:<hsi_client device name> diff --git a/Documentation/ABI/testing/sysfs-cfq-target-latency b/Documentation/ABI/testing/sysfs-cfq-target-latency new file mode 100644 index 000000000000..df0f7828c5e3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-cfq-target-latency @@ -0,0 +1,8 @@ +What: /sys/block/<device>/iosched/target_latency +Date: March 2012 +contact: Tao Ma <boyu.mt@taobao.com> +Description: + The /sys/block/<device>/iosched/target_latency only exists + when the user sets cfq to /sys/block/<device>/scheduler. + It contains an estimated latency time for the cfq. cfq will + use it to calculate the time slice used for every task. diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt index b768cc0e402b..5c72eed89563 100644 --- a/Documentation/DMA-attributes.txt +++ b/Documentation/DMA-attributes.txt @@ -31,3 +31,21 @@ may be weakly ordered, that is that reads and writes may pass each other. Since it is optional for platforms to implement DMA_ATTR_WEAK_ORDERING, those that do not will simply ignore the attribute and exhibit default behavior. + +DMA_ATTR_WRITE_COMBINE +---------------------- + +DMA_ATTR_WRITE_COMBINE specifies that writes to the mapping may be +buffered to improve performance. + +Since it is optional for platforms to implement DMA_ATTR_WRITE_COMBINE, +those that do not will simply ignore the attribute and exhibit default +behavior. + +DMA_ATTR_NON_CONSISTENT +----------------------- + +DMA_ATTR_NON_CONSISTENT lets the platform to choose to return either +consistent or non-consistent memory as it sees fit. By using this API, +you are guaranteeing to the platform that you have all the correct and +necessary sync points for this memory in the driver. diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 9c27e5125dd2..7514dbf0a679 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -446,4 +446,21 @@ X!Idrivers/video/console/fonts.c !Edrivers/i2c/i2c-core.c </chapter> + <chapter id="hsi"> + <title>High Speed Synchronous Serial Interface (HSI)</title> + + <para> + High Speed Synchronous Serial Interface (HSI) is a + serial interface mainly used for connecting application + engines (APE) with cellular modem engines (CMT) in cellular + handsets. + + HSI provides multiplexing for up to 16 logical channels, + low-latency and full duplex communication. + </para> + +!Iinclude/linux/hsi/hsi.h +!Edrivers/hsi/hsi.c + </chapter> + </book> diff --git a/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml b/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml index 3fd3ce5df270..5274c24d11e0 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml @@ -1,6 +1,6 @@ <refentry id="V4L2-PIX-FMT-NV12M"> <refmeta> - <refentrytitle>V4L2_PIX_FMT_NV12M ('NV12M')</refentrytitle> + <refentrytitle>V4L2_PIX_FMT_NV12M ('NM12')</refentrytitle> &manvol; </refmeta> <refnamediv> diff --git a/Documentation/DocBook/media/v4l/pixfmt-yuv420m.xml b/Documentation/DocBook/media/v4l/pixfmt-yuv420m.xml index 9957863daf18..60308f1eefdf 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-yuv420m.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-yuv420m.xml @@ -1,6 +1,6 @@ <refentry id="V4L2-PIX-FMT-YUV420M"> <refmeta> - <refentrytitle>V4L2_PIX_FMT_YUV420M ('YU12M')</refentrytitle> + <refentrytitle>V4L2_PIX_FMT_YUV420M ('YM12')</refentrytitle> &manvol; </refmeta> <refnamediv> diff --git a/Documentation/cgroups/cpusets.txt b/Documentation/cgroups/cpusets.txt index 5c51ed406d1d..cefd3d8bbd11 100644 --- a/Documentation/cgroups/cpusets.txt +++ b/Documentation/cgroups/cpusets.txt @@ -217,7 +217,7 @@ and name space for cpusets, with a minimum of additional kernel code. The cpus and mems files in the root (top_cpuset) cpuset are read-only. The cpus file automatically tracks the value of -cpu_online_map using a CPU hotplug notifier, and the mems file +cpu_online_mask using a CPU hotplug notifier, and the mems file automatically tracks the value of node_states[N_HIGH_MEMORY]--i.e., nodes with memory--using the cpuset_track_online_nodes() hook. diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index 4c95c0034a4b..9b1067afb224 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt @@ -34,8 +34,7 @@ Current Status: linux-2.6.34-mmotm(development version of 2010/April) Features: - accounting anonymous pages, file caches, swap caches usage and limiting them. - - private LRU and reclaim routine. (system's global LRU and private LRU - work independently from each other) + - pages are linked to per-memcg LRU exclusively, and there is no global LRU. - optionally, memory+swap usage can be accounted and limited. - hierarchical accounting - soft limit @@ -154,7 +153,7 @@ updated. page_cgroup has its own LRU on cgroup. 2.2.1 Accounting details All mapped anon pages (RSS) and cache pages (Page Cache) are accounted. -Some pages which are never reclaimable and will not be on the global LRU +Some pages which are never reclaimable and will not be on the LRU are not accounted. We just account pages under usual VM management. RSS pages are accounted at page_fault unless they've already been accounted diff --git a/Documentation/cpu-hotplug.txt b/Documentation/cpu-hotplug.txt index a20bfd415e41..66ef8f35613d 100644 --- a/Documentation/cpu-hotplug.txt +++ b/Documentation/cpu-hotplug.txt @@ -47,7 +47,7 @@ maxcpus=n Restrict boot time cpus to n. Say if you have 4 cpus, using other cpus later online, read FAQ's for more info. additional_cpus=n (*) Use this to limit hotpluggable cpus. This option sets - cpu_possible_map = cpu_present_map + additional_cpus + cpu_possible_mask = cpu_present_mask + additional_cpus cede_offline={"off","on"} Use this option to disable/enable putting offlined processors to an extended H_CEDE state on @@ -64,11 +64,11 @@ should only rely on this to count the # of cpus, but *MUST* not rely on the apicid values in those tables for disabled apics. In the event BIOS doesn't mark such hot-pluggable cpus as disabled entries, one could use this parameter "additional_cpus=x" to represent those cpus in the -cpu_possible_map. +cpu_possible_mask. possible_cpus=n [s390,x86_64] use this to set hotpluggable cpus. This option sets possible_cpus bits in - cpu_possible_map. Thus keeping the numbers of bits set + cpu_possible_mask. Thus keeping the numbers of bits set constant even if the machine gets rebooted. CPU maps and such @@ -76,7 +76,7 @@ CPU maps and such [More on cpumaps and primitive to manipulate, please check include/linux/cpumask.h that has more descriptive text.] -cpu_possible_map: Bitmap of possible CPUs that can ever be available in the +cpu_possible_mask: Bitmap of possible CPUs that can ever be available in the system. This is used to allocate some boot time memory for per_cpu variables that aren't designed to grow/shrink as CPUs are made available or removed. Once set during boot time discovery phase, the map is static, i.e no bits @@ -84,13 +84,13 @@ are added or removed anytime. Trimming it accurately for your system needs upfront can save some boot time memory. See below for how we use heuristics in x86_64 case to keep this under check. -cpu_online_map: Bitmap of all CPUs currently online. Its set in __cpu_up() +cpu_online_mask: Bitmap of all CPUs currently online. Its set in __cpu_up() after a cpu is available for kernel scheduling and ready to receive interrupts from devices. Its cleared when a cpu is brought down using __cpu_disable(), before which all OS services including interrupts are migrated to another target CPU. -cpu_present_map: Bitmap of CPUs currently present in the system. Not all +cpu_present_mask: Bitmap of CPUs currently present in the system. Not all of them may be online. When physical hotplug is processed by the relevant subsystem (e.g ACPI) can change and new bit either be added or removed from the map depending on the event is hot-add/hot-remove. There are currently @@ -99,22 +99,22 @@ at which time hotplug is disabled. You really dont need to manipulate any of the system cpu maps. They should be read-only for most use. When setting up per-cpu resources almost always use -cpu_possible_map/for_each_possible_cpu() to iterate. +cpu_possible_mask/for_each_possible_cpu() to iterate. Never use anything other than cpumask_t to represent bitmap of CPUs. #include <linux/cpumask.h> - for_each_possible_cpu - Iterate over cpu_possible_map - for_each_online_cpu - Iterate over cpu_online_map - for_each_present_cpu - Iterate over cpu_present_map + for_each_possible_cpu - Iterate over cpu_possible_mask + for_each_online_cpu - Iterate over cpu_online_mask + for_each_present_cpu - Iterate over cpu_present_mask for_each_cpu_mask(x,mask) - Iterate over some random collection of cpu mask. #include <linux/cpu.h> get_online_cpus() and put_online_cpus(): The above calls are used to inhibit cpu hotplug operations. While the -cpu_hotplug.refcount is non zero, the cpu_online_map will not change. +cpu_hotplug.refcount is non zero, the cpu_online_mask will not change. If you merely need to avoid cpus going away, you could also use preempt_disable() and preempt_enable() for those sections. Just remember the critical section cannot call any diff --git a/Documentation/devicetree/bindings/ata/calxeda-sata.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt index 79caa5651f53..8bb8a76d42e8 100644 --- a/Documentation/devicetree/bindings/ata/calxeda-sata.txt +++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt @@ -1,10 +1,10 @@ -* Calxeda SATA Controller +* AHCI SATA Controller SATA nodes are defined to describe on-chip Serial ATA controllers. Each SATA controller should have its own node. Required properties: -- compatible : compatible list, contains "calxeda,hb-ahci" +- compatible : compatible list, contains "calxeda,hb-ahci" or "snps,spear-ahci" - interrupts : <interrupt mapping for SATA IRQ> - reg : <registers mapping> @@ -14,4 +14,3 @@ Example: reg = <0xffe08000 0x1000>; interrupts = <115>; }; - diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index 5903ecf6e895..a20069502f5a 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -27,13 +27,13 @@ nand0: nand@40000000,0 { reg = <0x40000000 0x10000000 0xffffe800 0x200 >; - atmel,nand-addr-offset = <21>; - atmel,nand-cmd-offset = <22>; + atmel,nand-addr-offset = <21>; /* ale */ + atmel,nand-cmd-offset = <22>; /* cle */ nand-on-flash-bbt; nand-ecc-mode = "soft"; - gpios = <&pioC 13 0 - &pioC 14 0 - 0 + gpios = <&pioC 13 0 /* rdy */ + &pioC 14 0 /* nce */ + 0 /* cd */ >; partition@0 { ... diff --git a/Documentation/devicetree/bindings/regulator/anatop-regulator.txt b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt new file mode 100644 index 000000000000..357758cb6e92 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt @@ -0,0 +1,29 @@ +Anatop Voltage regulators + +Required properties: +- compatible: Must be "fsl,anatop-regulator" +- anatop-reg-offset: Anatop MFD register offset +- anatop-vol-bit-shift: Bit shift for the register +- anatop-vol-bit-width: Number of bits used in the register +- anatop-min-bit-val: Minimum value of this register +- anatop-min-voltage: Minimum voltage of this regulator +- anatop-max-voltage: Maximum voltage of this regulator + +Any property defined as part of the core regulator +binding, defined in regulator.txt, can also be used. + +Example: + + regulator-vddpu { + compatible = "fsl,anatop-regulator"; + regulator-name = "vddpu"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + anatop-reg-offset = <0x140>; + anatop-vol-bit-shift = <9>; + anatop-vol-bit-width = <5>; + anatop-min-bit-val = <1>; + anatop-min-voltage = <725000>; + anatop-max-voltage = <1300000>; + }; diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt new file mode 100644 index 000000000000..e4acdd891e49 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt @@ -0,0 +1,49 @@ +Freescale i.MX audio complex with SGTL5000 codec + +Required properties: +- compatible : "fsl,imx-audio-sgtl5000" +- model : The user-visible name of this sound complex +- ssi-controller : The phandle of the i.MX SSI controller +- audio-codec : The phandle of the SGTL5000 audio codec +- audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names could be power + supplies, SGTL5000 pins, and the jacks on the board: + + Power supplies: + * Mic Bias + + SGTL5000 pins: + * MIC_IN + * LINE_IN + * HP_OUT + * LINE_OUT + + Board connectors: + * Mic Jack + * Line In Jack + * Headphone Jack + * Line Out Jack + * Ext Spk + +- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) +- mux-ext-port : The external port of the i.MX audio muxer + +Note: The AUDMUX port numbering should start at 1, which is consistent with +hardware manual. + +Example: + +sound { + compatible = "fsl,imx51-babbage-sgtl5000", + "fsl,imx-audio-sgtl5000"; + model = "imx51-babbage-sgtl5000"; + ssi-controller = <&ssi1>; + audio-codec = <&sgtl5000>; + audio-routing = + "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + mux-int-port = <1>; + mux-ext-port = <3>; +}; diff --git a/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt new file mode 100644 index 000000000000..601c518eddaa --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt @@ -0,0 +1,17 @@ +* Freescale MXS audio complex with SGTL5000 codec + +Required properties: +- compatible: "fsl,mxs-audio-sgtl5000" +- model: The user-visible name of this sound complex +- saif-controllers: The phandle list of the MXS SAIF controller +- audio-codec: The phandle of the SGTL5000 audio codec + +Example: + +sound { + compatible = "fsl,imx28-evk-sgtl5000", + "fsl,mxs-audio-sgtl5000"; + model = "imx28-evk-sgtl5000"; + saif-controllers = <&saif0 &saif1>; + audio-codec = <&sgtl5000>; +}; diff --git a/Documentation/devicetree/bindings/sound/mxs-saif.txt b/Documentation/devicetree/bindings/sound/mxs-saif.txt new file mode 100644 index 000000000000..c37ba6143d9b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mxs-saif.txt @@ -0,0 +1,36 @@ +* Freescale MXS Serial Audio Interface (SAIF) + +Required properties: +- compatible: Should be "fsl,<chip>-saif" +- reg: Should contain registers location and length +- interrupts: Should contain ERROR and DMA interrupts +- fsl,saif-dma-channel: APBX DMA channel for the SAIF + +Optional properties: +- fsl,saif-master: phandle to the master SAIF. It's only required for + the slave SAIF. + +Note: Each SAIF controller should have an alias correctly numbered +in "aliases" node. + +Example: + +aliases { + saif0 = &saif0; + saif1 = &saif1; +}; + +saif0: saif@80042000 { + compatible = "fsl,imx28-saif"; + reg = <0x80042000 2000>; + interrupts = <59 80>; + fsl,saif-dma-channel = <4>; +}; + +saif1: saif@80046000 { + compatible = "fsl,imx28-saif"; + reg = <0x80046000 2000>; + interrupts = <58 81>; + fsl,saif-dma-channel = <5>; + fsl,saif-master = <&saif0>; +}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt new file mode 100644 index 000000000000..1ac7b1642186 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt @@ -0,0 +1,32 @@ +NVIDIA Tegra30 AHUB (Audio Hub) + +Required properties: +- compatible : "nvidia,tegra30-ahub" +- reg : Should contain the register physical address and length for each of + the AHUB's APBIF registers and the AHUB's own registers. +- interrupts : Should contain AHUB interrupt +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for the first APBIF channel. +- ranges : The bus address mapping for the configlink register bus. + Can be empty since the mapping is 1:1. +- #address-cells : For the configlink bus. Should be <1>; +- #size-cells : For the configlink bus. Should be <1>. + +AHUB client modules need to specify the IDs of their CIFs (Client InterFaces). +For RX CIFs, the numbers indicate the register number within AHUB routing +register space (APBIF 0..3 RX, I2S 0..5 RX, DAM 0..2 RX 0..1, SPDIF RX 0..1). +For TX CIFs, the numbers indicate the bit position within the AHUB routing +registers (APBIF 0..3 TX, I2S 0..5 TX, DAM 0..2 TX, SPDIF TX 0..1). + +Example: + +ahub@70080000 { + compatible = "nvidia,tegra30-ahub"; + reg = <0x70080000 0x200 0x70080200 0x100>; + interrupts = < 0 103 0x04 >; + nvidia,dma-request-selector = <&apbdma 1>; + + ranges; + #address-cells = <1>; + #size-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt new file mode 100644 index 000000000000..dfa6c037124a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt @@ -0,0 +1,15 @@ +NVIDIA Tegra30 I2S controller + +Required properties: +- compatible : "nvidia,tegra30-i2s" +- reg : Should contain I2S registers location and length +- nvidia,ahub-cif-ids : The list of AHUB CIF IDs for this port, rx (playback) + first, tx (capture) second. See nvidia,tegra30-ahub.txt for values. + +Example: + +i2s@70002800 { + compatible = "nvidia,tegra30-i2s"; + reg = <0x70080300 0x100>; + nvidia,ahub-cif-ids = <4 4>; +}; diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt b/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt new file mode 100644 index 000000000000..04b14cfb1f16 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt @@ -0,0 +1,14 @@ +NVIDIA Tegra audio complex for TrimSlice + +Required properties: +- compatible : "nvidia,tegra-audio-trimslice" +- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller +- nvidia,audio-codec : The phandle of the WM8903 audio codec + +Example: + +sound { + compatible = "nvidia,tegra-audio-trimslice"; + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&codec>; +}; diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index c1be8066ea59..03ca210406ed 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -6,14 +6,6 @@ be removed from this file. --------------------------- -What: x86 floppy disable_hlt -When: 2012 -Why: ancient workaround of dubious utility clutters the - code used by everybody else. -Who: Len Brown <len.brown@intel.com> - ---------------------------- - What: CONFIG_APM_CPU_IDLE, and its ability to call APM BIOS in idle When: 2012 Why: This optional sub-feature of APM is of dubious reliability, @@ -539,3 +531,11 @@ Why: There appear to be no production users of the get_robust_list syscall, of ASLR. It was only ever intended for debugging, so it should be removed. Who: Kees Cook <keescook@chromium.org> + +---------------------------- + +What: setitimer accepts user NULL pointer (value) +When: 3.6 +Why: setitimer is not returning -EFAULT if user pointer is NULL. This + violates the spec. +Who: Sasikantha Babu <sasikanth.v19@gmail.com> diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index e916e3d36488..0d0492028082 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -114,7 +114,7 @@ members are defined: struct file_system_type { const char *name; int fs_flags; - struct dentry (*mount) (struct file_system_type *, int, + struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); void (*kill_sb) (struct super_block *); struct module *owner; diff --git a/Documentation/hwmon/k10temp b/Documentation/hwmon/k10temp index a10f73624ad3..90956b618025 100644 --- a/Documentation/hwmon/k10temp +++ b/Documentation/hwmon/k10temp @@ -11,7 +11,7 @@ Supported chips: Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra) * AMD Family 12h processors: "Llano" (E2/A4/A6/A8-Series) * AMD Family 14h processors: "Brazos" (C/E/G/Z-Series) -* AMD Family 15h processors: "Bulldozer" +* AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity" Prefix: 'k10temp' Addresses scanned: PCI space diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 3b7488fc3373..e34b531dc316 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -225,6 +225,7 @@ Code Seq#(hex) Include File Comments 'j' 00-3F linux/joystick.h 'k' 00-0F linux/spi/spidev.h conflict! 'k' 00-05 video/kyro.h conflict! +'k' 10-17 linux/hsi/hsi_char.h HSI character device 'l' 00-3F linux/tcfs_fs.h transparent cryptographic file system <http://web.archive.org/web/*/http://mikonos.dia.unisa.it/tcfs> 'l' 40-7F linux/udf_fs_i.h in development: diff --git a/Documentation/networking/driver.txt b/Documentation/networking/driver.txt index 03283daa64fe..da59e2884130 100644 --- a/Documentation/networking/driver.txt +++ b/Documentation/networking/driver.txt @@ -2,16 +2,16 @@ Document about softnet driver issues Transmit path guidelines: -1) The hard_start_xmit method must never return '1' under any - normal circumstances. It is considered a hard error unless +1) The ndo_start_xmit method must not return NETDEV_TX_BUSY under + any normal circumstances. It is considered a hard error unless there is no way your device can tell ahead of time when it's transmit function will become busy. Instead it must maintain the queue properly. For example, for a driver implementing scatter-gather this means: - static int drv_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev) + static netdev_tx_t drv_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct drv *dp = netdev_priv(dev); @@ -23,7 +23,7 @@ Transmit path guidelines: unlock_tx(dp); printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", dev->name); - return 1; + return NETDEV_TX_BUSY; } ... queue packet to card ... @@ -35,6 +35,7 @@ Transmit path guidelines: ... unlock_tx(dp); ... + return NETDEV_TX_OK; } And then at the end of your TX reclamation event handling: @@ -58,15 +59,12 @@ Transmit path guidelines: TX_BUFFS_AVAIL(dp) > 0) netif_wake_queue(dp->dev); -2) Do not forget to update netdev->trans_start to jiffies after - each new tx packet is given to the hardware. - -3) A hard_start_xmit method must not modify the shared parts of a +2) An ndo_start_xmit method must not modify the shared parts of a cloned SKB. -4) Do not forget that once you return 0 from your hard_start_xmit - method, it is your driver's responsibility to free up the SKB - and in some finite amount of time. +3) Do not forget that once you return NETDEV_TX_OK from your + ndo_start_xmit method, it is your driver's responsibility to free + up the SKB and in some finite amount of time. For example, this means that it is not allowed for your TX mitigation scheme to let TX packets "hang out" in the TX @@ -74,8 +72,9 @@ Transmit path guidelines: This error can deadlock sockets waiting for send buffer room to be freed up. - If you return 1 from the hard_start_xmit method, you must not keep - any reference to that SKB and you must not attempt to free it up. + If you return NETDEV_TX_BUSY from the ndo_start_xmit method, you + must not keep any reference to that SKB and you must not attempt + to free it up. Probing guidelines: @@ -85,10 +84,10 @@ Probing guidelines: Close/stop guidelines: -1) After the dev->stop routine has been called, the hardware must +1) After the ndo_stop routine has been called, the hardware must not receive or transmit any data. All in flight packets must be aborted. If necessary, poll or wait for completion of any reset commands. -2) The dev->stop routine will be called by unregister_netdevice +2) The ndo_stop routine will be called by unregister_netdevice if device is still UP. diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index ad3e80e17b4f..1619a8c80873 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -147,7 +147,7 @@ tcp_adv_win_scale - INTEGER (if tcp_adv_win_scale > 0) or bytes-bytes/2^(-tcp_adv_win_scale), if it is <= 0. Possible values are [-31, 31], inclusive. - Default: 2 + Default: 1 tcp_allowed_congestion_control - STRING Show/set the congestion control choices available to non-privileged @@ -410,7 +410,7 @@ tcp_rmem - vector of 3 INTEGERs: min, default, max net.core.rmem_max. Calling setsockopt() with SO_RCVBUF disables automatic tuning of that socket's receive buffer size, in which case this value is ignored. - Default: between 87380B and 4MB, depending on RAM size. + Default: between 87380B and 6MB, depending on RAM size. tcp_sack - BOOLEAN Enable select acknowledgments (SACKS). @@ -604,15 +604,8 @@ IP Variables: ip_local_port_range - 2 INTEGERS Defines the local port range that is used by TCP and UDP to choose the local port. The first number is the first, the - second the last local port number. Default value depends on - amount of memory available on the system: - > 128Mb 32768-61000 - < 128Mb 1024-4999 or even less. - This number defines number of active connections, which this - system can issue simultaneously to systems not supporting - TCP extensions (timestamps). With tcp_tw_recycle enabled - (i.e. by default) range 1024-4999 is enough to issue up to - 2000 connections per second to systems supporting timestamps. + second the last local port number. The default values are + 32768 and 61000 respectively. ip_local_reserved_ports - list of comma separated ranges Specify the ports which are reserved for known third-party diff --git a/Documentation/networking/netdevices.txt b/Documentation/networking/netdevices.txt index 89358341682a..c7ecc7080494 100644 --- a/Documentation/networking/netdevices.txt +++ b/Documentation/networking/netdevices.txt @@ -47,26 +47,25 @@ packets is preferred. struct net_device synchronization rules ======================================= -dev->open: +ndo_open: Synchronization: rtnl_lock() semaphore. Context: process -dev->stop: +ndo_stop: Synchronization: rtnl_lock() semaphore. Context: process - Note1: netif_running() is guaranteed false - Note2: dev->poll() is guaranteed to be stopped + Note: netif_running() is guaranteed false -dev->do_ioctl: +ndo_do_ioctl: Synchronization: rtnl_lock() semaphore. Context: process -dev->get_stats: +ndo_get_stats: Synchronization: dev_base_lock rwlock. Context: nominally process, but don't sleep inside an rwlock -dev->hard_start_xmit: - Synchronization: netif_tx_lock spinlock. +ndo_start_xmit: + Synchronization: __netif_tx_lock spinlock. When the driver sets NETIF_F_LLTX in dev->features this will be called without holding netif_tx_lock. In this case the driver @@ -87,20 +86,20 @@ dev->hard_start_xmit: o NETDEV_TX_LOCKED Locking failed, please retry quickly. Only valid when NETIF_F_LLTX is set. -dev->tx_timeout: - Synchronization: netif_tx_lock spinlock. +ndo_tx_timeout: + Synchronization: netif_tx_lock spinlock; all TX queues frozen. Context: BHs disabled Notes: netif_queue_stopped() is guaranteed true -dev->set_rx_mode: - Synchronization: netif_tx_lock spinlock. +ndo_set_rx_mode: + Synchronization: netif_addr_lock spinlock. Context: BHs disabled struct napi_struct synchronization rules ======================================== napi->poll: Synchronization: NAPI_STATE_SCHED bit in napi->state. Device - driver's dev->close method will invoke napi_disable() on + driver's ndo_stop method will invoke napi_disable() on all NAPI instances which will do a sleeping poll on the NAPI_STATE_SCHED napi->state bit, waiting for all pending NAPI activity to cease. diff --git a/Documentation/power/freezing-of-tasks.txt b/Documentation/power/freezing-of-tasks.txt index ec715cd78fbb..6ec291ea1c78 100644 --- a/Documentation/power/freezing-of-tasks.txt +++ b/Documentation/power/freezing-of-tasks.txt @@ -9,7 +9,7 @@ architectures). II. How does it work? -There are four per-task flags used for that, PF_NOFREEZE, PF_FROZEN, TIF_FREEZE +There are three per-task flags used for that, PF_NOFREEZE, PF_FROZEN and PF_FREEZER_SKIP (the last one is auxiliary). The tasks that have PF_NOFREEZE unset (all user space processes and some kernel threads) are regarded as 'freezable' and treated in a special way before the system enters a @@ -17,30 +17,31 @@ suspend state as well as before a hibernation image is created (in what follows we only consider hibernation, but the description also applies to suspend). Namely, as the first step of the hibernation procedure the function -freeze_processes() (defined in kernel/power/process.c) is called. It executes -try_to_freeze_tasks() that sets TIF_FREEZE for all of the freezable tasks and -either wakes them up, if they are kernel threads, or sends fake signals to them, -if they are user space processes. A task that has TIF_FREEZE set, should react -to it by calling the function called __refrigerator() (defined in -kernel/freezer.c), which sets the task's PF_FROZEN flag, changes its state -to TASK_UNINTERRUPTIBLE and makes it loop until PF_FROZEN is cleared for it. -Then, we say that the task is 'frozen' and therefore the set of functions -handling this mechanism is referred to as 'the freezer' (these functions are -defined in kernel/power/process.c, kernel/freezer.c & include/linux/freezer.h). -User space processes are generally frozen before kernel threads. +freeze_processes() (defined in kernel/power/process.c) is called. A system-wide +variable system_freezing_cnt (as opposed to a per-task flag) is used to indicate +whether the system is to undergo a freezing operation. And freeze_processes() +sets this variable. After this, it executes try_to_freeze_tasks() that sends a +fake signal to all user space processes, and wakes up all the kernel threads. +All freezable tasks must react to that by calling try_to_freeze(), which +results in a call to __refrigerator() (defined in kernel/freezer.c), which sets +the task's PF_FROZEN flag, changes its state to TASK_UNINTERRUPTIBLE and makes +it loop until PF_FROZEN is cleared for it. Then, we say that the task is +'frozen' and therefore the set of functions handling this mechanism is referred +to as 'the freezer' (these functions are defined in kernel/power/process.c, +kernel/freezer.c & include/linux/freezer.h). User space processes are generally +frozen before kernel threads. __refrigerator() must not be called directly. Instead, use the try_to_freeze() function (defined in include/linux/freezer.h), that checks -the task's TIF_FREEZE flag and makes the task enter __refrigerator() if the -flag is set. +if the task is to be frozen and makes the task enter __refrigerator(). For user space processes try_to_freeze() is called automatically from the signal-handling code, but the freezable kernel threads need to call it explicitly in suitable places or use the wait_event_freezable() or wait_event_freezable_timeout() macros (defined in include/linux/freezer.h) -that combine interruptible sleep with checking if TIF_FREEZE is set and calling -try_to_freeze(). The main loop of a freezable kernel thread may look like the -following one: +that combine interruptible sleep with checking if the task is to be frozen and +calling try_to_freeze(). The main loop of a freezable kernel thread may look +like the following one: set_freezable(); do { @@ -53,7 +54,7 @@ following one: (from drivers/usb/core/hub.c::hub_thread()). If a freezable kernel thread fails to call try_to_freeze() after the freezer has -set TIF_FREEZE for it, the freezing of tasks will fail and the entire +initiated a freezing operation, the freezing of tasks will fail and the entire hibernation operation will be cancelled. For this reason, freezable kernel threads must call try_to_freeze() somewhere or use one of the wait_event_freezable() and wait_event_freezable_timeout() macros. diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 787717091421..d389acd31e19 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -123,7 +123,7 @@ KEY SERVICE OVERVIEW The key service provides a number of features besides keys: - (*) The key service defines two special key types: + (*) The key service defines three special key types: (+) "keyring" @@ -137,6 +137,18 @@ The key service provides a number of features besides keys: blobs of data. These can be created, updated and read by userspace, and aren't intended for use by kernel services. + (+) "logon" + + Like a "user" key, a "logon" key has a payload that is an arbitrary + blob of data. It is intended as a place to store secrets which are + accessible to the kernel but not to userspace programs. + + The description can be arbitrary, but must be prefixed with a non-zero + length string that describes the key "subclass". The subclass is + separated from the rest of the description by a ':'. "logon" keys can + be created and updated from userspace, but the payload is only + readable from kernel space. + (*) Each process subscribes to three keyrings: a thread-specific keyring, a process-specific keyring, and a session-specific keyring. diff --git a/Documentation/usb/URB.txt b/Documentation/usb/URB.txt index 8ffce746d496..00d2c644068e 100644 --- a/Documentation/usb/URB.txt +++ b/Documentation/usb/URB.txt @@ -168,6 +168,28 @@ that if the completion handler or anyone else tries to resubmit it they will get a -EPERM error. Thus you can be sure that when usb_kill_urb() returns, the URB is totally idle. +There is a lifetime issue to consider. An URB may complete at any +time, and the completion handler may free the URB. If this happens +while usb_unlink_urb or usb_kill_urb is running, it will cause a +memory-access violation. The driver is responsible for avoiding this, +which often means some sort of lock will be needed to prevent the URB +from being deallocated while it is still in use. + +On the other hand, since usb_unlink_urb may end up calling the +completion handler, the handler must not take any lock that is held +when usb_unlink_urb is invoked. The general solution to this problem +is to increment the URB's reference count while holding the lock, then +drop the lock and call usb_unlink_urb or usb_kill_urb, and then +decrement the URB's reference count. You increment the reference +count by calling + + struct urb *usb_get_urb(struct urb *urb) + +(ignore the return value; it is the same as the argument) and +decrement the reference count by calling usb_free_urb. Of course, +none of this is necessary if there's no danger of the URB being freed +by the completion handler. + 1.7. What about the completion handler? diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index 5335fa8b06eb..c42bb9cd3b43 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -183,10 +183,10 @@ An input control transfer to get a port status. d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 < d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000 -An output bulk transfer to send a SCSI command 0x5E in a 31-byte Bulk wrapper -to a storage device at address 5: +An output bulk transfer to send a SCSI command 0x28 (READ_10) in a 31-byte +Bulk wrapper to a storage device at address 5: -dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000 +dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000 dd65f0e8 4128379808 C Bo:1:005:2 0 31 > * Raw binary format and API diff --git a/MAINTAINERS b/MAINTAINERS index 5190cf25fd8d..fc203d019e61 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -228,7 +228,7 @@ M: Len Brown <lenb@kernel.org> L: linux-acpi@vger.kernel.org W: http://www.lesswatts.org/projects/acpi/ Q: http://patchwork.kernel.org/project/linux-acpi/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux S: Supported F: drivers/acpi/ F: drivers/pnp/pnpacpi/ @@ -1251,7 +1251,6 @@ ATHEROS ATH5K WIRELESS DRIVER M: Jiri Slaby <jirislaby@gmail.com> M: Nick Kossifidis <mickflemm@gmail.com> M: "Luis R. Rodriguez" <mcgrof@qca.qualcomm.com> -M: Bob Copeland <me@bobcopeland.com> L: linux-wireless@vger.kernel.org L: ath5k-devel@lists.ath5k.org W: http://wireless.kernel.org/en/users/Drivers/ath5k @@ -1522,8 +1521,8 @@ M: Gustavo Padovan <gustavo@padovan.org> M: Johan Hedberg <johan.hedberg@gmail.com> L: linux-bluetooth@vger.kernel.org W: http://www.bluez.org/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth.git -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jh/bluetooth.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git S: Maintained F: drivers/bluetooth/ @@ -1533,8 +1532,8 @@ M: Gustavo Padovan <gustavo@padovan.org> M: Johan Hedberg <johan.hedberg@gmail.com> L: linux-bluetooth@vger.kernel.org W: http://www.bluez.org/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth.git -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jh/bluetooth.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git S: Maintained F: net/bluetooth/ F: include/net/bluetooth/ @@ -1969,10 +1968,9 @@ S: Maintained F: drivers/net/ethernet/ti/cpmac.c CPU FREQUENCY DRIVERS -M: Dave Jones <davej@redhat.com> +M: Rafael J. Wysocki <rjw@sisk.pl> L: cpufreq@vger.kernel.org -W: http://www.codemonkey.org.uk/projects/cpufreq/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/davej/cpufreq.git +L: linux-pm@vger.kernel.org S: Maintained F: drivers/cpufreq/ F: include/linux/cpufreq.h @@ -2322,9 +2320,9 @@ S: Supported F: drivers/acpi/dock.c DOCUMENTATION -M: Randy Dunlap <rdunlap@xenotime.net> +M: Rob Landley <rob@landley.net> L: linux-doc@vger.kernel.org -T: quilt http://xenotime.net/kernel-doc-patches/current/ +T: TBD S: Maintained F: Documentation/ @@ -2451,17 +2449,17 @@ F: fs/ecryptfs/ EDAC-CORE M: Doug Thompson <dougthompson@xmission.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Supported F: Documentation/edac.txt -F: drivers/edac/edac_* +F: drivers/edac/ F: include/linux/edac.h EDAC-AMD64 M: Doug Thompson <dougthompson@xmission.com> M: Borislav Petkov <borislav.petkov@amd.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Supported F: drivers/edac/amd64_edac* @@ -2469,35 +2467,35 @@ F: drivers/edac/amd64_edac* EDAC-E752X M: Mark Gross <mark.gross@intel.com> M: Doug Thompson <dougthompson@xmission.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/e752x_edac.c EDAC-E7XXX M: Doug Thompson <dougthompson@xmission.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/e7xxx_edac.c EDAC-I82443BXGX M: Tim Small <tim@buttersideup.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i82443bxgx_edac.c EDAC-I3000 M: Jason Uhlenkott <juhlenko@akamai.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i3000_edac.c EDAC-I5000 M: Doug Thompson <dougthompson@xmission.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i5000_edac.c @@ -2526,21 +2524,21 @@ F: drivers/edac/i7core_edac.c EDAC-I82975X M: Ranganathan Desikan <ravi@jetztechnologies.com> M: "Arvind R." <arvino55@gmail.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i82975x_edac.c EDAC-PASEMI M: Egor Martovetsky <egor@pasemi.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/pasemi_edac.c EDAC-R82600 M: Tim Small <tim@buttersideup.com> -L: bluesmoke-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/r82600_edac.c @@ -3557,17 +3555,13 @@ L: linux-pm@vger.kernel.org S: Supported F: arch/x86/platform/mrst/pmu.* -INTEL PRO/WIRELESS 2100 NETWORK CONNECTION SUPPORT +INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT +M: Stanislav Yakovlev <stas.yakovlev@gmail.com> L: linux-wireless@vger.kernel.org -S: Orphan +S: Maintained F: Documentation/networking/README.ipw2100 -F: drivers/net/wireless/ipw2x00/ipw2100.* - -INTEL PRO/WIRELESS 2915ABG NETWORK CONNECTION SUPPORT -L: linux-wireless@vger.kernel.org -S: Orphan F: Documentation/networking/README.ipw2200 -F: drivers/net/wireless/ipw2x00/ipw2200.* +F: drivers/net/wireless/ipw2x00/ INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT) M: Joseph Cihula <joseph.cihula@intel.com> @@ -3597,6 +3591,7 @@ S: Supported F: drivers/net/wireless/iwlegacy/ INTEL WIRELESS WIFI LINK (iwlwifi) +M: Johannes Berg <johannes.berg@intel.com> M: Wey-Yi Guy <wey-yi.w.guy@intel.com> M: Intel Linux Wireless <ilw@linux.intel.com> L: linux-wireless@vger.kernel.org @@ -4041,6 +4036,7 @@ F: Documentation/scsi/53c700.txt F: drivers/scsi/53c700* LED SUBSYSTEM +M: Bryan Wu <bryan.wu@canonical.com> M: Richard Purdie <rpurdie@rpsys.net> S: Maintained F: drivers/leds/ @@ -4314,6 +4310,13 @@ W: http://www.kernel.org/doc/man-pages L: linux-man@vger.kernel.org S: Maintained +MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2) +M: Mirko Lindner <mlindner@marvell.com> +M: Stephen Hemminger <shemminger@vyatta.com> +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/marvell/sk* + MARVELL LIBERTAS WIRELESS DRIVER M: Dan Williams <dcbw@redhat.com> L: libertas-dev@lists.infradead.org @@ -4344,12 +4347,6 @@ M: Nicolas Pitre <nico@fluxnic.net> S: Odd Fixes F: drivers/mmc/host/mvsdio.* -MARVELL YUKON / SYSKONNECT DRIVER -M: Mirko Lindner <mlindner@syskonnect.de> -M: Ralph Roesler <rroesler@syskonnect.de> -W: http://www.syskonnect.com -S: Supported - MATROX FRAMEBUFFER DRIVER L: linux-fbdev@vger.kernel.org S: Orphan @@ -4537,8 +4534,7 @@ S: Supported F: drivers/net/ethernet/myricom/myri10ge/ NATSEMI ETHERNET DRIVER (DP8381x) -M: Tim Hockin <thockin@hockin.org> -S: Maintained +S: Orphan F: drivers/net/ethernet/natsemi/natsemi.c NATIVE INSTRUMENTS USB SOUND INTERFACE DRIVER @@ -5122,6 +5118,11 @@ F: drivers/i2c/busses/i2c-pca-* F: include/linux/i2c-algo-pca.h F: include/linux/i2c-pca-platform.h +PCDP - PRIMARY CONSOLE AND DEBUG PORT +M: Khalid Aziz <khalid.aziz@hp.com> +S: Maintained +F: drivers/firmware/pcdp.* + PCI ERROR RECOVERY M: Linas Vepstas <linasvepstas@gmail.com> L: linux-pci@vger.kernel.org @@ -5643,7 +5644,7 @@ M: Ohad Ben-Cohen <ohad@wizery.com> S: Maintained F: drivers/remoteproc/ F: Documentation/remoteproc.txt -F: include/linux/remoteproc.txt +F: include/linux/remoteproc.h RFKILL M: Johannes Berg <johannes@sipsolutions.net> @@ -5891,11 +5892,11 @@ F: Documentation/scsi/st.txt F: drivers/scsi/st* SCTP PROTOCOL -M: Vlad Yasevich <vladislav.yasevich@hp.com> +M: Vlad Yasevich <vyasevich@gmail.com> M: Sridhar Samudrala <sri@us.ibm.com> L: linux-sctp@vger.kernel.org W: http://lksctp.sourceforge.net -S: Supported +S: Maintained F: Documentation/networking/sctp.txt F: include/linux/sctp.h F: include/net/sctp/ @@ -6122,12 +6123,6 @@ W: http://www.winischhofer.at/linuxsisusbvga.shtml S: Maintained F: drivers/usb/misc/sisusbvga/ -SKGE, SKY2 10/100/1000 GIGABIT ETHERNET DRIVERS -M: Stephen Hemminger <shemminger@vyatta.com> -L: netdev@vger.kernel.org -S: Maintained -F: drivers/net/ethernet/marvell/sk* - SLAB ALLOCATOR M: Christoph Lameter <cl@linux-foundation.org> M: Pekka Enberg <penberg@kernel.org> @@ -6293,6 +6288,15 @@ F: drivers/tty/serial/sunsu.c F: drivers/tty/serial/sunzilog.c F: drivers/tty/serial/sunzilog.h +SPARSE CHECKER +M: "Christopher Li" <sparse@chrisli.org> +L: linux-sparse@vger.kernel.org +W: https://sparse.wiki.kernel.org/ +T: git git://git.kernel.org/pub/scm/devel/sparse/sparse.git +T: git git://git.kernel.org/pub/scm/devel/sparse/chrisl/sparse.git +S: Maintained +F: include/linux/compiler.h + SPEAR PLATFORM SUPPORT M: Viresh Kumar <viresh.kumar@st.com> L: spear-devel@list.st.com @@ -6468,6 +6472,7 @@ S: Odd Fixes F: drivers/staging/olpc_dcon/ STAGING - OZMO DEVICES USB OVER WIFI DRIVER +M: Rupesh Gujare <rgujare@ozmodevices.com> M: Chris Kelly <ckelly@ozmodevices.com> S: Maintained F: drivers/staging/ozwpan/ @@ -6665,6 +6670,12 @@ F: drivers/misc/tifm* F: drivers/mmc/host/tifm_sd.c F: include/linux/tifm.h +TI LM49xxx FAMILY ASoC CODEC DRIVERS +M: M R Swami Reddy <mr.swami.reddy@ti.com> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: sound/soc/codecs/lm49453* + TI TWL4030 SERIES SOC CODEC DRIVER M: Peter Ujfalusi <peter.ujfalusi@ti.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) @@ -7574,8 +7585,8 @@ F: Documentation/filesystems/xfs.txt F: fs/xfs/ XILINX AXI ETHERNET DRIVER -M: Ariane Keller <ariane.keller@tik.ee.ethz.ch> -M: Daniel Borkmann <daniel.borkmann@tik.ee.ethz.ch> +M: Anirudha Sarangi <anirudh@xilinx.com> +M: John Linn <John.Linn@xilinx.com> S: Maintained F: drivers/net/ethernet/xilinx/xilinx_axienet* @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 4 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc7 NAME = Saber-toothed Squirrel # *DOCUMENTATION* diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 56a4df952fb0..22e58a99f38b 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -477,7 +477,7 @@ config ALPHA_BROKEN_IRQ_MASK config VGA_HOSE bool - depends on ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL || ALPHA_TSUNAMI + depends on VGA_CONSOLE && (ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL || ALPHA_TSUNAMI) default y help Support VGA on an arbitrary hose; needed for several platforms diff --git a/arch/alpha/include/asm/atomic.h b/arch/alpha/include/asm/atomic.h index f62251e82ffa..3bb7ffeae3bc 100644 --- a/arch/alpha/include/asm/atomic.h +++ b/arch/alpha/include/asm/atomic.h @@ -3,6 +3,7 @@ #include <linux/types.h> #include <asm/barrier.h> +#include <asm/cmpxchg.h> /* * Atomic operations that C can't guarantee us. Useful for @@ -168,73 +169,6 @@ static __inline__ long atomic64_sub_return(long i, atomic64_t * v) return result; } -/* - * Atomic exchange routines. - */ - -#define __ASM__MB -#define ____xchg(type, args...) __xchg ## type ## _local(args) -#define ____cmpxchg(type, args...) __cmpxchg ## type ## _local(args) -#include <asm/xchg.h> - -#define xchg_local(ptr,x) \ - ({ \ - __typeof__(*(ptr)) _x_ = (x); \ - (__typeof__(*(ptr))) __xchg_local((ptr), (unsigned long)_x_, \ - sizeof(*(ptr))); \ - }) - -#define cmpxchg_local(ptr, o, n) \ - ({ \ - __typeof__(*(ptr)) _o_ = (o); \ - __typeof__(*(ptr)) _n_ = (n); \ - (__typeof__(*(ptr))) __cmpxchg_local((ptr), (unsigned long)_o_, \ - (unsigned long)_n_, \ - sizeof(*(ptr))); \ - }) - -#define cmpxchg64_local(ptr, o, n) \ - ({ \ - BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ - cmpxchg_local((ptr), (o), (n)); \ - }) - -#ifdef CONFIG_SMP -#undef __ASM__MB -#define __ASM__MB "\tmb\n" -#endif -#undef ____xchg -#undef ____cmpxchg -#define ____xchg(type, args...) __xchg ##type(args) -#define ____cmpxchg(type, args...) __cmpxchg ##type(args) -#include <asm/xchg.h> - -#define xchg(ptr,x) \ - ({ \ - __typeof__(*(ptr)) _x_ = (x); \ - (__typeof__(*(ptr))) __xchg((ptr), (unsigned long)_x_, \ - sizeof(*(ptr))); \ - }) - -#define cmpxchg(ptr, o, n) \ - ({ \ - __typeof__(*(ptr)) _o_ = (o); \ - __typeof__(*(ptr)) _n_ = (n); \ - (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ - (unsigned long)_n_, sizeof(*(ptr)));\ - }) - -#define cmpxchg64(ptr, o, n) \ - ({ \ - BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ - cmpxchg((ptr), (o), (n)); \ - }) - -#undef __ASM__MB -#undef ____cmpxchg - -#define __HAVE_ARCH_CMPXCHG 1 - #define atomic64_cmpxchg(v, old, new) (cmpxchg(&((v)->counter), old, new)) #define atomic64_xchg(v, new) (xchg(&((v)->counter), new)) diff --git a/arch/alpha/include/asm/cmpxchg.h b/arch/alpha/include/asm/cmpxchg.h new file mode 100644 index 000000000000..429e8cd0d78e --- /dev/null +++ b/arch/alpha/include/asm/cmpxchg.h @@ -0,0 +1,71 @@ +#ifndef _ALPHA_CMPXCHG_H +#define _ALPHA_CMPXCHG_H + +/* + * Atomic exchange routines. + */ + +#define __ASM__MB +#define ____xchg(type, args...) __xchg ## type ## _local(args) +#define ____cmpxchg(type, args...) __cmpxchg ## type ## _local(args) +#include <asm/xchg.h> + +#define xchg_local(ptr, x) \ +({ \ + __typeof__(*(ptr)) _x_ = (x); \ + (__typeof__(*(ptr))) __xchg_local((ptr), (unsigned long)_x_, \ + sizeof(*(ptr))); \ +}) + +#define cmpxchg_local(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg_local((ptr), (unsigned long)_o_, \ + (unsigned long)_n_, \ + sizeof(*(ptr))); \ +}) + +#define cmpxchg64_local(ptr, o, n) \ +({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ +}) + +#ifdef CONFIG_SMP +#undef __ASM__MB +#define __ASM__MB "\tmb\n" +#endif +#undef ____xchg +#undef ____cmpxchg +#define ____xchg(type, args...) __xchg ##type(args) +#define ____cmpxchg(type, args...) __cmpxchg ##type(args) +#include <asm/xchg.h> + +#define xchg(ptr, x) \ +({ \ + __typeof__(*(ptr)) _x_ = (x); \ + (__typeof__(*(ptr))) __xchg((ptr), (unsigned long)_x_, \ + sizeof(*(ptr))); \ +}) + +#define cmpxchg(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ + (unsigned long)_n_, sizeof(*(ptr)));\ +}) + +#define cmpxchg64(ptr, o, n) \ +({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg((ptr), (o), (n)); \ +}) + +#undef __ASM__MB +#undef ____cmpxchg + +#define __HAVE_ARCH_CMPXCHG 1 + +#endif /* _ALPHA_CMPXCHG_H */ diff --git a/arch/alpha/include/asm/dma-mapping.h b/arch/alpha/include/asm/dma-mapping.h index 4567aca6fdd6..dfa32f061320 100644 --- a/arch/alpha/include/asm/dma-mapping.h +++ b/arch/alpha/include/asm/dma-mapping.h @@ -12,16 +12,22 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev) #include <asm-generic/dma-mapping-common.h> -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs) { - return get_dma_ops(dev)->alloc_coherent(dev, size, dma_handle, gfp); + return get_dma_ops(dev)->alloc(dev, size, dma_handle, gfp, attrs); } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { - get_dma_ops(dev)->free_coherent(dev, size, vaddr, dma_handle); + get_dma_ops(dev)->free(dev, size, vaddr, dma_handle, attrs); } static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) diff --git a/arch/alpha/include/asm/rtc.h b/arch/alpha/include/asm/rtc.h index 1f7fba671ae6..d70408d36677 100644 --- a/arch/alpha/include/asm/rtc.h +++ b/arch/alpha/include/asm/rtc.h @@ -1,14 +1,10 @@ #ifndef _ALPHA_RTC_H #define _ALPHA_RTC_H -#if defined(CONFIG_ALPHA_GENERIC) +#if defined(CONFIG_ALPHA_MARVEL) && defined(CONFIG_SMP) \ + || defined(CONFIG_ALPHA_GENERIC) # define get_rtc_time alpha_mv.rtc_get_time # define set_rtc_time alpha_mv.rtc_set_time -#else -# if defined(CONFIG_ALPHA_MARVEL) && defined(CONFIG_SMP) -# define get_rtc_time marvel_get_rtc_time -# define set_rtc_time marvel_set_rtc_time -# endif #endif #include <asm-generic/rtc.h> diff --git a/arch/alpha/include/asm/xchg.h b/arch/alpha/include/asm/xchg.h index 1d1b436fbff2..0ca9724597c1 100644 --- a/arch/alpha/include/asm/xchg.h +++ b/arch/alpha/include/asm/xchg.h @@ -1,10 +1,10 @@ -#ifndef _ALPHA_ATOMIC_H +#ifndef _ALPHA_CMPXCHG_H #error Do not include xchg.h directly! #else /* * xchg/xchg_local and cmpxchg/cmpxchg_local share the same code * except that local version do not have the expensive memory barrier. - * So this file is included twice from asm/system.h. + * So this file is included twice from asm/cmpxchg.h. */ /* diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c index 5e7c28f92f19..61893d7bdda5 100644 --- a/arch/alpha/kernel/core_tsunami.c +++ b/arch/alpha/kernel/core_tsunami.c @@ -11,6 +11,7 @@ #include <asm/core_tsunami.h> #undef __EXTERN_INLINE +#include <linux/module.h> #include <linux/types.h> #include <linux/pci.h> #include <linux/sched.h> diff --git a/arch/alpha/kernel/pci-noop.c b/arch/alpha/kernel/pci-noop.c index 04eea4894ef3..df24b76f9246 100644 --- a/arch/alpha/kernel/pci-noop.c +++ b/arch/alpha/kernel/pci-noop.c @@ -108,7 +108,8 @@ sys_pciconfig_write(unsigned long bus, unsigned long dfn, } static void *alpha_noop_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs) { void *ret; @@ -123,7 +124,8 @@ static void *alpha_noop_alloc_coherent(struct device *dev, size_t size, } static void alpha_noop_free_coherent(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_addr) + void *cpu_addr, dma_addr_t dma_addr, + struct dma_attrs *attrs) { free_pages((unsigned long)cpu_addr, get_order(size)); } @@ -174,8 +176,8 @@ static int alpha_noop_set_mask(struct device *dev, u64 mask) } struct dma_map_ops alpha_noop_ops = { - .alloc_coherent = alpha_noop_alloc_coherent, - .free_coherent = alpha_noop_free_coherent, + .alloc = alpha_noop_alloc_coherent, + .free = alpha_noop_free_coherent, .map_page = alpha_noop_map_page, .map_sg = alpha_noop_map_sg, .mapping_error = alpha_noop_mapping_error, diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c index 43610804987d..cd634795aa9c 100644 --- a/arch/alpha/kernel/pci_iommu.c +++ b/arch/alpha/kernel/pci_iommu.c @@ -434,7 +434,8 @@ static void alpha_pci_unmap_page(struct device *dev, dma_addr_t dma_addr, else DMA_ADDRP is undefined. */ static void *alpha_pci_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_addrp, gfp_t gfp) + dma_addr_t *dma_addrp, gfp_t gfp, + struct dma_attrs *attrs) { struct pci_dev *pdev = alpha_gendev_to_pci(dev); void *cpu_addr; @@ -478,7 +479,8 @@ try_again: DMA_ADDR past this call are illegal. */ static void alpha_pci_free_coherent(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_addr) + void *cpu_addr, dma_addr_t dma_addr, + struct dma_attrs *attrs) { struct pci_dev *pdev = alpha_gendev_to_pci(dev); pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); @@ -952,8 +954,8 @@ static int alpha_pci_set_mask(struct device *dev, u64 mask) } struct dma_map_ops alpha_pci_ops = { - .alloc_coherent = alpha_pci_alloc_coherent, - .free_coherent = alpha_pci_free_coherent, + .alloc = alpha_pci_alloc_coherent, + .free = alpha_pci_free_coherent, .map_page = alpha_pci_map_page, .unmap_page = alpha_pci_unmap_page, .map_sg = alpha_pci_map_sg, diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index 6f7feb5db271..35f2ef44de12 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -120,12 +120,13 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig, const struct sigaction __user *, act, */ SYSCALL_DEFINE1(sigsuspend, old_sigset_t, mask) { - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); + sigset_t blocked; + current->saved_sigmask = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + + mask &= _BLOCKABLE; + siginitset(&blocked, mask); + set_current_blocked(&blocked); current->state = TASK_INTERRUPTIBLE; schedule(); @@ -238,10 +239,7 @@ do_sigreturn(struct sigcontext __user *sc, struct pt_regs *regs, goto give_sigsegv; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); if (restore_sigcontext(sc, regs, sw)) goto give_sigsegv; @@ -276,10 +274,7 @@ do_rt_sigreturn(struct rt_sigframe __user *frame, struct pt_regs *regs, goto give_sigsegv; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); if (restore_sigcontext(&frame->uc.uc_mcontext, regs, sw)) goto give_sigsegv; @@ -501,14 +496,8 @@ handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, else ret = setup_frame(sig, ka, oldset, regs, sw); - if (ret == 0) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } + if (ret == 0) + block_sigmask(ka, sig); return ret; } diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 4087a569b43b..50d438db1f6b 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -450,7 +450,7 @@ setup_smp(void) smp_num_probed = 1; } - printk(KERN_INFO "SMP: %d CPUs probed -- cpu_present_map = %lx\n", + printk(KERN_INFO "SMP: %d CPUs probed -- cpu_present_mask = %lx\n", smp_num_probed, cpumask_bits(cpu_present_mask)[0]); } diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c index 14a4b6a7cf59..407accc80877 100644 --- a/arch/alpha/kernel/sys_marvel.c +++ b/arch/alpha/kernel/sys_marvel.c @@ -317,7 +317,7 @@ marvel_init_irq(void) } static int -marvel_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +marvel_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { struct pci_controller *hose = dev->sysdata; struct io7_port *io7_port = hose->sysdata; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 93180845ae16..36586dba6fa6 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -338,6 +338,7 @@ config ARCH_AT91 select HAVE_CLK select CLKDEV_LOOKUP select IRQ_DOMAIN + select NEED_MACH_IO_H if PCCARD help This enables support for systems based on the Atmel AT91RM9200, AT91SAM9 processors. @@ -1185,6 +1186,15 @@ if !MMU source "arch/arm/Kconfig-nommu" endif +config ARM_ERRATA_326103 + bool "ARM errata: FSR write bit incorrect on a SWP to read-only memory" + depends on CPU_V6 + help + Executing a SWP instruction to read-only memory does not set bit 11 + of the FSR on the ARM 1136 prior to r1p0. This causes the kernel to + treat the access as a read, preventing a COW from occurring and + causing the faulting task to livelock. + config ARM_ERRATA_411920 bool "ARM errata: Invalidation of the Instruction Cache operation can fail" depends on CPU_V6 || CPU_V6K diff --git a/arch/arm/boot/compressed/atags_to_fdt.c b/arch/arm/boot/compressed/atags_to_fdt.c index 6ce11c481178..797f04bedb47 100644 --- a/arch/arm/boot/compressed/atags_to_fdt.c +++ b/arch/arm/boot/compressed/atags_to_fdt.c @@ -77,6 +77,8 @@ int atags_to_fdt(void *atag_list, void *fdt, int total_space) } else if (atag->hdr.tag == ATAG_MEM) { if (memcount >= sizeof(mem_reg_property)/4) continue; + if (!atag->u.mem.size) + continue; mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.start); mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.size); } else if (atag->hdr.tag == ATAG_INITRD2) { diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 5f6045f1766c..dc7e8ce8e6be 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -273,7 +273,7 @@ restart: adr r0, LC0 add r0, r0, #0x100 mov r1, r6 sub r2, sp, r6 - blne atags_to_fdt + bleq atags_to_fdt ldmfd sp!, {r0-r3, ip, lr} sub sp, sp, #0x10000 diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index 92f36627e7f8..773ef484037a 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -35,7 +35,7 @@ }; }; - memory@20000000 { + memory { reg = <0x20000000 0x08000000>; }; @@ -55,7 +55,6 @@ #interrupt-cells = <2>; compatible = "atmel,at91rm9200-aic"; interrupt-controller; - interrupt-parent; reg = <0xfffff000 0x200>; }; diff --git a/arch/arm/boot/dts/at91sam9g25ek.dts b/arch/arm/boot/dts/at91sam9g25ek.dts index ac0dc0031dda..7829a4d0cb22 100644 --- a/arch/arm/boot/dts/at91sam9g25ek.dts +++ b/arch/arm/boot/dts/at91sam9g25ek.dts @@ -37,8 +37,8 @@ usb0: ohci@00600000 { status = "okay"; num-ports = <2>; - atmel,vbus-gpio = <&pioD 19 0 - &pioD 20 0 + atmel,vbus-gpio = <&pioD 19 1 + &pioD 20 1 >; }; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 3d0c32fb218f..c8042147eaa2 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -36,7 +36,7 @@ }; }; - memory@70000000 { + memory { reg = <0x70000000 0x10000000>; }; @@ -56,7 +56,6 @@ #interrupt-cells = <2>; compatible = "atmel,at91rm9200-aic"; interrupt-controller; - interrupt-parent; reg = <0xfffff000 0x200>; }; diff --git a/arch/arm/boot/dts/at91sam9m10g45ek.dts b/arch/arm/boot/dts/at91sam9m10g45ek.dts index c4c8ae4123d5..a3633bd13111 100644 --- a/arch/arm/boot/dts/at91sam9m10g45ek.dts +++ b/arch/arm/boot/dts/at91sam9m10g45ek.dts @@ -17,7 +17,7 @@ bootargs = "mem=64M console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2"; }; - memory@70000000 { + memory { reg = <0x70000000 0x4000000>; }; @@ -73,8 +73,8 @@ usb0: ohci@00700000 { status = "okay"; num-ports = <2>; - atmel,vbus-gpio = <&pioD 1 0 - &pioD 3 0>; + atmel,vbus-gpio = <&pioD 1 1 + &pioD 3 1>; }; usb1: ehci@00800000 { diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index c111001f254e..dd4ed748469a 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -34,7 +34,7 @@ }; }; - memory@20000000 { + memory { reg = <0x20000000 0x10000000>; }; @@ -54,7 +54,6 @@ #interrupt-cells = <2>; compatible = "atmel,at91rm9200-aic"; interrupt-controller; - interrupt-parent; reg = <0xfffff000 0x200>; }; @@ -201,8 +200,8 @@ >; atmel,nand-addr-offset = <21>; atmel,nand-cmd-offset = <22>; - gpios = <&pioC 8 0 - &pioC 14 0 + gpios = <&pioD 5 0 + &pioD 4 0 0 >; status = "disabled"; diff --git a/arch/arm/boot/dts/at91sam9x5cm.dtsi b/arch/arm/boot/dts/at91sam9x5cm.dtsi index 67936f83c694..31e7be23703d 100644 --- a/arch/arm/boot/dts/at91sam9x5cm.dtsi +++ b/arch/arm/boot/dts/at91sam9x5cm.dtsi @@ -8,7 +8,7 @@ */ / { - memory@20000000 { + memory { reg = <0x20000000 0x8000000>; }; diff --git a/arch/arm/boot/dts/db8500.dtsi b/arch/arm/boot/dts/db8500.dtsi index d73dce645667..14bc30705099 100644 --- a/arch/arm/boot/dts/db8500.dtsi +++ b/arch/arm/boot/dts/db8500.dtsi @@ -24,7 +24,6 @@ #interrupt-cells = <3>; #address-cells = <1>; interrupt-controller; - interrupt-parent; reg = <0xa0411000 0x1000>, <0xa0410100 0x100>; }; diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 37c0ff9c8b90..83e72294aefb 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -89,7 +89,6 @@ #size-cells = <0>; #address-cells = <1>; interrupt-controller; - interrupt-parent; reg = <0xfff11000 0x1000>, <0xfff10100 0x100>; }; diff --git a/arch/arm/boot/dts/msm8660-surf.dts b/arch/arm/boot/dts/msm8660-surf.dts index 15ded0deaa79..45bc4bb04e57 100644 --- a/arch/arm/boot/dts/msm8660-surf.dts +++ b/arch/arm/boot/dts/msm8660-surf.dts @@ -10,7 +10,7 @@ intc: interrupt-controller@02080000 { compatible = "qcom,msm-8660-qgic"; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <3>; reg = < 0x02080000 0x1000 >, < 0x02081000 0x1000 >; }; @@ -19,6 +19,6 @@ compatible = "qcom,msm-hsuart", "qcom,msm-uart"; reg = <0x19c40000 0x1000>, <0x19c00000 0x1000>; - interrupts = <195>; + interrupts = <0 195 0x0>; }; }; diff --git a/arch/arm/boot/dts/usb_a9g20.dts b/arch/arm/boot/dts/usb_a9g20.dts index 3b3c4e0fa79f..7c2399c532e5 100644 --- a/arch/arm/boot/dts/usb_a9g20.dts +++ b/arch/arm/boot/dts/usb_a9g20.dts @@ -16,7 +16,7 @@ bootargs = "mem=64M console=ttyS0,115200 root=/dev/mtdblock5 rw rootfstype=ubifs"; }; - memory@20000000 { + memory { reg = <0x20000000 0x4000000>; }; diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts index 0b32925f2147..e2fe3195c0d1 100644 --- a/arch/arm/boot/dts/versatile-ab.dts +++ b/arch/arm/boot/dts/versatile-ab.dts @@ -173,7 +173,7 @@ mmc@5000 { compatible = "arm,primecell"; reg = < 0x5000 0x1000>; - interrupts = <22>; + interrupts = <22 34>; }; kmi@6000 { compatible = "arm,pl050", "arm,primecell"; diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts index 166461073b78..7e8175269064 100644 --- a/arch/arm/boot/dts/versatile-pb.dts +++ b/arch/arm/boot/dts/versatile-pb.dts @@ -41,7 +41,7 @@ mmc@b000 { compatible = "arm,primecell"; reg = <0xb000 0x1000>; - interrupts = <23>; + interrupts = <23 34>; }; }; }; diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index 7a66311f3066..7e288f96cedf 100644 --- a/arch/arm/common/vic.c +++ b/arch/arm/common/vic.c @@ -427,19 +427,18 @@ int __init vic_of_init(struct device_node *node, struct device_node *parent) /* * Handle each interrupt in a single VIC. Returns non-zero if we've - * handled at least one interrupt. This does a single read of the - * status register and handles all interrupts in order from LSB first. + * handled at least one interrupt. This reads the status register + * before handling each interrupt, which is necessary given that + * handle_IRQ may briefly re-enable interrupts for soft IRQ handling. */ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) { u32 stat, irq; int handled = 0; - stat = readl_relaxed(vic->base + VIC_IRQ_STATUS); - while (stat) { + while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) { irq = ffs(stat) - 1; handle_IRQ(irq_find_mapping(vic->domain, irq), regs); - stat &= ~(1 << irq); handled = 1; } diff --git a/arch/arm/configs/imx_v4_v5_defconfig b/arch/arm/configs/imx_v4_v5_defconfig index b5ac644e12af..6b31cb60daab 100644 --- a/arch/arm/configs/imx_v4_v5_defconfig +++ b/arch/arm/configs/imx_v4_v5_defconfig @@ -112,6 +112,7 @@ CONFIG_WATCHDOG=y CONFIG_IMX2_WDT=y CONFIG_MFD_MC13XXX=y CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_MC13783=y CONFIG_REGULATOR_MC13892=y CONFIG_FB=y diff --git a/arch/arm/configs/mini2440_defconfig b/arch/arm/configs/mini2440_defconfig index 42da9183acc8..082175c54e7c 100644 --- a/arch/arm/configs/mini2440_defconfig +++ b/arch/arm/configs/mini2440_defconfig @@ -14,6 +14,8 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set CONFIG_BLK_DEV_INTEGRITY=y CONFIG_ARCH_S3C24XX=y +# CONFIG_CPU_S3C2410 is not set +CONFIG_CPU_S3C2440=y CONFIG_S3C_ADC=y CONFIG_S3C24XX_PWM=y CONFIG_MACH_MINI2440=y diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index 889d73ac1ae1..7e84f453e8a6 100644 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -8,8 +8,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_LBDAF is not set # CONFIG_BLK_DEV_BSG is not set CONFIG_ARCH_U8500=y -CONFIG_UX500_SOC_DB5500=y -CONFIG_UX500_SOC_DB8500=y CONFIG_MACH_HREFV60=y CONFIG_MACH_SNOWBALL=y CONFIG_MACH_U5500=y @@ -39,7 +37,6 @@ CONFIG_CAIF=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=65536 -CONFIG_MISC_DEVICES=y CONFIG_AB8500_PWM=y CONFIG_SENSORS_BH1780=y CONFIG_NETDEVICES=y @@ -65,16 +62,18 @@ CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_NOMADIK=y -CONFIG_I2C=y -CONFIG_I2C_NOMADIK=y CONFIG_SPI=y CONFIG_SPI_PL022=y CONFIG_GPIO_STMPE=y CONFIG_GPIO_TC3589X=y +CONFIG_POWER_SUPPLY=y +CONFIG_AB8500_BM=y +CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL=y CONFIG_MFD_STMPE=y CONFIG_MFD_TC3589X=y CONFIG_AB5500_CORE=y CONFIG_AB8500_CORE=y +CONFIG_REGULATOR=y CONFIG_REGULATOR_AB8500=y # CONFIG_HID_SUPPORT is not set CONFIG_USB_GADGET=y diff --git a/arch/arm/include/asm/barrier.h b/arch/arm/include/asm/barrier.h index 44f4a09ff37b..05112380dc53 100644 --- a/arch/arm/include/asm/barrier.h +++ b/arch/arm/include/asm/barrier.h @@ -2,6 +2,7 @@ #define __ASM_BARRIER_H #ifndef __ASSEMBLY__ +#include <asm/outercache.h> #define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t"); @@ -39,7 +40,6 @@ #ifdef CONFIG_ARCH_HAS_BARRIERS #include <mach/barriers.h> #elif defined(CONFIG_ARM_DMA_MEM_BUFFERABLE) || defined(CONFIG_SMP) -#include <asm/outercache.h> #define mb() do { dsb(); outer_sync(); } while (0) #define rmb() dsb() #define wmb() mb() diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index df0ac0bb39aa..9af5563dd3eb 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -119,7 +119,7 @@ static inline void __iomem *__typesafe_io(unsigned long addr) #ifdef CONFIG_NEED_MACH_IO_H #include <mach/io.h> #else -#define __io(a) ({ (void)(a); __typesafe_io(0); }) +#define __io(a) __typesafe_io((a) & IO_SPACE_LIMIT) #endif /* diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h index 5c5ca2ea62b0..bfc198c75913 100644 --- a/arch/arm/include/asm/jump_label.h +++ b/arch/arm/include/asm/jump_label.h @@ -14,7 +14,7 @@ #define JUMP_LABEL_NOP "nop" #endif -static __always_inline bool arch_static_branch(struct jump_label_key *key) +static __always_inline bool arch_static_branch(struct static_key *key) { asm goto("1:\n\t" JUMP_LABEL_NOP "\n\t" diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index d4c24d412a8d..0f04d84582e1 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -118,6 +118,13 @@ extern void iwmmxt_task_switch(struct thread_info *); extern void vfp_sync_hwstate(struct thread_info *); extern void vfp_flush_hwstate(struct thread_info *); +struct user_vfp; +struct user_vfp_exc; + +extern int vfp_preserve_user_clear_hwstate(struct user_vfp __user *, + struct user_vfp_exc __user *); +extern int vfp_restore_user_hwstate(struct user_vfp __user *, + struct user_vfp_exc __user *); #endif /* diff --git a/arch/arm/include/asm/tls.h b/arch/arm/include/asm/tls.h index 60843eb0f61c..73409e6c0251 100644 --- a/arch/arm/include/asm/tls.h +++ b/arch/arm/include/asm/tls.h @@ -7,6 +7,8 @@ .macro set_tls_v6k, tp, tmp1, tmp2 mcr p15, 0, \tp, c13, c0, 3 @ set TLS register + mov \tmp1, #0 + mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register .endm .macro set_tls_v6, tp, tmp1, tmp2 @@ -15,6 +17,8 @@ mov \tmp2, #0xffff0fff tst \tmp1, #HWCAP_TLS @ hardware TLS available? mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register + movne \tmp1, #0 + mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 632df9a66f8c..ede5f7741c42 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -299,7 +299,6 @@ static inline int pdev_bad_for_parity(struct pci_dev *dev) */ void pcibios_fixup_bus(struct pci_bus *bus) { - struct pci_sys_data *root = bus->sysdata; struct pci_dev *dev; u16 features = PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_FAST_BACK; diff --git a/arch/arm/kernel/insn.c b/arch/arm/kernel/insn.c index ab312e516546..b760340b7014 100644 --- a/arch/arm/kernel/insn.c +++ b/arch/arm/kernel/insn.c @@ -1,3 +1,4 @@ +#include <linux/bug.h> #include <linux/kernel.h> #include <asm/opcodes.h> diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 71ccdbfed662..8349d4e97e2b 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -155,10 +155,10 @@ static bool migrate_one_irq(struct irq_desc *desc) } c = irq_data_get_irq_chip(d); - if (c->irq_set_affinity) - c->irq_set_affinity(d, affinity, true); - else + if (!c->irq_set_affinity) pr_debug("IRQ%u: unable to set affinity\n", d->irq); + else if (c->irq_set_affinity(d, affinity, true) == IRQ_SET_MASK_OK && ret) + cpumask_copy(d->affinity, affinity); return ret; } diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c index ab1869dac97a..4dd41fc9e235 100644 --- a/arch/arm/kernel/kprobes.c +++ b/arch/arm/kernel/kprobes.c @@ -152,7 +152,7 @@ int __kprobes __arch_disarm_kprobe(void *p) void __kprobes arch_disarm_kprobe(struct kprobe *p) { - stop_machine(__arch_disarm_kprobe, p, &cpu_online_map); + stop_machine(__arch_disarm_kprobe, p, cpu_online_mask); } void __kprobes arch_remove_kprobe(struct kprobe *p) diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 45956c9d0ef0..9650c143afc1 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -256,7 +256,7 @@ static int ptrace_read_user(struct task_struct *tsk, unsigned long off, { unsigned long tmp; - if (off & 3 || off >= sizeof(struct user)) + if (off & 3) return -EIO; tmp = 0; @@ -268,6 +268,8 @@ static int ptrace_read_user(struct task_struct *tsk, unsigned long off, tmp = tsk->mm->end_code; else if (off < sizeof(struct pt_regs)) tmp = get_user_reg(tsk, off >> 2); + else if (off >= sizeof(struct user)) + return -EIO; return put_user(tmp, ret); } @@ -904,27 +906,14 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } -#ifdef __ARMEB__ -#define AUDIT_ARCH_NR AUDIT_ARCH_ARMEB -#else -#define AUDIT_ARCH_NR AUDIT_ARCH_ARM -#endif - asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) { unsigned long ip; - /* - * Save IP. IP is used to denote syscall entry/exit: - * IP = 0 -> entry, = 1 -> exit - */ - ip = regs->ARM_ip; - regs->ARM_ip = why; - - if (!ip) + if (why) audit_syscall_exit(regs); else - audit_syscall_entry(AUDIT_ARCH_NR, scno, regs->ARM_r0, + audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); if (!test_thread_flag(TIF_SYSCALL_TRACE)) @@ -934,6 +923,13 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) current_thread_info()->syscall = scno; + /* + * IP is used to denote syscall entry/exit: + * IP = 0 -> entry, =1 -> exit + */ + ip = regs->ARM_ip; + regs->ARM_ip = why; + /* the 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index b91411371ae1..ebfac782593f 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -523,7 +523,21 @@ int __init arm_add_memory(phys_addr_t start, unsigned long size) */ size -= start & ~PAGE_MASK; bank->start = PAGE_ALIGN(start); - bank->size = size & PAGE_MASK; + +#ifndef CONFIG_LPAE + if (bank->start + size < bank->start) { + printk(KERN_CRIT "Truncating memory at 0x%08llx to fit in " + "32-bit physical address space\n", (long long)start); + /* + * To ensure bank->start + bank->size is representable in + * 32 bits, we use ULONG_MAX as the upper limit rather than 4GB. + * This means we lose a page after masking. + */ + size = ULONG_MAX - bank->start; + } +#endif + + bank->size = size & PAGE_MASK; /* * Check whether this memory region has non-zero size or diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 7cb532fc8aa4..d68d1b694680 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -180,44 +180,23 @@ static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) static int preserve_vfp_context(struct vfp_sigframe __user *frame) { - struct thread_info *thread = current_thread_info(); - struct vfp_hard_struct *h = &thread->vfpstate.hard; const unsigned long magic = VFP_MAGIC; const unsigned long size = VFP_STORAGE_SIZE; int err = 0; - vfp_sync_hwstate(thread); __put_user_error(magic, &frame->magic, err); __put_user_error(size, &frame->size, err); - /* - * Copy the floating point registers. There can be unused - * registers see asm/hwcap.h for details. - */ - err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs, - sizeof(h->fpregs)); - /* - * Copy the status and control register. - */ - __put_user_error(h->fpscr, &frame->ufp.fpscr, err); - - /* - * Copy the exception registers. - */ - __put_user_error(h->fpexc, &frame->ufp_exc.fpexc, err); - __put_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); - __put_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); + if (err) + return -EFAULT; - return err ? -EFAULT : 0; + return vfp_preserve_user_clear_hwstate(&frame->ufp, &frame->ufp_exc); } static int restore_vfp_context(struct vfp_sigframe __user *frame) { - struct thread_info *thread = current_thread_info(); - struct vfp_hard_struct *h = &thread->vfpstate.hard; unsigned long magic; unsigned long size; - unsigned long fpexc; int err = 0; __get_user_error(magic, &frame->magic, err); @@ -228,33 +207,7 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) return -EINVAL; - vfp_flush_hwstate(thread); - - /* - * Copy the floating point registers. There can be unused - * registers see asm/hwcap.h for details. - */ - err |= __copy_from_user(&h->fpregs, &frame->ufp.fpregs, - sizeof(h->fpregs)); - /* - * Copy the status and control register. - */ - __get_user_error(h->fpscr, &frame->ufp.fpscr, err); - - /* - * Sanitise and restore the exception registers. - */ - __get_user_error(fpexc, &frame->ufp_exc.fpexc, err); - /* Ensure the VFP is enabled. */ - fpexc |= FPEXC_EN; - /* Ensure FPINST2 is invalid and the exception flag is cleared. */ - fpexc &= ~(FPEXC_EX | FPEXC_FP2V); - h->fpexc = fpexc; - - __get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); - __get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); - - return err ? -EFAULT : 0; + return vfp_restore_user_hwstate(&frame->ufp, &frame->ufp_exc); } #endif diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 2cee7d1eb958..8f4644659777 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -251,8 +251,6 @@ asmlinkage void __cpuinit secondary_start_kernel(void) struct mm_struct *mm = &init_mm; unsigned int cpu = smp_processor_id(); - printk("CPU%u: Booted secondary processor\n", cpu); - /* * All kernel threads share the same mm context; grab a * reference and switch to it. @@ -264,6 +262,8 @@ asmlinkage void __cpuinit secondary_start_kernel(void) enter_lazy_tlb(mm, current); local_flush_tlb_all(); + printk("CPU%u: Booted secondary processor\n", cpu); + cpu_init(); preempt_disable(); trace_hardirqs_off(); @@ -349,7 +349,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) * re-initialize the map in platform_smp_prepare_cpus() if * present != possible (e.g. physical hotplug). */ - init_cpu_present(&cpu_possible_map); + init_cpu_present(cpu_possible_mask); /* * Initialise the SCU if there are more than one CPU @@ -510,10 +510,6 @@ static void ipi_cpu_stop(unsigned int cpu) local_fiq_disable(); local_irq_disable(); -#ifdef CONFIG_HOTPLUG_CPU - platform_cpu_kill(cpu); -#endif - while (1) cpu_relax(); } @@ -576,16 +572,25 @@ void smp_send_reschedule(int cpu) smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE); } +#ifdef CONFIG_HOTPLUG_CPU +static void smp_kill_cpus(cpumask_t *mask) +{ + unsigned int cpu; + for_each_cpu(cpu, mask) + platform_cpu_kill(cpu); +} +#else +static void smp_kill_cpus(cpumask_t *mask) { } +#endif + void smp_send_stop(void) { unsigned long timeout; + struct cpumask mask; - if (num_online_cpus() > 1) { - cpumask_t mask = cpu_online_map; - cpu_clear(smp_processor_id(), mask); - - smp_cross_call(&mask, IPI_CPU_STOP); - } + cpumask_copy(&mask, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &mask); + smp_cross_call(&mask, IPI_CPU_STOP); /* Wait up to one second for other CPUs to stop */ timeout = USEC_PER_SEC; @@ -594,6 +599,8 @@ void smp_send_stop(void) if (num_online_cpus() > 1) pr_warning("SMP: failed to stop secondary CPUs\n"); + + smp_kill_cpus(&mask); } /* diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index d2b177905cdb..76cbb055dd05 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c @@ -115,7 +115,7 @@ int kernel_execve(const char *filename, "Ir" (THREAD_START_SP - sizeof(regs)), "r" (®s), "Ir" (sizeof(regs)) - : "r0", "r1", "r2", "r3", "ip", "lr", "memory"); + : "r0", "r1", "r2", "r3", "r8", "r9", "ip", "lr", "memory"); out: return ret; diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c index 99ce5c955e39..05774e5b1cba 100644 --- a/arch/arm/mach-at91/at91rm9200_devices.c +++ b/arch/arm/mach-at91/at91rm9200_devices.c @@ -1173,7 +1173,6 @@ void __init at91_add_device_serial(void) printk(KERN_INFO "AT91: No default serial console defined.\n"); } #else -void __init __deprecated at91_init_serial(struct at91_uart_config *config) {} void __init at91_register_uart(unsigned id, unsigned portnr, unsigned pins) {} void __init at91_set_serial_console(unsigned portnr) {} void __init at91_add_device_serial(void) {} diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c index dd7f782b0b91..104ca40d8d18 100644 --- a/arch/arm/mach-at91/at91rm9200_time.c +++ b/arch/arm/mach-at91/at91rm9200_time.c @@ -23,6 +23,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/clockchips.h> +#include <linux/export.h> #include <asm/mach/time.h> @@ -176,6 +177,7 @@ static struct clock_event_device clkevt = { }; void __iomem *at91_st_base; +EXPORT_SYMBOL_GPL(at91_st_base); void __init at91rm9200_ioremap_st(u32 addr) { diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 7e5651ee9f85..5652dde4bbe2 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -598,6 +598,9 @@ void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) else cs_pin = spi1_standard_cs[devices[i].chip_select]; + if (!gpio_is_valid(cs_pin)) + continue; + if (devices[i].bus_num == 0) enable_spi0 = 1; else diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c index 096da87dc00d..4db961a93085 100644 --- a/arch/arm/mach-at91/at91sam9261_devices.c +++ b/arch/arm/mach-at91/at91sam9261_devices.c @@ -415,6 +415,9 @@ void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) else cs_pin = spi1_standard_cs[devices[i].chip_select]; + if (!gpio_is_valid(cs_pin)) + continue; + if (devices[i].bus_num == 0) enable_spi0 = 1; else diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index 53688c46f956..fe99206de880 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c @@ -72,7 +72,8 @@ void __init at91_add_device_usbh(struct at91_usbh_data *data) /* Enable VBus control for UHP ports */ for (i = 0; i < data->ports; i++) { if (gpio_is_valid(data->vbus_pin[i])) - at91_set_gpio_output(data->vbus_pin[i], 0); + at91_set_gpio_output(data->vbus_pin[i], + data->vbus_pin_active_low[i]); } /* Enable overcurrent notification */ @@ -671,6 +672,9 @@ void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) else cs_pin = spi1_standard_cs[devices[i].chip_select]; + if (!gpio_is_valid(cs_pin)) + continue; + if (devices[i].bus_num == 0) enable_spi0 = 1; else diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index 698479f1e197..6b008aee1dff 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -127,12 +127,13 @@ void __init at91_add_device_usbh_ohci(struct at91_usbh_data *data) /* Enable VBus control for UHP ports */ for (i = 0; i < data->ports; i++) { if (gpio_is_valid(data->vbus_pin[i])) - at91_set_gpio_output(data->vbus_pin[i], 0); + at91_set_gpio_output(data->vbus_pin[i], + data->vbus_pin_active_low[i]); } /* Enable overcurrent notification */ for (i = 0; i < data->ports; i++) { - if (data->overcurrent_pin[i]) + if (gpio_is_valid(data->overcurrent_pin[i])) at91_set_gpio_input(data->overcurrent_pin[i], 1); } @@ -188,7 +189,8 @@ void __init at91_add_device_usbh_ehci(struct at91_usbh_data *data) /* Enable VBus control for UHP ports */ for (i = 0; i < data->ports; i++) { if (gpio_is_valid(data->vbus_pin[i])) - at91_set_gpio_output(data->vbus_pin[i], 0); + at91_set_gpio_output(data->vbus_pin[i], + data->vbus_pin_active_low[i]); } usbh_ehci_data = *data; @@ -785,6 +787,9 @@ void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) else cs_pin = spi1_standard_cs[devices[i].chip_select]; + if (!gpio_is_valid(cs_pin)) + continue; + if (devices[i].bus_num == 0) enable_spi0 = 1; else diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c index eda72e83037d..fe4ae22e8561 100644 --- a/arch/arm/mach-at91/at91sam9rl_devices.c +++ b/arch/arm/mach-at91/at91sam9rl_devices.c @@ -419,6 +419,9 @@ void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) else cs_pin = spi_standard_cs[devices[i].chip_select]; + if (!gpio_is_valid(cs_pin)) + continue; + /* enable chip-select pin */ at91_set_gpio_output(cs_pin, 1); diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index b6831eeb7b76..13c8cae60462 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -223,6 +223,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("usart", "f8028000.serial", &usart3_clk), CLKDEV_CON_DEV_ID("t0_clk", "f8008000.timer", &tcb0_clk), CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk), + CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk), + CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk), CLKDEV_CON_ID("pioA", &pioAB_clk), CLKDEV_CON_ID("pioB", &pioAB_clk), CLKDEV_CON_ID("pioC", &pioCD_clk), diff --git a/arch/arm/mach-at91/board-rm9200ek.c b/arch/arm/mach-at91/board-rm9200ek.c index 11cbaa8946fe..b2e4fe21f346 100644 --- a/arch/arm/mach-at91/board-rm9200ek.c +++ b/arch/arm/mach-at91/board-rm9200ek.c @@ -117,7 +117,7 @@ static struct i2c_board_info __initdata ek_i2c_devices[] = { }; #define EK_FLASH_BASE AT91_CHIPSELECT_0 -#define EK_FLASH_SIZE SZ_2M +#define EK_FLASH_SIZE SZ_8M static struct physmap_flash_data ek_flash_data = { .width = 2, diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c index c3f994462864..065fed342424 100644 --- a/arch/arm/mach-at91/board-sam9261ek.c +++ b/arch/arm/mach-at91/board-sam9261ek.c @@ -85,8 +85,6 @@ static struct resource dm9000_resource[] = { .flags = IORESOURCE_MEM }, [2] = { - .start = AT91_PIN_PC11, - .end = AT91_PIN_PC11, .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE, } @@ -130,6 +128,8 @@ static struct sam9_smc_config __initdata dm9000_smc_config = { static void __init ek_add_device_dm9000(void) { + struct resource *r = &dm9000_resource[2]; + /* Configure chip-select 2 (DM9000) */ sam9_smc_configure(0, 2, &dm9000_smc_config); @@ -139,6 +139,7 @@ static void __init ek_add_device_dm9000(void) /* Configure Interrupt pin as input, no pull-up */ at91_set_gpio_input(AT91_PIN_PC11, 0); + r->start = r->end = gpio_to_irq(AT91_PIN_PC11); platform_device_register(&dm9000_device); } #else diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c index 66f0ddf4b2ae..2ffe50f3a9e9 100644 --- a/arch/arm/mach-at91/board-sam9263ek.c +++ b/arch/arm/mach-at91/board-sam9263ek.c @@ -74,6 +74,7 @@ static void __init ek_init_early(void) static struct at91_usbh_data __initdata ek_usbh_data = { .ports = 2, .vbus_pin = { AT91_PIN_PA24, AT91_PIN_PA21 }, + .vbus_pin_active_low = {1, 1}, .overcurrent_pin= {-EINVAL, -EINVAL}, }; diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c index e1bea73e6b30..c88e908ddd82 100644 --- a/arch/arm/mach-at91/board-sam9m10g45ek.c +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c @@ -71,6 +71,7 @@ static void __init ek_init_early(void) static struct at91_usbh_data __initdata ek_usbh_hs_data = { .ports = 2, .vbus_pin = {AT91_PIN_PD1, AT91_PIN_PD3}, + .vbus_pin_active_low = {1, 1}, .overcurrent_pin= {-EINVAL, -EINVAL}, }; diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c index a0f4d7424cdc..6b692824c988 100644 --- a/arch/arm/mach-at91/clock.c +++ b/arch/arm/mach-at91/clock.c @@ -35,6 +35,7 @@ #include "generic.h" void __iomem *at91_pmc_base; +EXPORT_SYMBOL_GPL(at91_pmc_base); /* * There's a lot more which can be done with clocks, including cpufreq diff --git a/arch/arm/mach-at91/include/mach/at91_pmc.h b/arch/arm/mach-at91/include/mach/at91_pmc.h index 36604782a78f..ea2c57a86ca6 100644 --- a/arch/arm/mach-at91/include/mach/at91_pmc.h +++ b/arch/arm/mach-at91/include/mach/at91_pmc.h @@ -25,7 +25,7 @@ extern void __iomem *at91_pmc_base; #define at91_pmc_write(field, value) \ __raw_writel(value, at91_pmc_base + field) #else -.extern at91_aic_base +.extern at91_pmc_base #endif #define AT91_PMC_SCER 0x00 /* System Clock Enable Register */ diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index 544a5d5ce416..49a821192c65 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -86,14 +86,15 @@ extern void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *d extern void __init at91_add_device_eth(struct macb_platform_data *data); /* USB Host */ +#define AT91_MAX_USBH_PORTS 3 struct at91_usbh_data { - u8 ports; /* number of ports on root hub */ - int vbus_pin[2]; /* port power-control pin */ - u8 vbus_pin_active_low[2]; + int vbus_pin[AT91_MAX_USBH_PORTS]; /* port power-control pin */ + int overcurrent_pin[AT91_MAX_USBH_PORTS]; + u8 ports; /* number of ports on root hub */ u8 overcurrent_supported; - int overcurrent_pin[2]; - u8 overcurrent_status[2]; - u8 overcurrent_changed[2]; + u8 vbus_pin_active_low[AT91_MAX_USBH_PORTS]; + u8 overcurrent_status[AT91_MAX_USBH_PORTS]; + u8 overcurrent_changed[AT91_MAX_USBH_PORTS]; }; extern void __init at91_add_device_usbh(struct at91_usbh_data *data); extern void __init at91_add_device_usbh_ohci(struct at91_usbh_data *data); diff --git a/arch/arm/mach-at91/include/mach/io.h b/arch/arm/mach-at91/include/mach/io.h new file mode 100644 index 000000000000..2d9ca0455745 --- /dev/null +++ b/arch/arm/mach-at91/include/mach/io.h @@ -0,0 +1,27 @@ +/* + * arch/arm/mach-at91/include/mach/io.h + * + * Copyright (C) 2003 SAN People + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_ARCH_IO_H +#define __ASM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xFFFFFFFF +#define __io(a) __typesafe_io(a) + +#endif diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 97cc04dc8073..f44a2e7272e3 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -54,6 +54,7 @@ void __init at91_init_interrupts(unsigned int *priority) } void __iomem *at91_ramc_base[2]; +EXPORT_SYMBOL_GPL(at91_ramc_base); void __init at91_ioremap_ramc(int id, u32 addr, u32 size) { @@ -292,6 +293,7 @@ void __init at91_ioremap_rstc(u32 base_addr) } void __iomem *at91_matrix_base; +EXPORT_SYMBOL_GPL(at91_matrix_base); void __init at91_ioremap_matrix(u32 base_addr) { diff --git a/arch/arm/mach-bcmring/core.c b/arch/arm/mach-bcmring/core.c index 22e4e0a28ad1..adbfb1994582 100644 --- a/arch/arm/mach-bcmring/core.c +++ b/arch/arm/mach-bcmring/core.c @@ -52,8 +52,8 @@ #include <mach/csp/chipcHw_inline.h> #include <mach/csp/tmrHw_reg.h> -static AMBA_APB_DEVICE(uartA, "uarta", MM_ADDR_IO_UARTA, { IRQ_UARTA }, NULL); -static AMBA_APB_DEVICE(uartB, "uartb", MM_ADDR_IO_UARTB, { IRQ_UARTB }, NULL); +static AMBA_APB_DEVICE(uartA, "uartA", 0, MM_ADDR_IO_UARTA, {IRQ_UARTA}, NULL); +static AMBA_APB_DEVICE(uartB, "uartB", 0, MM_ADDR_IO_UARTB, {IRQ_UARTB}, NULL); static struct clk pll1_clk = { .name = "PLL1", diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 0491ceef1cda..b8df521fb68e 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -232,6 +232,9 @@ config MACH_ARMLEX4210 config MACH_UNIVERSAL_C210 bool "Mobile UNIVERSAL_C210 Board" select CPU_EXYNOS4210 + select S5P_HRT + select CLKSRC_MMIO + select HAVE_SCHED_CLOCK select S5P_GPIO_INT select S5P_DEV_FIMC0 select S5P_DEV_FIMC1 @@ -368,6 +371,7 @@ comment "Flattened Device Tree based board for EXYNOS SoCs" config MACH_EXYNOS4_DT bool "Samsung Exynos4 Machine using device tree" + depends on ARCH_EXYNOS4 select CPU_EXYNOS4210 select USE_OF select ARM_AMBA @@ -380,6 +384,7 @@ config MACH_EXYNOS4_DT config MACH_EXYNOS5_DT bool "SAMSUNG EXYNOS5 Machine using device tree" + depends on ARCH_EXYNOS5 select SOC_EXYNOS5250 select USE_OF select ARM_AMBA diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index df54c2a92225..6efd1e5919fd 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -497,25 +497,25 @@ static struct clk exynos4_init_clocks_off[] = { .ctrlbit = (1 << 3), }, { .name = "hsmmc", - .devname = "s3c-sdhci.0", + .devname = "exynos4-sdhci.0", .parent = &exynos4_clk_aclk_133.clk, .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 5), }, { .name = "hsmmc", - .devname = "s3c-sdhci.1", + .devname = "exynos4-sdhci.1", .parent = &exynos4_clk_aclk_133.clk, .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 6), }, { .name = "hsmmc", - .devname = "s3c-sdhci.2", + .devname = "exynos4-sdhci.2", .parent = &exynos4_clk_aclk_133.clk, .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 7), }, { .name = "hsmmc", - .devname = "s3c-sdhci.3", + .devname = "exynos4-sdhci.3", .parent = &exynos4_clk_aclk_133.clk, .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 8), @@ -1202,7 +1202,7 @@ static struct clksrc_clk exynos4_clk_sclk_uart3 = { static struct clksrc_clk exynos4_clk_sclk_mmc0 = { .clk = { .name = "sclk_mmc", - .devname = "s3c-sdhci.0", + .devname = "exynos4-sdhci.0", .parent = &exynos4_clk_dout_mmc0.clk, .enable = exynos4_clksrc_mask_fsys_ctrl, .ctrlbit = (1 << 0), @@ -1213,7 +1213,7 @@ static struct clksrc_clk exynos4_clk_sclk_mmc0 = { static struct clksrc_clk exynos4_clk_sclk_mmc1 = { .clk = { .name = "sclk_mmc", - .devname = "s3c-sdhci.1", + .devname = "exynos4-sdhci.1", .parent = &exynos4_clk_dout_mmc1.clk, .enable = exynos4_clksrc_mask_fsys_ctrl, .ctrlbit = (1 << 4), @@ -1224,7 +1224,7 @@ static struct clksrc_clk exynos4_clk_sclk_mmc1 = { static struct clksrc_clk exynos4_clk_sclk_mmc2 = { .clk = { .name = "sclk_mmc", - .devname = "s3c-sdhci.2", + .devname = "exynos4-sdhci.2", .parent = &exynos4_clk_dout_mmc2.clk, .enable = exynos4_clksrc_mask_fsys_ctrl, .ctrlbit = (1 << 8), @@ -1235,7 +1235,7 @@ static struct clksrc_clk exynos4_clk_sclk_mmc2 = { static struct clksrc_clk exynos4_clk_sclk_mmc3 = { .clk = { .name = "sclk_mmc", - .devname = "s3c-sdhci.3", + .devname = "exynos4-sdhci.3", .parent = &exynos4_clk_dout_mmc3.clk, .enable = exynos4_clksrc_mask_fsys_ctrl, .ctrlbit = (1 << 12), @@ -1340,10 +1340,10 @@ static struct clk_lookup exynos4_clk_lookup[] = { CLKDEV_INIT("exynos4210-uart.1", "clk_uart_baud0", &exynos4_clk_sclk_uart1.clk), CLKDEV_INIT("exynos4210-uart.2", "clk_uart_baud0", &exynos4_clk_sclk_uart2.clk), CLKDEV_INIT("exynos4210-uart.3", "clk_uart_baud0", &exynos4_clk_sclk_uart3.clk), - CLKDEV_INIT("s3c-sdhci.0", "mmc_busclk.2", &exynos4_clk_sclk_mmc0.clk), - CLKDEV_INIT("s3c-sdhci.1", "mmc_busclk.2", &exynos4_clk_sclk_mmc1.clk), - CLKDEV_INIT("s3c-sdhci.2", "mmc_busclk.2", &exynos4_clk_sclk_mmc2.clk), - CLKDEV_INIT("s3c-sdhci.3", "mmc_busclk.2", &exynos4_clk_sclk_mmc3.clk), + CLKDEV_INIT("exynos4-sdhci.0", "mmc_busclk.2", &exynos4_clk_sclk_mmc0.clk), + CLKDEV_INIT("exynos4-sdhci.1", "mmc_busclk.2", &exynos4_clk_sclk_mmc1.clk), + CLKDEV_INIT("exynos4-sdhci.2", "mmc_busclk.2", &exynos4_clk_sclk_mmc2.clk), + CLKDEV_INIT("exynos4-sdhci.3", "mmc_busclk.2", &exynos4_clk_sclk_mmc3.clk), CLKDEV_INIT("exynos4-fb.0", "lcd", &exynos4_clk_fimd0), CLKDEV_INIT("dma-pl330.0", "apb_pclk", &exynos4_clk_pdma0), CLKDEV_INIT("dma-pl330.1", "apb_pclk", &exynos4_clk_pdma1), diff --git a/arch/arm/mach-exynos/clock-exynos5.c b/arch/arm/mach-exynos/clock-exynos5.c index d013982d0f8e..7ac6ff4c46bd 100644 --- a/arch/arm/mach-exynos/clock-exynos5.c +++ b/arch/arm/mach-exynos/clock-exynos5.c @@ -455,25 +455,25 @@ static struct clk exynos5_init_clocks_off[] = { .ctrlbit = (1 << 20), }, { .name = "hsmmc", - .devname = "s3c-sdhci.0", + .devname = "exynos4-sdhci.0", .parent = &exynos5_clk_aclk_200.clk, .enable = exynos5_clk_ip_fsys_ctrl, .ctrlbit = (1 << 12), }, { .name = "hsmmc", - .devname = "s3c-sdhci.1", + .devname = "exynos4-sdhci.1", .parent = &exynos5_clk_aclk_200.clk, .enable = exynos5_clk_ip_fsys_ctrl, .ctrlbit = (1 << 13), }, { .name = "hsmmc", - .devname = "s3c-sdhci.2", + .devname = "exynos4-sdhci.2", .parent = &exynos5_clk_aclk_200.clk, .enable = exynos5_clk_ip_fsys_ctrl, .ctrlbit = (1 << 14), }, { .name = "hsmmc", - .devname = "s3c-sdhci.3", + .devname = "exynos4-sdhci.3", .parent = &exynos5_clk_aclk_200.clk, .enable = exynos5_clk_ip_fsys_ctrl, .ctrlbit = (1 << 15), @@ -678,7 +678,7 @@ static struct clk exynos5_clk_pdma1 = { .name = "dma", .devname = "dma-pl330.1", .enable = exynos5_clk_ip_fsys_ctrl, - .ctrlbit = (1 << 1), + .ctrlbit = (1 << 2), }; static struct clk exynos5_clk_mdma1 = { @@ -813,7 +813,7 @@ static struct clksrc_clk exynos5_clk_sclk_uart3 = { static struct clksrc_clk exynos5_clk_sclk_mmc0 = { .clk = { .name = "sclk_mmc", - .devname = "s3c-sdhci.0", + .devname = "exynos4-sdhci.0", .parent = &exynos5_clk_dout_mmc0.clk, .enable = exynos5_clksrc_mask_fsys_ctrl, .ctrlbit = (1 << 0), @@ -824,7 +824,7 @@ static struct clksrc_clk exynos5_clk_sclk_mmc0 = { static struct clksrc_clk exynos5_clk_sclk_mmc1 = { .clk = { .name = "sclk_mmc", - .devname = "s3c-sdhci.1", + .devname = "exynos4-sdhci.1", .parent = &exynos5_clk_dout_mmc1.clk, .enable = exynos5_clksrc_mask_fsys_ctrl, .ctrlbit = (1 << 4), @@ -835,7 +835,7 @@ static struct clksrc_clk exynos5_clk_sclk_mmc1 = { static struct clksrc_clk exynos5_clk_sclk_mmc2 = { .clk = { .name = "sclk_mmc", - .devname = "s3c-sdhci.2", + .devname = "exynos4-sdhci.2", .parent = &exynos5_clk_dout_mmc2.clk, .enable = exynos5_clksrc_mask_fsys_ctrl, .ctrlbit = (1 << 8), @@ -846,7 +846,7 @@ static struct clksrc_clk exynos5_clk_sclk_mmc2 = { static struct clksrc_clk exynos5_clk_sclk_mmc3 = { .clk = { .name = "sclk_mmc", - .devname = "s3c-sdhci.3", + .devname = "exynos4-sdhci.3", .parent = &exynos5_clk_dout_mmc3.clk, .enable = exynos5_clksrc_mask_fsys_ctrl, .ctrlbit = (1 << 12), @@ -990,10 +990,10 @@ static struct clk_lookup exynos5_clk_lookup[] = { CLKDEV_INIT("exynos4210-uart.1", "clk_uart_baud0", &exynos5_clk_sclk_uart1.clk), CLKDEV_INIT("exynos4210-uart.2", "clk_uart_baud0", &exynos5_clk_sclk_uart2.clk), CLKDEV_INIT("exynos4210-uart.3", "clk_uart_baud0", &exynos5_clk_sclk_uart3.clk), - CLKDEV_INIT("s3c-sdhci.0", "mmc_busclk.2", &exynos5_clk_sclk_mmc0.clk), - CLKDEV_INIT("s3c-sdhci.1", "mmc_busclk.2", &exynos5_clk_sclk_mmc1.clk), - CLKDEV_INIT("s3c-sdhci.2", "mmc_busclk.2", &exynos5_clk_sclk_mmc2.clk), - CLKDEV_INIT("s3c-sdhci.3", "mmc_busclk.2", &exynos5_clk_sclk_mmc3.clk), + CLKDEV_INIT("exynos4-sdhci.0", "mmc_busclk.2", &exynos5_clk_sclk_mmc0.clk), + CLKDEV_INIT("exynos4-sdhci.1", "mmc_busclk.2", &exynos5_clk_sclk_mmc1.clk), + CLKDEV_INIT("exynos4-sdhci.2", "mmc_busclk.2", &exynos5_clk_sclk_mmc2.clk), + CLKDEV_INIT("exynos4-sdhci.3", "mmc_busclk.2", &exynos5_clk_sclk_mmc3.clk), CLKDEV_INIT("dma-pl330.0", "apb_pclk", &exynos5_clk_pdma0), CLKDEV_INIT("dma-pl330.1", "apb_pclk", &exynos5_clk_pdma1), CLKDEV_INIT("dma-pl330.2", "apb_pclk", &exynos5_clk_mdma1), diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index e6cc50e94a58..5ccd6e80a607 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -326,6 +326,11 @@ static void __init exynos4_map_io(void) s3c_fimc_setname(2, "exynos4-fimc"); s3c_fimc_setname(3, "exynos4-fimc"); + s3c_sdhci_setname(0, "exynos4-sdhci"); + s3c_sdhci_setname(1, "exynos4-sdhci"); + s3c_sdhci_setname(2, "exynos4-sdhci"); + s3c_sdhci_setname(3, "exynos4-sdhci"); + /* The I2C bus controllers are directly compatible with s3c2440 */ s3c_i2c0_setname("s3c2440-i2c"); s3c_i2c1_setname("s3c2440-i2c"); @@ -344,6 +349,11 @@ static void __init exynos5_map_io(void) s3c_device_i2c0.resource[1].start = EXYNOS5_IRQ_IIC; s3c_device_i2c0.resource[1].end = EXYNOS5_IRQ_IIC; + s3c_sdhci_setname(0, "exynos4-sdhci"); + s3c_sdhci_setname(1, "exynos4-sdhci"); + s3c_sdhci_setname(2, "exynos4-sdhci"); + s3c_sdhci_setname(3, "exynos4-sdhci"); + /* The I2C bus controllers are directly compatible with s3c2440 */ s3c_i2c0_setname("s3c2440-i2c"); s3c_i2c1_setname("s3c2440-i2c"); @@ -537,7 +547,9 @@ void __init exynos5_init_irq(void) { int irq; - gic_init(0, IRQ_PPI(0), S5P_VA_GIC_DIST, S5P_VA_GIC_CPU); +#ifdef CONFIG_OF + of_irq_init(exynos4_dt_irq_match); +#endif for (irq = 0; irq < EXYNOS5_MAX_COMBINER_NR; irq++) { combiner_init(irq, (void __iomem *)S5P_VA_COMBINER(irq), @@ -583,10 +595,11 @@ core_initcall(exynos_core_init); #ifdef CONFIG_CACHE_L2X0 static int __init exynos4_l2x0_cache_init(void) { + int ret; + if (soc_is_exynos5250()) return 0; - int ret; ret = l2x0_of_init(L2_AUX_VAL, L2_AUX_MASK); if (!ret) { l2x0_regs_phys = virt_to_phys(&l2x0_saved_regs); diff --git a/arch/arm/mach-exynos/dev-dwmci.c b/arch/arm/mach-exynos/dev-dwmci.c index b025db4bf602..79035018fb74 100644 --- a/arch/arm/mach-exynos/dev-dwmci.c +++ b/arch/arm/mach-exynos/dev-dwmci.c @@ -16,6 +16,7 @@ #include <linux/dma-mapping.h> #include <linux/platform_device.h> #include <linux/interrupt.h> +#include <linux/ioport.h> #include <linux/mmc/dw_mmc.h> #include <plat/devs.h> @@ -33,16 +34,8 @@ static int exynos4_dwmci_init(u32 slot_id, irq_handler_t handler, void *data) } static struct resource exynos4_dwmci_resource[] = { - [0] = { - .start = EXYNOS4_PA_DWMCI, - .end = EXYNOS4_PA_DWMCI + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_DWMCI, - .end = IRQ_DWMCI, - .flags = IORESOURCE_IRQ, - } + [0] = DEFINE_RES_MEM(EXYNOS4_PA_DWMCI, SZ_4K), + [1] = DEFINE_RES_IRQ(EXYNOS4_IRQ_DWMCI), }; static struct dw_mci_board exynos4_dwci_pdata = { diff --git a/arch/arm/mach-exynos/dma.c b/arch/arm/mach-exynos/dma.c index 3983abee4264..69aaa4503205 100644 --- a/arch/arm/mach-exynos/dma.c +++ b/arch/arm/mach-exynos/dma.c @@ -35,8 +35,6 @@ #include <mach/irqs.h> #include <mach/dma.h> -static u64 dma_dmamask = DMA_BIT_MASK(32); - static u8 exynos4210_pdma0_peri[] = { DMACH_PCM0_RX, DMACH_PCM0_TX, diff --git a/arch/arm/mach-exynos/include/mach/debug-macro.S b/arch/arm/mach-exynos/include/mach/debug-macro.S index 6c857ff0b5d8..e0c86ea475e7 100644 --- a/arch/arm/mach-exynos/include/mach/debug-macro.S +++ b/arch/arm/mach-exynos/include/mach/debug-macro.S @@ -21,10 +21,9 @@ */ .macro addruart, rp, rv, tmp - mov \rp, #0x10000000 - ldr \rp, [\rp, #0x0] - and \rp, \rp, #0xf00000 - teq \rp, #0x500000 @@ EXYNOS5 + mrc p15, 0, \tmp, c0, c0, 0 + and \tmp, \tmp, #0xf0 + teq \tmp, #0xf0 @@ A15 ldreq \rp, =EXYNOS5_PA_UART movne \rp, #EXYNOS4_PA_UART @@ EXYNOS4 ldr \rv, =S3C_VA_UART diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h index 9bee8535d9e0..591e78521a9f 100644 --- a/arch/arm/mach-exynos/include/mach/irqs.h +++ b/arch/arm/mach-exynos/include/mach/irqs.h @@ -212,6 +212,8 @@ #define IRQ_MFC EXYNOS4_IRQ_MFC #define IRQ_SDO EXYNOS4_IRQ_SDO +#define IRQ_I2S0 EXYNOS4_IRQ_I2S0 + #define IRQ_ADC EXYNOS4_IRQ_ADC0 #define IRQ_TC EXYNOS4_IRQ_PEN0 diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h index 024d38ff1718..6e6d11ff352a 100644 --- a/arch/arm/mach-exynos/include/mach/map.h +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -89,6 +89,10 @@ #define EXYNOS4_PA_MDMA1 0x12840000 #define EXYNOS4_PA_PDMA0 0x12680000 #define EXYNOS4_PA_PDMA1 0x12690000 +#define EXYNOS5_PA_MDMA0 0x10800000 +#define EXYNOS5_PA_MDMA1 0x11C10000 +#define EXYNOS5_PA_PDMA0 0x121A0000 +#define EXYNOS5_PA_PDMA1 0x121B0000 #define EXYNOS4_PA_SYSMMU_MDMA 0x10A40000 #define EXYNOS4_PA_SYSMMU_SSS 0x10A50000 diff --git a/arch/arm/mach-exynos/include/mach/regs-clock.h b/arch/arm/mach-exynos/include/mach/regs-clock.h index e141c1fd68d8..d9578a58ae7f 100644 --- a/arch/arm/mach-exynos/include/mach/regs-clock.h +++ b/arch/arm/mach-exynos/include/mach/regs-clock.h @@ -255,9 +255,15 @@ /* For EXYNOS5250 */ +#define EXYNOS5_APLL_LOCK EXYNOS_CLKREG(0x00000) #define EXYNOS5_APLL_CON0 EXYNOS_CLKREG(0x00100) #define EXYNOS5_CLKSRC_CPU EXYNOS_CLKREG(0x00200) +#define EXYNOS5_CLKMUX_STATCPU EXYNOS_CLKREG(0x00400) #define EXYNOS5_CLKDIV_CPU0 EXYNOS_CLKREG(0x00500) +#define EXYNOS5_CLKDIV_CPU1 EXYNOS_CLKREG(0x00504) +#define EXYNOS5_CLKDIV_STATCPU0 EXYNOS_CLKREG(0x00600) +#define EXYNOS5_CLKDIV_STATCPU1 EXYNOS_CLKREG(0x00604) + #define EXYNOS5_MPLL_CON0 EXYNOS_CLKREG(0x04100) #define EXYNOS5_CLKSRC_CORE1 EXYNOS_CLKREG(0x04204) diff --git a/arch/arm/mach-exynos/include/mach/uncompress.h b/arch/arm/mach-exynos/include/mach/uncompress.h index 493f4f365ddf..2979995d5a6a 100644 --- a/arch/arm/mach-exynos/include/mach/uncompress.h +++ b/arch/arm/mach-exynos/include/mach/uncompress.h @@ -20,9 +20,24 @@ volatile u8 *uart_base; #include <plat/uncompress.h> +static unsigned int __raw_readl(unsigned int ptr) +{ + return *((volatile unsigned int *)ptr); +} + static void arch_detect_cpu(void) { - if (machine_is_smdk5250()) + u32 chip_id = __raw_readl(EXYNOS_PA_CHIPID); + + /* + * product_id is bits 31:12 + * bits 23:20 describe the exynosX family + * + */ + chip_id >>= 20; + chip_id &= 0xf; + + if (chip_id == 0x5) uart_base = (volatile u8 *)EXYNOS5_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); else uart_base = (volatile u8 *)EXYNOS4_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 0d26f50081ad..4711c8920e37 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -45,7 +45,7 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos4210-uart.3", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA0, "dma-pl330.0", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA1, "dma-pl330.1", NULL), - OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA1, "dma-pl330.2", NULL), + OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_MDMA1, "dma-pl330.2", NULL), {}, }; diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index b3982c867c9c..ed90aef404c3 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -112,6 +112,7 @@ static struct s3c_sdhci_platdata nuri_hsmmc0_data __initdata = { .host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_ERASE), + .host_caps2 = MMC_CAP2_BROKEN_VOLTAGE, .cd_type = S3C_SDHCI_CD_PERMANENT, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, }; @@ -307,49 +308,7 @@ static struct i2c_board_info i2c1_devs[] __initdata = { }; /* TSP */ -static u8 mxt_init_vals[] = { - /* MXT_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* MXT_GEN_ACQUIRE(8) */ - 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* MXT_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* MXT_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, - /* MXT_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_GRIPFACE(20) */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, - 0x0f, 0x0a, - /* MXT_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, - 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* MXT_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x02, 0x08, 0x10, 0x00, -}; - static struct mxt_platform_data mxt_platform_data = { - .config = mxt_init_vals, - .config_length = ARRAY_SIZE(mxt_init_vals), - .x_line = 18, .y_line = 11, .x_size = 1024, @@ -571,7 +530,7 @@ static struct regulator_init_data __initdata max8997_ldo7_data = { static struct regulator_init_data __initdata max8997_ldo8_data = { .constraints = { - .name = "VUSB/VDAC_3.3V_C210", + .name = "VUSB+VDAC_3.3V_C210", .min_uV = 3300000, .max_uV = 3300000, .valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -1347,6 +1306,7 @@ static struct platform_device *nuri_devices[] __initdata = { static void __init nuri_map_io(void) { + clk_xusbxti.rate = 24000000; exynos_init_io(NULL, 0); s3c24xx_init_clocks(24000000); s3c24xx_init_uarts(nuri_uartcfgs, ARRAY_SIZE(nuri_uartcfgs)); @@ -1379,7 +1339,6 @@ static void __init nuri_machine_init(void) nuri_camera_init(); nuri_ehci_init(); - clk_xusbxti.rate = 24000000; /* Last */ platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices)); diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index 6bb9dbdd73fd..a34036eb8ba2 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -29,6 +29,7 @@ #include <asm/mach-types.h> #include <plat/regs-serial.h> +#include <plat/clock.h> #include <plat/cpu.h> #include <plat/devs.h> #include <plat/iic.h> @@ -39,6 +40,7 @@ #include <plat/pd.h> #include <plat/regs-fb-v4.h> #include <plat/fimc-core.h> +#include <plat/s5p-time.h> #include <plat/camport.h> #include <plat/mipi_csis.h> @@ -746,6 +748,7 @@ static struct s3c_sdhci_platdata universal_hsmmc0_data __initdata = { .max_width = 8, .host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), + .host_caps2 = MMC_CAP2_BROKEN_VOLTAGE, .cd_type = S3C_SDHCI_CD_PERMANENT, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, }; @@ -1057,9 +1060,11 @@ static struct platform_device *universal_devices[] __initdata = { static void __init universal_map_io(void) { + clk_xusbxti.rate = 24000000; exynos_init_io(NULL, 0); s3c24xx_init_clocks(24000000); s3c24xx_init_uarts(universal_uartcfgs, ARRAY_SIZE(universal_uartcfgs)); + s5p_set_timer_source(S5P_PWM2, S5P_PWM4); } static void s5p_tv_setup(void) @@ -1110,7 +1115,7 @@ MACHINE_START(UNIVERSAL_C210, "UNIVERSAL_C210") .map_io = universal_map_io, .handle_irq = gic_handle_irq, .init_machine = universal_machine_init, - .timer = &exynos4_timer, + .timer = &s5p_timer, .reserve = &universal_reserve, .restart = exynos4_restart, MACHINE_END diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 7561eca131b0..472d47b50cd6 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -151,6 +151,7 @@ config MACH_MX25_3DS select IMX_HAVE_PLATFORM_IMX2_WDT select IMX_HAVE_PLATFORM_IMXDI_RTC select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_SSI select IMX_HAVE_PLATFORM_IMX_FB select IMX_HAVE_PLATFORM_IMX_KEYPAD select IMX_HAVE_PLATFORM_IMX_UART @@ -493,6 +494,7 @@ config MACH_MX31MOBOARD select IMX_HAVE_PLATFORM_FSL_USB2_UDC select IMX_HAVE_PLATFORM_IMX2_WDT select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_SSI select IMX_HAVE_PLATFORM_IMX_UART select IMX_HAVE_PLATFORM_IPU_CORE select IMX_HAVE_PLATFORM_MXC_EHCI diff --git a/arch/arm/mach-imx/clock-imx27.c b/arch/arm/mach-imx/clock-imx27.c index b9a95ed75553..98e04f5a87dd 100644 --- a/arch/arm/mach-imx/clock-imx27.c +++ b/arch/arm/mach-imx/clock-imx27.c @@ -662,6 +662,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "dma", dma_clk) _REGISTER_CLOCK(NULL, "rtic", rtic_clk) _REGISTER_CLOCK(NULL, "brom", brom_clk) + _REGISTER_CLOCK(NULL, "emma", emma_clk) _REGISTER_CLOCK("m2m-emmaprp.0", NULL, emma_clk) _REGISTER_CLOCK(NULL, "slcdc", slcdc_clk) _REGISTER_CLOCK("imx27-fec.0", NULL, fec_clk) diff --git a/arch/arm/mach-imx/clock-imx35.c b/arch/arm/mach-imx/clock-imx35.c index 1e279af656ad..e56c1a83eee3 100644 --- a/arch/arm/mach-imx/clock-imx35.c +++ b/arch/arm/mach-imx/clock-imx35.c @@ -483,7 +483,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("imx2-wdt.0", NULL, wdog_clk) _REGISTER_CLOCK(NULL, "max", max_clk) _REGISTER_CLOCK(NULL, "audmux", audmux_clk) - _REGISTER_CLOCK(NULL, "csi", csi_clk) + _REGISTER_CLOCK("mx3-camera.0", NULL, csi_clk) _REGISTER_CLOCK(NULL, "iim", iim_clk) _REGISTER_CLOCK(NULL, "gpu2d", gpu2d_clk) _REGISTER_CLOCK("mxc_nand.0", NULL, nfc_clk) diff --git a/arch/arm/mach-imx/imx27-dt.c b/arch/arm/mach-imx/imx27-dt.c index 861ceb8232d6..ed38d03c61f2 100644 --- a/arch/arm/mach-imx/imx27-dt.c +++ b/arch/arm/mach-imx/imx27-dt.c @@ -35,7 +35,7 @@ static const struct of_dev_auxdata imx27_auxdata_lookup[] __initconst = { static int __init imx27_avic_add_irq_domain(struct device_node *np, struct device_node *interrupt_parent) { - irq_domain_add_simple(np, 0); + irq_domain_add_legacy(np, 64, 0, 0, &irq_domain_simple_ops, NULL); return 0; } @@ -44,7 +44,9 @@ static int __init imx27_gpio_add_irq_domain(struct device_node *np, { static int gpio_irq_base = MXC_GPIO_IRQ_START + ARCH_NR_GPIOS; - irq_domain_add_simple(np, gpio_irq_base); + gpio_irq_base -= 32; + irq_domain_add_legacy(np, 32, gpio_irq_base, 0, &irq_domain_simple_ops, + NULL); return 0; } diff --git a/arch/arm/mach-imx/mach-armadillo5x0.c b/arch/arm/mach-imx/mach-armadillo5x0.c index 27bc27e6ea41..c650145d1646 100644 --- a/arch/arm/mach-imx/mach-armadillo5x0.c +++ b/arch/arm/mach-imx/mach-armadillo5x0.c @@ -38,6 +38,8 @@ #include <linux/usb/otg.h> #include <linux/usb/ulpi.h> #include <linux/delay.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> #include <mach/hardware.h> #include <asm/mach-types.h> @@ -479,6 +481,11 @@ static struct platform_device *devices[] __initdata = { &armadillo5x0_smc911x_device, }; +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vdd33a", "smsc911x"), + REGULATOR_SUPPLY("vddvario", "smsc911x"), +}; + /* * Perform board specific initializations */ @@ -489,6 +496,8 @@ static void __init armadillo5x0_init(void) mxc_iomux_setup_multiple_pins(armadillo5x0_pins, ARRAY_SIZE(armadillo5x0_pins), "armadillo5x0"); + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); + platform_add_devices(devices, ARRAY_SIZE(devices)); imx_add_gpio_keys(&armadillo5x0_button_data); imx31_add_imx_i2c1(NULL); diff --git a/arch/arm/mach-imx/mach-kzm_arm11_01.c b/arch/arm/mach-imx/mach-kzm_arm11_01.c index fc78e8071cd1..15a26e908260 100644 --- a/arch/arm/mach-imx/mach-kzm_arm11_01.c +++ b/arch/arm/mach-imx/mach-kzm_arm11_01.c @@ -24,6 +24,8 @@ #include <linux/serial_8250.h> #include <linux/smsc911x.h> #include <linux/types.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> #include <asm/irq.h> #include <asm/mach-types.h> @@ -166,6 +168,11 @@ static struct platform_device kzm_smsc9118_device = { }, }; +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vdd33a", "smsc911x"), + REGULATOR_SUPPLY("vddvario", "smsc911x"), +}; + static int __init kzm_init_smsc9118(void) { /* @@ -175,6 +182,8 @@ static int __init kzm_init_smsc9118(void) gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2), "smsc9118-int"); gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2)); + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); + return platform_device_register(&kzm_smsc9118_device); } #else diff --git a/arch/arm/mach-imx/mach-mx31_3ds.c b/arch/arm/mach-imx/mach-mx31_3ds.c index 4d1aab154400..4eafdf275ea2 100644 --- a/arch/arm/mach-imx/mach-mx31_3ds.c +++ b/arch/arm/mach-imx/mach-mx31_3ds.c @@ -156,6 +156,11 @@ static int mx31_3ds_pins[] = { MX31_PIN_CSI_VSYNC__CSI_VSYNC, MX31_PIN_CSI_D5__GPIO3_5, /* CMOS PWDN */ IOMUX_MODE(MX31_PIN_RI_DTE1, IOMUX_CONFIG_GPIO), /* CMOS reset */ + /* SSI */ + MX31_PIN_STXD4__STXD4, + MX31_PIN_SRXD4__SRXD4, + MX31_PIN_SCK4__SCK4, + MX31_PIN_SFS4__SFS4, }; /* @@ -488,12 +493,23 @@ static struct mc13xxx_regulator_init_data mx31_3ds_regulators[] = { }; /* MC13783 */ +static struct mc13xxx_codec_platform_data mx31_3ds_codec = { + .dac_ssi_port = MC13783_SSI1_PORT, + .adc_ssi_port = MC13783_SSI1_PORT, +}; + static struct mc13xxx_platform_data mc13783_pdata = { .regulators = { .regulators = mx31_3ds_regulators, .num_regulators = ARRAY_SIZE(mx31_3ds_regulators), }, - .flags = MC13XXX_USE_TOUCHSCREEN | MC13XXX_USE_RTC, + .codec = &mx31_3ds_codec, + .flags = MC13XXX_USE_TOUCHSCREEN | MC13XXX_USE_RTC | MC13XXX_USE_CODEC, + +}; + +static struct imx_ssi_platform_data mx31_3ds_ssi_pdata = { + .flags = IMX_SSI_DMA | IMX_SSI_NET, }; /* SPI */ @@ -741,6 +757,10 @@ static void __init mx31_3ds_init(void) } mx31_3ds_init_camera(); + + imx31_add_imx_ssi(0, &mx31_3ds_ssi_pdata); + + imx_add_platform_device("imx_mc13783", 0, NULL, 0, NULL, 0); } static void __init mx31_3ds_timer_init(void) diff --git a/arch/arm/mach-imx/mach-mx31lilly.c b/arch/arm/mach-imx/mach-mx31lilly.c index 02401bbd6d53..83714b0cc290 100644 --- a/arch/arm/mach-imx/mach-mx31lilly.c +++ b/arch/arm/mach-imx/mach-mx31lilly.c @@ -34,6 +34,8 @@ #include <linux/mfd/mc13783.h> #include <linux/usb/otg.h> #include <linux/usb/ulpi.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -242,6 +244,11 @@ static struct platform_device *devices[] __initdata = { static int mx31lilly_baseboard; core_param(mx31lilly_baseboard, mx31lilly_baseboard, int, 0444); +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vdd33a", "smsc911x"), + REGULATOR_SUPPLY("vddvario", "smsc911x"), +}; + static void __init mx31lilly_board_init(void) { imx31_soc_init(); @@ -280,6 +287,8 @@ static void __init mx31lilly_board_init(void) imx31_add_spi_imx1(&spi1_pdata); spi_register_board_info(&mc13783_dev, 1); + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); + platform_add_devices(devices, ARRAY_SIZE(devices)); /* USB */ diff --git a/arch/arm/mach-imx/mach-mx31lite.c b/arch/arm/mach-imx/mach-mx31lite.c index ef80751712e7..0abef5f13df5 100644 --- a/arch/arm/mach-imx/mach-mx31lite.c +++ b/arch/arm/mach-imx/mach-mx31lite.c @@ -29,6 +29,8 @@ #include <linux/usb/ulpi.h> #include <linux/mtd/physmap.h> #include <linux/delay.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -226,6 +228,11 @@ void __init mx31lite_map_io(void) static int mx31lite_baseboard; core_param(mx31lite_baseboard, mx31lite_baseboard, int, 0444); +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vdd33a", "smsc911x"), + REGULATOR_SUPPLY("vddvario", "smsc911x"), +}; + static void __init mx31lite_init(void) { int ret; @@ -259,6 +266,8 @@ static void __init mx31lite_init(void) if (usbh2_pdata.otg) imx31_add_mxc_ehci_hs(2, &usbh2_pdata); + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); + /* SMSC9117 IRQ pin */ ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_SFS6), "sms9117-irq"); if (ret) diff --git a/arch/arm/mach-imx/mach-mx31moboard.c b/arch/arm/mach-imx/mach-mx31moboard.c index f17a15f28316..31e5b3d5da73 100644 --- a/arch/arm/mach-imx/mach-mx31moboard.c +++ b/arch/arm/mach-imx/mach-mx31moboard.c @@ -47,6 +47,7 @@ #include <mach/hardware.h> #include <mach/iomux-mx3.h> #include <mach/ulpi.h> +#include <mach/ssi.h> #include "devices-imx31.h" @@ -102,6 +103,9 @@ static unsigned int moboard_pins[] = { MX31_PIN_CSPI3_MOSI__MOSI, MX31_PIN_CSPI3_MISO__MISO, MX31_PIN_CSPI3_SCLK__SCLK, MX31_PIN_CSPI3_SPI_RDY__SPI_RDY, MX31_PIN_CSPI2_SS1__CSPI3_SS1, + /* SSI */ + MX31_PIN_STXD4__STXD4, MX31_PIN_SRXD4__SRXD4, + MX31_PIN_SCK4__SCK4, MX31_PIN_SFS4__SFS4, }; static struct physmap_flash_data mx31moboard_flash_data = { @@ -276,6 +280,11 @@ static struct mc13xxx_buttons_platform_data moboard_buttons = { .b1on_key = KEY_POWER, }; +static struct mc13xxx_codec_platform_data moboard_codec = { + .dac_ssi_port = MC13783_SSI1_PORT, + .adc_ssi_port = MC13783_SSI1_PORT, +}; + static struct mc13xxx_platform_data moboard_pmic = { .regulators = { .regulators = moboard_regulators, @@ -283,7 +292,12 @@ static struct mc13xxx_platform_data moboard_pmic = { }, .leds = &moboard_leds, .buttons = &moboard_buttons, - .flags = MC13XXX_USE_RTC | MC13XXX_USE_ADC, + .codec = &moboard_codec, + .flags = MC13XXX_USE_RTC | MC13XXX_USE_ADC | MC13XXX_USE_CODEC, +}; + +static struct imx_ssi_platform_data moboard_ssi_pdata = { + .flags = IMX_SSI_DMA | IMX_SSI_NET, }; static struct spi_board_info moboard_spi_board_info[] __initdata = { @@ -554,6 +568,10 @@ static void __init mx31moboard_init(void) moboard_usbh2_init(); + imx31_add_imx_ssi(0, &moboard_ssi_pdata); + + imx_add_platform_device("imx_mc13783", 0, NULL, 0, NULL, 0); + pm_power_off = mx31moboard_poweroff; switch (mx31moboard_baseboard) { diff --git a/arch/arm/mach-imx/mach-mx35_3ds.c b/arch/arm/mach-imx/mach-mx35_3ds.c index e14291d89e4f..6ae51c6b95b7 100644 --- a/arch/arm/mach-imx/mach-mx35_3ds.c +++ b/arch/arm/mach-imx/mach-mx35_3ds.c @@ -97,7 +97,7 @@ static struct i2c_board_info __initdata i2c_devices_3ds[] = { static int lcd_power_gpio = -ENXIO; static int mc9s08dz60_gpiochip_match(struct gpio_chip *chip, - void *data) + const void *data) { return !strcmp(chip->label, data); } diff --git a/arch/arm/mach-imx/mach-mx53_ard.c b/arch/arm/mach-imx/mach-mx53_ard.c index 753f4fc9ec04..05641980dc5e 100644 --- a/arch/arm/mach-imx/mach-mx53_ard.c +++ b/arch/arm/mach-imx/mach-mx53_ard.c @@ -23,6 +23,8 @@ #include <linux/delay.h> #include <linux/gpio.h> #include <linux/smsc911x.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> #include <mach/common.h> #include <mach/hardware.h> @@ -214,6 +216,11 @@ static int weim_cs_config(void) return 0; } +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vdd33a", "smsc911x"), + REGULATOR_SUPPLY("vddvario", "smsc911x"), +}; + void __init imx53_ard_common_init(void) { mxc_iomux_v3_setup_multiple_pads(mx53_ard_pads, @@ -232,6 +239,7 @@ static void __init mx53_ard_board_init(void) imx53_ard_common_init(); mx53_ard_io_init(); + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); platform_add_devices(devices, ARRAY_SIZE(devices)); imx53_add_sdhci_esdhc_imx(0, &mx53_ard_sd1_data); diff --git a/arch/arm/mach-imx/mm-imx5.c b/arch/arm/mach-imx/mm-imx5.c index 05250aed61fb..e10f3914fcfe 100644 --- a/arch/arm/mach-imx/mm-imx5.c +++ b/arch/arm/mach-imx/mm-imx5.c @@ -35,7 +35,7 @@ static void imx5_idle(void) } clk_enable(gpc_dvfs_clk); mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); - if (tzic_enable_wake() != 0) + if (!tzic_enable_wake()) cpu_do_idle(); clk_disable(gpc_dvfs_clk); } diff --git a/arch/arm/mach-kirkwood/board-dt.c b/arch/arm/mach-kirkwood/board-dt.c index 1c672d9e6656..f7fe1b9f3170 100644 --- a/arch/arm/mach-kirkwood/board-dt.c +++ b/arch/arm/mach-kirkwood/board-dt.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/kexec.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #include <mach/bridge-regs.h> diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 3698a370d636..26aac363a064 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -86,9 +86,6 @@ static void __init halibut_init(void) static void __init halibut_fixup(struct tag *tags, char **cmdline, struct meminfo *mi) { - mi->nr_banks=1; - mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].size = (101*1024*1024); } static void __init halibut_map_io(void) diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c index 962e71169750..fb3496a52ef4 100644 --- a/arch/arm/mach-msm/board-msm8x60.c +++ b/arch/arm/mach-msm/board-msm8x60.c @@ -17,6 +17,7 @@ #include <linux/irqdomain.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/memblock.h> @@ -49,10 +50,22 @@ static void __init msm8x60_map_io(void) msm_map_msm8x60_io(); } +#ifdef CONFIG_OF +static struct of_device_id msm_dt_gic_match[] __initdata = { + { .compatible = "qcom,msm-8660-qgic", .data = gic_of_init }, + {} +}; +#endif + static void __init msm8x60_init_irq(void) { - gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, - (void *)MSM_QGIC_CPU_BASE); + if (!of_have_populated_dt()) + gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, + (void *)MSM_QGIC_CPU_BASE); +#ifdef CONFIG_OF + else + of_irq_init(msm_dt_gic_match); +#endif /* Edge trigger PPIs except AVS_SVICINT and AVS_SVICINTSWDONE */ writel(0xFFFFD7FF, MSM_QGIC_DIST_BASE + GIC_DIST_CONFIG + 4); @@ -73,16 +86,8 @@ static struct of_dev_auxdata msm_auxdata_lookup[] __initdata = { {} }; -static struct of_device_id msm_dt_gic_match[] __initdata = { - { .compatible = "qcom,msm-8660-qgic", }, - {} -}; - static void __init msm8x60_dt_init(void) { - irq_domain_generate_simple(msm_dt_gic_match, MSM8X60_QGIC_DIST_PHYS, - GIC_SPI_START); - if (of_machine_is_compatible("qcom,msm8660-surf")) { printk(KERN_INFO "Init surf UART registers\n"); msm8x60_init_uart12dm(); diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c index 25105c1027fe..89bf6b426699 100644 --- a/arch/arm/mach-msm/board-trout-panel.c +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -12,6 +12,7 @@ #include <asm/io.h> #include <asm/mach-types.h> +#include <asm/system_info.h> #include <mach/msm_fb.h> #include <mach/vreg.h> diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 5414f76ec0a9..d4060a37e23d 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <linux/clkdev.h> +#include <asm/system_info.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> diff --git a/arch/arm/mach-msm/include/mach/uncompress.h b/arch/arm/mach-msm/include/mach/uncompress.h index 169a84007456..c14011fe832d 100644 --- a/arch/arm/mach-msm/include/mach/uncompress.h +++ b/arch/arm/mach-msm/include/mach/uncompress.h @@ -16,6 +16,7 @@ #ifndef __ASM_ARCH_MSM_UNCOMPRESS_H #define __ASM_ARCH_MSM_UNCOMPRESS_H +#include <asm/barrier.h> #include <asm/processor.h> #include <mach/msm_iomap.h> diff --git a/arch/arm/mach-msm/proc_comm.c b/arch/arm/mach-msm/proc_comm.c index 67e701c7f183..9980dc736e7b 100644 --- a/arch/arm/mach-msm/proc_comm.c +++ b/arch/arm/mach-msm/proc_comm.c @@ -121,7 +121,7 @@ int msm_proc_comm(unsigned cmd, unsigned *data1, unsigned *data2) * and unknown state. This function should be called early to * wait on the ARM9. */ -void __init proc_comm_boot_wait(void) +void __devinit proc_comm_boot_wait(void) { void __iomem *base = MSM_SHARED_RAM_BASE; diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c index 0c56a5aaf588..c56df9e932ae 100644 --- a/arch/arm/mach-msm/smd_debug.c +++ b/arch/arm/mach-msm/smd_debug.c @@ -203,15 +203,9 @@ static ssize_t debug_read(struct file *file, char __user *buf, return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); } -static int debug_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static const struct file_operations debug_ops = { .read = debug_read, - .open = debug_open, + .open = simple_open, .llseek = default_llseek, }; diff --git a/arch/arm/mach-omap1/ams-delta-fiq.c b/arch/arm/mach-omap1/ams-delta-fiq.c index fcce7ff37630..cfd98b186fcc 100644 --- a/arch/arm/mach-omap1/ams-delta-fiq.c +++ b/arch/arm/mach-omap1/ams-delta-fiq.c @@ -48,7 +48,7 @@ static irqreturn_t deferred_fiq(int irq, void *dev_id) struct irq_chip *irq_chip = NULL; int gpio, irq_num, fiq_count; - irq_desc = irq_to_desc(IH_GPIO_BASE); + irq_desc = irq_to_desc(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK)); if (irq_desc) irq_chip = irq_desc->irq_data.chip; diff --git a/arch/arm/mach-omap1/include/mach/io.h b/arch/arm/mach-omap1/include/mach/io.h new file mode 100644 index 000000000000..ce4f8005b26f --- /dev/null +++ b/arch/arm/mach-omap1/include/mach/io.h @@ -0,0 +1,45 @@ +/* + * arch/arm/mach-omap1/include/mach/io.h + * + * IO definitions for TI OMAP processors and boards + * + * Copied from arch/arm/mach-sa1100/include/mach/io.h + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Modifications: + * 06-12-1997 RMK Created. + * 07-04-1999 RMK Major cleanup + */ + +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +/* + * We don't actually have real ISA nor PCI buses, but there is so many + * drivers out there that might just work if we fake them... + */ +#define __io(a) __typesafe_io(a) + +#endif diff --git a/arch/arm/mach-omap1/mux.c b/arch/arm/mach-omap1/mux.c index 087dba0df47e..e9cc52d4cb28 100644 --- a/arch/arm/mach-omap1/mux.c +++ b/arch/arm/mach-omap1/mux.c @@ -27,6 +27,7 @@ #include <linux/io.h> #include <linux/spinlock.h> +#include <mach/hardware.h> #include <plat/mux.h> diff --git a/arch/arm/mach-omap1/timer.c b/arch/arm/mach-omap1/timer.c index 6e90665a7c47..fb202af01d0d 100644 --- a/arch/arm/mach-omap1/timer.c +++ b/arch/arm/mach-omap1/timer.c @@ -47,9 +47,9 @@ static int omap1_dm_timer_set_src(struct platform_device *pdev, int n = (pdev->id - 1) << 1; u32 l; - l = __raw_readl(MOD_CONF_CTRL_1) & ~(0x03 << n); + l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n); l |= source << n; - __raw_writel(l, MOD_CONF_CTRL_1); + omap_writel(l, MOD_CONF_CTRL_1); return 0; } diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index a39fc4bbd2b8..130ab00c09a2 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -20,6 +20,7 @@ #include <linux/usb/otg.h> #include <linux/spi/spi.h> #include <linux/i2c/twl.h> +#include <linux/mfd/twl6040.h> #include <linux/gpio_keys.h> #include <linux/regulator/machine.h> #include <linux/regulator/fixed.h> @@ -560,7 +561,7 @@ static struct regulator_init_data sdp4430_vusim = { }, }; -static struct twl4030_codec_data twl6040_codec = { +static struct twl6040_codec_data twl6040_codec = { /* single-step ramp for headset and handsfree */ .hs_left_step = 0x0f, .hs_right_step = 0x0f, @@ -568,7 +569,7 @@ static struct twl4030_codec_data twl6040_codec = { .hf_right_step = 0x1d, }; -static struct twl4030_vibra_data twl6040_vibra = { +static struct twl6040_vibra_data twl6040_vibra = { .vibldrv_res = 8, .vibrdrv_res = 3, .viblmotor_res = 10, @@ -577,16 +578,14 @@ static struct twl4030_vibra_data twl6040_vibra = { .vddvibr_uV = 0, /* fixed volt supply - VBAT */ }; -static struct twl4030_audio_data twl6040_audio = { +static struct twl6040_platform_data twl6040_data = { .codec = &twl6040_codec, .vibra = &twl6040_vibra, .audpwron_gpio = 127, - .naudint_irq = OMAP44XX_IRQ_SYS_2N, .irq_base = TWL6040_CODEC_IRQ_BASE, }; static struct twl4030_platform_data sdp4430_twldata = { - .audio = &twl6040_audio, /* Regulators */ .vusim = &sdp4430_vusim, .vaux1 = &sdp4430_vaux1, @@ -617,7 +616,8 @@ static int __init omap4_i2c_init(void) TWL_COMMON_REGULATOR_VCXIO | TWL_COMMON_REGULATOR_VUSB | TWL_COMMON_REGULATOR_CLK32KG); - omap4_pmic_init("twl6030", &sdp4430_twldata); + omap4_pmic_init("twl6030", &sdp4430_twldata, + &twl6040_data, OMAP44XX_IRQ_SYS_2N); omap_register_i2c_bus(2, 400, NULL, 0); omap_register_i2c_bus(3, 400, sdp4430_i2c_3_boardinfo, ARRAY_SIZE(sdp4430_i2c_3_boardinfo)); diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index 41b0a2fe0b04..909a8b91b564 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -26,6 +26,7 @@ #include <linux/i2c/at24.h> #include <linux/i2c/twl.h> +#include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> #include <linux/mmc/host.h> @@ -81,8 +82,23 @@ static struct omap_smsc911x_platform_data sb_t35_smsc911x_cfg = { .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS, }; +static struct regulator_consumer_supply cm_t35_smsc911x_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), +}; + +static struct regulator_consumer_supply sb_t35_smsc911x_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.1"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.1"), +}; + static void __init cm_t35_init_ethernet(void) { + regulator_register_fixed(0, cm_t35_smsc911x_supplies, + ARRAY_SIZE(cm_t35_smsc911x_supplies)); + regulator_register_fixed(1, sb_t35_smsc911x_supplies, + ARRAY_SIZE(sb_t35_smsc911x_supplies)); + gpmc_smsc911x_init(&cm_t35_smsc911x_cfg); gpmc_smsc911x_init(&sb_t35_smsc911x_cfg); } diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c index 74e1687b5170..098d183a0086 100644 --- a/arch/arm/mach-omap2/board-generic.c +++ b/arch/arm/mach-omap2/board-generic.c @@ -137,7 +137,7 @@ static struct twl4030_platform_data sdp4430_twldata = { static void __init omap4_i2c_init(void) { - omap4_pmic_init("twl6030", &sdp4430_twldata); + omap4_pmic_init("twl6030", &sdp4430_twldata, NULL, 0); } static void __init omap4_init(void) diff --git a/arch/arm/mach-omap2/board-igep0020.c b/arch/arm/mach-omap2/board-igep0020.c index e558800adfdf..740cee9369ba 100644 --- a/arch/arm/mach-omap2/board-igep0020.c +++ b/arch/arm/mach-omap2/board-igep0020.c @@ -634,8 +634,14 @@ static void __init igep_wlan_bt_init(void) static inline void __init igep_wlan_bt_init(void) { } #endif +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), +}; + static void __init igep_init(void) { + regulator_register_fixed(1, dummy_supplies, ARRAY_SIZE(dummy_supplies)); omap3_mux_init(board_mux, OMAP_PACKAGE_CBB); /* Get IGEP2 hardware revision */ diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c index d50a562adfa0..1b6049567ab4 100644 --- a/arch/arm/mach-omap2/board-ldp.c +++ b/arch/arm/mach-omap2/board-ldp.c @@ -22,6 +22,7 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/spi/spi.h> +#include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> #include <linux/i2c/twl.h> #include <linux/io.h> @@ -410,8 +411,14 @@ static struct mtd_partition ldp_nand_partitions[] = { }; +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), +}; + static void __init omap_ldp_init(void) { + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); omap3_mux_init(board_mux, OMAP_PACKAGE_CBB); ldp_init_smsc911x(); omap_i2c_init(); diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index 4c90f078abe1..49df12735b41 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -114,15 +114,6 @@ static struct omap_smsc911x_platform_data smsc911x_cfg = { static inline void __init omap3evm_init_smsc911x(void) { - struct clk *l3ck; - unsigned int rate; - - l3ck = clk_get(NULL, "l3_ck"); - if (IS_ERR(l3ck)) - rate = 100000000; - else - rate = clk_get_rate(l3ck); - /* Configure ethernet controller reset gpio */ if (cpu_is_omap3430()) { if (get_omap3_evm_rev() == OMAP3EVM_BOARD_GEN_1) @@ -632,9 +623,15 @@ static void __init omap3_evm_wl12xx_init(void) #endif } +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), +}; + static void __init omap3_evm_init(void) { omap3_evm_get_revision(); + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); if (cpu_is_omap3630()) omap3_mux_init(omap36x_board_mux, OMAP_PACKAGE_CBB); diff --git a/arch/arm/mach-omap2/board-omap3logic.c b/arch/arm/mach-omap2/board-omap3logic.c index 4a7d8c8a75da..9b3c141ff51b 100644 --- a/arch/arm/mach-omap2/board-omap3logic.c +++ b/arch/arm/mach-omap2/board-omap3logic.c @@ -23,6 +23,7 @@ #include <linux/io.h> #include <linux/gpio.h> +#include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> #include <linux/i2c/twl.h> @@ -188,8 +189,14 @@ static struct omap_board_mux board_mux[] __initdata = { }; #endif +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), +}; + static void __init omap3logic_init(void) { + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); omap3_mux_init(board_mux, OMAP_PACKAGE_CBB); omap3torpedo_fix_pbias_voltage(); omap3logic_i2c_init(); diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c index 641004380795..4dffc95bddd2 100644 --- a/arch/arm/mach-omap2/board-omap3stalker.c +++ b/arch/arm/mach-omap2/board-omap3stalker.c @@ -24,6 +24,7 @@ #include <linux/input.h> #include <linux/gpio_keys.h> +#include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> #include <linux/i2c/twl.h> #include <linux/mmc/host.h> @@ -72,15 +73,6 @@ static struct omap_smsc911x_platform_data smsc911x_cfg = { static inline void __init omap3stalker_init_eth(void) { - struct clk *l3ck; - unsigned int rate; - - l3ck = clk_get(NULL, "l3_ck"); - if (IS_ERR(l3ck)) - rate = 100000000; - else - rate = clk_get_rate(l3ck); - omap_mux_init_gpio(19, OMAP_PIN_INPUT_PULLUP); gpmc_smsc911x_init(&smsc911x_cfg); } @@ -419,8 +411,14 @@ static struct omap_board_mux board_mux[] __initdata = { }; #endif +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), +}; + static void __init omap3_stalker_init(void) { + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); omap3_mux_init(board_mux, OMAP_PACKAGE_CUS); omap_board_config = omap3_stalker_config; omap_board_config_size = ARRAY_SIZE(omap3_stalker_config); diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index d8c0e89f0126..1b782ba53433 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -25,6 +25,7 @@ #include <linux/gpio.h> #include <linux/usb/otg.h> #include <linux/i2c/twl.h> +#include <linux/mfd/twl6040.h> #include <linux/regulator/machine.h> #include <linux/regulator/fixed.h> #include <linux/wl12xx.h> @@ -284,7 +285,7 @@ static int __init omap4_twl6030_hsmmc_init(struct omap2_hsmmc_info *controllers) return 0; } -static struct twl4030_codec_data twl6040_codec = { +static struct twl6040_codec_data twl6040_codec = { /* single-step ramp for headset and handsfree */ .hs_left_step = 0x0f, .hs_right_step = 0x0f, @@ -292,17 +293,14 @@ static struct twl4030_codec_data twl6040_codec = { .hf_right_step = 0x1d, }; -static struct twl4030_audio_data twl6040_audio = { +static struct twl6040_platform_data twl6040_data = { .codec = &twl6040_codec, .audpwron_gpio = 127, - .naudint_irq = OMAP44XX_IRQ_SYS_2N, .irq_base = TWL6040_CODEC_IRQ_BASE, }; /* Panda board uses the common PMIC configuration */ -static struct twl4030_platform_data omap4_panda_twldata = { - .audio = &twl6040_audio, -}; +static struct twl4030_platform_data omap4_panda_twldata; /* * Display monitor features are burnt in their EEPROM as EDID data. The EEPROM @@ -326,7 +324,8 @@ static int __init omap4_panda_i2c_init(void) TWL_COMMON_REGULATOR_VCXIO | TWL_COMMON_REGULATOR_VUSB | TWL_COMMON_REGULATOR_CLK32KG); - omap4_pmic_init("twl6030", &omap4_panda_twldata); + omap4_pmic_init("twl6030", &omap4_panda_twldata, + &twl6040_data, OMAP44XX_IRQ_SYS_2N); omap_register_i2c_bus(2, 400, NULL, 0); /* * Bus 3 is attached to the DVI port where devices like the pico DLP diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 668533e2a379..33aa3910b09e 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -498,10 +498,18 @@ static struct gpio overo_bt_gpios[] __initdata = { { OVERO_GPIO_BT_NRESET, GPIOF_OUT_INIT_HIGH, "lcd bl enable" }, }; +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), + REGULATOR_SUPPLY("vddvario", "smsc911x.1"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.1"), +}; + static void __init overo_init(void) { int ret; + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); omap3_mux_init(board_mux, OMAP_PACKAGE_CBB); omap_hsmmc_init(mmc); overo_i2c_init(); diff --git a/arch/arm/mach-omap2/board-zoom-debugboard.c b/arch/arm/mach-omap2/board-zoom-debugboard.c index 1e8540eabde9..f64f44173061 100644 --- a/arch/arm/mach-omap2/board-zoom-debugboard.c +++ b/arch/arm/mach-omap2/board-zoom-debugboard.c @@ -14,6 +14,9 @@ #include <linux/smsc911x.h> #include <linux/interrupt.h> +#include <linux/regulator/fixed.h> +#include <linux/regulator/machine.h> + #include <plat/gpmc.h> #include <plat/gpmc-smsc911x.h> @@ -117,11 +120,17 @@ static struct platform_device *zoom_devices[] __initdata = { &zoom_debugboard_serial_device, }; +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), +}; + int __init zoom_debugboard_init(void) { if (!omap_zoom_debugboard_detect()) return 0; + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); zoom_init_smsc911x(); zoom_init_quaduart(); return platform_add_devices(zoom_devices, ARRAY_SIZE(zoom_devices)); diff --git a/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c b/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c index 7072e0d651b1..3d9d746b221a 100644 --- a/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c +++ b/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c @@ -165,83 +165,3 @@ int omap2_select_table_rate(struct clk *clk, unsigned long rate) return 0; } - -#ifdef CONFIG_CPU_FREQ -/* - * Walk PRCM rate table and fillout cpufreq freq_table - * XXX This should be replaced by an OPP layer in the near future - */ -static struct cpufreq_frequency_table *freq_table; - -void omap2_clk_init_cpufreq_table(struct cpufreq_frequency_table **table) -{ - const struct prcm_config *prcm; - int i = 0; - int tbl_sz = 0; - - if (!cpu_is_omap24xx()) - return; - - for (prcm = rate_table; prcm->mpu_speed; prcm++) { - if (!(prcm->flags & cpu_mask)) - continue; - if (prcm->xtal_speed != sclk->rate) - continue; - - /* don't put bypass rates in table */ - if (prcm->dpll_speed == prcm->xtal_speed) - continue; - - tbl_sz++; - } - - /* - * XXX Ensure that we're doing what CPUFreq expects for this error - * case and the following one - */ - if (tbl_sz == 0) { - pr_warning("%s: no matching entries in rate_table\n", - __func__); - return; - } - - /* Include the CPUFREQ_TABLE_END terminator entry */ - tbl_sz++; - - freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * tbl_sz, - GFP_ATOMIC); - if (!freq_table) { - pr_err("%s: could not kzalloc frequency table\n", __func__); - return; - } - - for (prcm = rate_table; prcm->mpu_speed; prcm++) { - if (!(prcm->flags & cpu_mask)) - continue; - if (prcm->xtal_speed != sclk->rate) - continue; - - /* don't put bypass rates in table */ - if (prcm->dpll_speed == prcm->xtal_speed) - continue; - - freq_table[i].index = i; - freq_table[i].frequency = prcm->mpu_speed / 1000; - i++; - } - - freq_table[i].index = i; - freq_table[i].frequency = CPUFREQ_TABLE_END; - - *table = &freq_table[0]; -} - -void omap2_clk_exit_cpufreq_table(struct cpufreq_frequency_table **table) -{ - if (!cpu_is_omap24xx()) - return; - - kfree(freq_table); -} - -#endif diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index f57ed5baeccf..d9f4931513f9 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c @@ -536,10 +536,5 @@ struct clk_functions omap2_clk_functions = { .clk_set_rate = omap2_clk_set_rate, .clk_set_parent = omap2_clk_set_parent, .clk_disable_unused = omap2_clk_disable_unused, -#ifdef CONFIG_CPU_FREQ - /* These will be removed when the OPP code is integrated */ - .clk_init_cpufreq_table = omap2_clk_init_cpufreq_table, - .clk_exit_cpufreq_table = omap2_clk_exit_cpufreq_table, -#endif }; diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index b8c2a686481c..a1bb23a23351 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h @@ -146,14 +146,6 @@ extern const struct clksel_rate gpt_sys_rates[]; extern const struct clksel_rate gfx_l3_rates[]; extern const struct clksel_rate dsp_ick_rates[]; -#if defined(CONFIG_ARCH_OMAP2) && defined(CONFIG_CPU_FREQ) -extern void omap2_clk_init_cpufreq_table(struct cpufreq_frequency_table **table); -extern void omap2_clk_exit_cpufreq_table(struct cpufreq_frequency_table **table); -#else -#define omap2_clk_init_cpufreq_table 0 -#define omap2_clk_exit_cpufreq_table 0 -#endif - extern const struct clkops clkops_omap2_iclk_dflt_wait; extern const struct clkops clkops_omap2_iclk_dflt; extern const struct clkops clkops_omap2_iclk_idle_only; diff --git a/arch/arm/mach-omap2/clock3xxx_data.c b/arch/arm/mach-omap2/clock3xxx_data.c index 480fb8f09aed..f4a626f7c79e 100644 --- a/arch/arm/mach-omap2/clock3xxx_data.c +++ b/arch/arm/mach-omap2/clock3xxx_data.c @@ -747,7 +747,7 @@ static struct clk dpll4_m3_ck = { .parent = &dpll4_ck, .init = &omap2_init_clksel_parent, .clksel_reg = OMAP_CM_REGADDR(OMAP3430_DSS_MOD, CM_CLKSEL), - .clksel_mask = OMAP3430_CLKSEL_TV_MASK, + .clksel_mask = OMAP3630_CLKSEL_TV_MASK, .clksel = dpll4_clksel, .clkdm_name = "dpll4_clkdm", .recalc = &omap2_clksel_recalc, @@ -832,7 +832,7 @@ static struct clk dpll4_m4_ck = { .parent = &dpll4_ck, .init = &omap2_init_clksel_parent, .clksel_reg = OMAP_CM_REGADDR(OMAP3430_DSS_MOD, CM_CLKSEL), - .clksel_mask = OMAP3430_CLKSEL_DSS1_MASK, + .clksel_mask = OMAP3630_CLKSEL_DSS1_MASK, .clksel = dpll4_clksel, .clkdm_name = "dpll4_clkdm", .recalc = &omap2_clksel_recalc, @@ -859,7 +859,7 @@ static struct clk dpll4_m5_ck = { .parent = &dpll4_ck, .init = &omap2_init_clksel_parent, .clksel_reg = OMAP_CM_REGADDR(OMAP3430_CAM_MOD, CM_CLKSEL), - .clksel_mask = OMAP3430_CLKSEL_CAM_MASK, + .clksel_mask = OMAP3630_CLKSEL_CAM_MASK, .clksel = dpll4_clksel, .clkdm_name = "dpll4_clkdm", .set_rate = &omap2_clksel_set_rate, @@ -886,7 +886,7 @@ static struct clk dpll4_m6_ck = { .parent = &dpll4_ck, .init = &omap2_init_clksel_parent, .clksel_reg = OMAP_CM_REGADDR(OMAP3430_EMU_MOD, CM_CLKSEL1), - .clksel_mask = OMAP3430_DIV_DPLL4_MASK, + .clksel_mask = OMAP3630_DIV_DPLL4_MASK, .clksel = dpll4_clksel, .clkdm_name = "dpll4_clkdm", .recalc = &omap2_clksel_recalc, @@ -1394,6 +1394,7 @@ static struct clk cpefuse_fck = { .name = "cpefuse_fck", .ops = &clkops_omap2_dflt, .parent = &sys_ck, + .clkdm_name = "core_l4_clkdm", .enable_reg = OMAP_CM_REGADDR(CORE_MOD, OMAP3430ES2_CM_FCLKEN3), .enable_bit = OMAP3430ES2_EN_CPEFUSE_SHIFT, .recalc = &followparent_recalc, @@ -1403,6 +1404,7 @@ static struct clk ts_fck = { .name = "ts_fck", .ops = &clkops_omap2_dflt, .parent = &omap_32k_fck, + .clkdm_name = "core_l4_clkdm", .enable_reg = OMAP_CM_REGADDR(CORE_MOD, OMAP3430ES2_CM_FCLKEN3), .enable_bit = OMAP3430ES2_EN_TS_SHIFT, .recalc = &followparent_recalc, @@ -1412,6 +1414,7 @@ static struct clk usbtll_fck = { .name = "usbtll_fck", .ops = &clkops_omap2_dflt_wait, .parent = &dpll5_m2_ck, + .clkdm_name = "core_l4_clkdm", .enable_reg = OMAP_CM_REGADDR(CORE_MOD, OMAP3430ES2_CM_FCLKEN3), .enable_bit = OMAP3430ES2_EN_USBTLL_SHIFT, .recalc = &followparent_recalc, @@ -1617,6 +1620,7 @@ static struct clk fshostusb_fck = { .name = "fshostusb_fck", .ops = &clkops_omap2_dflt_wait, .parent = &core_48m_fck, + .clkdm_name = "core_l4_clkdm", .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1), .enable_bit = OMAP3430ES1_EN_FSHOSTUSB_SHIFT, .recalc = &followparent_recalc, @@ -2043,6 +2047,7 @@ static struct clk omapctrl_ick = { .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1), .enable_bit = OMAP3430_EN_OMAPCTRL_SHIFT, .flags = ENABLE_ON_INIT, + .clkdm_name = "core_l4_clkdm", .recalc = &followparent_recalc, }; @@ -2094,6 +2099,7 @@ static struct clk usb_l4_ick = { .clksel_reg = OMAP_CM_REGADDR(CORE_MOD, CM_CLKSEL), .clksel_mask = OMAP3430ES1_CLKSEL_FSHOSTUSB_MASK, .clksel = usb_l4_clksel, + .clkdm_name = "core_l4_clkdm", .recalc = &omap2_clksel_recalc, }; @@ -3467,8 +3473,8 @@ static struct omap_clk omap3xxx_clks[] = { CLK(NULL, "ipss_ick", &ipss_ick, CK_AM35XX), CLK(NULL, "rmii_ck", &rmii_ck, CK_AM35XX), CLK(NULL, "pclk_ck", &pclk_ck, CK_AM35XX), - CLK("davinci_emac", "emac_clk", &emac_ick, CK_AM35XX), - CLK("davinci_emac", "phy_clk", &emac_fck, CK_AM35XX), + CLK("davinci_emac", NULL, &emac_ick, CK_AM35XX), + CLK("davinci_mdio.0", NULL, &emac_fck, CK_AM35XX), CLK("vpfe-capture", "master", &vpfe_ick, CK_AM35XX), CLK("vpfe-capture", "slave", &vpfe_fck, CK_AM35XX), CLK("musb-am35x", "ick", &hsotgusb_ick_am35xx, CK_AM35XX), diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c index c03c1108468e..fa6ea65ad44b 100644 --- a/arch/arm/mach-omap2/clock44xx_data.c +++ b/arch/arm/mach-omap2/clock44xx_data.c @@ -957,8 +957,8 @@ static struct dpll_data dpll_usb_dd = { .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), .autoidle_reg = OMAP4430_CM_AUTOIDLE_DPLL_USB, .idlest_reg = OMAP4430_CM_IDLEST_DPLL_USB, - .mult_mask = OMAP4430_DPLL_MULT_MASK, - .div1_mask = OMAP4430_DPLL_DIV_MASK, + .mult_mask = OMAP4430_DPLL_MULT_USB_MASK, + .div1_mask = OMAP4430_DPLL_DIV_0_7_MASK, .enable_mask = OMAP4430_DPLL_EN_MASK, .autoidle_mask = OMAP4430_AUTO_DPLL_MODE_MASK, .idlest_mask = OMAP4430_ST_DPLL_CLK_MASK, @@ -978,6 +978,7 @@ static struct clk dpll_usb_ck = { .recalc = &omap3_dpll_recalc, .round_rate = &omap2_dpll_round_rate, .set_rate = &omap3_noncore_dpll_set_rate, + .clkdm_name = "l3_init_clkdm", }; static struct clk dpll_usb_clkdcoldo_ck = { diff --git a/arch/arm/mach-omap2/clockdomains44xx_data.c b/arch/arm/mach-omap2/clockdomains44xx_data.c index 9299ac291d28..bd7ed13515cc 100644 --- a/arch/arm/mach-omap2/clockdomains44xx_data.c +++ b/arch/arm/mach-omap2/clockdomains44xx_data.c @@ -390,7 +390,7 @@ static struct clockdomain emu_sys_44xx_clkdm = { .prcm_partition = OMAP4430_PRM_PARTITION, .cm_inst = OMAP4430_PRM_EMU_CM_INST, .clkdm_offs = OMAP4430_PRM_EMU_CM_EMU_CDOFFS, - .flags = CLKDM_CAN_HWSUP, + .flags = CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_FORCE_WAKEUP, }; static struct clockdomain l3_dma_44xx_clkdm = { diff --git a/arch/arm/mach-omap2/gpmc-smsc911x.c b/arch/arm/mach-omap2/gpmc-smsc911x.c index 5e5880d6d099..b6c77be3e8f7 100644 --- a/arch/arm/mach-omap2/gpmc-smsc911x.c +++ b/arch/arm/mach-omap2/gpmc-smsc911x.c @@ -19,15 +19,11 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/smsc911x.h> -#include <linux/regulator/fixed.h> -#include <linux/regulator/machine.h> #include <plat/board.h> #include <plat/gpmc.h> #include <plat/gpmc-smsc911x.h> -static struct omap_smsc911x_platform_data *gpmc_cfg; - static struct resource gpmc_smsc911x_resources[] = { [0] = { .flags = IORESOURCE_MEM, @@ -41,51 +37,6 @@ static struct smsc911x_platform_config gpmc_smsc911x_config = { .phy_interface = PHY_INTERFACE_MODE_MII, .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, - .flags = SMSC911X_USE_16BIT, -}; - -static struct regulator_consumer_supply gpmc_smsc911x_supply[] = { - REGULATOR_SUPPLY("vddvario", "smsc911x.0"), - REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), -}; - -/* Generic regulator definition to satisfy smsc911x */ -static struct regulator_init_data gpmc_smsc911x_reg_init_data = { - .constraints = { - .min_uV = 3300000, - .max_uV = 3300000, - .valid_modes_mask = REGULATOR_MODE_NORMAL - | REGULATOR_MODE_STANDBY, - .valid_ops_mask = REGULATOR_CHANGE_MODE - | REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(gpmc_smsc911x_supply), - .consumer_supplies = gpmc_smsc911x_supply, -}; - -static struct fixed_voltage_config gpmc_smsc911x_fixed_reg_data = { - .supply_name = "gpmc_smsc911x", - .microvolts = 3300000, - .gpio = -EINVAL, - .startup_delay = 0, - .enable_high = 0, - .enabled_at_boot = 1, - .init_data = &gpmc_smsc911x_reg_init_data, -}; - -/* - * Platform device id of 42 is a temporary fix to avoid conflicts - * with other reg-fixed-voltage devices. The real fix should - * involve the driver core providing a way of dynamically - * assigning a unique id on registration for platform devices - * in the same name space. - */ -static struct platform_device gpmc_smsc911x_regulator = { - .name = "reg-fixed-voltage", - .id = 42, - .dev = { - .platform_data = &gpmc_smsc911x_fixed_reg_data, - }, }; /* @@ -93,23 +44,12 @@ static struct platform_device gpmc_smsc911x_regulator = { * assume that pin multiplexing is done in the board-*.c file, * or in the bootloader. */ -void __init gpmc_smsc911x_init(struct omap_smsc911x_platform_data *board_data) +void __init gpmc_smsc911x_init(struct omap_smsc911x_platform_data *gpmc_cfg) { struct platform_device *pdev; unsigned long cs_mem_base; int ret; - gpmc_cfg = board_data; - - if (!gpmc_cfg->id) { - ret = platform_device_register(&gpmc_smsc911x_regulator); - if (ret < 0) { - pr_err("Unable to register smsc911x regulators: %d\n", - ret); - return; - } - } - if (gpmc_cs_request(gpmc_cfg->cs, SZ_16M, &cs_mem_base) < 0) { pr_err("Failed to request GPMC mem region\n"); return; @@ -139,8 +79,7 @@ void __init gpmc_smsc911x_init(struct omap_smsc911x_platform_data *board_data) gpio_set_value(gpmc_cfg->gpio_reset, 1); } - if (gpmc_cfg->flags) - gpmc_smsc911x_config.flags = gpmc_cfg->flags; + gpmc_smsc911x_config.flags = gpmc_cfg->flags ? : SMSC911X_USE_16BIT; pdev = platform_device_register_resndata(NULL, "smsc911x", gpmc_cfg->id, gpmc_smsc911x_resources, ARRAY_SIZE(gpmc_smsc911x_resources), diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index 100db6217f39..b0268eaffe13 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -506,6 +506,13 @@ static void __init omap_hsmmc_init_one(struct omap2_hsmmc_info *hsmmcinfo, if (oh->dev_attr != NULL) { mmc_dev_attr = oh->dev_attr; mmc_data->controller_flags = mmc_dev_attr->flags; + /* + * erratum 2.1.1.128 doesn't apply if board has + * a transceiver is attached + */ + if (hsmmcinfo->transceiver) + mmc_data->controller_flags &= + ~OMAP_HSMMC_BROKEN_MULTIBLOCK_READ; } pdev = platform_device_alloc(name, ctrl_nr - 1); diff --git a/arch/arm/mach-omap2/include/mach/barriers.h b/arch/arm/mach-omap2/include/mach/barriers.h index 4fa72c7cc7cd..1c582a8592b9 100644 --- a/arch/arm/mach-omap2/include/mach/barriers.h +++ b/arch/arm/mach-omap2/include/mach/barriers.h @@ -22,6 +22,8 @@ #ifndef __MACH_BARRIERS_H #define __MACH_BARRIERS_H +#include <asm/outercache.h> + extern void omap_bus_sync(void); #define rmb() dsb() diff --git a/arch/arm/mach-omap2/include/mach/ctrl_module_pad_core_44xx.h b/arch/arm/mach-omap2/include/mach/ctrl_module_pad_core_44xx.h index 1e2d3322f33e..c88420de1151 100644 --- a/arch/arm/mach-omap2/include/mach/ctrl_module_pad_core_44xx.h +++ b/arch/arm/mach-omap2/include/mach/ctrl_module_pad_core_44xx.h @@ -941,10 +941,10 @@ #define OMAP4_DSI2_LANEENABLE_MASK (0x7 << 29) #define OMAP4_DSI1_LANEENABLE_SHIFT 24 #define OMAP4_DSI1_LANEENABLE_MASK (0x1f << 24) -#define OMAP4_DSI2_PIPD_SHIFT 19 -#define OMAP4_DSI2_PIPD_MASK (0x1f << 19) -#define OMAP4_DSI1_PIPD_SHIFT 14 -#define OMAP4_DSI1_PIPD_MASK (0x1f << 14) +#define OMAP4_DSI1_PIPD_SHIFT 19 +#define OMAP4_DSI1_PIPD_MASK (0x1f << 19) +#define OMAP4_DSI2_PIPD_SHIFT 14 +#define OMAP4_DSI2_PIPD_MASK (0x1f << 14) /* CONTROL_MCBSPLP */ #define OMAP4_ALBCTRLRX_FSX_SHIFT 31 diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index eba6cd3816f5..7144ae651d3d 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1395,7 +1395,7 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name) */ static int _ocp_softreset(struct omap_hwmod *oh) { - u32 v; + u32 v, softrst_mask; int c = 0; int ret = 0; @@ -1422,16 +1422,21 @@ static int _ocp_softreset(struct omap_hwmod *oh) goto dis_opt_clks; _write_sysconfig(v, oh); + if (oh->class->sysc->srst_udelay) + udelay(oh->class->sysc->srst_udelay); + if (oh->class->sysc->sysc_flags & SYSS_HAS_RESET_STATUS) omap_test_timeout((omap_hwmod_read(oh, oh->class->sysc->syss_offs) & SYSS_RESETDONE_MASK), MAX_MODULE_SOFTRESET_WAIT, c); - else if (oh->class->sysc->sysc_flags & SYSC_HAS_RESET_STATUS) + else if (oh->class->sysc->sysc_flags & SYSC_HAS_RESET_STATUS) { + softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift); omap_test_timeout(!(omap_hwmod_read(oh, oh->class->sysc->sysc_offs) - & SYSC_TYPE2_SOFTRESET_MASK), + & softrst_mask), MAX_MODULE_SOFTRESET_WAIT, c); + } if (c == MAX_MODULE_SOFTRESET_WAIT) pr_warning("omap_hwmod: %s: softreset failed (waited %d usec)\n", @@ -1477,6 +1482,11 @@ static int _reset(struct omap_hwmod *oh) ret = (oh->class->reset) ? oh->class->reset(oh) : _ocp_softreset(oh); + if (oh->class->sysc) { + _update_sysc_cache(oh); + _enable_sysc(oh); + } + return ret; } @@ -1786,20 +1796,9 @@ static int _setup(struct omap_hwmod *oh, void *data) return 0; } - if (!(oh->flags & HWMOD_INIT_NO_RESET)) { + if (!(oh->flags & HWMOD_INIT_NO_RESET)) _reset(oh); - /* - * OCP_SYSCONFIG bits need to be reprogrammed after a softreset. - * The _enable() function should be split to - * avoid the rewrite of the OCP_SYSCONFIG register. - */ - if (oh->class->sysc) { - _update_sysc_cache(oh); - _enable_sysc(oh); - } - } - postsetup_state = oh->_postsetup_state; if (postsetup_state == _HWMOD_STATE_UNKNOWN) postsetup_state = _HWMOD_STATE_ENABLED; @@ -2463,26 +2462,28 @@ int omap_hwmod_del_initiator_dep(struct omap_hwmod *oh, * @oh: struct omap_hwmod * * * Sets the module OCP socket ENAWAKEUP bit to allow the module to - * send wakeups to the PRCM. Eventually this should sets PRCM wakeup - * registers to cause the PRCM to receive wakeup events from the - * module. Does not set any wakeup routing registers beyond this - * point - if the module is to wake up any other module or subsystem, - * that must be set separately. Called by omap_device code. Returns - * -EINVAL on error or 0 upon success. + * send wakeups to the PRCM, and enable I/O ring wakeup events for + * this IP block if it has dynamic mux entries. Eventually this + * should set PRCM wakeup registers to cause the PRCM to receive + * wakeup events from the module. Does not set any wakeup routing + * registers beyond this point - if the module is to wake up any other + * module or subsystem, that must be set separately. Called by + * omap_device code. Returns -EINVAL on error or 0 upon success. */ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) { unsigned long flags; u32 v; - if (!oh->class->sysc || - !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) - return -EINVAL; - spin_lock_irqsave(&oh->_lock, flags); - v = oh->_sysc_cache; - _enable_wakeup(oh, &v); - _write_sysconfig(v, oh); + + if (oh->class->sysc && + (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) { + v = oh->_sysc_cache; + _enable_wakeup(oh, &v); + _write_sysconfig(v, oh); + } + _set_idle_ioring_wakeup(oh, true); spin_unlock_irqrestore(&oh->_lock, flags); @@ -2494,26 +2495,28 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) * @oh: struct omap_hwmod * * * Clears the module OCP socket ENAWAKEUP bit to prevent the module - * from sending wakeups to the PRCM. Eventually this should clear - * PRCM wakeup registers to cause the PRCM to ignore wakeup events - * from the module. Does not set any wakeup routing registers beyond - * this point - if the module is to wake up any other module or - * subsystem, that must be set separately. Called by omap_device - * code. Returns -EINVAL on error or 0 upon success. + * from sending wakeups to the PRCM, and disable I/O ring wakeup + * events for this IP block if it has dynamic mux entries. Eventually + * this should clear PRCM wakeup registers to cause the PRCM to ignore + * wakeup events from the module. Does not set any wakeup routing + * registers beyond this point - if the module is to wake up any other + * module or subsystem, that must be set separately. Called by + * omap_device code. Returns -EINVAL on error or 0 upon success. */ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh) { unsigned long flags; u32 v; - if (!oh->class->sysc || - !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) - return -EINVAL; - spin_lock_irqsave(&oh->_lock, flags); - v = oh->_sysc_cache; - _disable_wakeup(oh, &v); - _write_sysconfig(v, oh); + + if (oh->class->sysc && + (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) { + v = oh->_sysc_cache; + _disable_wakeup(oh, &v); + _write_sysconfig(v, oh); + } + _set_idle_ioring_wakeup(oh, false); spin_unlock_irqrestore(&oh->_lock, flags); diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index a5409ce3f323..a6bde34e443a 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -1000,7 +1000,6 @@ static struct omap_hwmod_ocp_if omap2420_l4_core__dss_venc = { .flags = OMAP_FIREWALL_L4, } }, - .flags = OCPIF_SWSUP_IDLE, .user = OCP_USER_MPU | OCP_USER_SDMA, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index c4f56cb60d7d..04a3885f4475 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -1049,7 +1049,6 @@ static struct omap_hwmod_ocp_if omap2430_l4_core__dss_venc = { .slave = &omap2430_dss_venc_hwmod, .clk = "dss_ick", .addr = omap2_dss_venc_addrs, - .flags = OCPIF_SWSUP_IDLE, .user = OCP_USER_MPU | OCP_USER_SDMA, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 34b9766d1d23..db86ce90c69f 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -1676,7 +1676,6 @@ static struct omap_hwmod_ocp_if omap3xxx_l4_core__dss_venc = { .flags = OMAP_FIREWALL_L4, } }, - .flags = OCPIF_SWSUP_IDLE, .user = OCP_USER_MPU | OCP_USER_SDMA, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 08daa5e0eb5f..6abc75753e42 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -2594,6 +2594,15 @@ static struct omap_hwmod omap44xx_ipu_hwmod = { static struct omap_hwmod_class_sysconfig omap44xx_iss_sysc = { .rev_offs = 0x0000, .sysc_offs = 0x0010, + /* + * ISS needs 100 OCP clk cycles delay after a softreset before + * accessing sysconfig again. + * The lowest frequency at the moment for L3 bus is 100 MHz, so + * 1usec delay is needed. Add an x2 margin to be safe (2 usecs). + * + * TODO: Indicate errata when available. + */ + .srst_udelay = 2, .sysc_flags = (SYSC_HAS_MIDLEMODE | SYSC_HAS_RESET_STATUS | SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET), .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | @@ -2996,6 +3005,11 @@ static struct omap_hwmod_ocp_if *omap44xx_mcbsp1_slaves[] = { &omap44xx_l4_abe__mcbsp1_dma, }; +static struct omap_hwmod_opt_clk mcbsp1_opt_clks[] = { + { .role = "pad_fck", .clk = "pad_clks_ck" }, + { .role = "prcm_clk", .clk = "mcbsp1_sync_mux_ck" }, +}; + static struct omap_hwmod omap44xx_mcbsp1_hwmod = { .name = "mcbsp1", .class = &omap44xx_mcbsp_hwmod_class, @@ -3012,6 +3026,8 @@ static struct omap_hwmod omap44xx_mcbsp1_hwmod = { }, .slaves = omap44xx_mcbsp1_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_mcbsp1_slaves), + .opt_clks = mcbsp1_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp1_opt_clks), }; /* mcbsp2 */ @@ -3071,6 +3087,11 @@ static struct omap_hwmod_ocp_if *omap44xx_mcbsp2_slaves[] = { &omap44xx_l4_abe__mcbsp2_dma, }; +static struct omap_hwmod_opt_clk mcbsp2_opt_clks[] = { + { .role = "pad_fck", .clk = "pad_clks_ck" }, + { .role = "prcm_clk", .clk = "mcbsp2_sync_mux_ck" }, +}; + static struct omap_hwmod omap44xx_mcbsp2_hwmod = { .name = "mcbsp2", .class = &omap44xx_mcbsp_hwmod_class, @@ -3087,6 +3108,8 @@ static struct omap_hwmod omap44xx_mcbsp2_hwmod = { }, .slaves = omap44xx_mcbsp2_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_mcbsp2_slaves), + .opt_clks = mcbsp2_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp2_opt_clks), }; /* mcbsp3 */ @@ -3146,6 +3169,11 @@ static struct omap_hwmod_ocp_if *omap44xx_mcbsp3_slaves[] = { &omap44xx_l4_abe__mcbsp3_dma, }; +static struct omap_hwmod_opt_clk mcbsp3_opt_clks[] = { + { .role = "pad_fck", .clk = "pad_clks_ck" }, + { .role = "prcm_clk", .clk = "mcbsp3_sync_mux_ck" }, +}; + static struct omap_hwmod omap44xx_mcbsp3_hwmod = { .name = "mcbsp3", .class = &omap44xx_mcbsp_hwmod_class, @@ -3162,6 +3190,8 @@ static struct omap_hwmod omap44xx_mcbsp3_hwmod = { }, .slaves = omap44xx_mcbsp3_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_mcbsp3_slaves), + .opt_clks = mcbsp3_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp3_opt_clks), }; /* mcbsp4 */ @@ -3200,6 +3230,11 @@ static struct omap_hwmod_ocp_if *omap44xx_mcbsp4_slaves[] = { &omap44xx_l4_per__mcbsp4, }; +static struct omap_hwmod_opt_clk mcbsp4_opt_clks[] = { + { .role = "pad_fck", .clk = "pad_clks_ck" }, + { .role = "prcm_clk", .clk = "mcbsp4_sync_mux_ck" }, +}; + static struct omap_hwmod omap44xx_mcbsp4_hwmod = { .name = "mcbsp4", .class = &omap44xx_mcbsp_hwmod_class, @@ -3216,6 +3251,8 @@ static struct omap_hwmod omap44xx_mcbsp4_hwmod = { }, .slaves = omap44xx_mcbsp4_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_mcbsp4_slaves), + .opt_clks = mcbsp4_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp4_opt_clks), }; /* diff --git a/arch/arm/mach-omap2/opp.c b/arch/arm/mach-omap2/opp.c index 9262a6b47702..de6d46451746 100644 --- a/arch/arm/mach-omap2/opp.c +++ b/arch/arm/mach-omap2/opp.c @@ -64,10 +64,10 @@ int __init omap_init_opp_table(struct omap_opp_def *opp_def, } oh = omap_hwmod_lookup(opp_def->hwmod_name); if (!oh || !oh->od) { - pr_warn("%s: no hwmod or odev for %s, [%d] " + pr_debug("%s: no hwmod or odev for %s, [%d] " "cannot add OPPs.\n", __func__, opp_def->hwmod_name, i); - return -EINVAL; + continue; } dev = &oh->od->pdev->dev; diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 238defc6f6df..703bd1099259 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -153,8 +153,7 @@ static void omap3_save_secure_ram_context(void) pwrdm_set_next_pwrst(mpu_pwrdm, mpu_next_state); /* Following is for error tracking, it should not happen */ if (ret) { - printk(KERN_ERR "save_secure_sram() returns %08x\n", - ret); + pr_err("save_secure_sram() returns %08x\n", ret); while (1) ; } @@ -289,7 +288,7 @@ void omap_sram_idle(void) break; default: /* Invalid state */ - printk(KERN_ERR "Invalid mpu state in sram_idle\n"); + pr_err("Invalid mpu state in sram_idle\n"); return; } @@ -439,18 +438,17 @@ restore: list_for_each_entry(pwrst, &pwrst_list, node) { state = pwrdm_read_prev_pwrst(pwrst->pwrdm); if (state > pwrst->next_state) { - printk(KERN_INFO "Powerdomain (%s) didn't enter " - "target state %d\n", + pr_info("Powerdomain (%s) didn't enter " + "target state %d\n", pwrst->pwrdm->name, pwrst->next_state); ret = -1; } omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); } if (ret) - printk(KERN_ERR "Could not enter target state in pm_suspend\n"); + pr_err("Could not enter target state in pm_suspend\n"); else - printk(KERN_INFO "Successfully put all powerdomains " - "to target state\n"); + pr_info("Successfully put all powerdomains to target state\n"); return ret; } @@ -734,21 +732,22 @@ static int __init omap3_pm_init(void) if (ret) { pr_err("pm: Failed to request pm_io irq\n"); - goto err1; + goto err2; } ret = pwrdm_for_each(pwrdms_setup, NULL); if (ret) { - printk(KERN_ERR "Failed to setup powerdomains\n"); - goto err2; + pr_err("Failed to setup powerdomains\n"); + goto err3; } (void) clkdm_for_each(omap_pm_clkdms_setup, NULL); mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); if (mpu_pwrdm == NULL) { - printk(KERN_ERR "Failed to get mpu_pwrdm\n"); - goto err2; + pr_err("Failed to get mpu_pwrdm\n"); + ret = -EINVAL; + goto err3; } neon_pwrdm = pwrdm_lookup("neon_pwrdm"); @@ -781,8 +780,8 @@ static int __init omap3_pm_init(void) omap3_secure_ram_storage = kmalloc(0x803F, GFP_KERNEL); if (!omap3_secure_ram_storage) - printk(KERN_ERR "Memory allocation failed when" - "allocating for secure sram context\n"); + pr_err("Memory allocation failed when " + "allocating for secure sram context\n"); local_irq_disable(); local_fiq_disable(); @@ -796,14 +795,17 @@ static int __init omap3_pm_init(void) } omap3_save_scratchpad_contents(); -err1: return ret; -err2: - free_irq(INT_34XX_PRCM_MPU_IRQ, NULL); + +err3: list_for_each_entry_safe(pwrst, tmp, &pwrst_list, node) { list_del(&pwrst->node); kfree(pwrst); } + free_irq(omap_prcm_event_to_irq("io"), omap3_pm_init); +err2: + free_irq(omap_prcm_event_to_irq("wkup"), NULL); +err1: return ret; } diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c index 9ccaadc2cf07..885625352429 100644 --- a/arch/arm/mach-omap2/pm44xx.c +++ b/arch/arm/mach-omap2/pm44xx.c @@ -144,7 +144,7 @@ static void omap_default_idle(void) static int __init omap4_pm_init(void) { int ret; - struct clockdomain *emif_clkdm, *mpuss_clkdm, *l3_1_clkdm; + struct clockdomain *emif_clkdm, *mpuss_clkdm, *l3_1_clkdm, *l4wkup; struct clockdomain *ducati_clkdm, *l3_2_clkdm, *l4_per_clkdm; if (!cpu_is_omap44xx()) @@ -168,14 +168,19 @@ static int __init omap4_pm_init(void) * MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as * expected. The hardware recommendation is to enable static * dependencies for these to avoid system lock ups or random crashes. + * The L4 wakeup depedency is added to workaround the OCP sync hardware + * BUG with 32K synctimer which lead to incorrect timer value read + * from the 32K counter. The BUG applies for GPTIMER1 and WDT2 which + * are part of L4 wakeup clockdomain. */ mpuss_clkdm = clkdm_lookup("mpuss_clkdm"); emif_clkdm = clkdm_lookup("l3_emif_clkdm"); l3_1_clkdm = clkdm_lookup("l3_1_clkdm"); l3_2_clkdm = clkdm_lookup("l3_2_clkdm"); l4_per_clkdm = clkdm_lookup("l4_per_clkdm"); + l4wkup = clkdm_lookup("l4_wkup_clkdm"); ducati_clkdm = clkdm_lookup("ducati_clkdm"); - if ((!mpuss_clkdm) || (!emif_clkdm) || (!l3_1_clkdm) || + if ((!mpuss_clkdm) || (!emif_clkdm) || (!l3_1_clkdm) || (!l4wkup) || (!l3_2_clkdm) || (!ducati_clkdm) || (!l4_per_clkdm)) goto err2; @@ -183,6 +188,7 @@ static int __init omap4_pm_init(void) ret |= clkdm_add_wkdep(mpuss_clkdm, l3_1_clkdm); ret |= clkdm_add_wkdep(mpuss_clkdm, l3_2_clkdm); ret |= clkdm_add_wkdep(mpuss_clkdm, l4_per_clkdm); + ret |= clkdm_add_wkdep(mpuss_clkdm, l4wkup); ret |= clkdm_add_wkdep(ducati_clkdm, l3_1_clkdm); ret |= clkdm_add_wkdep(ducati_clkdm, l3_2_clkdm); if (ret) { diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index 8a18d1bd61c8..96ad3dbeac34 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c @@ -972,7 +972,13 @@ int pwrdm_wait_transition(struct powerdomain *pwrdm) int pwrdm_state_switch(struct powerdomain *pwrdm) { - return _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); + int ret; + + ret = pwrdm_wait_transition(pwrdm); + if (!ret) + ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); + + return ret; } int pwrdm_clkdm_state_switch(struct clockdomain *clkdm) diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index eac623c7c3d8..f106d21ff581 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c @@ -147,8 +147,9 @@ static inline u32 _read_pending_irq_reg(u16 irqen_offs, u16 irqst_offs) u32 mask, st; /* XXX read mask from RAM? */ - mask = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, irqen_offs); - st = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, irqst_offs); + mask = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, + irqen_offs); + st = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, irqst_offs); return mask & st; } @@ -180,7 +181,7 @@ void omap44xx_prm_read_pending_irqs(unsigned long *events) */ void omap44xx_prm_ocp_barrier(void) { - omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_REVISION_PRM_OFFSET); } @@ -198,19 +199,19 @@ void omap44xx_prm_ocp_barrier(void) void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask) { saved_mask[0] = - omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQSTATUS_MPU_OFFSET); saved_mask[1] = - omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET); - omap4_prm_write_inst_reg(0, OMAP4430_PRM_DEVICE_INST, + omap4_prm_write_inst_reg(0, OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQENABLE_MPU_OFFSET); - omap4_prm_write_inst_reg(0, OMAP4430_PRM_DEVICE_INST, + omap4_prm_write_inst_reg(0, OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQENABLE_MPU_2_OFFSET); /* OCP barrier */ - omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_REVISION_PRM_OFFSET); } @@ -226,9 +227,9 @@ void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask) */ void omap44xx_prm_restore_irqen(u32 *saved_mask) { - omap4_prm_write_inst_reg(saved_mask[0], OMAP4430_PRM_DEVICE_INST, + omap4_prm_write_inst_reg(saved_mask[0], OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQENABLE_MPU_OFFSET); - omap4_prm_write_inst_reg(saved_mask[1], OMAP4430_PRM_DEVICE_INST, + omap4_prm_write_inst_reg(saved_mask[1], OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQENABLE_MPU_2_OFFSET); } diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 873b51d494ea..d28f848897d6 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c @@ -290,7 +290,7 @@ int omap_prcm_register_chain_handler(struct omap_prcm_irq_setup *irq_setup) goto err; } - for (i = 0; i <= irq_setup->nr_regs; i++) { + for (i = 0; i < irq_setup->nr_regs; i++) { gc = irq_alloc_generic_chip("PRCM", 1, irq_setup->base_irq + i * 32, prm_base, handle_level_irq); diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index 0cdd359a128e..9fc2f44188cb 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -108,8 +108,14 @@ static void omap_uart_set_noidle(struct platform_device *pdev) static void omap_uart_set_smartidle(struct platform_device *pdev) { struct omap_device *od = to_omap_device(pdev); + u8 idlemode; - omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_SMART); + if (od->hwmods[0]->class->sysc->idlemodes & SIDLE_SMART_WKUP) + idlemode = HWMOD_IDLEMODE_SMART_WKUP; + else + idlemode = HWMOD_IDLEMODE_SMART; + + omap_hwmod_set_slave_idlemode(od->hwmods[0], idlemode); } #else @@ -120,124 +126,8 @@ static void omap_uart_set_smartidle(struct platform_device *pdev) {} #endif /* CONFIG_PM */ #ifdef CONFIG_OMAP_MUX -static struct omap_device_pad default_uart1_pads[] __initdata = { - { - .name = "uart1_cts.uart1_cts", - .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0, - }, - { - .name = "uart1_rts.uart1_rts", - .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0, - }, - { - .name = "uart1_tx.uart1_tx", - .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0, - }, - { - .name = "uart1_rx.uart1_rx", - .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP, - .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0, - .idle = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0, - }, -}; - -static struct omap_device_pad default_uart2_pads[] __initdata = { - { - .name = "uart2_cts.uart2_cts", - .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0, - }, - { - .name = "uart2_rts.uart2_rts", - .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0, - }, - { - .name = "uart2_tx.uart2_tx", - .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0, - }, - { - .name = "uart2_rx.uart2_rx", - .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP, - .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0, - .idle = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0, - }, -}; - -static struct omap_device_pad default_uart3_pads[] __initdata = { - { - .name = "uart3_cts_rctx.uart3_cts_rctx", - .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0, - }, - { - .name = "uart3_rts_sd.uart3_rts_sd", - .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0, - }, - { - .name = "uart3_tx_irtx.uart3_tx_irtx", - .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0, - }, - { - .name = "uart3_rx_irrx.uart3_rx_irrx", - .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP, - .enable = OMAP_PIN_INPUT | OMAP_MUX_MODE0, - .idle = OMAP_PIN_INPUT | OMAP_MUX_MODE0, - }, -}; - -static struct omap_device_pad default_omap36xx_uart4_pads[] __initdata = { - { - .name = "gpmc_wait2.uart4_tx", - .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0, - }, - { - .name = "gpmc_wait3.uart4_rx", - .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP, - .enable = OMAP_PIN_INPUT | OMAP_MUX_MODE2, - .idle = OMAP_PIN_INPUT | OMAP_MUX_MODE2, - }, -}; - -static struct omap_device_pad default_omap4_uart4_pads[] __initdata = { - { - .name = "uart4_tx.uart4_tx", - .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0, - }, - { - .name = "uart4_rx.uart4_rx", - .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP, - .enable = OMAP_PIN_INPUT | OMAP_MUX_MODE0, - .idle = OMAP_PIN_INPUT | OMAP_MUX_MODE0, - }, -}; - static void omap_serial_fill_default_pads(struct omap_board_data *bdata) { - switch (bdata->id) { - case 0: - bdata->pads = default_uart1_pads; - bdata->pads_cnt = ARRAY_SIZE(default_uart1_pads); - break; - case 1: - bdata->pads = default_uart2_pads; - bdata->pads_cnt = ARRAY_SIZE(default_uart2_pads); - break; - case 2: - bdata->pads = default_uart3_pads; - bdata->pads_cnt = ARRAY_SIZE(default_uart3_pads); - break; - case 3: - if (cpu_is_omap44xx()) { - bdata->pads = default_omap4_uart4_pads; - bdata->pads_cnt = - ARRAY_SIZE(default_omap4_uart4_pads); - } else if (cpu_is_omap3630()) { - bdata->pads = default_omap36xx_uart4_pads; - bdata->pads_cnt = - ARRAY_SIZE(default_omap36xx_uart4_pads); - } - break; - default: - break; - } } #else static void omap_serial_fill_default_pads(struct omap_board_data *bdata) {} diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c index 4b57757bf9d1..7a7b89304c48 100644 --- a/arch/arm/mach-omap2/twl-common.c +++ b/arch/arm/mach-omap2/twl-common.c @@ -37,6 +37,16 @@ static struct i2c_board_info __initdata pmic_i2c_board_info = { .flags = I2C_CLIENT_WAKE, }; +static struct i2c_board_info __initdata omap4_i2c1_board_info[] = { + { + .addr = 0x48, + .flags = I2C_CLIENT_WAKE, + }, + { + I2C_BOARD_INFO("twl6040", 0x4b), + }, +}; + void __init omap_pmic_init(int bus, u32 clkrate, const char *pmic_type, int pmic_irq, struct twl4030_platform_data *pmic_data) @@ -49,14 +59,31 @@ void __init omap_pmic_init(int bus, u32 clkrate, omap_register_i2c_bus(bus, clkrate, &pmic_i2c_board_info, 1); } +void __init omap4_pmic_init(const char *pmic_type, + struct twl4030_platform_data *pmic_data, + struct twl6040_platform_data *twl6040_data, int twl6040_irq) +{ + /* PMIC part*/ + strncpy(omap4_i2c1_board_info[0].type, pmic_type, + sizeof(omap4_i2c1_board_info[0].type)); + omap4_i2c1_board_info[0].irq = OMAP44XX_IRQ_SYS_1N; + omap4_i2c1_board_info[0].platform_data = pmic_data; + + /* TWL6040 audio IC part */ + omap4_i2c1_board_info[1].irq = twl6040_irq; + omap4_i2c1_board_info[1].platform_data = twl6040_data; + + omap_register_i2c_bus(1, 400, omap4_i2c1_board_info, 2); + +} + void __init omap_pmic_late_init(void) { /* Init the OMAP TWL parameters (if PMIC has been registerd) */ - if (!pmic_i2c_board_info.irq) - return; - - omap3_twl_init(); - omap4_twl_init(); + if (pmic_i2c_board_info.irq) + omap3_twl_init(); + if (omap4_i2c1_board_info[0].irq) + omap4_twl_init(); } #if defined(CONFIG_ARCH_OMAP3) diff --git a/arch/arm/mach-omap2/twl-common.h b/arch/arm/mach-omap2/twl-common.h index 275dde8cb27a..09627483a57f 100644 --- a/arch/arm/mach-omap2/twl-common.h +++ b/arch/arm/mach-omap2/twl-common.h @@ -29,6 +29,7 @@ struct twl4030_platform_data; +struct twl6040_platform_data; void omap_pmic_init(int bus, u32 clkrate, const char *pmic_type, int pmic_irq, struct twl4030_platform_data *pmic_data); @@ -46,12 +47,9 @@ static inline void omap3_pmic_init(const char *pmic_type, omap_pmic_init(1, 2600, pmic_type, INT_34XX_SYS_NIRQ, pmic_data); } -static inline void omap4_pmic_init(const char *pmic_type, - struct twl4030_platform_data *pmic_data) -{ - /* Phoenix Audio IC needs I2C1 to start with 400 KHz or less */ - omap_pmic_init(1, 400, pmic_type, OMAP44XX_IRQ_SYS_1N, pmic_data); -} +void omap4_pmic_init(const char *pmic_type, + struct twl4030_platform_data *pmic_data, + struct twl6040_platform_data *audio_data, int twl6040_irq); void omap3_pmic_get_config(struct twl4030_platform_data *pmic_data, u32 pdata_flags, u32 regulators_flags); diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c index f51348dafafd..dde8a11f47d5 100644 --- a/arch/arm/mach-omap2/usb-host.c +++ b/arch/arm/mach-omap2/usb-host.c @@ -54,7 +54,7 @@ static struct omap_device_pm_latency omap_uhhtll_latency[] = { /* * setup_ehci_io_mux - initialize IO pad mux for USBHOST */ -static void setup_ehci_io_mux(const enum usbhs_omap_port_mode *port_mode) +static void __init setup_ehci_io_mux(const enum usbhs_omap_port_mode *port_mode) { switch (port_mode[0]) { case OMAP_EHCI_PORT_MODE_PHY: @@ -197,7 +197,8 @@ static void setup_ehci_io_mux(const enum usbhs_omap_port_mode *port_mode) return; } -static void setup_4430ehci_io_mux(const enum usbhs_omap_port_mode *port_mode) +static +void __init setup_4430ehci_io_mux(const enum usbhs_omap_port_mode *port_mode) { switch (port_mode[0]) { case OMAP_EHCI_PORT_MODE_PHY: @@ -315,7 +316,7 @@ static void setup_4430ehci_io_mux(const enum usbhs_omap_port_mode *port_mode) } } -static void setup_ohci_io_mux(const enum usbhs_omap_port_mode *port_mode) +static void __init setup_ohci_io_mux(const enum usbhs_omap_port_mode *port_mode) { switch (port_mode[0]) { case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0: @@ -412,7 +413,8 @@ static void setup_ohci_io_mux(const enum usbhs_omap_port_mode *port_mode) } } -static void setup_4430ohci_io_mux(const enum usbhs_omap_port_mode *port_mode) +static +void __init setup_4430ohci_io_mux(const enum usbhs_omap_port_mode *port_mode) { switch (port_mode[0]) { case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0: diff --git a/arch/arm/mach-orion5x/mpp.h b/arch/arm/mach-orion5x/mpp.h index eac68978a2c2..db70e79a1198 100644 --- a/arch/arm/mach-orion5x/mpp.h +++ b/arch/arm/mach-orion5x/mpp.h @@ -65,8 +65,8 @@ #define MPP8_GIGE MPP(8, 0x1, 0, 0, 1, 1, 1) #define MPP9_UNUSED MPP(9, 0x0, 0, 0, 1, 1, 1) -#define MPP9_GPIO MPP(9, 0x0, 0, 0, 1, 1, 1) -#define MPP9_GIGE MPP(9, 0x1, 1, 1, 1, 1, 1) +#define MPP9_GPIO MPP(9, 0x0, 1, 1, 1, 1, 1) +#define MPP9_GIGE MPP(9, 0x1, 0, 0, 1, 1, 1) #define MPP10_UNUSED MPP(10, 0x0, 0, 0, 1, 1, 1) #define MPP10_GPIO MPP(10, 0x0, 1, 1, 1, 1, 1) diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index 109ccd2a8885..fe2d1f80ef50 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig @@ -113,6 +113,7 @@ config MACH_ARMCORE select IWMMXT select PXA25x select MIGHT_HAVE_PCI + select NEED_MACH_IO_H if PCI config MACH_EM_X270 bool "CompuLab EM-x270 platform" diff --git a/arch/arm/mach-pxa/include/mach/io.h b/arch/arm/mach-pxa/include/mach/io.h new file mode 100644 index 000000000000..cd78b7fe3567 --- /dev/null +++ b/arch/arm/mach-pxa/include/mach/io.h @@ -0,0 +1,17 @@ +/* + * arch/arm/mach-pxa/include/mach/io.h + * + * Copied from asm/arch/sa1100/io.h + */ +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +/* + * We don't actually have real ISA nor PCI buses, but there is so many + * drivers out there that might just work if we fake them... + */ +#define __io(a) __typesafe_io(a) + +#endif diff --git a/arch/arm/mach-pxa/include/mach/mfp-pxa2xx.h b/arch/arm/mach-pxa/include/mach/mfp-pxa2xx.h index c54cef25895c..cbf51ae81855 100644 --- a/arch/arm/mach-pxa/include/mach/mfp-pxa2xx.h +++ b/arch/arm/mach-pxa/include/mach/mfp-pxa2xx.h @@ -17,6 +17,7 @@ * * bit 23 - Input/Output (PXA2xx specific) * bit 24 - Wakeup Enable(PXA2xx specific) + * bit 25 - Keep Output (PXA2xx specific) */ #define MFP_DIR_IN (0x0 << 23) @@ -25,6 +26,12 @@ #define MFP_DIR(x) (((x) >> 23) & 0x1) #define MFP_LPM_CAN_WAKEUP (0x1 << 24) + +/* + * MFP_LPM_KEEP_OUTPUT must be specified for pins that need to + * retain their last output level (low or high). + * Note: MFP_LPM_KEEP_OUTPUT has no effect on pins configured for input. + */ #define MFP_LPM_KEEP_OUTPUT (0x1 << 25) #define WAKEUP_ON_EDGE_RISE (MFP_LPM_CAN_WAKEUP | MFP_LPM_EDGE_RISE) diff --git a/arch/arm/mach-pxa/mfp-pxa2xx.c b/arch/arm/mach-pxa/mfp-pxa2xx.c index b0a842887780..ef0426a159d4 100644 --- a/arch/arm/mach-pxa/mfp-pxa2xx.c +++ b/arch/arm/mach-pxa/mfp-pxa2xx.c @@ -33,6 +33,8 @@ #define BANK_OFF(n) (((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2)) #define GPLR(x) __REG2(0x40E00000, BANK_OFF((x) >> 5)) #define GPDR(x) __REG2(0x40E00000, BANK_OFF((x) >> 5) + 0x0c) +#define GPSR(x) __REG2(0x40E00000, BANK_OFF((x) >> 5) + 0x18) +#define GPCR(x) __REG2(0x40E00000, BANK_OFF((x) >> 5) + 0x24) #define PWER_WE35 (1 << 24) @@ -348,6 +350,7 @@ static inline void pxa27x_mfp_init(void) {} #ifdef CONFIG_PM static unsigned long saved_gafr[2][4]; static unsigned long saved_gpdr[4]; +static unsigned long saved_gplr[4]; static unsigned long saved_pgsr[4]; static int pxa2xx_mfp_suspend(void) @@ -366,14 +369,26 @@ static int pxa2xx_mfp_suspend(void) } for (i = 0; i <= gpio_to_bank(pxa_last_gpio); i++) { - saved_gafr[0][i] = GAFR_L(i); saved_gafr[1][i] = GAFR_U(i); saved_gpdr[i] = GPDR(i * 32); + saved_gplr[i] = GPLR(i * 32); saved_pgsr[i] = PGSR(i); - GPDR(i * 32) = gpdr_lpm[i]; + GPSR(i * 32) = PGSR(i); + GPCR(i * 32) = ~PGSR(i); + } + + /* set GPDR bits taking into account MFP_LPM_KEEP_OUTPUT */ + for (i = 0; i < pxa_last_gpio; i++) { + if ((gpdr_lpm[gpio_to_bank(i)] & GPIO_bit(i)) || + ((gpio_desc[i].config & MFP_LPM_KEEP_OUTPUT) && + (saved_gpdr[gpio_to_bank(i)] & GPIO_bit(i)))) + GPDR(i) |= GPIO_bit(i); + else + GPDR(i) &= ~GPIO_bit(i); } + return 0; } @@ -384,6 +399,8 @@ static void pxa2xx_mfp_resume(void) for (i = 0; i <= gpio_to_bank(pxa_last_gpio); i++) { GAFR_L(i) = saved_gafr[0][i]; GAFR_U(i) = saved_gafr[1][i]; + GPSR(i * 32) = saved_gplr[i]; + GPCR(i * 32) = ~saved_gplr[i]; GPDR(i * 32) = saved_gpdr[i]; PGSR(i) = saved_pgsr[i]; } diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 6bce78edce7a..4726c246dcdc 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -421,8 +421,11 @@ void __init pxa27x_set_i2c_power_info(struct i2c_pxa_platform_data *info) pxa_register_device(&pxa27x_device_i2c_power, info); } +static struct pxa_gpio_platform_data pxa27x_gpio_info __initdata = { + .gpio_set_wake = gpio_set_wake, +}; + static struct platform_device *devices[] __initdata = { - &pxa_device_gpio, &pxa27x_device_udc, &pxa_device_pmu, &pxa_device_i2s, @@ -458,6 +461,7 @@ static int __init pxa27x_init(void) register_syscore_ops(&pxa2xx_mfp_syscore_ops); register_syscore_ops(&pxa2xx_clock_syscore_ops); + pxa_register_device(&pxa_device_gpio, &pxa27x_gpio_info); ret = platform_add_devices(devices, ARRAY_SIZE(devices)); } diff --git a/arch/arm/mach-s3c24xx/Kconfig b/arch/arm/mach-s3c24xx/Kconfig index 0f3a327ebcaa..b34287ab5afd 100644 --- a/arch/arm/mach-s3c24xx/Kconfig +++ b/arch/arm/mach-s3c24xx/Kconfig @@ -111,10 +111,6 @@ config S3C24XX_SETUP_TS help Compile in platform device definition for Samsung TouchScreen. -# cpu-specific sections - -if CPU_S3C2410 - config S3C2410_DMA bool depends on S3C24XX_DMA && (CPU_S3C2410 || CPU_S3C2442) @@ -127,6 +123,10 @@ config S3C2410_PM help Power Management code common to S3C2410 and better +# cpu-specific sections + +if CPU_S3C2410 + config S3C24XX_SIMTEC_NOR bool help diff --git a/arch/arm/mach-s3c24xx/common.h b/arch/arm/mach-s3c24xx/common.h new file mode 100644 index 000000000000..c2f596e7bc2d --- /dev/null +++ b/arch/arm/mach-s3c24xx/common.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Common Header for S3C24XX SoCs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ARCH_ARM_MACH_S3C24XX_COMMON_H +#define __ARCH_ARM_MACH_S3C24XX_COMMON_H __FILE__ + +void s3c2410_restart(char mode, const char *cmd); +void s3c244x_restart(char mode, const char *cmd); + +#endif /* __ARCH_ARM_MACH_S3C24XX_COMMON_H */ diff --git a/arch/arm/mach-s5pv210/dma.c b/arch/arm/mach-s5pv210/dma.c index 86ce62f66190..b8337e248b09 100644 --- a/arch/arm/mach-s5pv210/dma.c +++ b/arch/arm/mach-s5pv210/dma.c @@ -33,8 +33,6 @@ #include <mach/irqs.h> #include <mach/dma.h> -static u64 dma_dmamask = DMA_BIT_MASK(32); - static u8 pdma0_peri[] = { DMACH_UART0_RX, DMACH_UART0_TX, diff --git a/arch/arm/mach-s5pv210/mach-aquila.c b/arch/arm/mach-s5pv210/mach-aquila.c index a9ea64e0da0d..48d018f2332b 100644 --- a/arch/arm/mach-s5pv210/mach-aquila.c +++ b/arch/arm/mach-s5pv210/mach-aquila.c @@ -484,8 +484,8 @@ static struct wm8994_pdata wm8994_platform_data = { .gpio_defaults[8] = 0x0100, .gpio_defaults[9] = 0x0100, .gpio_defaults[10] = 0x0100, - .ldo[0] = { S5PV210_MP03(6), NULL, &wm8994_ldo1_data }, /* XM0FRNB_2 */ - .ldo[1] = { 0, NULL, &wm8994_ldo2_data }, + .ldo[0] = { S5PV210_MP03(6), &wm8994_ldo1_data }, /* XM0FRNB_2 */ + .ldo[1] = { 0, &wm8994_ldo2_data }, }; /* GPIO I2C PMIC */ diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 2cf5ed75f390..32395664e879 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -25,6 +25,7 @@ #include <linux/gpio_keys.h> #include <linux/input.h> #include <linux/gpio.h> +#include <linux/mmc/host.h> #include <linux/interrupt.h> #include <asm/hardware/vic.h> @@ -674,8 +675,8 @@ static struct wm8994_pdata wm8994_platform_data = { .gpio_defaults[8] = 0x0100, .gpio_defaults[9] = 0x0100, .gpio_defaults[10] = 0x0100, - .ldo[0] = { S5PV210_MP03(6), NULL, &wm8994_ldo1_data }, /* XM0FRNB_2 */ - .ldo[1] = { 0, NULL, &wm8994_ldo2_data }, + .ldo[0] = { S5PV210_MP03(6), &wm8994_ldo1_data }, /* XM0FRNB_2 */ + .ldo[1] = { 0, &wm8994_ldo2_data }, }; /* GPIO I2C PMIC */ @@ -765,6 +766,7 @@ static void __init goni_pmic_init(void) /* MoviNAND */ static struct s3c_sdhci_platdata goni_hsmmc0_data __initdata = { .max_width = 4, + .host_caps2 = MMC_CAP2_BROKEN_VOLTAGE, .cd_type = S3C_SDHCI_CD_PERMANENT, }; diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index 48885b7efd6b..c7f418b0cde9 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -313,6 +313,10 @@ static struct sa1100fb_mach_info collie_lcd_info = { .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), + +#ifdef CONFIG_BACKLIGHT_LOCOMO + .lcd_power = locomolcd_power +#endif }; static void __init collie_init(void) diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 7c524b4e415d..16be4c56abe3 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -306,7 +306,7 @@ void sa11x0_register_irda(struct irda_platform_data *irda) } static struct resource sa1100_rtc_resources[] = { - DEFINE_RES_MEM(0x90010000, 0x9001003f), + DEFINE_RES_MEM(0x90010000, 0x40), DEFINE_RES_IRQ_NAMED(IRQ_RTC1Hz, "rtc 1Hz"), DEFINE_RES_IRQ_NAMED(IRQ_RTCAlrm, "rtc alarm"), }; diff --git a/arch/arm/mach-sa1100/include/mach/collie.h b/arch/arm/mach-sa1100/include/mach/collie.h index 52acda7061b7..f33679d2d3ee 100644 --- a/arch/arm/mach-sa1100/include/mach/collie.h +++ b/arch/arm/mach-sa1100/include/mach/collie.h @@ -1,7 +1,7 @@ /* * arch/arm/mach-sa1100/include/mach/collie.h * - * This file contains the hardware specific definitions for Assabet + * This file contains the hardware specific definitions for Collie * Only include this file from SA1100-specific files. * * ChangeLog: @@ -13,6 +13,7 @@ #ifndef __ASM_ARCH_COLLIE_H #define __ASM_ARCH_COLLIE_H +extern void locomolcd_power(int on); #define COLLIE_SCOOP_GPIO_BASE (GPIO_MAX + 1) #define COLLIE_GPIO_CHARGE_ON (COLLIE_SCOOP_GPIO_BASE + 0) diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index 34560cab45d9..2cda0c2af230 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -58,6 +58,7 @@ config MACH_AP4EVB depends on ARCH_SH7372 select ARCH_REQUIRE_GPIOLIB select SH_LCD_MIPI_DSI + select SND_SOC_AK4642 if SND_SIMPLE_CARD choice prompt "AP4EVB LCD panel selection" @@ -82,6 +83,7 @@ config MACH_MACKEREL bool "mackerel board" depends on ARCH_SH7372 select ARCH_REQUIRE_GPIOLIB + select SND_SOC_AK4642 if SND_SIMPLE_CARD config MACH_KOTA2 bool "KOTA2 board" diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c index cb224a344af0..0891ec6e27f5 100644 --- a/arch/arm/mach-shmobile/board-ag5evm.c +++ b/arch/arm/mach-shmobile/board-ag5evm.c @@ -365,23 +365,13 @@ static struct platform_device mipidsi0_device = { }; /* SDHI0 */ -static irqreturn_t ag5evm_sdhi0_gpio_cd(int irq, void *arg) -{ - struct device *dev = arg; - struct sh_mobile_sdhi_info *info = dev->platform_data; - struct tmio_mmc_data *pdata = info->pdata; - - tmio_mmc_cd_wakeup(pdata); - - return IRQ_HANDLED; -} - static struct sh_mobile_sdhi_info sdhi0_info = { .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, + .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, .tmio_caps = MMC_CAP_SD_HIGHSPEED, .tmio_ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .cd_gpio = GPIO_PORT251, }; static struct resource sdhi0_resources[] = { @@ -557,7 +547,6 @@ static void __init ag5evm_init(void) lcd_backlight_reset(); /* enable SDHI0 on CN15 [SD I/F] */ - gpio_request(GPIO_FN_SDHICD0, NULL); gpio_request(GPIO_FN_SDHIWP0, NULL); gpio_request(GPIO_FN_SDHICMD0, NULL); gpio_request(GPIO_FN_SDHICLK0, NULL); @@ -566,13 +555,6 @@ static void __init ag5evm_init(void) gpio_request(GPIO_FN_SDHID0_1, NULL); gpio_request(GPIO_FN_SDHID0_0, NULL); - if (!request_irq(intcs_evt2irq(0x3c0), ag5evm_sdhi0_gpio_cd, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "sdhi0 cd", &sdhi0_device.dev)) - sdhi0_info.tmio_flags |= TMIO_MMC_HAS_COLD_CD; - else - pr_warn("Unable to setup SDHI0 GPIO IRQ\n"); - /* enable SDHI1 on CN4 [WLAN I/F] */ gpio_request(GPIO_FN_SDHICLK1, NULL); gpio_request(GPIO_FN_SDHICMD1_PU, NULL); diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index b56dde2732bb..8302265977a8 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -50,6 +50,7 @@ #include <media/soc_camera.h> #include <sound/sh_fsi.h> +#include <sound/simple_card.h> #include <video/sh_mobile_hdmi.h> #include <video/sh_mobile_lcdc.h> @@ -785,17 +786,25 @@ static struct platform_device fsi_device = { }, }; -static struct fsi_ak4642_info fsi2_ak4643_info = { +static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = { + .fmt = SND_SOC_DAIFMT_LEFT_J, + .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, + .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, + .sysclk = 11289600, +}; + +static struct asoc_simple_card_info fsi2_ak4643_info = { .name = "AK4643", .card = "FSI2A-AK4643", .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi2", - .id = FSI_PORT_A, + .codec_dai = "ak4642-hifi", + .init = &fsi2_ak4643_init_info, }; static struct platform_device fsi_ak4643_device = { - .name = "fsi-ak4642-audio", + .name = "asoc-simple-card", .dev = { .platform_data = &fsi2_ak4643_info, }, @@ -900,8 +909,26 @@ static struct platform_device lcdc1_device = { }, }; +static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { + .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM, +}; + +static struct asoc_simple_card_info fsi2_hdmi_info = { + .name = "HDMI", + .card = "FSI2B-HDMI", + .cpu_dai = "fsib-dai", + .codec = "sh-mobile-hdmi", + .platform = "sh_fsi2", + .codec_dai = "sh_mobile_hdmi-hifi", + .init = &fsi2_hdmi_init_info, +}; + static struct platform_device fsi_hdmi_device = { - .name = "sh_fsi2_b_hdmi", + .name = "asoc-simple-card", + .id = 1, + .dev = { + .platform_data = &fsi2_hdmi_info, + }, }; static struct gpio_led ap4evb_leds[] = { diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index f49e28abe0ab..fb27bac4cfd1 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -53,6 +53,7 @@ #include <media/soc_camera.h> #include <media/soc_camera_platform.h> #include <sound/sh_fsi.h> +#include <sound/simple_card.h> #include <mach/common.h> #include <mach/irqs.h> @@ -502,8 +503,26 @@ static struct platform_device hdmi_lcdc_device = { }, }; +static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { + .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM, +}; + +static struct asoc_simple_card_info fsi2_hdmi_info = { + .name = "HDMI", + .card = "FSI2B-HDMI", + .cpu_dai = "fsib-dai", + .codec = "sh-mobile-hdmi", + .platform = "sh_fsi2", + .codec_dai = "sh_mobile_hdmi-hifi", + .init = &fsi2_hdmi_init_info, +}; + static struct platform_device fsi_hdmi_device = { - .name = "sh_fsi2_b_hdmi", + .name = "asoc-simple-card", + .id = 1, + .dev = { + .platform_data = &fsi2_hdmi_info, + }, }; static void __init hdmi_init_pm_clock(void) @@ -941,17 +960,25 @@ static struct platform_device fsi_device = { }, }; -static struct fsi_ak4642_info fsi2_ak4643_info = { +static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = { + .fmt = SND_SOC_DAIFMT_LEFT_J, + .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, + .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, + .sysclk = 11289600, +}; + +static struct asoc_simple_card_info fsi2_ak4643_info = { .name = "AK4643", .card = "FSI2A-AK4643", .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi2", - .id = FSI_PORT_A, + .codec_dai = "ak4642-hifi", + .init = &fsi2_ak4643_init_info, }; static struct platform_device fsi_ak4643_device = { - .name = "fsi-ak4642-audio", + .name = "asoc-simple-card", .dev = { .platform_data = &fsi2_ak4643_info, }, @@ -1011,21 +1038,12 @@ static int slot_cn7_get_cd(struct platform_device *pdev) } /* SDHI0 */ -static irqreturn_t mackerel_sdhi0_gpio_cd(int irq, void *arg) -{ - struct device *dev = arg; - struct sh_mobile_sdhi_info *info = dev->platform_data; - struct tmio_mmc_data *pdata = info->pdata; - - tmio_mmc_cd_wakeup(pdata); - - return IRQ_HANDLED; -} - static struct sh_mobile_sdhi_info sdhi0_info = { .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, + .tmio_flags = TMIO_MMC_USE_GPIO_CD, .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, + .cd_gpio = GPIO_PORT172, }; static struct resource sdhi0_resources[] = { @@ -1384,7 +1402,6 @@ static void __init mackerel_init(void) { u32 srcr4; struct clk *clk; - int ret; /* External clock source */ clk_set_rate(&sh7372_dv_clki_clk, 27000000); @@ -1481,7 +1498,6 @@ static void __init mackerel_init(void) irq_set_irq_type(IRQ21, IRQ_TYPE_LEVEL_HIGH); /* enable SDHI0 */ - gpio_request(GPIO_FN_SDHICD0, NULL); gpio_request(GPIO_FN_SDHIWP0, NULL); gpio_request(GPIO_FN_SDHICMD0, NULL); gpio_request(GPIO_FN_SDHICLK0, NULL); @@ -1490,13 +1506,6 @@ static void __init mackerel_init(void) gpio_request(GPIO_FN_SDHID0_1, NULL); gpio_request(GPIO_FN_SDHID0_0, NULL); - ret = request_irq(evt2irq(0x3340), mackerel_sdhi0_gpio_cd, - IRQF_TRIGGER_FALLING, "sdhi0 cd", &sdhi0_device.dev); - if (!ret) - sdhi0_info.tmio_flags |= TMIO_MMC_HAS_COLD_CD; - else - pr_err("Cannot get IRQ #%d: %d\n", evt2irq(0x3340), ret); - #if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE) /* enable SDHI1 */ gpio_request(GPIO_FN_SDHICMD1, NULL); diff --git a/arch/arm/mach-shmobile/headsmp.S b/arch/arm/mach-shmobile/headsmp.S index 6ac015c89206..b202c1272526 100644 --- a/arch/arm/mach-shmobile/headsmp.S +++ b/arch/arm/mach-shmobile/headsmp.S @@ -16,6 +16,59 @@ __CPUINIT +/* Cache invalidation nicked from arch/arm/mach-imx/head-v7.S, thanks! + * + * The secondary kernel init calls v7_flush_dcache_all before it enables + * the L1; however, the L1 comes out of reset in an undefined state, so + * the clean + invalidate performed by v7_flush_dcache_all causes a bunch + * of cache lines with uninitialized data and uninitialized tags to get + * written out to memory, which does really unpleasant things to the main + * processor. We fix this by performing an invalidate, rather than a + * clean + invalidate, before jumping into the kernel. + * + * This funciton is cloned from arch/arm/mach-tegra/headsmp.S, and needs + * to be called for both secondary cores startup and primary core resume + * procedures. Ideally, it should be moved into arch/arm/mm/cache-v7.S. + */ +ENTRY(v7_invalidate_l1) + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache + mcr p15, 2, r0, c0, c0, 0 + mrc p15, 1, r0, c0, c0, 0 + + ldr r1, =0x7fff + and r2, r1, r0, lsr #13 + + ldr r1, =0x3ff + + and r3, r1, r0, lsr #3 @ NumWays - 1 + add r2, r2, #1 @ NumSets + + and r0, r0, #0x7 + add r0, r0, #4 @ SetShift + + clz r1, r3 @ WayShift + add r4, r3, #1 @ NumWays +1: sub r2, r2, #1 @ NumSets-- + mov r3, r4 @ Temp = NumWays +2: subs r3, r3, #1 @ Temp-- + mov r5, r3, lsl r1 + mov r6, r2, lsl r0 + orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift) + mcr p15, 0, r5, c7, c6, 2 + bgt 2b + cmp r2, #0 + bgt 1b + dsb + isb + mov pc, lr +ENDPROC(v7_invalidate_l1) + +ENTRY(shmobile_invalidate_start) + bl v7_invalidate_l1 + b secondary_startup +ENDPROC(shmobile_invalidate_start) + /* * Reset vector for secondary CPUs. * This will be mapped at address 0 by SBAR register. @@ -24,4 +77,5 @@ .align 12 ENTRY(shmobile_secondary_vector) ldr pc, 1f -1: .long secondary_startup - PAGE_OFFSET + PLAT_PHYS_OFFSET +1: .long shmobile_invalidate_start - PAGE_OFFSET + PLAT_PHYS_OFFSET +ENDPROC(shmobile_secondary_vector) diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h index 83ad3fe0a75f..c85e6ecda606 100644 --- a/arch/arm/mach-shmobile/include/mach/common.h +++ b/arch/arm/mach-shmobile/include/mach/common.h @@ -4,7 +4,6 @@ extern void shmobile_earlytimer_init(void); extern struct sys_timer shmobile_timer; struct twd_local_timer; -void shmobile_twd_init(struct twd_local_timer *twd_local_timer); extern void shmobile_setup_console(void); extern void shmobile_secondary_vector(void); extern int shmobile_platform_cpu_kill(unsigned int cpu); @@ -82,5 +81,6 @@ extern int r8a7779_platform_cpu_kill(unsigned int cpu); extern void r8a7779_secondary_init(unsigned int cpu); extern int r8a7779_boot_secondary(unsigned int cpu); extern void r8a7779_smp_prepare_cpus(void); +extern void r8a7779_register_twd(void); #endif /* __ARCH_MACH_COMMON_H */ diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c index 12c6f529ab89..e98e46f6cf55 100644 --- a/arch/arm/mach-shmobile/setup-r8a7779.c +++ b/arch/arm/mach-shmobile/setup-r8a7779.c @@ -262,10 +262,14 @@ void __init r8a7779_add_standard_devices(void) ARRAY_SIZE(r8a7779_late_devices)); } +/* do nothing for !CONFIG_SMP or !CONFIG_HAVE_TWD */ +void __init __weak r8a7779_register_twd(void) { } + static void __init r8a7779_earlytimer_init(void) { r8a7779_clock_init(); shmobile_earlytimer_init(); + r8a7779_register_twd(); } void __init r8a7779_add_early_devices(void) diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c index 5bebffc10455..04a0dfe75493 100644 --- a/arch/arm/mach-shmobile/setup-sh73a0.c +++ b/arch/arm/mach-shmobile/setup-sh73a0.c @@ -688,10 +688,14 @@ void __init sh73a0_add_standard_devices(void) ARRAY_SIZE(sh73a0_late_devices)); } +/* do nothing for !CONFIG_SMP or !CONFIG_HAVE_TWD */ +void __init __weak sh73a0_register_twd(void) { } + static void __init sh73a0_earlytimer_init(void) { sh73a0_clock_init(); shmobile_earlytimer_init(); + sh73a0_register_twd(); } void __init sh73a0_add_early_devices(void) diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c index b62e19d4c9af..6d1d0238cbf7 100644 --- a/arch/arm/mach-shmobile/smp-r8a7779.c +++ b/arch/arm/mach-shmobile/smp-r8a7779.c @@ -64,8 +64,15 @@ static void __iomem *scu_base_addr(void) static DEFINE_SPINLOCK(scu_lock); static unsigned long tmp; +#ifdef CONFIG_HAVE_ARM_TWD static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, 0xf0000600, 29); +void __init r8a7779_register_twd(void) +{ + twd_local_timer_register(&twd_local_timer); +} +#endif + static void modify_scu_cpu_psr(unsigned long set, unsigned long clr) { void __iomem *scu_base = scu_base_addr(); @@ -84,7 +91,6 @@ unsigned int __init r8a7779_get_core_count(void) { void __iomem *scu_base = scu_base_addr(); - shmobile_twd_init(&twd_local_timer); return scu_get_core_count(scu_base); } diff --git a/arch/arm/mach-shmobile/smp-sh73a0.c b/arch/arm/mach-shmobile/smp-sh73a0.c index 14ad8b052f1a..e36c41c4ab40 100644 --- a/arch/arm/mach-shmobile/smp-sh73a0.c +++ b/arch/arm/mach-shmobile/smp-sh73a0.c @@ -42,7 +42,13 @@ static void __iomem *scu_base_addr(void) static DEFINE_SPINLOCK(scu_lock); static unsigned long tmp; +#ifdef CONFIG_HAVE_ARM_TWD static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, 0xf0000600, 29); +void __init sh73a0_register_twd(void) +{ + twd_local_timer_register(&twd_local_timer); +} +#endif static void modify_scu_cpu_psr(unsigned long set, unsigned long clr) { @@ -62,7 +68,6 @@ unsigned int __init sh73a0_get_core_count(void) { void __iomem *scu_base = scu_base_addr(); - shmobile_twd_init(&twd_local_timer); return scu_get_core_count(scu_base); } diff --git a/arch/arm/mach-shmobile/timer.c b/arch/arm/mach-shmobile/timer.c index 2fba5f3d1c8a..8b79e7917a23 100644 --- a/arch/arm/mach-shmobile/timer.c +++ b/arch/arm/mach-shmobile/timer.c @@ -46,15 +46,6 @@ static void __init shmobile_timer_init(void) { } -void __init shmobile_twd_init(struct twd_local_timer *twd_local_timer) -{ -#ifdef CONFIG_HAVE_ARM_TWD - int err = twd_local_timer_register(twd_local_timer); - if (err) - pr_err("twd_local_timer_register failed %d\n", err); -#endif -} - struct sys_timer shmobile_timer = { .init = shmobile_timer_init, }; diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c index 0952494f481a..86abcf0d2be9 100644 --- a/arch/arm/mach-tegra/board-dt-tegra20.c +++ b/arch/arm/mach-tegra/board-dt-tegra20.c @@ -64,9 +64,9 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C2_BASE, "tegra-i2c.1", NULL), OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C3_BASE, "tegra-i2c.2", NULL), OF_DEV_AUXDATA("nvidia,tegra20-i2c-dvc", TEGRA_DVC_BASE, "tegra-i2c.3", NULL), - OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra-i2s.0", NULL), - OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S2_BASE, "tegra-i2s.1", NULL), - OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra-das", NULL), + OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra20-i2s.0", NULL), + OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S2_BASE, "tegra20-i2s.1", NULL), + OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra20-das", NULL), OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB_BASE, "tegra-ehci.0", &tegra_ehci1_pdata), OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB2_BASE, "tegra-ehci.1", diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c index c00aadb01e09..5eb74ea98141 100644 --- a/arch/arm/mach-tegra/board-harmony.c +++ b/arch/arm/mach-tegra/board-harmony.c @@ -122,7 +122,6 @@ static struct platform_device *harmony_devices[] __initdata = { &tegra_ehci3_device, &tegra_i2s_device1, &tegra_das_device, - &tegra_pcm_device, &harmony_audio_device, }; diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c index d669847f0485..0e2957f90201 100644 --- a/arch/arm/mach-tegra/board-seaboard.c +++ b/arch/arm/mach-tegra/board-seaboard.c @@ -153,7 +153,6 @@ static struct platform_device *seaboard_devices[] __initdata = { &seaboard_gpio_keys_device, &tegra_i2s_device1, &tegra_das_device, - &tegra_pcm_device, &seaboard_audio_device, }; diff --git a/arch/arm/mach-tegra/board-trimslice.c b/arch/arm/mach-tegra/board-trimslice.c index cd52820a3e37..ba2e04756c5e 100644 --- a/arch/arm/mach-tegra/board-trimslice.c +++ b/arch/arm/mach-tegra/board-trimslice.c @@ -86,7 +86,6 @@ static struct platform_device *trimslice_devices[] __initdata = { &tegra_sdhci_device4, &tegra_i2s_device1, &tegra_das_device, - &tegra_pcm_device, &trimslice_audio_device, }; diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c index 5f6b867e20b4..d6c9b2624d85 100644 --- a/arch/arm/mach-tegra/devices.c +++ b/arch/arm/mach-tegra/devices.c @@ -671,14 +671,14 @@ static struct resource i2s_resource2[] = { }; struct platform_device tegra_i2s_device1 = { - .name = "tegra-i2s", + .name = "tegra20-i2s", .id = 0, .resource = i2s_resource1, .num_resources = ARRAY_SIZE(i2s_resource1), }; struct platform_device tegra_i2s_device2 = { - .name = "tegra-i2s", + .name = "tegra20-i2s", .id = 1, .resource = i2s_resource2, .num_resources = ARRAY_SIZE(i2s_resource2), @@ -693,13 +693,8 @@ static struct resource tegra_das_resources[] = { }; struct platform_device tegra_das_device = { - .name = "tegra-das", + .name = "tegra20-das", .id = -1, .num_resources = ARRAY_SIZE(tegra_das_resources), .resource = tegra_das_resources, }; - -struct platform_device tegra_pcm_device = { - .name = "tegra-pcm-audio", - .id = -1, -}; diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h index ec455679b219..138c642e59f4 100644 --- a/arch/arm/mach-tegra/devices.h +++ b/arch/arm/mach-tegra/devices.h @@ -52,6 +52,5 @@ extern struct platform_device tegra_pmu_device; extern struct platform_device tegra_i2s_device1; extern struct platform_device tegra_i2s_device2; extern struct platform_device tegra_das_device; -extern struct platform_device tegra_pcm_device; #endif diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index 592a4eeb5328..cf4999bcd4d8 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -2142,8 +2142,8 @@ static struct clk tegra_list_clks[] = { PERIPH_CLK("apbdma", "tegra-dma", NULL, 34, 0, 108000000, mux_pclk, 0), PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET), PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0), - PERIPH_CLK("i2s1", "tegra-i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), - PERIPH_CLK("i2s2", "tegra-i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("i2s1", "tegra20-i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("i2s2", "tegra20-i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, 100000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71), PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71), diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 1621ad07d284..33339745d432 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -1667,8 +1667,10 @@ void __init u300_init_irq(void) for (i = 0; i < U300_VIC_IRQS_END; i++) set_bit(i, (unsigned long *) &mask[0]); - vic_init((void __iomem *) U300_INTCON0_VBASE, 0, mask[0], mask[0]); - vic_init((void __iomem *) U300_INTCON1_VBASE, 32, mask[1], mask[1]); + vic_init((void __iomem *) U300_INTCON0_VBASE, IRQ_U300_INTCON0_START, + mask[0], mask[0]); + vic_init((void __iomem *) U300_INTCON1_VBASE, IRQ_U300_INTCON1_START, + mask[1], mask[1]); } diff --git a/arch/arm/mach-u300/i2c.c b/arch/arm/mach-u300/i2c.c index a38f80238ea9..cb04bd6ab3e7 100644 --- a/arch/arm/mach-u300/i2c.c +++ b/arch/arm/mach-u300/i2c.c @@ -146,9 +146,6 @@ static struct ab3100_platform_data ab3100_plf_data = { .min_uV = 1800000, .max_uV = 1800000, .valid_modes_mask = REGULATOR_MODE_NORMAL, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS, .always_on = 1, .boot_on = 1, }, @@ -160,9 +157,6 @@ static struct ab3100_platform_data ab3100_plf_data = { .min_uV = 2500000, .max_uV = 2500000, .valid_modes_mask = REGULATOR_MODE_NORMAL, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS, .always_on = 1, .boot_on = 1, }, @@ -230,8 +224,7 @@ static struct ab3100_platform_data ab3100_plf_data = { .max_uV = 1800000, .valid_modes_mask = REGULATOR_MODE_NORMAL, .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS, + REGULATOR_CHANGE_VOLTAGE, .always_on = 1, .boot_on = 1, }, diff --git a/arch/arm/mach-u300/include/mach/irqs.h b/arch/arm/mach-u300/include/mach/irqs.h index ee78a26707eb..ec09c1e07b1a 100644 --- a/arch/arm/mach-u300/include/mach/irqs.h +++ b/arch/arm/mach-u300/include/mach/irqs.h @@ -12,101 +12,101 @@ #ifndef __MACH_IRQS_H #define __MACH_IRQS_H -#define IRQ_U300_INTCON0_START 0 -#define IRQ_U300_INTCON1_START 32 +#define IRQ_U300_INTCON0_START 1 +#define IRQ_U300_INTCON1_START 33 /* These are on INTCON0 - 30 lines */ -#define IRQ_U300_IRQ0_EXT 0 -#define IRQ_U300_IRQ1_EXT 1 -#define IRQ_U300_DMA 2 -#define IRQ_U300_VIDEO_ENC_0 3 -#define IRQ_U300_VIDEO_ENC_1 4 -#define IRQ_U300_AAIF_RX 5 -#define IRQ_U300_AAIF_TX 6 -#define IRQ_U300_AAIF_VGPIO 7 -#define IRQ_U300_AAIF_WAKEUP 8 -#define IRQ_U300_PCM_I2S0_FRAME 9 -#define IRQ_U300_PCM_I2S0_FIFO 10 -#define IRQ_U300_PCM_I2S1_FRAME 11 -#define IRQ_U300_PCM_I2S1_FIFO 12 -#define IRQ_U300_XGAM_GAMCON 13 -#define IRQ_U300_XGAM_CDI 14 -#define IRQ_U300_XGAM_CDICON 15 +#define IRQ_U300_IRQ0_EXT 1 +#define IRQ_U300_IRQ1_EXT 2 +#define IRQ_U300_DMA 3 +#define IRQ_U300_VIDEO_ENC_0 4 +#define IRQ_U300_VIDEO_ENC_1 5 +#define IRQ_U300_AAIF_RX 6 +#define IRQ_U300_AAIF_TX 7 +#define IRQ_U300_AAIF_VGPIO 8 +#define IRQ_U300_AAIF_WAKEUP 9 +#define IRQ_U300_PCM_I2S0_FRAME 10 +#define IRQ_U300_PCM_I2S0_FIFO 11 +#define IRQ_U300_PCM_I2S1_FRAME 12 +#define IRQ_U300_PCM_I2S1_FIFO 13 +#define IRQ_U300_XGAM_GAMCON 14 +#define IRQ_U300_XGAM_CDI 15 +#define IRQ_U300_XGAM_CDICON 16 #if defined(CONFIG_MACH_U300_BS2X) || defined(CONFIG_MACH_U300_BS330) /* MMIACC not used on the DB3210 or DB3350 chips */ -#define IRQ_U300_XGAM_MMIACC 16 +#define IRQ_U300_XGAM_MMIACC 17 #endif -#define IRQ_U300_XGAM_PDI 17 -#define IRQ_U300_XGAM_PDICON 18 -#define IRQ_U300_XGAM_GAMEACC 19 -#define IRQ_U300_XGAM_MCIDCT 20 -#define IRQ_U300_APEX 21 -#define IRQ_U300_UART0 22 -#define IRQ_U300_SPI 23 -#define IRQ_U300_TIMER_APP_OS 24 -#define IRQ_U300_TIMER_APP_DD 25 -#define IRQ_U300_TIMER_APP_GP1 26 -#define IRQ_U300_TIMER_APP_GP2 27 -#define IRQ_U300_TIMER_OS 28 -#define IRQ_U300_TIMER_MS 29 -#define IRQ_U300_KEYPAD_KEYBF 30 -#define IRQ_U300_KEYPAD_KEYBR 31 +#define IRQ_U300_XGAM_PDI 18 +#define IRQ_U300_XGAM_PDICON 19 +#define IRQ_U300_XGAM_GAMEACC 20 +#define IRQ_U300_XGAM_MCIDCT 21 +#define IRQ_U300_APEX 22 +#define IRQ_U300_UART0 23 +#define IRQ_U300_SPI 24 +#define IRQ_U300_TIMER_APP_OS 25 +#define IRQ_U300_TIMER_APP_DD 26 +#define IRQ_U300_TIMER_APP_GP1 27 +#define IRQ_U300_TIMER_APP_GP2 28 +#define IRQ_U300_TIMER_OS 29 +#define IRQ_U300_TIMER_MS 30 +#define IRQ_U300_KEYPAD_KEYBF 31 +#define IRQ_U300_KEYPAD_KEYBR 32 /* These are on INTCON1 - 32 lines */ -#define IRQ_U300_GPIO_PORT0 32 -#define IRQ_U300_GPIO_PORT1 33 -#define IRQ_U300_GPIO_PORT2 34 +#define IRQ_U300_GPIO_PORT0 33 +#define IRQ_U300_GPIO_PORT1 34 +#define IRQ_U300_GPIO_PORT2 35 #if defined(CONFIG_MACH_U300_BS2X) || defined(CONFIG_MACH_U300_BS330) || \ defined(CONFIG_MACH_U300_BS335) /* These are for DB3150, DB3200 and DB3350 */ -#define IRQ_U300_WDOG 35 -#define IRQ_U300_EVHIST 36 -#define IRQ_U300_MSPRO 37 -#define IRQ_U300_MMCSD_MCIINTR0 38 -#define IRQ_U300_MMCSD_MCIINTR1 39 -#define IRQ_U300_I2C0 40 -#define IRQ_U300_I2C1 41 -#define IRQ_U300_RTC 42 -#define IRQ_U300_NFIF 43 -#define IRQ_U300_NFIF2 44 +#define IRQ_U300_WDOG 36 +#define IRQ_U300_EVHIST 37 +#define IRQ_U300_MSPRO 38 +#define IRQ_U300_MMCSD_MCIINTR0 39 +#define IRQ_U300_MMCSD_MCIINTR1 40 +#define IRQ_U300_I2C0 41 +#define IRQ_U300_I2C1 42 +#define IRQ_U300_RTC 43 +#define IRQ_U300_NFIF 44 +#define IRQ_U300_NFIF2 45 #endif /* DB3150 and DB3200 have only 45 IRQs */ #if defined(CONFIG_MACH_U300_BS2X) || defined(CONFIG_MACH_U300_BS330) -#define U300_VIC_IRQS_END 45 +#define U300_VIC_IRQS_END 46 #endif /* The DB3350-specific interrupt lines */ #ifdef CONFIG_MACH_U300_BS335 -#define IRQ_U300_ISP_F0 45 -#define IRQ_U300_ISP_F1 46 -#define IRQ_U300_ISP_F2 47 -#define IRQ_U300_ISP_F3 48 -#define IRQ_U300_ISP_F4 49 -#define IRQ_U300_GPIO_PORT3 50 -#define IRQ_U300_SYSCON_PLL_LOCK 51 -#define IRQ_U300_UART1 52 -#define IRQ_U300_GPIO_PORT4 53 -#define IRQ_U300_GPIO_PORT5 54 -#define IRQ_U300_GPIO_PORT6 55 -#define U300_VIC_IRQS_END 56 +#define IRQ_U300_ISP_F0 46 +#define IRQ_U300_ISP_F1 47 +#define IRQ_U300_ISP_F2 48 +#define IRQ_U300_ISP_F3 49 +#define IRQ_U300_ISP_F4 50 +#define IRQ_U300_GPIO_PORT3 51 +#define IRQ_U300_SYSCON_PLL_LOCK 52 +#define IRQ_U300_UART1 53 +#define IRQ_U300_GPIO_PORT4 54 +#define IRQ_U300_GPIO_PORT5 55 +#define IRQ_U300_GPIO_PORT6 56 +#define U300_VIC_IRQS_END 57 #endif /* The DB3210-specific interrupt lines */ #ifdef CONFIG_MACH_U300_BS365 -#define IRQ_U300_GPIO_PORT3 35 -#define IRQ_U300_GPIO_PORT4 36 -#define IRQ_U300_WDOG 37 -#define IRQ_U300_EVHIST 38 -#define IRQ_U300_MSPRO 39 -#define IRQ_U300_MMCSD_MCIINTR0 40 -#define IRQ_U300_MMCSD_MCIINTR1 41 -#define IRQ_U300_I2C0 42 -#define IRQ_U300_I2C1 43 -#define IRQ_U300_RTC 44 -#define IRQ_U300_NFIF 45 -#define IRQ_U300_NFIF2 46 -#define IRQ_U300_SYSCON_PLL_LOCK 47 -#define U300_VIC_IRQS_END 48 +#define IRQ_U300_GPIO_PORT3 36 +#define IRQ_U300_GPIO_PORT4 37 +#define IRQ_U300_WDOG 38 +#define IRQ_U300_EVHIST 39 +#define IRQ_U300_MSPRO 40 +#define IRQ_U300_MMCSD_MCIINTR0 41 +#define IRQ_U300_MMCSD_MCIINTR1 42 +#define IRQ_U300_I2C0 43 +#define IRQ_U300_I2C1 44 +#define IRQ_U300_RTC 45 +#define IRQ_U300_NFIF 46 +#define IRQ_U300_NFIF2 47 +#define IRQ_U300_SYSCON_PLL_LOCK 48 +#define U300_VIC_IRQS_END 49 #endif /* Maximum 8*7 GPIO lines */ @@ -117,6 +117,6 @@ #define IRQ_U300_GPIO_END (U300_VIC_IRQS_END) #endif -#define NR_IRQS (IRQ_U300_GPIO_END) +#define NR_IRQS (IRQ_U300_GPIO_END - IRQ_U300_INTCON0_START) #endif diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig index 880d02ec89d4..ef7099eea0f2 100644 --- a/arch/arm/mach-ux500/Kconfig +++ b/arch/arm/mach-ux500/Kconfig @@ -17,6 +17,7 @@ config UX500_SOC_DB5500 config UX500_SOC_DB8500 bool select MFD_DB8500_PRCMU + select REGULATOR select REGULATOR_DB8500_PRCMU select CPU_FREQ_TABLE if CPU_FREQ diff --git a/arch/arm/mach-ux500/mbox-db5500.c b/arch/arm/mach-ux500/mbox-db5500.c index 2b2d51caf9d8..0127490218cd 100644 --- a/arch/arm/mach-ux500/mbox-db5500.c +++ b/arch/arm/mach-ux500/mbox-db5500.c @@ -168,7 +168,7 @@ static ssize_t mbox_read_fifo(struct device *dev, return sprintf(buf, "0x%X\n", mbox_value); } -static DEVICE_ATTR(fifo, S_IWUGO | S_IRUGO, mbox_read_fifo, mbox_write_fifo); +static DEVICE_ATTR(fifo, S_IWUSR | S_IRUGO, mbox_read_fifo, mbox_write_fifo); static int mbox_show(struct seq_file *s, void *data) { diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c index d2058ef8345f..eff5842f6232 100644 --- a/arch/arm/mach-ux500/platsmp.c +++ b/arch/arm/mach-ux500/platsmp.c @@ -99,7 +99,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) */ write_pen_release(cpu_logical_map(cpu)); - gic_raise_softirq(cpumask_of(cpu), 1); + smp_send_reschedule(cpu); timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { diff --git a/arch/arm/mach-versatile/pci.c b/arch/arm/mach-versatile/pci.c index a6e23f464528..d2268be8c34c 100644 --- a/arch/arm/mach-versatile/pci.c +++ b/arch/arm/mach-versatile/pci.c @@ -190,7 +190,7 @@ static struct resource pre_mem = { .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH, }; -static int __init pci_versatile_setup_resources(struct list_head *resources) +static int __init pci_versatile_setup_resources(struct pci_sys_data *sys) { int ret = 0; @@ -218,9 +218,9 @@ static int __init pci_versatile_setup_resources(struct list_head *resources) * the mem resource for this bus * the prefetch mem resource for this bus */ - pci_add_resource_offset(resources, &io_mem, sys->io_offset); - pci_add_resource_offset(resources, &non_mem, sys->mem_offset); - pci_add_resource_offset(resources, &pre_mem, sys->mem_offset); + pci_add_resource_offset(&sys->resources, &io_mem, sys->io_offset); + pci_add_resource_offset(&sys->resources, &non_mem, sys->mem_offset); + pci_add_resource_offset(&sys->resources, &pre_mem, sys->mem_offset); goto out; @@ -249,7 +249,7 @@ int __init pci_versatile_setup(int nr, struct pci_sys_data *sys) if (nr == 0) { sys->mem_offset = 0; - ret = pci_versatile_setup_resources(&sys->resources); + ret = pci_versatile_setup_resources(sys); if (ret < 0) { printk("pci_versatile_setup: resources... oops?\n"); goto out; diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 7edef9121632..7c8a7d8467bf 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -723,7 +723,7 @@ config CPU_HIGH_VECTOR bool "Select the High exception vector" help Say Y here to select high exception vector(0xFFFF0000~). - The exception vector can be vary depending on the platform + The exception vector can vary depending on the platform design in nommu mode. If your platform needs to select high exception vector, say Y. Otherwise or if you are unsure, say N, and the low exception diff --git a/arch/arm/mm/abort-ev6.S b/arch/arm/mm/abort-ev6.S index ff1f7cc11f87..80741992a9fc 100644 --- a/arch/arm/mm/abort-ev6.S +++ b/arch/arm/mm/abort-ev6.S @@ -26,18 +26,23 @@ ENTRY(v6_early_abort) mrc p15, 0, r1, c5, c0, 0 @ get FSR mrc p15, 0, r0, c6, c0, 0 @ get FAR /* - * Faulty SWP instruction on 1136 doesn't set bit 11 in DFSR (erratum 326103). - * The test below covers all the write situations, including Java bytecodes + * Faulty SWP instruction on 1136 doesn't set bit 11 in DFSR. */ - bic r1, r1, #1 << 11 @ clear bit 11 of FSR +#ifdef CONFIG_ARM_ERRATA_326103 + ldr ip, =0x4107b36 + mrc p15, 0, r3, c0, c0, 0 @ get processor id + teq ip, r3, lsr #4 @ r0 ARM1136? + bne do_DataAbort tst r5, #PSR_J_BIT @ Java? + tsteq r5, #PSR_T_BIT @ Thumb? bne do_DataAbort - do_thumb_abort fsr=r1, pc=r4, psr=r5, tmp=r3 - ldreq r3, [r4] @ read aborted ARM instruction + bic r1, r1, #1 << 11 @ clear bit 11 of FSR + ldr r3, [r4] @ read aborted ARM instruction #ifdef CONFIG_CPU_ENDIAN_BE8 - reveq r3, r3 + rev r3, r3 #endif do_ldrd_abort tmp=ip, insn=r3 tst r3, #1 << 20 @ L = 0 -> write orreq r1, r1, #1 << 11 @ yes. +#endif b do_DataAbort diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index a53fd2aaa2f4..2a8e380501e8 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -32,6 +32,7 @@ static void __iomem *l2x0_base; static DEFINE_RAW_SPINLOCK(l2x0_lock); static u32 l2x0_way_mask; /* Bitmask of active ways */ static u32 l2x0_size; +static unsigned long sync_reg_offset = L2X0_CACHE_SYNC; struct l2x0_regs l2x0_saved_regs; @@ -61,12 +62,7 @@ static inline void cache_sync(void) { void __iomem *base = l2x0_base; -#ifdef CONFIG_PL310_ERRATA_753970 - /* write to an unmmapped register */ - writel_relaxed(0, base + L2X0_DUMMY_REG); -#else - writel_relaxed(0, base + L2X0_CACHE_SYNC); -#endif + writel_relaxed(0, base + sync_reg_offset); cache_wait(base + L2X0_CACHE_SYNC, 1); } @@ -85,10 +81,13 @@ static inline void l2x0_inv_line(unsigned long addr) } #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) +static inline void debug_writel(unsigned long val) +{ + if (outer_cache.set_debug) + outer_cache.set_debug(val); +} -#define debug_writel(val) outer_cache.set_debug(val) - -static void l2x0_set_debug(unsigned long val) +static void pl310_set_debug(unsigned long val) { writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL); } @@ -98,7 +97,7 @@ static inline void debug_writel(unsigned long val) { } -#define l2x0_set_debug NULL +#define pl310_set_debug NULL #endif #ifdef CONFIG_PL310_ERRATA_588369 @@ -331,6 +330,11 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) else ways = 8; type = "L310"; +#ifdef CONFIG_PL310_ERRATA_753970 + /* Unmapped register. */ + sync_reg_offset = L2X0_DUMMY_REG; +#endif + outer_cache.set_debug = pl310_set_debug; break; case L2X0_CACHE_ID_PART_L210: ways = (aux >> 13) & 0xf; @@ -379,7 +383,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) outer_cache.flush_all = l2x0_flush_all; outer_cache.inv_all = l2x0_inv_all; outer_cache.disable = l2x0_disable; - outer_cache.set_debug = l2x0_set_debug; printk(KERN_INFO "%s cache controller enabled\n", type); printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n", diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 9055b5a84ec5..f07467533365 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -320,7 +320,7 @@ retry: */ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr); - if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (!(fault & VM_FAULT_ERROR) && flags & FAULT_FLAG_ALLOW_RETRY) { if (fault & VM_FAULT_MAJOR) { tsk->maj_flt++; perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 595079fa9d1d..8f5813bbffb5 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -293,11 +293,11 @@ EXPORT_SYMBOL(pfn_valid); #endif #ifndef CONFIG_SPARSEMEM -static void arm_memory_present(void) +static void __init arm_memory_present(void) { } #else -static void arm_memory_present(void) +static void __init arm_memory_present(void) { struct memblock_region *reg; diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index b86f8933ff91..2c7cf2f9c837 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -618,8 +618,8 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr, } } -static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end, - unsigned long phys, const struct mem_type *type) +static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, + unsigned long end, unsigned long phys, const struct mem_type *type) { pud_t *pud = pud_offset(pgd, addr); unsigned long next; diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index 6486d2f253cd..d51225f90ae2 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -13,6 +13,7 @@ #include <asm/sections.h> #include <asm/page.h> #include <asm/setup.h> +#include <asm/traps.h> #include <asm/mach/arch.h> #include "mm.h" @@ -39,6 +40,7 @@ void __init sanity_check_meminfo(void) */ void __init paging_init(struct machine_desc *mdesc) { + early_trap_init((void *)CONFIG_VECTORS_BASE); bootmem_init(); } diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index f1c8486f7501..c2e2b66f72b5 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -255,6 +255,18 @@ __v7_setup: mcr p15, 0, r5, c10, c2, 0 @ write PRRR mcr p15, 0, r6, c10, c2, 1 @ write NMRR #endif +#ifndef CONFIG_ARM_THUMBEE + mrc p15, 0, r0, c0, c1, 0 @ read ID_PFR0 for ThumbEE + and r0, r0, #(0xf << 12) @ ThumbEE enabled field + teq r0, #(1 << 12) @ check if ThumbEE is present + bne 1f + mov r5, #0 + mcr p14, 6, r5, c1, c0, 0 @ Initialize TEEHBR to 0 + mrc p14, 6, r0, c0, c0, 0 @ load TEECR + orr r0, r0, #1 @ set the 1st bit in order to + mcr p14, 6, r0, c0, c0, 0 @ stop userspace TEEHBR access +1: +#endif adr r5, v7_crval ldmia r5, {r5, r6} #ifdef CONFIG_CPU_ENDIAN_BE8 diff --git a/arch/arm/plat-mxc/3ds_debugboard.c b/arch/arm/plat-mxc/3ds_debugboard.c index d1e31fa1b0c3..5cac2c540f4f 100644 --- a/arch/arm/plat-mxc/3ds_debugboard.c +++ b/arch/arm/plat-mxc/3ds_debugboard.c @@ -80,7 +80,7 @@ static struct smsc911x_platform_config smsc911x_config = { static struct platform_device smsc_lan9217_device = { .name = "smsc911x", - .id = 0, + .id = -1, .dev = { .platform_data = &smsc911x_config, }, diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index ce1e9b96ba1a..ad95c7a5d009 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -17,6 +17,7 @@ config ARCH_OMAP1 select IRQ_DOMAIN select HAVE_IDE select NEED_MACH_MEMORY_H + select NEED_MACH_IO_H if PCCARD help "Systems based on omap7xx, omap15xx or omap16xx" diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index 56b6f8b7053e..62ec5c452792 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -398,32 +398,6 @@ struct clk dummy_ck = { .ops = &clkops_null, }; -#ifdef CONFIG_CPU_FREQ -void clk_init_cpufreq_table(struct cpufreq_frequency_table **table) -{ - unsigned long flags; - - if (!arch_clock || !arch_clock->clk_init_cpufreq_table) - return; - - spin_lock_irqsave(&clockfw_lock, flags); - arch_clock->clk_init_cpufreq_table(table); - spin_unlock_irqrestore(&clockfw_lock, flags); -} - -void clk_exit_cpufreq_table(struct cpufreq_frequency_table **table) -{ - unsigned long flags; - - if (!arch_clock || !arch_clock->clk_exit_cpufreq_table) - return; - - spin_lock_irqsave(&clockfw_lock, flags); - arch_clock->clk_exit_cpufreq_table(table); - spin_unlock_irqrestore(&clockfw_lock, flags); -} -#endif - /* * */ @@ -441,6 +415,8 @@ static int __init clk_disable_unused(void) return 0; pr_info("clock: disabling unused clocks to save power\n"); + + spin_lock_irqsave(&clockfw_lock, flags); list_for_each_entry(ck, &clocks, node) { if (ck->ops == &clkops_null) continue; @@ -448,10 +424,9 @@ static int __init clk_disable_unused(void) if (ck->usecount > 0 || !ck->enable_reg) continue; - spin_lock_irqsave(&clockfw_lock, flags); arch_clock->clk_disable_unused(ck); - spin_unlock_irqrestore(&clockfw_lock, flags); } + spin_unlock_irqrestore(&clockfw_lock, flags); return 0; } diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index ecdb3da0dea9..c58d896cd5c3 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -916,6 +916,13 @@ void omap_start_dma(int lch) l |= OMAP_DMA_CCR_BUFFERING_DISABLE; l |= OMAP_DMA_CCR_EN; + /* + * As dma_write() uses IO accessors which are weakly ordered, there + * is no guarantee that data in coherent DMA memory will be visible + * to the DMA device. Add a memory barrier here to ensure that any + * such data is visible prior to enabling DMA. + */ + mb(); p->dma_write(l, CCR, lch); dma_chan[lch].flags |= OMAP_DMA_ACTIVE; @@ -965,6 +972,13 @@ void omap_stop_dma(int lch) p->dma_write(l, CCR, lch); } + /* + * Ensure that data transferred by DMA is visible to any access + * after DMA has been disabled. This is important for coherent + * DMA regions. + */ + mb(); + if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { int next_lch, cur_lch = lch; char dma_chan_link_map[dma_lch_count]; diff --git a/arch/arm/plat-omap/include/plat/clock.h b/arch/arm/plat-omap/include/plat/clock.h index 240a7b9fd946..d0ef57c1d71b 100644 --- a/arch/arm/plat-omap/include/plat/clock.h +++ b/arch/arm/plat-omap/include/plat/clock.h @@ -272,8 +272,6 @@ struct clk { #endif }; -struct cpufreq_frequency_table; - struct clk_functions { int (*clk_enable)(struct clk *clk); void (*clk_disable)(struct clk *clk); @@ -283,10 +281,6 @@ struct clk_functions { void (*clk_allow_idle)(struct clk *clk); void (*clk_deny_idle)(struct clk *clk); void (*clk_disable_unused)(struct clk *clk); -#ifdef CONFIG_CPU_FREQ - void (*clk_init_cpufreq_table)(struct cpufreq_frequency_table **); - void (*clk_exit_cpufreq_table)(struct cpufreq_frequency_table **); -#endif }; extern int mpurate; @@ -301,10 +295,6 @@ extern void recalculate_root_clocks(void); extern unsigned long followparent_recalc(struct clk *clk); extern void clk_enable_init_clocks(void); unsigned long omap_fixed_divisor_recalc(struct clk *clk); -#ifdef CONFIG_CPU_FREQ -extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table); -extern void clk_exit_cpufreq_table(struct cpufreq_frequency_table **table); -#endif extern struct clk *omap_clk_get_by_name(const char *name); extern int omap_clk_enable_autoidle_all(void); extern int omap_clk_disable_autoidle_all(void); diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h index 9e8e63d52aab..3f26db4ee8e6 100644 --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h @@ -47,17 +47,17 @@ extern struct omap_hwmod_sysc_fields omap_hwmod_sysc_type2; * with the original PRCM protocol defined for OMAP2420 */ #define SYSC_TYPE1_MIDLEMODE_SHIFT 12 -#define SYSC_TYPE1_MIDLEMODE_MASK (0x3 << SYSC_MIDLEMODE_SHIFT) +#define SYSC_TYPE1_MIDLEMODE_MASK (0x3 << SYSC_TYPE1_MIDLEMODE_SHIFT) #define SYSC_TYPE1_CLOCKACTIVITY_SHIFT 8 -#define SYSC_TYPE1_CLOCKACTIVITY_MASK (0x3 << SYSC_CLOCKACTIVITY_SHIFT) +#define SYSC_TYPE1_CLOCKACTIVITY_MASK (0x3 << SYSC_TYPE1_CLOCKACTIVITY_SHIFT) #define SYSC_TYPE1_SIDLEMODE_SHIFT 3 -#define SYSC_TYPE1_SIDLEMODE_MASK (0x3 << SYSC_SIDLEMODE_SHIFT) +#define SYSC_TYPE1_SIDLEMODE_MASK (0x3 << SYSC_TYPE1_SIDLEMODE_SHIFT) #define SYSC_TYPE1_ENAWAKEUP_SHIFT 2 -#define SYSC_TYPE1_ENAWAKEUP_MASK (1 << SYSC_ENAWAKEUP_SHIFT) +#define SYSC_TYPE1_ENAWAKEUP_MASK (1 << SYSC_TYPE1_ENAWAKEUP_SHIFT) #define SYSC_TYPE1_SOFTRESET_SHIFT 1 -#define SYSC_TYPE1_SOFTRESET_MASK (1 << SYSC_SOFTRESET_SHIFT) +#define SYSC_TYPE1_SOFTRESET_MASK (1 << SYSC_TYPE1_SOFTRESET_SHIFT) #define SYSC_TYPE1_AUTOIDLE_SHIFT 0 -#define SYSC_TYPE1_AUTOIDLE_MASK (1 << SYSC_AUTOIDLE_SHIFT) +#define SYSC_TYPE1_AUTOIDLE_MASK (1 << SYSC_TYPE1_AUTOIDLE_SHIFT) /* * OCP SYSCONFIG bit shifts/masks TYPE2. These are for IPs compliant @@ -305,6 +305,7 @@ struct omap_hwmod_sysc_fields { * @rev_offs: IP block revision register offset (from module base addr) * @sysc_offs: OCP_SYSCONFIG register offset (from module base addr) * @syss_offs: OCP_SYSSTATUS register offset (from module base addr) + * @srst_udelay: Delay needed after doing a softreset in usecs * @idlemodes: One or more of {SIDLE,MSTANDBY}_{OFF,FORCE,SMART} * @sysc_flags: SYS{C,S}_HAS* flags indicating SYSCONFIG bits supported * @clockact: the default value of the module CLOCKACTIVITY bits @@ -330,9 +331,10 @@ struct omap_hwmod_class_sysconfig { u16 sysc_offs; u16 syss_offs; u16 sysc_flags; + struct omap_hwmod_sysc_fields *sysc_fields; + u8 srst_udelay; u8 idlemodes; u8 clockact; - struct omap_hwmod_sysc_fields *sysc_fields; }; /** diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index eec98afa0f83..f9a8c5341ee9 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -348,7 +348,6 @@ u32 omap3_configure_core_dpll(u32 m2, u32 unlock_dll, u32 f, u32 inc, sdrc_actim_ctrl_b_1, sdrc_mr_1); } -#ifdef CONFIG_PM void omap3_sram_restore_context(void) { omap_sram_ceil = omap_sram_base + omap_sram_size; @@ -358,17 +357,18 @@ void omap3_sram_restore_context(void) omap3_sram_configure_core_dpll_sz); omap_push_sram_idle(); } -#endif /* CONFIG_PM */ - -#endif /* CONFIG_ARCH_OMAP3 */ static inline int omap34xx_sram_init(void) { -#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM) omap3_sram_restore_context(); -#endif return 0; } +#else +static inline int omap34xx_sram_init(void) +{ + return 0; +} +#endif /* CONFIG_ARCH_OMAP3 */ static inline int am33xx_sram_init(void) { diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 71553f410016..a0ffc77da809 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -302,6 +302,7 @@ comment "Power management" config SAMSUNG_PM_DEBUG bool "S3C2410 PM Suspend debug" depends on PM + select DEBUG_LL help Say Y here if you want verbose debugging from the PM Suspend and Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt> diff --git a/arch/arm/plat-samsung/include/plat/sdhci.h b/arch/arm/plat-samsung/include/plat/sdhci.h index 317e246ffc56..e834c5ef437c 100644 --- a/arch/arm/plat-samsung/include/plat/sdhci.h +++ b/arch/arm/plat-samsung/include/plat/sdhci.h @@ -18,6 +18,8 @@ #ifndef __PLAT_S3C_SDHCI_H #define __PLAT_S3C_SDHCI_H __FILE__ +#include <plat/devs.h> + struct platform_device; struct mmc_host; struct mmc_card; @@ -356,4 +358,30 @@ static inline void exynos4_default_sdhci3(void) { } #endif /* CONFIG_EXYNOS4_SETUP_SDHCI */ +static inline void s3c_sdhci_setname(int id, char *name) +{ + switch (id) { +#ifdef CONFIG_S3C_DEV_HSMMC + case 0: + s3c_device_hsmmc0.name = name; + break; +#endif +#ifdef CONFIG_S3C_DEV_HSMMC1 + case 1: + s3c_device_hsmmc1.name = name; + break; +#endif +#ifdef CONFIG_S3C_DEV_HSMMC2 + case 2: + s3c_device_hsmmc2.name = name; + break; +#endif +#ifdef CONFIG_S3C_DEV_HSMMC3 + case 3: + s3c_device_hsmmc3.name = name; + break; +#endif + } +} + #endif /* __PLAT_S3C_SDHCI_H */ diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 858748eaa144..bc683b8219b5 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -17,6 +17,8 @@ #include <linux/sched.h> #include <linux/smp.h> #include <linux/init.h> +#include <linux/uaccess.h> +#include <linux/user.h> #include <asm/cp15.h> #include <asm/cputype.h> @@ -529,6 +531,103 @@ void vfp_flush_hwstate(struct thread_info *thread) } /* + * Save the current VFP state into the provided structures and prepare + * for entry into a new function (signal handler). + */ +int vfp_preserve_user_clear_hwstate(struct user_vfp __user *ufp, + struct user_vfp_exc __user *ufp_exc) +{ + struct thread_info *thread = current_thread_info(); + struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; + int err = 0; + + /* Ensure that the saved hwstate is up-to-date. */ + vfp_sync_hwstate(thread); + + /* + * Copy the floating point registers. There can be unused + * registers see asm/hwcap.h for details. + */ + err |= __copy_to_user(&ufp->fpregs, &hwstate->fpregs, + sizeof(hwstate->fpregs)); + /* + * Copy the status and control register. + */ + __put_user_error(hwstate->fpscr, &ufp->fpscr, err); + + /* + * Copy the exception registers. + */ + __put_user_error(hwstate->fpexc, &ufp_exc->fpexc, err); + __put_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); + __put_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); + + if (err) + return -EFAULT; + + /* Ensure that VFP is disabled. */ + vfp_flush_hwstate(thread); + + /* + * As per the PCS, clear the length and stride bits for function + * entry. + */ + hwstate->fpscr &= ~(FPSCR_LENGTH_MASK | FPSCR_STRIDE_MASK); + + /* + * Disable VFP in the hwstate so that we can detect if it gets + * used. + */ + hwstate->fpexc &= ~FPEXC_EN; + return 0; +} + +/* Sanitise and restore the current VFP state from the provided structures. */ +int vfp_restore_user_hwstate(struct user_vfp __user *ufp, + struct user_vfp_exc __user *ufp_exc) +{ + struct thread_info *thread = current_thread_info(); + struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; + unsigned long fpexc; + int err = 0; + + /* + * If VFP has been used, then disable it to avoid corrupting + * the new thread state. + */ + if (hwstate->fpexc & FPEXC_EN) + vfp_flush_hwstate(thread); + + /* + * Copy the floating point registers. There can be unused + * registers see asm/hwcap.h for details. + */ + err |= __copy_from_user(&hwstate->fpregs, &ufp->fpregs, + sizeof(hwstate->fpregs)); + /* + * Copy the status and control register. + */ + __get_user_error(hwstate->fpscr, &ufp->fpscr, err); + + /* + * Sanitise and restore the exception registers. + */ + __get_user_error(fpexc, &ufp_exc->fpexc, err); + + /* Ensure the VFP is enabled. */ + fpexc |= FPEXC_EN; + + /* Ensure FPINST2 is invalid and the exception flag is cleared. */ + fpexc &= ~(FPEXC_EX | FPEXC_FP2V); + hwstate->fpexc = fpexc; + + __get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); + __get_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); + + return err ? -EFAULT : 0; +} + +/* * VFP hardware can lose all context when a CPU goes offline. * As we will be running in SMP mode with CPU hotplug, we will save the * hardware state at every thread switch. We clear our held state when diff --git a/arch/avr32/include/asm/barrier.h b/arch/avr32/include/asm/barrier.h index 808001c9cf8c..0961275373db 100644 --- a/arch/avr32/include/asm/barrier.h +++ b/arch/avr32/include/asm/barrier.h @@ -8,6 +8,8 @@ #ifndef __ASM_AVR32_BARRIER_H #define __ASM_AVR32_BARRIER_H +#define nop() asm volatile("nop") + #define mb() asm volatile("" : : : "memory") #define rmb() mb() #define wmb() asm volatile("sync 0" : : : "memory") diff --git a/arch/avr32/include/asm/special_insns.h b/arch/avr32/include/asm/special_insns.h deleted file mode 100644 index f922218dfaa5..000000000000 --- a/arch/avr32/include/asm/special_insns.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) 2004-2006 Atmel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef __ASM_AVR32_SPECIAL_INSNS_H -#define __ASM_AVR32_SPECIAL_INSNS_H - -#define nop() asm volatile("nop") - -#endif /* __ASM_AVR32_SPECIAL_INSNS_H */ diff --git a/arch/avr32/mach-at32ap/include/mach/board.h b/arch/avr32/mach-at32ap/include/mach/board.h index 71733866cb4f..70742ec997f8 100644 --- a/arch/avr32/mach-at32ap/include/mach/board.h +++ b/arch/avr32/mach-at32ap/include/mach/board.h @@ -7,7 +7,7 @@ #include <linux/types.h> #include <linux/serial.h> #include <linux/platform_data/macb.h> -#include <linux/platform_data/atmel_nand.h> +#include <linux/platform_data/atmel.h> #define GPIO_PIN_NONE (-1) diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index c1269a1085e1..373a6902d8fa 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -823,7 +823,7 @@ config CACHELINE_ALIGNED_L1 bool "Locate cacheline_aligned data to L1 Data Memory" default y if !BF54x default n if BF54x - depends on !SMP && !BF531 + depends on !SMP && !BF531 && !CRC32 help If enabled, cacheline_aligned data is linked into L1 data memory. (less latency) diff --git a/arch/blackfin/configs/BF527-EZKIT_defconfig b/arch/blackfin/configs/BF527-EZKIT_defconfig index 9ccc18a6b4df..90b175323644 100644 --- a/arch/blackfin/configs/BF527-EZKIT_defconfig +++ b/arch/blackfin/configs/BF527-EZKIT_defconfig @@ -147,6 +147,7 @@ CONFIG_USB_OTG_BLACKLIST_HUB=y CONFIG_USB_MON=y CONFIG_USB_MUSB_HDRC=y CONFIG_USB_MUSB_BLACKFIN=y +CONFIG_MUSB_PIO_ONLY=y CONFIG_USB_STORAGE=y CONFIG_USB_GADGET=y CONFIG_RTC_CLASS=y diff --git a/arch/blackfin/include/asm/cmpxchg.h b/arch/blackfin/include/asm/cmpxchg.h index ba2484f4cb2a..c05868cc61c1 100644 --- a/arch/blackfin/include/asm/cmpxchg.h +++ b/arch/blackfin/include/asm/cmpxchg.h @@ -122,7 +122,8 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, (unsigned long)(n), sizeof(*(ptr)))) #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) -#include <asm-generic/cmpxchg.h> +#define cmpxchg(ptr, o, n) cmpxchg_local((ptr), (o), (n)) +#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n)) #endif /* !CONFIG_SMP */ diff --git a/arch/blackfin/include/asm/gpio.h b/arch/blackfin/include/asm/gpio.h index 5a25856381ff..12d3571b5232 100644 --- a/arch/blackfin/include/asm/gpio.h +++ b/arch/blackfin/include/asm/gpio.h @@ -244,16 +244,26 @@ static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) return -EINVAL; } -static inline int gpio_get_value(unsigned gpio) +static inline int __gpio_get_value(unsigned gpio) { return bfin_gpio_get_value(gpio); } -static inline void gpio_set_value(unsigned gpio, int value) +static inline void __gpio_set_value(unsigned gpio, int value) { return bfin_gpio_set_value(gpio, value); } +static inline int gpio_get_value(unsigned gpio) +{ + return __gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned gpio, int value) +{ + return __gpio_set_value(gpio, value); +} + static inline int gpio_to_irq(unsigned gpio) { if (likely(gpio < MAX_BLACKFIN_GPIOS)) diff --git a/arch/blackfin/mach-bf538/boards/ezkit.c b/arch/blackfin/mach-bf538/boards/ezkit.c index 1633a6f306c0..85038f54354d 100644 --- a/arch/blackfin/mach-bf538/boards/ezkit.c +++ b/arch/blackfin/mach-bf538/boards/ezkit.c @@ -38,7 +38,7 @@ static struct platform_device rtc_device = { .name = "rtc-bfin", .id = -1, }; -#endif +#endif /* CONFIG_RTC_DRV_BFIN */ #if defined(CONFIG_SERIAL_BFIN) || defined(CONFIG_SERIAL_BFIN_MODULE) #ifdef CONFIG_SERIAL_BFIN_UART0 @@ -100,7 +100,7 @@ static struct platform_device bfin_uart0_device = { .platform_data = &bfin_uart0_peripherals, /* Passed to driver */ }, }; -#endif +#endif /* CONFIG_SERIAL_BFIN_UART0 */ #ifdef CONFIG_SERIAL_BFIN_UART1 static struct resource bfin_uart1_resources[] = { { @@ -148,7 +148,7 @@ static struct platform_device bfin_uart1_device = { .platform_data = &bfin_uart1_peripherals, /* Passed to driver */ }, }; -#endif +#endif /* CONFIG_SERIAL_BFIN_UART1 */ #ifdef CONFIG_SERIAL_BFIN_UART2 static struct resource bfin_uart2_resources[] = { { @@ -196,8 +196,8 @@ static struct platform_device bfin_uart2_device = { .platform_data = &bfin_uart2_peripherals, /* Passed to driver */ }, }; -#endif -#endif +#endif /* CONFIG_SERIAL_BFIN_UART2 */ +#endif /* CONFIG_SERIAL_BFIN */ #if defined(CONFIG_BFIN_SIR) || defined(CONFIG_BFIN_SIR_MODULE) #ifdef CONFIG_BFIN_SIR0 @@ -224,7 +224,7 @@ static struct platform_device bfin_sir0_device = { .num_resources = ARRAY_SIZE(bfin_sir0_resources), .resource = bfin_sir0_resources, }; -#endif +#endif /* CONFIG_BFIN_SIR0 */ #ifdef CONFIG_BFIN_SIR1 static struct resource bfin_sir1_resources[] = { { @@ -249,7 +249,7 @@ static struct platform_device bfin_sir1_device = { .num_resources = ARRAY_SIZE(bfin_sir1_resources), .resource = bfin_sir1_resources, }; -#endif +#endif /* CONFIG_BFIN_SIR1 */ #ifdef CONFIG_BFIN_SIR2 static struct resource bfin_sir2_resources[] = { { @@ -274,8 +274,8 @@ static struct platform_device bfin_sir2_device = { .num_resources = ARRAY_SIZE(bfin_sir2_resources), .resource = bfin_sir2_resources, }; -#endif -#endif +#endif /* CONFIG_BFIN_SIR2 */ +#endif /* CONFIG_BFIN_SIR */ #if defined(CONFIG_SERIAL_BFIN_SPORT) || defined(CONFIG_SERIAL_BFIN_SPORT_MODULE) #ifdef CONFIG_SERIAL_BFIN_SPORT0_UART @@ -311,7 +311,7 @@ static struct platform_device bfin_sport0_uart_device = { .platform_data = &bfin_sport0_peripherals, /* Passed to driver */ }, }; -#endif +#endif /* CONFIG_SERIAL_BFIN_SPORT0_UART */ #ifdef CONFIG_SERIAL_BFIN_SPORT1_UART static struct resource bfin_sport1_uart_resources[] = { { @@ -345,7 +345,7 @@ static struct platform_device bfin_sport1_uart_device = { .platform_data = &bfin_sport1_peripherals, /* Passed to driver */ }, }; -#endif +#endif /* CONFIG_SERIAL_BFIN_SPORT1_UART */ #ifdef CONFIG_SERIAL_BFIN_SPORT2_UART static struct resource bfin_sport2_uart_resources[] = { { @@ -379,7 +379,7 @@ static struct platform_device bfin_sport2_uart_device = { .platform_data = &bfin_sport2_peripherals, /* Passed to driver */ }, }; -#endif +#endif /* CONFIG_SERIAL_BFIN_SPORT2_UART */ #ifdef CONFIG_SERIAL_BFIN_SPORT3_UART static struct resource bfin_sport3_uart_resources[] = { { @@ -413,8 +413,8 @@ static struct platform_device bfin_sport3_uart_device = { .platform_data = &bfin_sport3_peripherals, /* Passed to driver */ }, }; -#endif -#endif +#endif /* CONFIG_SERIAL_BFIN_SPORT3_UART */ +#endif /* CONFIG_SERIAL_BFIN_SPORT */ #if defined(CONFIG_CAN_BFIN) || defined(CONFIG_CAN_BFIN_MODULE) static unsigned short bfin_can_peripherals[] = { @@ -452,7 +452,7 @@ static struct platform_device bfin_can_device = { .platform_data = &bfin_can_peripherals, /* Passed to driver */ }, }; -#endif +#endif /* CONFIG_CAN_BFIN */ /* * USB-LAN EzExtender board @@ -488,7 +488,7 @@ static struct platform_device smc91x_device = { .platform_data = &smc91x_info, }, }; -#endif +#endif /* CONFIG_SMC91X */ #if defined(CONFIG_SPI_BFIN5XX) || defined(CONFIG_SPI_BFIN5XX_MODULE) /* all SPI peripherals info goes here */ @@ -518,7 +518,8 @@ static struct flash_platform_data bfin_spi_flash_data = { static struct bfin5xx_spi_chip spi_flash_chip_info = { .enable_dma = 0, /* use dma transfer with this chip*/ }; -#endif +#endif /* CONFIG_MTD_M25P80 */ +#endif /* CONFIG_SPI_BFIN5XX */ #if defined(CONFIG_TOUCHSCREEN_AD7879) || defined(CONFIG_TOUCHSCREEN_AD7879_MODULE) #include <linux/spi/ad7879.h> @@ -535,7 +536,7 @@ static const struct ad7879_platform_data bfin_ad7879_ts_info = { .gpio_export = 1, /* Export GPIO to gpiolib */ .gpio_base = -1, /* Dynamic allocation */ }; -#endif +#endif /* CONFIG_TOUCHSCREEN_AD7879 */ #if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) #include <asm/bfin-lq035q1.h> @@ -564,7 +565,7 @@ static struct platform_device bfin_lq035q1_device = { .platform_data = &bfin_lq035q1_data, }, }; -#endif +#endif /* CONFIG_FB_BFIN_LQ035Q1 */ static struct spi_board_info bf538_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) \ @@ -579,7 +580,7 @@ static struct spi_board_info bf538_spi_board_info[] __initdata = { .controller_data = &spi_flash_chip_info, .mode = SPI_MODE_3, }, -#endif +#endif /* CONFIG_MTD_M25P80 */ #if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) { .modalias = "ad7879", @@ -590,7 +591,7 @@ static struct spi_board_info bf538_spi_board_info[] __initdata = { .chip_select = 1, .mode = SPI_CPHA | SPI_CPOL, }, -#endif +#endif /* CONFIG_TOUCHSCREEN_AD7879_SPI */ #if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) { .modalias = "bfin-lq035q1-spi", @@ -599,7 +600,7 @@ static struct spi_board_info bf538_spi_board_info[] __initdata = { .chip_select = 2, .mode = SPI_CPHA | SPI_CPOL, }, -#endif +#endif /* CONFIG_FB_BFIN_LQ035Q1 */ #if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) { .modalias = "spidev", @@ -607,7 +608,7 @@ static struct spi_board_info bf538_spi_board_info[] __initdata = { .bus_num = 0, .chip_select = 1, }, -#endif +#endif /* CONFIG_SPI_SPIDEV */ }; /* SPI (0) */ @@ -716,8 +717,6 @@ static struct platform_device bf538_spi_master2 = { }, }; -#endif /* spi master and devices */ - #if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE) static struct resource bfin_twi0_resource[] = { [0] = { @@ -759,8 +758,8 @@ static struct platform_device i2c_bfin_twi1_device = { .num_resources = ARRAY_SIZE(bfin_twi1_resource), .resource = bfin_twi1_resource, }; -#endif -#endif +#endif /* CONFIG_BF542 */ +#endif /* CONFIG_I2C_BLACKFIN_TWI */ #if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE) #include <linux/gpio_keys.h> diff --git a/arch/c6x/include/asm/irq.h b/arch/c6x/include/asm/irq.h index f13b78d5e1ca..ab4577f93d96 100644 --- a/arch/c6x/include/asm/irq.h +++ b/arch/c6x/include/asm/irq.h @@ -42,10 +42,6 @@ /* This number is used when no interrupt has been assigned */ #define NO_IRQ 0 -struct irq_data; -extern irq_hw_number_t irqd_to_hwirq(struct irq_data *d); -extern irq_hw_number_t virq_to_hw(unsigned int virq); - extern void __init init_pic_c64xplus(void); extern void init_IRQ(void); diff --git a/arch/c6x/kernel/irq.c b/arch/c6x/kernel/irq.c index 65b8ddf54b44..c90fb5e82ad7 100644 --- a/arch/c6x/kernel/irq.c +++ b/arch/c6x/kernel/irq.c @@ -130,16 +130,3 @@ int arch_show_interrupts(struct seq_file *p, int prec) seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count); return 0; } - -irq_hw_number_t irqd_to_hwirq(struct irq_data *d) -{ - return d->hwirq; -} -EXPORT_SYMBOL_GPL(irqd_to_hwirq); - -irq_hw_number_t virq_to_hw(unsigned int virq) -{ - struct irq_data *irq_data = irq_get_irq_data(virq); - return WARN_ON(!irq_data) ? 0 : irq_data->hwirq; -} -EXPORT_SYMBOL_GPL(virq_to_hw); diff --git a/arch/c6x/kernel/signal.c b/arch/c6x/kernel/signal.c index 304f675826e9..3b5a05099989 100644 --- a/arch/c6x/kernel/signal.c +++ b/arch/c6x/kernel/signal.c @@ -85,10 +85,7 @@ asmlinkage int do_rt_sigreturn(struct pt_regs *regs) goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) goto badframe; @@ -279,15 +276,8 @@ static int handle_signal(int sig, /* Set up the stack frame */ ret = setup_rt_frame(sig, ka, info, oldset, regs); - if (ret == 0) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, - &ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked, sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } + if (ret == 0) + block_sigmask(ka, sig); return ret; } diff --git a/arch/frv/mb93090-mb00/pci-dma.c b/arch/frv/mb93090-mb00/pci-dma.c index 41098a3803a2..4f8d8bcdc7de 100644 --- a/arch/frv/mb93090-mb00/pci-dma.c +++ b/arch/frv/mb93090-mb00/pci-dma.c @@ -13,6 +13,7 @@ #include <linux/dma-mapping.h> #include <linux/list.h> #include <linux/pci.h> +#include <linux/export.h> #include <linux/highmem.h> #include <linux/scatterlist.h> #include <asm/io.h> diff --git a/arch/hexagon/include/asm/dma-mapping.h b/arch/hexagon/include/asm/dma-mapping.h index 448b224ba4ef..233ed3d2d25e 100644 --- a/arch/hexagon/include/asm/dma-mapping.h +++ b/arch/hexagon/include/asm/dma-mapping.h @@ -71,29 +71,35 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) return (dma_addr == bad_dma_address); } -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { void *ret; struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!dma_ops); - ret = ops->alloc_coherent(dev, size, dma_handle, flag); + ret = ops->alloc(dev, size, dma_handle, flag, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, ret); return ret; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { struct dma_map_ops *dma_ops = get_dma_ops(dev); BUG_ON(!dma_ops); - dma_ops->free_coherent(dev, size, cpu_addr, dma_handle); + dma_ops->free(dev, size, cpu_addr, dma_handle, attrs); debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); } diff --git a/arch/hexagon/kernel/dma.c b/arch/hexagon/kernel/dma.c index e711ace62fdf..0f2367cc5493 100644 --- a/arch/hexagon/kernel/dma.c +++ b/arch/hexagon/kernel/dma.c @@ -22,6 +22,7 @@ #include <linux/bootmem.h> #include <linux/genalloc.h> #include <asm/dma-mapping.h> +#include <linux/module.h> struct dma_map_ops *dma_ops; EXPORT_SYMBOL(dma_ops); @@ -54,7 +55,8 @@ static struct gen_pool *coherent_pool; /* Allocates from a pool of uncached memory that was reserved at boot time */ void *hexagon_dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_addr, gfp_t flag) + dma_addr_t *dma_addr, gfp_t flag, + struct dma_attrs *attrs) { void *ret; @@ -81,7 +83,7 @@ void *hexagon_dma_alloc_coherent(struct device *dev, size_t size, } static void hexagon_free_coherent(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_addr) + dma_addr_t dma_addr, struct dma_attrs *attrs) { gen_pool_free(coherent_pool, (unsigned long) vaddr, size); } @@ -202,8 +204,8 @@ static void hexagon_sync_single_for_device(struct device *dev, } struct dma_map_ops hexagon_dma_ops = { - .alloc_coherent = hexagon_dma_alloc_coherent, - .free_coherent = hexagon_free_coherent, + .alloc = hexagon_dma_alloc_coherent, + .free = hexagon_free_coherent, .map_sg = hexagon_map_sg, .map_page = hexagon_map_page, .sync_single_for_cpu = hexagon_sync_single_for_cpu, diff --git a/arch/hexagon/kernel/process.c b/arch/hexagon/kernel/process.c index 18c4f0b0f4ba..ff02821bfb7e 100644 --- a/arch/hexagon/kernel/process.c +++ b/arch/hexagon/kernel/process.c @@ -1,7 +1,7 @@ /* * Process creation support for Hexagon * - * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -88,7 +88,7 @@ void (*idle_sleep)(void) = default_idle; void cpu_idle(void) { while (1) { - tick_nohz_stop_sched_tick(1); + tick_nohz_idle_enter(); local_irq_disable(); while (!need_resched()) { idle_sleep(); @@ -97,7 +97,7 @@ void cpu_idle(void) local_irq_disable(); } local_irq_enable(); - tick_nohz_restart_sched_tick(); + tick_nohz_idle_exit(); schedule(); } } diff --git a/arch/hexagon/kernel/ptrace.c b/arch/hexagon/kernel/ptrace.c index 32342de1a79c..96c3b2c4dbad 100644 --- a/arch/hexagon/kernel/ptrace.c +++ b/arch/hexagon/kernel/ptrace.c @@ -28,6 +28,7 @@ #include <linux/ptrace.h> #include <linux/regset.h> #include <linux/user.h> +#include <linux/elf.h> #include <asm/user.h> diff --git a/arch/hexagon/kernel/smp.c b/arch/hexagon/kernel/smp.c index 15d1fd22bbc5..1298141874a3 100644 --- a/arch/hexagon/kernel/smp.c +++ b/arch/hexagon/kernel/smp.c @@ -1,7 +1,7 @@ /* * SMP support for Hexagon * - * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -28,6 +28,7 @@ #include <linux/sched.h> #include <linux/smp.h> #include <linux/spinlock.h> +#include <linux/cpu.h> #include <asm/time.h> /* timer_interrupt */ #include <asm/hexagon_vm.h> @@ -35,7 +36,7 @@ #define BASE_IPI_IRQ 26 /* - * cpu_possible_map needs to be filled out prior to setup_per_cpu_areas + * cpu_possible_mask needs to be filled out prior to setup_per_cpu_areas * (which is prior to any of our smp_prepare_cpu crap), in order to set * up the... per_cpu areas. */ @@ -177,7 +178,12 @@ void __cpuinit start_secondary(void) printk(KERN_INFO "%s cpu %d\n", __func__, current_thread_info()->cpu); + notify_cpu_starting(cpu); + + ipi_call_lock(); set_cpu_online(cpu, true); + ipi_call_unlock(); + local_irq_enable(); cpu_idle(); @@ -208,7 +214,7 @@ int __cpuinit __cpu_up(unsigned int cpu) stack_start = ((void *) thread) + THREAD_SIZE; __vmstart(start_secondary, stack_start); - while (!cpu_isset(cpu, cpu_online_map)) + while (!cpu_online(cpu)) barrier(); return 0; @@ -229,7 +235,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) /* Right now, let's just fake it. */ for (i = 0; i < max_cpus; i++) - cpu_set(i, cpu_present_map); + set_cpu_present(i, true); /* Also need to register the interrupts for IPI */ if (max_cpus > 1) @@ -269,5 +275,5 @@ void smp_start_cpus(void) int i; for (i = 0; i < NR_CPUS; i++) - cpu_set(i, cpu_possible_map); + set_cpu_possible(i, true); } diff --git a/arch/hexagon/kernel/time.c b/arch/hexagon/kernel/time.c index 6bee15c9c113..5d9b33b67935 100644 --- a/arch/hexagon/kernel/time.c +++ b/arch/hexagon/kernel/time.c @@ -28,6 +28,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/module.h> #include <asm/timer-regs.h> #include <asm/hexagon_vm.h> diff --git a/arch/hexagon/kernel/vdso.c b/arch/hexagon/kernel/vdso.c index f212a453b527..5d39f42f7085 100644 --- a/arch/hexagon/kernel/vdso.c +++ b/arch/hexagon/kernel/vdso.c @@ -21,6 +21,7 @@ #include <linux/err.h> #include <linux/mm.h> #include <linux/vmalloc.h> +#include <linux/binfmts.h> #include <asm/vdso.h> diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index f6ea3a3b4a84..bcda5b2d121a 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -1129,7 +1129,8 @@ void sba_unmap_single_attrs(struct device *dev, dma_addr_t iova, size_t size, * See Documentation/DMA-API-HOWTO.txt */ static void * -sba_alloc_coherent (struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flags) +sba_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, + gfp_t flags, struct dma_attrs *attrs) { struct ioc *ioc; void *addr; @@ -1191,8 +1192,8 @@ sba_alloc_coherent (struct device *dev, size_t size, dma_addr_t *dma_handle, gfp * * See Documentation/DMA-API-HOWTO.txt */ -static void sba_free_coherent (struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle) +static void sba_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle, struct dma_attrs *attrs) { sba_unmap_single_attrs(dev, dma_handle, size, 0, NULL); free_pages((unsigned long) vaddr, get_order(size)); @@ -2212,8 +2213,8 @@ sba_page_override(char *str) __setup("sbapagesize=",sba_page_override); struct dma_map_ops sba_dma_ops = { - .alloc_coherent = sba_alloc_coherent, - .free_coherent = sba_free_coherent, + .alloc = sba_alloc_coherent, + .free = sba_free_coherent, .map_page = sba_map_page, .unmap_page = sba_unmap_page, .map_sg = sba_map_sg_attrs, diff --git a/arch/ia64/include/asm/cmpxchg.h b/arch/ia64/include/asm/cmpxchg.h index 4c96187e2049..4f37dbbb8640 100644 --- a/arch/ia64/include/asm/cmpxchg.h +++ b/arch/ia64/include/asm/cmpxchg.h @@ -1 +1,147 @@ -#include <asm/intrinsics.h> +#ifndef _ASM_IA64_CMPXCHG_H +#define _ASM_IA64_CMPXCHG_H + +/* + * Compare/Exchange, forked from asm/intrinsics.h + * which was: + * + * Copyright (C) 2002-2003 Hewlett-Packard Co + * David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#ifndef __ASSEMBLY__ + +#include <linux/types.h> +/* include compiler specific intrinsics */ +#include <asm/ia64regs.h> +#ifdef __INTEL_COMPILER +# include <asm/intel_intrin.h> +#else +# include <asm/gcc_intrin.h> +#endif + +/* + * This function doesn't exist, so you'll get a linker error if + * something tries to do an invalid xchg(). + */ +extern void ia64_xchg_called_with_bad_pointer(void); + +#define __xchg(x, ptr, size) \ +({ \ + unsigned long __xchg_result; \ + \ + switch (size) { \ + case 1: \ + __xchg_result = ia64_xchg1((__u8 *)ptr, x); \ + break; \ + \ + case 2: \ + __xchg_result = ia64_xchg2((__u16 *)ptr, x); \ + break; \ + \ + case 4: \ + __xchg_result = ia64_xchg4((__u32 *)ptr, x); \ + break; \ + \ + case 8: \ + __xchg_result = ia64_xchg8((__u64 *)ptr, x); \ + break; \ + default: \ + ia64_xchg_called_with_bad_pointer(); \ + } \ + __xchg_result; \ +}) + +#define xchg(ptr, x) \ +((__typeof__(*(ptr))) __xchg((unsigned long) (x), (ptr), sizeof(*(ptr)))) + +/* + * Atomic compare and exchange. Compare OLD with MEM, if identical, + * store NEW in MEM. Return the initial value in MEM. Success is + * indicated by comparing RETURN with OLD. + */ + +#define __HAVE_ARCH_CMPXCHG 1 + +/* + * This function doesn't exist, so you'll get a linker error + * if something tries to do an invalid cmpxchg(). + */ +extern long ia64_cmpxchg_called_with_bad_pointer(void); + +#define ia64_cmpxchg(sem, ptr, old, new, size) \ +({ \ + __u64 _o_, _r_; \ + \ + switch (size) { \ + case 1: \ + _o_ = (__u8) (long) (old); \ + break; \ + case 2: \ + _o_ = (__u16) (long) (old); \ + break; \ + case 4: \ + _o_ = (__u32) (long) (old); \ + break; \ + case 8: \ + _o_ = (__u64) (long) (old); \ + break; \ + default: \ + break; \ + } \ + switch (size) { \ + case 1: \ + _r_ = ia64_cmpxchg1_##sem((__u8 *) ptr, new, _o_); \ + break; \ + \ + case 2: \ + _r_ = ia64_cmpxchg2_##sem((__u16 *) ptr, new, _o_); \ + break; \ + \ + case 4: \ + _r_ = ia64_cmpxchg4_##sem((__u32 *) ptr, new, _o_); \ + break; \ + \ + case 8: \ + _r_ = ia64_cmpxchg8_##sem((__u64 *) ptr, new, _o_); \ + break; \ + \ + default: \ + _r_ = ia64_cmpxchg_called_with_bad_pointer(); \ + break; \ + } \ + (__typeof__(old)) _r_; \ +}) + +#define cmpxchg_acq(ptr, o, n) \ + ia64_cmpxchg(acq, (ptr), (o), (n), sizeof(*(ptr))) +#define cmpxchg_rel(ptr, o, n) \ + ia64_cmpxchg(rel, (ptr), (o), (n), sizeof(*(ptr))) + +/* for compatibility with other platforms: */ +#define cmpxchg(ptr, o, n) cmpxchg_acq((ptr), (o), (n)) +#define cmpxchg64(ptr, o, n) cmpxchg_acq((ptr), (o), (n)) + +#define cmpxchg_local cmpxchg +#define cmpxchg64_local cmpxchg64 + +#ifdef CONFIG_IA64_DEBUG_CMPXCHG +# define CMPXCHG_BUGCHECK_DECL int _cmpxchg_bugcheck_count = 128; +# define CMPXCHG_BUGCHECK(v) \ +do { \ + if (_cmpxchg_bugcheck_count-- <= 0) { \ + void *ip; \ + extern int printk(const char *fmt, ...); \ + ip = (void *) ia64_getreg(_IA64_REG_IP); \ + printk("CMPXCHG_BUGCHECK: stuck at %p on word %p\n", ip, (v));\ + break; \ + } \ +} while (0) +#else /* !CONFIG_IA64_DEBUG_CMPXCHG */ +# define CMPXCHG_BUGCHECK_DECL +# define CMPXCHG_BUGCHECK(v) +#endif /* !CONFIG_IA64_DEBUG_CMPXCHG */ + +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_IA64_CMPXCHG_H */ diff --git a/arch/ia64/include/asm/dma-mapping.h b/arch/ia64/include/asm/dma-mapping.h index 4336d080b241..4f5e8148440d 100644 --- a/arch/ia64/include/asm/dma-mapping.h +++ b/arch/ia64/include/asm/dma-mapping.h @@ -23,23 +23,29 @@ extern void machvec_dma_sync_single(struct device *, dma_addr_t, size_t, extern void machvec_dma_sync_sg(struct device *, struct scatterlist *, int, enum dma_data_direction); -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *daddr, gfp_t gfp) +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *daddr, gfp_t gfp, + struct dma_attrs *attrs) { struct dma_map_ops *ops = platform_dma_get_ops(dev); void *caddr; - caddr = ops->alloc_coherent(dev, size, daddr, gfp); + caddr = ops->alloc(dev, size, daddr, gfp, attrs); debug_dma_alloc_coherent(dev, size, *daddr, caddr); return caddr; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *caddr, dma_addr_t daddr) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *caddr, dma_addr_t daddr, + struct dma_attrs *attrs) { struct dma_map_ops *ops = platform_dma_get_ops(dev); debug_dma_free_coherent(dev, size, caddr, daddr); - ops->free_coherent(dev, size, caddr, daddr); + ops->free(dev, size, caddr, daddr, attrs); } #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) diff --git a/arch/ia64/include/asm/futex.h b/arch/ia64/include/asm/futex.h index 0ab82cc2dc8f..d2bf1fd5e44f 100644 --- a/arch/ia64/include/asm/futex.h +++ b/arch/ia64/include/asm/futex.h @@ -106,15 +106,16 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, return -EFAULT; { - register unsigned long r8 __asm ("r8") = 0; + register unsigned long r8 __asm ("r8"); unsigned long prev; __asm__ __volatile__( " mf;; \n" - " mov ar.ccv=%3;; \n" - "[1:] cmpxchg4.acq %0=[%1],%2,ar.ccv \n" + " mov %0=r0 \n" + " mov ar.ccv=%4;; \n" + "[1:] cmpxchg4.acq %1=[%2],%3,ar.ccv \n" " .xdata4 \"__ex_table\", 1b-., 2f-. \n" "[2:]" - : "=r" (prev) + : "=r" (r8), "=r" (prev) : "r" (uaddr), "r" (newval), "rO" ((long) (unsigned) oldval) : "memory"); diff --git a/arch/ia64/include/asm/intrinsics.h b/arch/ia64/include/asm/intrinsics.h index e4076b511829..d129e367e764 100644 --- a/arch/ia64/include/asm/intrinsics.h +++ b/arch/ia64/include/asm/intrinsics.h @@ -18,6 +18,7 @@ #else # include <asm/gcc_intrin.h> #endif +#include <asm/cmpxchg.h> #define ia64_native_get_psr_i() (ia64_native_getreg(_IA64_REG_PSR) & IA64_PSR_I) @@ -81,119 +82,6 @@ extern unsigned long __bad_increment_for_ia64_fetch_and_add (void); #define ia64_fetch_and_add(i,v) (ia64_fetchadd(i, v, rel) + (i)) /* return new value */ -/* - * This function doesn't exist, so you'll get a linker error if - * something tries to do an invalid xchg(). - */ -extern void ia64_xchg_called_with_bad_pointer (void); - -#define __xchg(x,ptr,size) \ -({ \ - unsigned long __xchg_result; \ - \ - switch (size) { \ - case 1: \ - __xchg_result = ia64_xchg1((__u8 *)ptr, x); \ - break; \ - \ - case 2: \ - __xchg_result = ia64_xchg2((__u16 *)ptr, x); \ - break; \ - \ - case 4: \ - __xchg_result = ia64_xchg4((__u32 *)ptr, x); \ - break; \ - \ - case 8: \ - __xchg_result = ia64_xchg8((__u64 *)ptr, x); \ - break; \ - default: \ - ia64_xchg_called_with_bad_pointer(); \ - } \ - __xchg_result; \ -}) - -#define xchg(ptr,x) \ - ((__typeof__(*(ptr))) __xchg ((unsigned long) (x), (ptr), sizeof(*(ptr)))) - -/* - * Atomic compare and exchange. Compare OLD with MEM, if identical, - * store NEW in MEM. Return the initial value in MEM. Success is - * indicated by comparing RETURN with OLD. - */ - -#define __HAVE_ARCH_CMPXCHG 1 - -/* - * This function doesn't exist, so you'll get a linker error - * if something tries to do an invalid cmpxchg(). - */ -extern long ia64_cmpxchg_called_with_bad_pointer (void); - -#define ia64_cmpxchg(sem,ptr,old,new,size) \ -({ \ - __u64 _o_, _r_; \ - \ - switch (size) { \ - case 1: _o_ = (__u8 ) (long) (old); break; \ - case 2: _o_ = (__u16) (long) (old); break; \ - case 4: _o_ = (__u32) (long) (old); break; \ - case 8: _o_ = (__u64) (long) (old); break; \ - default: break; \ - } \ - switch (size) { \ - case 1: \ - _r_ = ia64_cmpxchg1_##sem((__u8 *) ptr, new, _o_); \ - break; \ - \ - case 2: \ - _r_ = ia64_cmpxchg2_##sem((__u16 *) ptr, new, _o_); \ - break; \ - \ - case 4: \ - _r_ = ia64_cmpxchg4_##sem((__u32 *) ptr, new, _o_); \ - break; \ - \ - case 8: \ - _r_ = ia64_cmpxchg8_##sem((__u64 *) ptr, new, _o_); \ - break; \ - \ - default: \ - _r_ = ia64_cmpxchg_called_with_bad_pointer(); \ - break; \ - } \ - (__typeof__(old)) _r_; \ -}) - -#define cmpxchg_acq(ptr, o, n) \ - ia64_cmpxchg(acq, (ptr), (o), (n), sizeof(*(ptr))) -#define cmpxchg_rel(ptr, o, n) \ - ia64_cmpxchg(rel, (ptr), (o), (n), sizeof(*(ptr))) - -/* for compatibility with other platforms: */ -#define cmpxchg(ptr, o, n) cmpxchg_acq((ptr), (o), (n)) -#define cmpxchg64(ptr, o, n) cmpxchg_acq((ptr), (o), (n)) - -#define cmpxchg_local cmpxchg -#define cmpxchg64_local cmpxchg64 - -#ifdef CONFIG_IA64_DEBUG_CMPXCHG -# define CMPXCHG_BUGCHECK_DECL int _cmpxchg_bugcheck_count = 128; -# define CMPXCHG_BUGCHECK(v) \ - do { \ - if (_cmpxchg_bugcheck_count-- <= 0) { \ - void *ip; \ - extern int printk(const char *fmt, ...); \ - ip = (void *) ia64_getreg(_IA64_REG_IP); \ - printk("CMPXCHG_BUGCHECK: stuck at %p on word %p\n", ip, (v)); \ - break; \ - } \ - } while (0) -#else /* !CONFIG_IA64_DEBUG_CMPXCHG */ -# define CMPXCHG_BUGCHECK_DECL -# define CMPXCHG_BUGCHECK(v) -#endif /* !CONFIG_IA64_DEBUG_CMPXCHG */ - #endif #ifdef __KERNEL__ diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index ac795d311f44..6f38b6120d96 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -839,7 +839,7 @@ static __init int setup_additional_cpus(char *s) early_param("additional_cpus", setup_additional_cpus); /* - * cpu_possible_map should be static, it cannot change as CPUs + * cpu_possible_mask should be static, it cannot change as CPUs * are onlined, or offlined. The reason is per-cpu data-structures * are allocated by some modules at init time, and dont expect to * do this dynamically on cpu arrival/departure. diff --git a/arch/ia64/kernel/pci-swiotlb.c b/arch/ia64/kernel/pci-swiotlb.c index d9485d952ed0..939260aeac98 100644 --- a/arch/ia64/kernel/pci-swiotlb.c +++ b/arch/ia64/kernel/pci-swiotlb.c @@ -15,16 +15,24 @@ int swiotlb __read_mostly; EXPORT_SYMBOL(swiotlb); static void *ia64_swiotlb_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs) { if (dev->coherent_dma_mask != DMA_BIT_MASK(64)) gfp |= GFP_DMA; return swiotlb_alloc_coherent(dev, size, dma_handle, gfp); } +static void ia64_swiotlb_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_addr, + struct dma_attrs *attrs) +{ + swiotlb_free_coherent(dev, size, vaddr, dma_addr); +} + struct dma_map_ops swiotlb_dma_ops = { - .alloc_coherent = ia64_swiotlb_alloc_coherent, - .free_coherent = swiotlb_free_coherent, + .alloc = ia64_swiotlb_alloc_coherent, + .free = ia64_swiotlb_free_coherent, .map_page = swiotlb_map_page, .unmap_page = swiotlb_unmap_page, .map_sg = swiotlb_map_sg_attrs, diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 9d0fd7d5bb82..f00ba025375d 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -604,12 +604,6 @@ pfm_unprotect_ctx_ctxsw(pfm_context_t *x, unsigned long f) spin_unlock(&(x)->ctx_lock); } -static inline unsigned int -pfm_do_munmap(struct mm_struct *mm, unsigned long addr, size_t len, int acct) -{ - return do_munmap(mm, addr, len); -} - static inline unsigned long pfm_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags, unsigned long exec) { @@ -1458,8 +1452,9 @@ pfm_unreserve_session(pfm_context_t *ctx, int is_syswide, unsigned int cpu) * a PROTECT_CTX() section. */ static int -pfm_remove_smpl_mapping(struct task_struct *task, void *vaddr, unsigned long size) +pfm_remove_smpl_mapping(void *vaddr, unsigned long size) { + struct task_struct *task = current; int r; /* sanity checks */ @@ -1473,13 +1468,8 @@ pfm_remove_smpl_mapping(struct task_struct *task, void *vaddr, unsigned long siz /* * does the actual unmapping */ - down_write(&task->mm->mmap_sem); + r = vm_munmap((unsigned long)vaddr, size); - DPRINT(("down_write done smpl_vaddr=%p size=%lu\n", vaddr, size)); - - r = pfm_do_munmap(task->mm, (unsigned long)vaddr, size, 0); - - up_write(&task->mm->mmap_sem); if (r !=0) { printk(KERN_ERR "perfmon: [%d] unable to unmap sampling buffer @%p size=%lu\n", task_pid_nr(task), vaddr, size); } @@ -1945,7 +1935,7 @@ pfm_flush(struct file *filp, fl_owner_t id) * because some VM function reenables interrupts. * */ - if (smpl_buf_vaddr) pfm_remove_smpl_mapping(current, smpl_buf_vaddr, smpl_buf_size); + if (smpl_buf_vaddr) pfm_remove_smpl_mapping(smpl_buf_vaddr, smpl_buf_size); return 0; } diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index f5104b7c52cd..463fb3bbe11e 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1174,7 +1174,7 @@ out: bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu) { - return irqchip_in_kernel(vcpu->kcm) == (vcpu->arch.apic != NULL); + return irqchip_in_kernel(vcpu->kvm) == (vcpu->arch.apic != NULL); } int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) diff --git a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c index a9d310de57da..3290d6e00c31 100644 --- a/arch/ia64/sn/pci/pci_dma.c +++ b/arch/ia64/sn/pci/pci_dma.c @@ -76,7 +76,8 @@ EXPORT_SYMBOL(sn_dma_set_mask); * more information. */ static void *sn_dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t * dma_handle, gfp_t flags) + dma_addr_t * dma_handle, gfp_t flags, + struct dma_attrs *attrs) { void *cpuaddr; unsigned long phys_addr; @@ -137,7 +138,7 @@ static void *sn_dma_alloc_coherent(struct device *dev, size_t size, * any associated IOMMU mappings. */ static void sn_dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, - dma_addr_t dma_handle) + dma_addr_t dma_handle, struct dma_attrs *attrs) { struct pci_dev *pdev = to_pci_dev(dev); struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev); @@ -466,8 +467,8 @@ int sn_pci_legacy_write(struct pci_bus *bus, u16 port, u32 val, u8 size) } static struct dma_map_ops sn_dma_ops = { - .alloc_coherent = sn_dma_alloc_coherent, - .free_coherent = sn_dma_free_coherent, + .alloc = sn_dma_alloc_coherent, + .free = sn_dma_free_coherent, .map_page = sn_dma_map_page, .unmap_page = sn_dma_unmap_page, .map_sg = sn_dma_map_sg, diff --git a/arch/m68k/configs/m5275evb_defconfig b/arch/m68k/configs/m5275evb_defconfig index 33c32aeca12b..a1230e82bb1e 100644 --- a/arch/m68k/configs/m5275evb_defconfig +++ b/arch/m68k/configs/m5275evb_defconfig @@ -49,7 +49,6 @@ CONFIG_BLK_DEV_RAM=y CONFIG_NETDEVICES=y CONFIG_NET_ETHERNET=y CONFIG_FEC=y -CONFIG_FEC2=y # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set CONFIG_PPP=y diff --git a/arch/m68k/include/asm/atomic.h b/arch/m68k/include/asm/atomic.h index 336e6173794f..f4e32de263a7 100644 --- a/arch/m68k/include/asm/atomic.h +++ b/arch/m68k/include/asm/atomic.h @@ -3,6 +3,7 @@ #include <linux/types.h> #include <linux/irqflags.h> +#include <asm/cmpxchg.h> /* * Atomic operations that C can't guarantee us. Useful for diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c index 96fa6ed7e799..d9f62e0f46c0 100644 --- a/arch/m68k/mac/config.c +++ b/arch/m68k/mac/config.c @@ -980,6 +980,9 @@ int __init mac_platform_init(void) { u8 *swim_base; + if (!MACH_IS_MAC) + return -ENODEV; + /* * Serial devices */ diff --git a/arch/m68k/platform/520x/config.c b/arch/m68k/platform/520x/config.c index 235947844f27..09df4b89e8be 100644 --- a/arch/m68k/platform/520x/config.c +++ b/arch/m68k/platform/520x/config.c @@ -22,7 +22,7 @@ /***************************************************************************/ -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) static void __init m520x_qspi_init(void) { @@ -35,7 +35,7 @@ static void __init m520x_qspi_init(void) writew(par, MCF_GPIO_PAR_UART); } -#endif /* CONFIG_SPI_COLDFIRE_QSPI */ +#endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */ /***************************************************************************/ @@ -79,7 +79,7 @@ void __init config_BSP(char *commandp, int size) mach_sched_init = hw_timer_init; m520x_uarts_init(); m520x_fec_init(); -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) m520x_qspi_init(); #endif } diff --git a/arch/m68k/platform/523x/config.c b/arch/m68k/platform/523x/config.c index c8b405d5a961..d47dfd8f50a2 100644 --- a/arch/m68k/platform/523x/config.c +++ b/arch/m68k/platform/523x/config.c @@ -22,7 +22,7 @@ /***************************************************************************/ -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) static void __init m523x_qspi_init(void) { @@ -36,7 +36,7 @@ static void __init m523x_qspi_init(void) writew(par, MCFGPIO_PAR_TIMER); } -#endif /* CONFIG_SPI_COLDFIRE_QSPI */ +#endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */ /***************************************************************************/ @@ -58,7 +58,7 @@ void __init config_BSP(char *commandp, int size) { mach_sched_init = hw_timer_init; m523x_fec_init(); -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) m523x_qspi_init(); #endif } diff --git a/arch/m68k/platform/5249/config.c b/arch/m68k/platform/5249/config.c index bbf05135bb98..300e729a58d0 100644 --- a/arch/m68k/platform/5249/config.c +++ b/arch/m68k/platform/5249/config.c @@ -51,7 +51,7 @@ static struct platform_device *m5249_devices[] __initdata = { /***************************************************************************/ -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) static void __init m5249_qspi_init(void) { @@ -61,7 +61,7 @@ static void __init m5249_qspi_init(void) mcf_mapirq2imr(MCF_IRQ_QSPI, MCFINTC_QSPI); } -#endif /* CONFIG_SPI_COLDFIRE_QSPI */ +#endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */ /***************************************************************************/ @@ -90,7 +90,7 @@ void __init config_BSP(char *commandp, int size) #ifdef CONFIG_M5249C3 m5249_smc91x_init(); #endif -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) m5249_qspi_init(); #endif } diff --git a/arch/m68k/platform/527x/config.c b/arch/m68k/platform/527x/config.c index 7ed848c3b848..b3cb378c5e94 100644 --- a/arch/m68k/platform/527x/config.c +++ b/arch/m68k/platform/527x/config.c @@ -23,7 +23,7 @@ /***************************************************************************/ -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) static void __init m527x_qspi_init(void) { @@ -42,7 +42,7 @@ static void __init m527x_qspi_init(void) #endif } -#endif /* CONFIG_SPI_COLDFIRE_QSPI */ +#endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */ /***************************************************************************/ @@ -74,9 +74,7 @@ static void __init m527x_fec_init(void) writew(par | 0xf00, MCF_IPSBAR + 0x100082); v = readb(MCF_IPSBAR + 0x100078); writeb(v | 0xc0, MCF_IPSBAR + 0x100078); -#endif -#ifdef CONFIG_FEC2 /* Set multi-function pins to ethernet mode for fec1 */ par = readw(MCF_IPSBAR + 0x100082); writew(par | 0xa0, MCF_IPSBAR + 0x100082); @@ -92,7 +90,7 @@ void __init config_BSP(char *commandp, int size) mach_sched_init = hw_timer_init; m527x_uarts_init(); m527x_fec_init(); -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) m527x_qspi_init(); #endif } diff --git a/arch/m68k/platform/528x/config.c b/arch/m68k/platform/528x/config.c index d4492926614c..c5f11ba49be5 100644 --- a/arch/m68k/platform/528x/config.c +++ b/arch/m68k/platform/528x/config.c @@ -24,7 +24,7 @@ /***************************************************************************/ -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) static void __init m528x_qspi_init(void) { @@ -32,7 +32,7 @@ static void __init m528x_qspi_init(void) __raw_writeb(0x07, MCFGPIO_PQSPAR); } -#endif /* CONFIG_SPI_COLDFIRE_QSPI */ +#endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */ /***************************************************************************/ @@ -98,7 +98,7 @@ void __init config_BSP(char *commandp, int size) mach_sched_init = hw_timer_init; m528x_uarts_init(); m528x_fec_init(); -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) m528x_qspi_init(); #endif } diff --git a/arch/m68k/platform/532x/config.c b/arch/m68k/platform/532x/config.c index 2bec3477b739..37082d02f2bd 100644 --- a/arch/m68k/platform/532x/config.c +++ b/arch/m68k/platform/532x/config.c @@ -30,7 +30,7 @@ /***************************************************************************/ -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) static void __init m532x_qspi_init(void) { @@ -38,7 +38,7 @@ static void __init m532x_qspi_init(void) writew(0x01f0, MCF_GPIO_PAR_QSPI); } -#endif /* CONFIG_SPI_COLDFIRE_QSPI */ +#endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */ /***************************************************************************/ @@ -77,7 +77,7 @@ void __init config_BSP(char *commandp, int size) mach_sched_init = hw_timer_init; m532x_uarts_init(); m532x_fec_init(); -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) m532x_qspi_init(); #endif diff --git a/arch/m68k/platform/68EZ328/Makefile b/arch/m68k/platform/68EZ328/Makefile index ee97735a242c..b44d799b1115 100644 --- a/arch/m68k/platform/68EZ328/Makefile +++ b/arch/m68k/platform/68EZ328/Makefile @@ -3,9 +3,3 @@ # obj-y := config.o - -extra-y := bootlogo.rh - -$(obj)/bootlogo.rh: $(src)/bootlogo.h - perl $(src)/../68328/bootlogo.pl < $(src)/bootlogo.h \ - > $(obj)/bootlogo.rh diff --git a/arch/m68k/platform/68VZ328/Makefile b/arch/m68k/platform/68VZ328/Makefile index 447ffa0fd7c7..a49d75e65489 100644 --- a/arch/m68k/platform/68VZ328/Makefile +++ b/arch/m68k/platform/68VZ328/Makefile @@ -3,14 +3,9 @@ # obj-y := config.o -logo-$(UCDIMM) := bootlogo.rh -logo-$(DRAGEN2) := screen.h -extra-y := $(logo-y) - -$(obj)/bootlogo.rh: $(src)/../68EZ328/bootlogo.h - perl $(src)/bootlogo.pl < $(src)/../68328/bootlogo.h > $(obj)/bootlogo.rh +extra-$(DRAGEN2):= screen.h $(obj)/screen.h: $(src)/screen.xbm $(src)/xbm2lcd.pl perl $(src)/xbm2lcd.pl < $(src)/screen.xbm > $(obj)/screen.h -clean-files := $(obj)/screen.h $(obj)/bootlogo.rh +clean-files := $(obj)/screen.h diff --git a/arch/m68k/platform/68EZ328/bootlogo.h b/arch/m68k/platform/68VZ328/bootlogo.h index e842bdae5839..b38e2b255142 100644 --- a/arch/m68k/platform/68EZ328/bootlogo.h +++ b/arch/m68k/platform/68VZ328/bootlogo.h @@ -1,6 +1,6 @@ #define splash_width 640 #define splash_height 480 -static unsigned char splash_bits[] = { +unsigned char __attribute__ ((aligned(16))) bootlogo_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/arch/m68k/platform/coldfire/device.c b/arch/m68k/platform/coldfire/device.c index fa50c48292ff..3aa77ddea89d 100644 --- a/arch/m68k/platform/coldfire/device.c +++ b/arch/m68k/platform/coldfire/device.c @@ -114,14 +114,14 @@ static struct resource mcf_fec1_resources[] = { static struct platform_device mcf_fec1 = { .name = "fec", - .id = 0, + .id = 1, .num_resources = ARRAY_SIZE(mcf_fec1_resources), .resource = mcf_fec1_resources, }; #endif /* MCFFEC_BASE1 */ #endif /* CONFIG_FEC */ -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) /* * The ColdFire QSPI module is an SPI protocol hardware block used * on a number of different ColdFire CPUs. @@ -274,7 +274,7 @@ static struct platform_device mcf_qspi = { .resource = mcf_qspi_resources, .dev.platform_data = &mcf_qspi_data, }; -#endif /* CONFIG_SPI_COLDFIRE_QSPI */ +#endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */ static struct platform_device *mcf_devices[] __initdata = { &mcf_uart, @@ -284,7 +284,7 @@ static struct platform_device *mcf_devices[] __initdata = { &mcf_fec1, #endif #endif -#ifdef CONFIG_SPI_COLDFIRE_QSPI +#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) &mcf_qspi, #endif }; diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c index 512adb64f7dd..8a1ce327c963 100644 --- a/arch/m68k/q40/config.c +++ b/arch/m68k/q40/config.c @@ -334,6 +334,9 @@ static __init int q40_add_kbd_device(void) { struct platform_device *pdev; + if (!MACH_IS_Q40) + return -ENODEV; + pdev = platform_device_register_simple("q40kbd", -1, NULL, 0); if (IS_ERR(pdev)) return PTR_ERR(pdev); diff --git a/arch/microblaze/include/asm/cmpxchg.h b/arch/microblaze/include/asm/cmpxchg.h index 0094859abd9b..538afc0ab9f3 100644 --- a/arch/microblaze/include/asm/cmpxchg.h +++ b/arch/microblaze/include/asm/cmpxchg.h @@ -1,6 +1,8 @@ #ifndef _ASM_MICROBLAZE_CMPXCHG_H #define _ASM_MICROBLAZE_CMPXCHG_H +#include <linux/irqflags.h> + void __bad_xchg(volatile void *ptr, int size); static inline unsigned long __xchg(unsigned long x, volatile void *ptr, diff --git a/arch/microblaze/include/asm/dma-mapping.h b/arch/microblaze/include/asm/dma-mapping.h index 3a3e5b886854..01d228286cb0 100644 --- a/arch/microblaze/include/asm/dma-mapping.h +++ b/arch/microblaze/include/asm/dma-mapping.h @@ -123,28 +123,34 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) +#define dma_alloc_coherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); void *memory; BUG_ON(!ops); - memory = ops->alloc_coherent(dev, size, dma_handle, flag); + memory = ops->alloc(dev, size, dma_handle, flag, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, memory); return memory; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d, s, c, h, NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!ops); debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); - ops->free_coherent(dev, size, cpu_addr, dma_handle); + ops->free(dev, size, cpu_addr, dma_handle, attrs); } static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, diff --git a/arch/microblaze/include/asm/futex.h b/arch/microblaze/include/asm/futex.h index b0526d2716fa..ff8cde159d9a 100644 --- a/arch/microblaze/include/asm/futex.h +++ b/arch/microblaze/include/asm/futex.h @@ -24,7 +24,7 @@ .word 1b,4b,2b,4b; \ .previous;" \ : "=&r" (oldval), "=&r" (ret) \ - : "b" (uaddr), "i" (-EFAULT), "r" (oparg) \ + : "r" (uaddr), "i" (-EFAULT), "r" (oparg) \ ); \ }) diff --git a/arch/microblaze/include/asm/processor.h b/arch/microblaze/include/asm/processor.h index 510a8e1c16ba..bffb54527299 100644 --- a/arch/microblaze/include/asm/processor.h +++ b/arch/microblaze/include/asm/processor.h @@ -31,6 +31,8 @@ extern const struct seq_operations cpuinfo_op; /* Do necessary setup to start up a newly executed thread. */ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp); +extern void ret_from_fork(void); + # endif /* __ASSEMBLY__ */ # ifndef CONFIG_MMU @@ -143,8 +145,6 @@ static inline void exit_thread(void) unsigned long get_wchan(struct task_struct *p); -extern void ret_from_fork(void); - /* The size allocated for kernel stacks. This _must_ be a power of two! */ # define KERNEL_STACK_SIZE 0x2000 diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c index 65a4af4cbbbe..a2bfa2ca5730 100644 --- a/arch/microblaze/kernel/dma.c +++ b/arch/microblaze/kernel/dma.c @@ -33,7 +33,8 @@ static unsigned long get_dma_direct_offset(struct device *dev) #define NOT_COHERENT_CACHE static void *dma_direct_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { #ifdef NOT_COHERENT_CACHE return consistent_alloc(flag, size, dma_handle); @@ -57,7 +58,8 @@ static void *dma_direct_alloc_coherent(struct device *dev, size_t size, } static void dma_direct_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { #ifdef NOT_COHERENT_CACHE consistent_free(size, vaddr); @@ -176,8 +178,8 @@ dma_direct_sync_sg_for_device(struct device *dev, } struct dma_map_ops dma_direct_ops = { - .alloc_coherent = dma_direct_alloc_coherent, - .free_coherent = dma_direct_free_coherent, + .alloc = dma_direct_alloc_coherent, + .free = dma_direct_free_coherent, .map_sg = dma_direct_map_sg, .unmap_sg = dma_direct_unmap_sg, .dma_supported = dma_direct_dma_supported, diff --git a/arch/microblaze/kernel/early_printk.c b/arch/microblaze/kernel/early_printk.c index ec485876d0d0..aba1f9a97d5d 100644 --- a/arch/microblaze/kernel/early_printk.c +++ b/arch/microblaze/kernel/early_printk.c @@ -176,6 +176,7 @@ void __init remap_early_printk(void) base_addr = (u32) ioremap(base_addr, PAGE_SIZE); printk(KERN_CONT "0x%x\n", base_addr); +#ifdef CONFIG_MMU /* * Early console is on the top of skipped TLB entries * decrease tlb_skip size ensure that hardcoded TLB entry will be @@ -189,6 +190,7 @@ void __init remap_early_printk(void) * cmp rX, orig_base_addr */ tlb_skip -= 1; +#endif } void __init disable_early_printk(void) diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 71af974aa24a..16d8dfd9094b 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -206,6 +206,7 @@ static int microblaze_debugfs_init(void) } arch_initcall(microblaze_debugfs_init); +# ifdef CONFIG_MMU static int __init debugfs_tlb(void) { struct dentry *d; @@ -218,6 +219,7 @@ static int __init debugfs_tlb(void) return -ENOMEM; } device_initcall(debugfs_tlb); +# endif #endif static int dflt_bus_notify(struct notifier_block *nb, diff --git a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c index 9781a528cfc9..6be4ae3c3351 100644 --- a/arch/microblaze/kernel/unwind.c +++ b/arch/microblaze/kernel/unwind.c @@ -24,6 +24,7 @@ #include <asm/sections.h> #include <asm/exceptions.h> #include <asm/unwind.h> +#include <asm/switch_to.h> struct stack_trace; diff --git a/arch/microblaze/lib/uaccess_old.S b/arch/microblaze/lib/uaccess_old.S index f037266cdaf3..f085995ee848 100644 --- a/arch/microblaze/lib/uaccess_old.S +++ b/arch/microblaze/lib/uaccess_old.S @@ -122,22 +122,22 @@ __strnlen_user: 15: swi r24, r5, 0x0018 + offset; \ 16: swi r25, r5, 0x001C + offset; \ .section __ex_table,"a"; \ - .word 1b, 0f; \ - .word 2b, 0f; \ - .word 3b, 0f; \ - .word 4b, 0f; \ - .word 5b, 0f; \ - .word 6b, 0f; \ - .word 7b, 0f; \ - .word 8b, 0f; \ - .word 9b, 0f; \ - .word 10b, 0f; \ - .word 11b, 0f; \ - .word 12b, 0f; \ - .word 13b, 0f; \ - .word 14b, 0f; \ - .word 15b, 0f; \ - .word 16b, 0f; \ + .word 1b, 33f; \ + .word 2b, 33f; \ + .word 3b, 33f; \ + .word 4b, 33f; \ + .word 5b, 33f; \ + .word 6b, 33f; \ + .word 7b, 33f; \ + .word 8b, 33f; \ + .word 9b, 33f; \ + .word 10b, 33f; \ + .word 11b, 33f; \ + .word 12b, 33f; \ + .word 13b, 33f; \ + .word 14b, 33f; \ + .word 15b, 33f; \ + .word 16b, 33f; \ .text #define COPY_80(offset) \ @@ -190,14 +190,17 @@ w2: sw r4, r5, r3 .align 4 /* Alignment is important to keep icache happy */ page: /* Create room on stack and save registers for storign values */ - addik r1, r1, -32 - swi r19, r1, 4 - swi r20, r1, 8 - swi r21, r1, 12 - swi r22, r1, 16 - swi r23, r1, 20 - swi r24, r1, 24 - swi r25, r1, 28 + addik r1, r1, -40 + swi r5, r1, 0 + swi r6, r1, 4 + swi r7, r1, 8 + swi r19, r1, 12 + swi r20, r1, 16 + swi r21, r1, 20 + swi r22, r1, 24 + swi r23, r1, 28 + swi r24, r1, 32 + swi r25, r1, 36 loop: /* r4, r19, r20, r21, r22, r23, r24, r25 are used for storing values */ /* Loop unrolling to get performance boost */ COPY_80(0x000); @@ -205,21 +208,44 @@ loop: /* r4, r19, r20, r21, r22, r23, r24, r25 are used for storing values */ COPY_80(0x100); COPY_80(0x180); /* copy loop */ - addik r6, r6, 0x200 - addik r7, r7, -0x200 - bneid r7, loop - addik r5, r5, 0x200 + addik r6, r6, 0x200 + addik r7, r7, -0x200 + bneid r7, loop + addik r5, r5, 0x200 + /* Restore register content */ - lwi r19, r1, 4 - lwi r20, r1, 8 - lwi r21, r1, 12 - lwi r22, r1, 16 - lwi r23, r1, 20 - lwi r24, r1, 24 - lwi r25, r1, 28 - addik r1, r1, 32 + lwi r5, r1, 0 + lwi r6, r1, 4 + lwi r7, r1, 8 + lwi r19, r1, 12 + lwi r20, r1, 16 + lwi r21, r1, 20 + lwi r22, r1, 24 + lwi r23, r1, 28 + lwi r24, r1, 32 + lwi r25, r1, 36 + addik r1, r1, 40 /* return back */ + addik r3, r0, 0 + rtsd r15, 8 + nop + +/* Fault case - return temp count */ +33: addik r3, r7, 0 + /* Restore register content */ + lwi r5, r1, 0 + lwi r6, r1, 4 + lwi r7, r1, 8 + lwi r19, r1, 12 + lwi r20, r1, 16 + lwi r21, r1, 20 + lwi r22, r1, 24 + lwi r23, r1, 28 + lwi r24, r1, 32 + lwi r25, r1, 36 + addik r1, r1, 40 + /* return back */ rtsd r15, 8 nop diff --git a/arch/mips/ath79/dev-wmac.c b/arch/mips/ath79/dev-wmac.c index e21507052066..9c717bf98ffe 100644 --- a/arch/mips/ath79/dev-wmac.c +++ b/arch/mips/ath79/dev-wmac.c @@ -58,8 +58,8 @@ static void __init ar913x_wmac_setup(void) static int ar933x_wmac_reset(void) { - ath79_device_reset_clear(AR933X_RESET_WMAC); ath79_device_reset_set(AR933X_RESET_WMAC); + ath79_device_reset_clear(AR933X_RESET_WMAC); return 0; } diff --git a/arch/mips/cavium-octeon/dma-octeon.c b/arch/mips/cavium-octeon/dma-octeon.c index b6bb92c16a47..41dd00884975 100644 --- a/arch/mips/cavium-octeon/dma-octeon.c +++ b/arch/mips/cavium-octeon/dma-octeon.c @@ -157,7 +157,7 @@ static void octeon_dma_sync_sg_for_device(struct device *dev, } static void *octeon_dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) + dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs) { void *ret; @@ -192,7 +192,7 @@ static void *octeon_dma_alloc_coherent(struct device *dev, size_t size, } static void octeon_dma_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs) { int order = get_order(size); @@ -240,8 +240,8 @@ EXPORT_SYMBOL(dma_to_phys); static struct octeon_dma_map_ops octeon_linear_dma_map_ops = { .dma_map_ops = { - .alloc_coherent = octeon_dma_alloc_coherent, - .free_coherent = octeon_dma_free_coherent, + .alloc = octeon_dma_alloc_coherent, + .free = octeon_dma_free_coherent, .map_page = octeon_dma_map_page, .unmap_page = swiotlb_unmap_page, .map_sg = octeon_dma_map_sg, @@ -325,8 +325,8 @@ void __init plat_swiotlb_setup(void) #ifdef CONFIG_PCI static struct octeon_dma_map_ops _octeon_pci_dma_map_ops = { .dma_map_ops = { - .alloc_coherent = octeon_dma_alloc_coherent, - .free_coherent = octeon_dma_free_coherent, + .alloc = octeon_dma_alloc_coherent, + .free = octeon_dma_free_coherent, .map_page = octeon_dma_map_page, .unmap_page = swiotlb_unmap_page, .map_sg = octeon_dma_map_sg, diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c index c3e2b85c3b02..97e7ce9b50ed 100644 --- a/arch/mips/cavium-octeon/smp.c +++ b/arch/mips/cavium-octeon/smp.c @@ -78,7 +78,7 @@ static inline void octeon_send_ipi_mask(const struct cpumask *mask, } /** - * Detect available CPUs, populate cpu_possible_map + * Detect available CPUs, populate cpu_possible_mask */ static void octeon_smp_hotplug_setup(void) { @@ -268,7 +268,7 @@ static int octeon_cpu_disable(void) spin_lock(&smp_reserve_lock); - cpu_clear(cpu, cpu_online_map); + set_cpu_online(cpu, false); cpu_clear(cpu, cpu_callin_map); local_irq_disable(); fixup_irqs(); diff --git a/arch/mips/include/asm/dma-mapping.h b/arch/mips/include/asm/dma-mapping.h index 7aa37ddfca4b..be39a12901c6 100644 --- a/arch/mips/include/asm/dma-mapping.h +++ b/arch/mips/include/asm/dma-mapping.h @@ -57,25 +57,31 @@ dma_set_mask(struct device *dev, u64 mask) extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size, enum dma_data_direction direction); -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs) { void *ret; struct dma_map_ops *ops = get_dma_ops(dev); - ret = ops->alloc_coherent(dev, size, dma_handle, gfp); + ret = ops->alloc(dev, size, dma_handle, gfp, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, ret); return ret; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); - ops->free_coherent(dev, size, vaddr, dma_handle); + ops->free(dev, size, vaddr, dma_handle, attrs); debug_dma_free_coherent(dev, size, vaddr, dma_handle); } diff --git a/arch/mips/include/asm/mach-jz4740/irq.h b/arch/mips/include/asm/mach-jz4740/irq.h index a865c983c70a..5ad1a9c113c6 100644 --- a/arch/mips/include/asm/mach-jz4740/irq.h +++ b/arch/mips/include/asm/mach-jz4740/irq.h @@ -45,7 +45,7 @@ #define JZ4740_IRQ_LCD JZ4740_IRQ(30) /* 2nd-level interrupts */ -#define JZ4740_IRQ_DMA(x) (JZ4740_IRQ(32) + (X)) +#define JZ4740_IRQ_DMA(x) (JZ4740_IRQ(32) + (x)) #define JZ4740_IRQ_INTC_GPIO(x) (JZ4740_IRQ_GPIO0 - (x)) #define JZ4740_IRQ_GPIO(x) (JZ4740_IRQ(48) + (x)) diff --git a/arch/mips/include/asm/mmu_context.h b/arch/mips/include/asm/mmu_context.h index 73c0d45798de..9b02cfba7449 100644 --- a/arch/mips/include/asm/mmu_context.h +++ b/arch/mips/include/asm/mmu_context.h @@ -37,12 +37,6 @@ extern void tlbmiss_handler_setup_pgd(unsigned long pgd); write_c0_xcontext((unsigned long) smp_processor_id() << 51); \ } while (0) - -static inline unsigned long get_current_pgd(void) -{ - return PHYS_TO_XKSEG_CACHED((read_c0_context() >> 11) & ~0xfffUL); -} - #else /* CONFIG_MIPS_PGD_C0_CONTEXT: using pgd_current*/ /* diff --git a/arch/mips/kernel/mips-mt-fpaff.c b/arch/mips/kernel/mips-mt-fpaff.c index 802e6160f37e..33f63bab478a 100644 --- a/arch/mips/kernel/mips-mt-fpaff.c +++ b/arch/mips/kernel/mips-mt-fpaff.c @@ -173,7 +173,7 @@ asmlinkage long mipsmt_sys_sched_getaffinity(pid_t pid, unsigned int len, if (retval) goto out_unlock; - cpus_and(mask, p->thread.user_cpus_allowed, cpu_possible_map); + cpumask_and(&mask, &p->thread.user_cpus_allowed, cpu_possible_mask); out_unlock: read_unlock(&tasklist_lock); diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index e309665b6c81..f8b2c592514d 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -25,7 +25,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) int i; #ifdef CONFIG_SMP - if (!cpu_isset(n, cpu_online_map)) + if (!cpu_online(n)) return 0; #endif diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 185ca00c4c84..d5a338a1739c 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -257,11 +257,8 @@ asmlinkage int sys_sigsuspend(nabi_no_regargs struct pt_regs regs) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); current->saved_sigmask = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&newset); current->state = TASK_INTERRUPTIBLE; schedule(); @@ -286,11 +283,8 @@ asmlinkage int sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); current->saved_sigmask = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&newset); current->state = TASK_INTERRUPTIBLE; schedule(); @@ -362,10 +356,7 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) goto badframe; sigdelsetmask(&blocked, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = blocked; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&blocked); sig = restore_sigcontext(®s, &frame->sf_sc); if (sig < 0) @@ -401,10 +392,7 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); sig = restore_sigcontext(®s, &frame->rs_uc.uc_mcontext); if (sig < 0) @@ -580,12 +568,7 @@ static int handle_signal(unsigned long sig, siginfo_t *info, if (ret) return ret; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked, sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + block_sigmask(ka, sig); return ret; } diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c index 06b5da392e24..ac3b8d89aae5 100644 --- a/arch/mips/kernel/signal32.c +++ b/arch/mips/kernel/signal32.c @@ -290,11 +290,8 @@ asmlinkage int sys32_sigsuspend(nabi_no_regargs struct pt_regs regs) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); current->saved_sigmask = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&newset); current->state = TASK_INTERRUPTIBLE; schedule(); @@ -318,11 +315,8 @@ asmlinkage int sys32_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); current->saved_sigmask = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&newset); current->state = TASK_INTERRUPTIBLE; schedule(); @@ -488,10 +482,7 @@ asmlinkage void sys32_sigreturn(nabi_no_regargs struct pt_regs regs) goto badframe; sigdelsetmask(&blocked, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = blocked; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&blocked); sig = restore_sigcontext32(®s, &frame->sf_sc); if (sig < 0) @@ -529,10 +520,7 @@ asmlinkage void sys32_rt_sigreturn(nabi_no_regargs struct pt_regs regs) goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); sig = restore_sigcontext32(®s, &frame->rs_uc.uc_mcontext); if (sig < 0) diff --git a/arch/mips/kernel/signal_n32.c b/arch/mips/kernel/signal_n32.c index ae29e894ab8d..86eb4b04631c 100644 --- a/arch/mips/kernel/signal_n32.c +++ b/arch/mips/kernel/signal_n32.c @@ -93,11 +93,8 @@ asmlinkage int sysn32_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) sigset_from_compat(&newset, &uset); sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); current->saved_sigmask = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&newset); current->state = TASK_INTERRUPTIBLE; schedule(); @@ -121,10 +118,7 @@ asmlinkage void sysn32_rt_sigreturn(nabi_no_regargs struct pt_regs regs) goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); sig = restore_sigcontext(®s, &frame->rs_uc.uc_mcontext); if (sig < 0) diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c index ca673569fd24..3046e2986006 100644 --- a/arch/mips/kernel/smp-bmips.c +++ b/arch/mips/kernel/smp-bmips.c @@ -317,7 +317,7 @@ static int bmips_cpu_disable(void) pr_info("SMP: CPU%d is offline\n", cpu); - cpu_clear(cpu, cpu_online_map); + set_cpu_online(cpu, false); cpu_clear(cpu, cpu_callin_map); local_flush_tlb_all(); diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 9c1cce9de35f..ba9376bf52a1 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -148,7 +148,7 @@ static void stop_this_cpu(void *dummy) /* * Remove this CPU: */ - cpu_clear(smp_processor_id(), cpu_online_map); + set_cpu_online(smp_processor_id(), false); for (;;) { if (cpu_wait) (*cpu_wait)(); /* Wait if available. */ @@ -174,7 +174,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) mp_ops->prepare_cpus(max_cpus); set_cpu_sibling_map(0); #ifndef CONFIG_HOTPLUG_CPU - init_cpu_present(&cpu_possible_map); + init_cpu_present(cpu_possible_mask); #endif } @@ -248,7 +248,7 @@ int __cpuinit __cpu_up(unsigned int cpu) while (!cpu_isset(cpu, cpu_callin_map)) udelay(100); - cpu_set(cpu, cpu_online_map); + set_cpu_online(cpu, true); return 0; } @@ -320,13 +320,12 @@ void flush_tlb_mm(struct mm_struct *mm) if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { smp_on_other_tlbs(flush_tlb_mm_ipi, mm); } else { - cpumask_t mask = cpu_online_map; unsigned int cpu; - cpu_clear(smp_processor_id(), mask); - for_each_cpu_mask(cpu, mask) - if (cpu_context(cpu, mm)) + for_each_online_cpu(cpu) { + if (cpu != smp_processor_id() && cpu_context(cpu, mm)) cpu_context(cpu, mm) = 0; + } } local_flush_tlb_mm(mm); @@ -360,13 +359,12 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned l smp_on_other_tlbs(flush_tlb_range_ipi, &fd); } else { - cpumask_t mask = cpu_online_map; unsigned int cpu; - cpu_clear(smp_processor_id(), mask); - for_each_cpu_mask(cpu, mask) - if (cpu_context(cpu, mm)) + for_each_online_cpu(cpu) { + if (cpu != smp_processor_id() && cpu_context(cpu, mm)) cpu_context(cpu, mm) = 0; + } } local_flush_tlb_range(vma, start, end); preempt_enable(); @@ -407,13 +405,12 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) smp_on_other_tlbs(flush_tlb_page_ipi, &fd); } else { - cpumask_t mask = cpu_online_map; unsigned int cpu; - cpu_clear(smp_processor_id(), mask); - for_each_cpu_mask(cpu, mask) - if (cpu_context(cpu, vma->vm_mm)) + for_each_online_cpu(cpu) { + if (cpu != smp_processor_id() && cpu_context(cpu, vma->vm_mm)) cpu_context(cpu, vma->vm_mm) = 0; + } } local_flush_tlb_page(vma, page); preempt_enable(); diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index c4f75bbc0bd6..f5dd38f1d015 100644 --- a/arch/mips/kernel/smtc.c +++ b/arch/mips/kernel/smtc.c @@ -291,7 +291,7 @@ static void smtc_configure_tlb(void) * possibly leave some TCs/VPEs as "slave" processors. * * Use c0_MVPConf0 to find out how many TCs are available, setting up - * cpu_possible_map and the logical/physical mappings. + * cpu_possible_mask and the logical/physical mappings. */ int __init smtc_build_cpu_map(int start_cpu_slot) diff --git a/arch/mips/mm/c-octeon.c b/arch/mips/mm/c-octeon.c index 1f9ca07f53c8..47037ec5589b 100644 --- a/arch/mips/mm/c-octeon.c +++ b/arch/mips/mm/c-octeon.c @@ -80,9 +80,9 @@ static void octeon_flush_icache_all_cores(struct vm_area_struct *vma) if (vma) mask = *mm_cpumask(vma->vm_mm); else - mask = cpu_online_map; - cpu_clear(cpu, mask); - for_each_cpu_mask(cpu, mask) + mask = *cpu_online_mask; + cpumask_clear_cpu(cpu, &mask); + for_each_cpu(cpu, &mask) octeon_send_ipi_single(cpu, SMP_ICACHE_FLUSH); preempt_enable(); diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c index 46084912e588..3fab2046c8a4 100644 --- a/arch/mips/mm/dma-default.c +++ b/arch/mips/mm/dma-default.c @@ -98,7 +98,7 @@ void *dma_alloc_noncoherent(struct device *dev, size_t size, EXPORT_SYMBOL(dma_alloc_noncoherent); static void *mips_dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t * dma_handle, gfp_t gfp) + dma_addr_t * dma_handle, gfp_t gfp, struct dma_attrs *attrs) { void *ret; @@ -132,7 +132,7 @@ void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, EXPORT_SYMBOL(dma_free_noncoherent); static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle) + dma_addr_t dma_handle, struct dma_attrs *attrs) { unsigned long addr = (unsigned long) vaddr; int order = get_order(size); @@ -323,8 +323,8 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size, EXPORT_SYMBOL(dma_cache_sync); static struct dma_map_ops mips_default_dma_map_ops = { - .alloc_coherent = mips_dma_alloc_coherent, - .free_coherent = mips_dma_free_coherent, + .alloc = mips_dma_alloc_coherent, + .free = mips_dma_free_coherent, .map_page = mips_dma_map_page, .unmap_page = mips_dma_unmap_page, .map_sg = mips_dma_map_sg, diff --git a/arch/mips/netlogic/common/smp.c b/arch/mips/netlogic/common/smp.c index db17f49886c2..fab316de57e9 100644 --- a/arch/mips/netlogic/common/smp.c +++ b/arch/mips/netlogic/common/smp.c @@ -165,7 +165,7 @@ void __init nlm_smp_setup(void) cpu_set(boot_cpu, phys_cpu_present_map); __cpu_number_map[boot_cpu] = 0; __cpu_logical_map[0] = boot_cpu; - cpu_set(0, cpu_possible_map); + set_cpu_possible(0, true); num_cpus = 1; for (i = 0; i < NR_CPUS; i++) { @@ -177,14 +177,14 @@ void __init nlm_smp_setup(void) cpu_set(i, phys_cpu_present_map); __cpu_number_map[i] = num_cpus; __cpu_logical_map[num_cpus] = i; - cpu_set(num_cpus, cpu_possible_map); + set_cpu_possible(num_cpus, true); ++num_cpus; } } pr_info("Phys CPU present map: %lx, possible map %lx\n", (unsigned long)phys_cpu_present_map.bits[0], - (unsigned long)cpu_possible_map.bits[0]); + (unsigned long)cpumask_bits(cpu_possible_mask)[0]); pr_info("Detected %i Slave CPU(s)\n", num_cpus); nlm_set_nmi_handler(nlm_boot_secondary_cpus); diff --git a/arch/mips/pmc-sierra/yosemite/smp.c b/arch/mips/pmc-sierra/yosemite/smp.c index 2608752898c0..b71fae231049 100644 --- a/arch/mips/pmc-sierra/yosemite/smp.c +++ b/arch/mips/pmc-sierra/yosemite/smp.c @@ -146,7 +146,7 @@ static void __cpuinit yos_boot_secondary(int cpu, struct task_struct *idle) } /* - * Detect available CPUs, populate cpu_possible_map before smp_init + * Detect available CPUs, populate cpu_possible_mask before smp_init * * We don't want to start the secondary CPU yet nor do we have a nice probing * feature in PMON so we just assume presence of the secondary core. @@ -155,10 +155,10 @@ static void __init yos_smp_setup(void) { int i; - cpus_clear(cpu_possible_map); + init_cpu_possible(cpu_none_mask); for (i = 0; i < 2; i++) { - cpu_set(i, cpu_possible_map); + set_cpu_possible(i, true); __cpu_number_map[i] = i; __cpu_logical_map[i] = i; } @@ -169,7 +169,7 @@ static void __init yos_prepare_cpus(unsigned int max_cpus) /* * Be paranoid. Enable the IPI only if we're really about to go SMP. */ - if (cpus_weight(cpu_possible_map)) + if (num_possible_cpus()) set_c0_status(STATUSF_IP5); } diff --git a/arch/mips/sgi-ip27/ip27-smp.c b/arch/mips/sgi-ip27/ip27-smp.c index c6851df9ab74..735b43bf8f82 100644 --- a/arch/mips/sgi-ip27/ip27-smp.c +++ b/arch/mips/sgi-ip27/ip27-smp.c @@ -76,7 +76,7 @@ static int do_cpumask(cnodeid_t cnode, nasid_t nasid, int highest) /* Only let it join in if it's marked enabled */ if ((acpu->cpu_info.flags & KLINFO_ENABLE) && (tot_cpus_found != NR_CPUS)) { - cpu_set(cpuid, cpu_possible_map); + set_cpu_possible(cpuid, true); alloc_cpupda(cpuid, tot_cpus_found); cpus_found++; tot_cpus_found++; diff --git a/arch/mips/sibyte/bcm1480/smp.c b/arch/mips/sibyte/bcm1480/smp.c index d667875be564..de88e22694a0 100644 --- a/arch/mips/sibyte/bcm1480/smp.c +++ b/arch/mips/sibyte/bcm1480/smp.c @@ -138,7 +138,7 @@ static void __cpuinit bcm1480_boot_secondary(int cpu, struct task_struct *idle) /* * Use CFE to find out how many CPUs are available, setting up - * cpu_possible_map and the logical/physical mappings. + * cpu_possible_mask and the logical/physical mappings. * XXXKW will the boot CPU ever not be physical 0? * * Common setup before any secondaries are started @@ -147,14 +147,13 @@ static void __init bcm1480_smp_setup(void) { int i, num; - cpus_clear(cpu_possible_map); - cpu_set(0, cpu_possible_map); + init_cpu_possible(cpumask_of(0)); __cpu_number_map[0] = 0; __cpu_logical_map[0] = 0; for (i = 1, num = 0; i < NR_CPUS; i++) { if (cfe_cpu_stop(i) == 0) { - cpu_set(i, cpu_possible_map); + set_cpu_possible(i, true); __cpu_number_map[i] = ++num; __cpu_logical_map[num] = i; } diff --git a/arch/mips/sibyte/sb1250/smp.c b/arch/mips/sibyte/sb1250/smp.c index 38e7f6bd7922..285cfef4ebc0 100644 --- a/arch/mips/sibyte/sb1250/smp.c +++ b/arch/mips/sibyte/sb1250/smp.c @@ -126,7 +126,7 @@ static void __cpuinit sb1250_boot_secondary(int cpu, struct task_struct *idle) /* * Use CFE to find out how many CPUs are available, setting up - * cpu_possible_map and the logical/physical mappings. + * cpu_possible_mask and the logical/physical mappings. * XXXKW will the boot CPU ever not be physical 0? * * Common setup before any secondaries are started @@ -135,14 +135,13 @@ static void __init sb1250_smp_setup(void) { int i, num; - cpus_clear(cpu_possible_map); - cpu_set(0, cpu_possible_map); + init_cpu_possible(cpumask_of(0)); __cpu_number_map[0] = 0; __cpu_logical_map[0] = 0; for (i = 1, num = 0; i < NR_CPUS; i++) { if (cfe_cpu_stop(i) == 0) { - cpu_set(i, cpu_possible_map); + set_cpu_possible(i, true); __cpu_number_map[i] = ++num; __cpu_logical_map[num] = i; } diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h index 3ae56073cc3d..6c6defc24619 100644 --- a/arch/parisc/include/asm/atomic.h +++ b/arch/parisc/include/asm/atomic.h @@ -6,6 +6,7 @@ #define _ASM_PARISC_ATOMIC_H_ #include <linux/types.h> +#include <asm/cmpxchg.h> /* * Atomic operations that C can't guarantee us. Useful for @@ -48,112 +49,6 @@ extern arch_spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned; # define _atomic_spin_unlock_irqrestore(l,f) do { local_irq_restore(f); } while (0) #endif -/* This should get optimized out since it's never called. -** Or get a link error if xchg is used "wrong". -*/ -extern void __xchg_called_with_bad_pointer(void); - - -/* __xchg32/64 defined in arch/parisc/lib/bitops.c */ -extern unsigned long __xchg8(char, char *); -extern unsigned long __xchg32(int, int *); -#ifdef CONFIG_64BIT -extern unsigned long __xchg64(unsigned long, unsigned long *); -#endif - -/* optimizer better get rid of switch since size is a constant */ -static __inline__ unsigned long -__xchg(unsigned long x, __volatile__ void * ptr, int size) -{ - switch(size) { -#ifdef CONFIG_64BIT - case 8: return __xchg64(x,(unsigned long *) ptr); -#endif - case 4: return __xchg32((int) x, (int *) ptr); - case 1: return __xchg8((char) x, (char *) ptr); - } - __xchg_called_with_bad_pointer(); - return x; -} - - -/* -** REVISIT - Abandoned use of LDCW in xchg() for now: -** o need to test sizeof(*ptr) to avoid clearing adjacent bytes -** o and while we are at it, could CONFIG_64BIT code use LDCD too? -** -** if (__builtin_constant_p(x) && (x == NULL)) -** if (((unsigned long)p & 0xf) == 0) -** return __ldcw(p); -*/ -#define xchg(ptr,x) \ - ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) - - -#define __HAVE_ARCH_CMPXCHG 1 - -/* bug catcher for when unsupported size is used - won't link */ -extern void __cmpxchg_called_with_bad_pointer(void); - -/* __cmpxchg_u32/u64 defined in arch/parisc/lib/bitops.c */ -extern unsigned long __cmpxchg_u32(volatile unsigned int *m, unsigned int old, unsigned int new_); -extern unsigned long __cmpxchg_u64(volatile unsigned long *ptr, unsigned long old, unsigned long new_); - -/* don't worry...optimizer will get rid of most of this */ -static __inline__ unsigned long -__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size) -{ - switch(size) { -#ifdef CONFIG_64BIT - case 8: return __cmpxchg_u64((unsigned long *)ptr, old, new_); -#endif - case 4: return __cmpxchg_u32((unsigned int *)ptr, (unsigned int) old, (unsigned int) new_); - } - __cmpxchg_called_with_bad_pointer(); - return old; -} - -#define cmpxchg(ptr,o,n) \ - ({ \ - __typeof__(*(ptr)) _o_ = (o); \ - __typeof__(*(ptr)) _n_ = (n); \ - (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ - (unsigned long)_n_, sizeof(*(ptr))); \ - }) - -#include <asm-generic/cmpxchg-local.h> - -static inline unsigned long __cmpxchg_local(volatile void *ptr, - unsigned long old, - unsigned long new_, int size) -{ - switch (size) { -#ifdef CONFIG_64BIT - case 8: return __cmpxchg_u64((unsigned long *)ptr, old, new_); -#endif - case 4: return __cmpxchg_u32(ptr, old, new_); - default: - return __cmpxchg_local_generic(ptr, old, new_, size); - } -} - -/* - * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make - * them available. - */ -#define cmpxchg_local(ptr, o, n) \ - ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ - (unsigned long)(n), sizeof(*(ptr)))) -#ifdef CONFIG_64BIT -#define cmpxchg64_local(ptr, o, n) \ - ({ \ - BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ - cmpxchg_local((ptr), (o), (n)); \ - }) -#else -#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) -#endif - /* * Note that we need not lock read accesses - aligned word writes/reads * are atomic, so a reader never sees inconsistent values. diff --git a/arch/parisc/include/asm/cmpxchg.h b/arch/parisc/include/asm/cmpxchg.h new file mode 100644 index 000000000000..dbd13354ec41 --- /dev/null +++ b/arch/parisc/include/asm/cmpxchg.h @@ -0,0 +1,116 @@ +/* + * forked from parisc asm/atomic.h which was: + * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2006 Kyle McMartin <kyle@parisc-linux.org> + */ + +#ifndef _ASM_PARISC_CMPXCHG_H_ +#define _ASM_PARISC_CMPXCHG_H_ + +/* This should get optimized out since it's never called. +** Or get a link error if xchg is used "wrong". +*/ +extern void __xchg_called_with_bad_pointer(void); + +/* __xchg32/64 defined in arch/parisc/lib/bitops.c */ +extern unsigned long __xchg8(char, char *); +extern unsigned long __xchg32(int, int *); +#ifdef CONFIG_64BIT +extern unsigned long __xchg64(unsigned long, unsigned long *); +#endif + +/* optimizer better get rid of switch since size is a constant */ +static inline unsigned long +__xchg(unsigned long x, __volatile__ void *ptr, int size) +{ + switch (size) { +#ifdef CONFIG_64BIT + case 8: return __xchg64(x, (unsigned long *) ptr); +#endif + case 4: return __xchg32((int) x, (int *) ptr); + case 1: return __xchg8((char) x, (char *) ptr); + } + __xchg_called_with_bad_pointer(); + return x; +} + +/* +** REVISIT - Abandoned use of LDCW in xchg() for now: +** o need to test sizeof(*ptr) to avoid clearing adjacent bytes +** o and while we are at it, could CONFIG_64BIT code use LDCD too? +** +** if (__builtin_constant_p(x) && (x == NULL)) +** if (((unsigned long)p & 0xf) == 0) +** return __ldcw(p); +*/ +#define xchg(ptr, x) \ + ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))) + +#define __HAVE_ARCH_CMPXCHG 1 + +/* bug catcher for when unsupported size is used - won't link */ +extern void __cmpxchg_called_with_bad_pointer(void); + +/* __cmpxchg_u32/u64 defined in arch/parisc/lib/bitops.c */ +extern unsigned long __cmpxchg_u32(volatile unsigned int *m, unsigned int old, + unsigned int new_); +extern unsigned long __cmpxchg_u64(volatile unsigned long *ptr, + unsigned long old, unsigned long new_); + +/* don't worry...optimizer will get rid of most of this */ +static inline unsigned long +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size) +{ + switch (size) { +#ifdef CONFIG_64BIT + case 8: return __cmpxchg_u64((unsigned long *)ptr, old, new_); +#endif + case 4: return __cmpxchg_u32((unsigned int *)ptr, + (unsigned int)old, (unsigned int)new_); + } + __cmpxchg_called_with_bad_pointer(); + return old; +} + +#define cmpxchg(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ + (unsigned long)_n_, sizeof(*(ptr))); \ +}) + +#include <asm-generic/cmpxchg-local.h> + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new_, int size) +{ + switch (size) { +#ifdef CONFIG_64BIT + case 8: return __cmpxchg_u64((unsigned long *)ptr, old, new_); +#endif + case 4: return __cmpxchg_u32(ptr, old, new_); + default: + return __cmpxchg_local_generic(ptr, old, new_, size); + } +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#ifdef CONFIG_64BIT +#define cmpxchg64_local(ptr, o, n) \ +({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ +}) +#else +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) +#endif + +#endif /* _ASM_PARISC_CMPXCHG_H_ */ diff --git a/arch/parisc/include/asm/hardware.h b/arch/parisc/include/asm/hardware.h index 4e9626836bab..d1d864b81bae 100644 --- a/arch/parisc/include/asm/hardware.h +++ b/arch/parisc/include/asm/hardware.h @@ -2,7 +2,6 @@ #define _PARISC_HARDWARE_H #include <linux/mod_devicetable.h> -#include <asm/pdc.h> #define HWTYPE_ANY_ID PA_HWTYPE_ANY_ID #define HVERSION_ANY_ID PA_HVERSION_ANY_ID @@ -95,12 +94,14 @@ struct bc_module { #define HPHW_MC 15 #define HPHW_FAULTY 31 +struct parisc_device_id; /* hardware.c: */ extern const char *parisc_hardware_description(struct parisc_device_id *id); extern enum cpu_type parisc_get_cpu_type(unsigned long hversion); struct pci_dev; +struct hardware_path; /* drivers.c: */ extern struct parisc_device *alloc_pa_dev(unsigned long hpa, diff --git a/arch/parisc/include/asm/page.h b/arch/parisc/include/asm/page.h index a84cc1f925f6..4e0e7dbf0f3f 100644 --- a/arch/parisc/include/asm/page.h +++ b/arch/parisc/include/asm/page.h @@ -160,5 +160,11 @@ extern int npmem_ranges; #include <asm-generic/memory_model.h> #include <asm-generic/getorder.h> +#include <asm/pdc.h> + +#define PAGE0 ((struct zeropage *)__PAGE_OFFSET) + +/* DEFINITION OF THE ZERO-PAGE (PAG0) */ +/* based on work by Jason Eckhardt (jason@equator.com) */ #endif /* _PARISC_PAGE_H */ diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h index 4ca510b3c6f8..7f0f2d23059d 100644 --- a/arch/parisc/include/asm/pdc.h +++ b/arch/parisc/include/asm/pdc.h @@ -343,8 +343,6 @@ #ifdef __KERNEL__ -#include <asm/page.h> /* for __PAGE_OFFSET */ - extern int pdc_type; /* Values for pdc_type */ @@ -677,11 +675,6 @@ static inline char * os_id_to_string(u16 os_id) { #endif /* __KERNEL__ */ -#define PAGE0 ((struct zeropage *)__PAGE_OFFSET) - -/* DEFINITION OF THE ZERO-PAGE (PAG0) */ -/* based on work by Jason Eckhardt (jason@equator.com) */ - /* flags of the device_path */ #define PF_AUTOBOOT 0x80 #define PF_AUTOSEARCH 0x40 diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h index 22dadeb58695..ee99f2339356 100644 --- a/arch/parisc/include/asm/pgtable.h +++ b/arch/parisc/include/asm/pgtable.h @@ -44,6 +44,8 @@ struct vm_area_struct; #endif /* !__ASSEMBLY__ */ +#include <asm/page.h> + #define pte_ERROR(e) \ printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) #define pmd_ERROR(e) \ diff --git a/arch/parisc/include/asm/spinlock.h b/arch/parisc/include/asm/spinlock.h index 804aa28ab1d6..3516e0b27044 100644 --- a/arch/parisc/include/asm/spinlock.h +++ b/arch/parisc/include/asm/spinlock.h @@ -1,6 +1,8 @@ #ifndef __ASM_SPINLOCK_H #define __ASM_SPINLOCK_H +#include <asm/barrier.h> +#include <asm/ldcw.h> #include <asm/processor.h> #include <asm/spinlock_types.h> diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c index 4f004596a6e7..47341aa208f2 100644 --- a/arch/parisc/kernel/pdc_cons.c +++ b/arch/parisc/kernel/pdc_cons.c @@ -50,6 +50,7 @@ #include <linux/init.h> #include <linux/major.h> #include <linux/tty.h> +#include <asm/page.h> /* for PAGE0 */ #include <asm/pdc.h> /* for iodc_call() proto and friends */ static DEFINE_SPINLOCK(pdc_console_lock); @@ -104,7 +105,7 @@ static int pdc_console_tty_open(struct tty_struct *tty, struct file *filp) static void pdc_console_tty_close(struct tty_struct *tty, struct file *filp) { - if (!tty->count) { + if (tty->count == 1) { del_timer_sync(&pdc_console_timer); tty_port_tty_set(&tty_port, NULL); } diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c index 7c0774397b89..70e105d62423 100644 --- a/arch/parisc/kernel/time.c +++ b/arch/parisc/kernel/time.c @@ -29,6 +29,7 @@ #include <asm/uaccess.h> #include <asm/io.h> #include <asm/irq.h> +#include <asm/page.h> #include <asm/param.h> #include <asm/pdc.h> #include <asm/led.h> diff --git a/arch/powerpc/boot/dts/fsl/pq3-mpic-message-B.dtsi b/arch/powerpc/boot/dts/fsl/pq3-mpic-message-B.dtsi new file mode 100644 index 000000000000..1cf0b77b1efe --- /dev/null +++ b/arch/powerpc/boot/dts/fsl/pq3-mpic-message-B.dtsi @@ -0,0 +1,43 @@ +/* + * PQ3 MPIC Message (Group B) device tree stub [ controller @ offset 0x42400 ] + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +message@42400 { + compatible = "fsl,mpic-v3.1-msgr"; + reg = <0x42400 0x200>; + interrupts = < + 0xb4 2 0 0 + 0xb5 2 0 0 + 0xb6 2 0 0 + 0xb7 2 0 0>; +}; diff --git a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi index fdedf7b1fe0f..71c30eb10056 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi @@ -53,6 +53,16 @@ timer@41100 { 3 0 3 0>; }; +message@41400 { + compatible = "fsl,mpic-v3.1-msgr"; + reg = <0x41400 0x200>; + interrupts = < + 0xb0 2 0 0 + 0xb1 2 0 0 + 0xb2 2 0 0 + 0xb3 2 0 0>; +}; + msi@41600 { compatible = "fsl,mpic-msi"; reg = <0x41600 0x80>; diff --git a/arch/powerpc/boot/dts/p1020mbg-pc.dtsi b/arch/powerpc/boot/dts/p1020mbg-pc.dtsi new file mode 100644 index 000000000000..a24699cfea9c --- /dev/null +++ b/arch/powerpc/boot/dts/p1020mbg-pc.dtsi @@ -0,0 +1,151 @@ +/* + * P1020 MBG-PC Device Tree Source stub (no addresses or top-level ranges) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +&lbc { + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x4000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + /* 128KB for DTB Image */ + reg = <0x0 0x00020000>; + label = "NOR DTB Image"; + }; + + partition@20000 { + /* 3.875 MB for Linux Kernel Image */ + reg = <0x00020000 0x003e0000>; + label = "NOR Linux Kernel Image"; + }; + + partition@400000 { + /* 58MB for Root file System */ + reg = <0x00400000 0x03a00000>; + label = "NOR Root File System"; + }; + + partition@3e00000 { + /* This location must not be altered */ + /* 1M for Vitesse 7385 Switch firmware */ + reg = <0x3e00000 0x00100000>; + label = "NOR Vitesse-7385 Firmware"; + read-only; + }; + + partition@3f00000 { + /* This location must not be altered */ + /* 512KB for u-boot Bootloader Image */ + /* 512KB for u-boot Environment Variables */ + reg = <0x03f00000 0x00100000>; + label = "NOR U-Boot Image"; + read-only; + }; + }; + + L2switch@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "vitesse-7385"; + reg = <0x2 0x0 0x20000>; + }; +}; + +&soc { + i2c@3000 { + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mdio@24000 { + phy0: ethernet-phy@0 { + interrupts = <3 1 0 0>; + reg = <0x0>; + }; + phy1: ethernet-phy@1 { + interrupts = <2 1 0 0>; + reg = <0x1>; + }; + }; + + mdio@25000 { + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@26000 { + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet0: ethernet@b0000 { + fixed-link = <1 1 1000 0 0>; + phy-connection-type = "rgmii-id"; + }; + + enet1: ethernet@b1000 { + phy-handle = <&phy0>; + tbi-handle = <&tbi1>; + phy-connection-type = "sgmii"; + }; + + enet2: ethernet@b2000 { + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + }; + + usb@22000 { + phy_type = "ulpi"; + }; + + /* USB2 is shared with localbus, so it must be disabled + by default. We can't put 'status = "disabled";' here + since U-Boot doesn't clear the status property when + it enables USB2. OTOH, U-Boot does create a new node + when there isn't any. So, just comment it out. + */ + usb@23000 { + status = "disabled"; + phy_type = "ulpi"; + }; +}; diff --git a/arch/powerpc/boot/dts/p1020mbg-pc_32b.dts b/arch/powerpc/boot/dts/p1020mbg-pc_32b.dts new file mode 100644 index 000000000000..ab8f076eae90 --- /dev/null +++ b/arch/powerpc/boot/dts/p1020mbg-pc_32b.dts @@ -0,0 +1,89 @@ +/* + * P1020 MBG-PC Device Tree Source (32-bit address map) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1020si-pre.dtsi" +/ { + model = "fsl,P1020MBG-PC"; + compatible = "fsl,P1020MBG-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@ffe05000 { + reg = <0x0 0xffe05000 0x0 0x1000>; + + /* NOR and L2 switch */ + ranges = <0x0 0x0 0x0 0xec000000 0x04000000 + 0x1 0x0 0x0 0xffa00000 0x00040000 + 0x2 0x0 0x0 0xffb00000 0x00020000>; + }; + + soc: soc@ffe00000 { + ranges = <0x0 0x0 0xffe00000 0x100000>; + }; + + pci0: pcie@ffe09000 { + reg = <0x0 0xffe09000 0x0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0x0 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0x0 0xffc10000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@ffe0a000 { + reg = <0x0 0xffe0a000 0x0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0x0 0x80000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0x0 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p1020mbg-pc.dtsi" +/include/ "fsl/p1020si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p1020mbg-pc_36b.dts b/arch/powerpc/boot/dts/p1020mbg-pc_36b.dts new file mode 100644 index 000000000000..9e9f401419b1 --- /dev/null +++ b/arch/powerpc/boot/dts/p1020mbg-pc_36b.dts @@ -0,0 +1,89 @@ +/* + * P1020 MBG-PC Device Tree Source (36-bit address map) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1020si-pre.dtsi" +/ { + model = "fsl,P1020MBG-PC"; + compatible = "fsl,P1020MBG-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@fffe05000 { + reg = <0xf 0xffe05000 0x0 0x1000>; + + /* NOR and L2 switch */ + ranges = <0x0 0x0 0xf 0xec000000 0x04000000 + 0x1 0x0 0xf 0xffa00000 0x00040000 + 0x2 0x0 0xf 0xffb00000 0x00020000>; + }; + + soc: soc@fffe00000 { + ranges = <0x0 0xf 0xffe00000 0x100000>; + }; + + pci0: pcie@fffe09000 { + reg = <0xf 0xffe09000 0x0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc10000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@fffe0a000 { + reg = <0xf 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p1020mbg-pc.dtsi" +/include/ "fsl/p1020si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p1020utm-pc.dtsi b/arch/powerpc/boot/dts/p1020utm-pc.dtsi new file mode 100644 index 000000000000..7ea85eabcc5c --- /dev/null +++ b/arch/powerpc/boot/dts/p1020utm-pc.dtsi @@ -0,0 +1,140 @@ +/* + * P1020 UTM-PC Device Tree Source stub (no addresses or top-level ranges) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +&lbc { + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x2000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + /* 256KB for DTB Image */ + reg = <0x0 0x00040000>; + label = "NOR DTB Image"; + }; + + partition@40000 { + /* 3.75 MB for Linux Kernel Image */ + reg = <0x00040000 0x003c0000>; + label = "NOR Linux Kernel Image"; + }; + + partition@400000 { + /* 27MB for Root file System */ + reg = <0x00400000 0x01b00000>; + label = "NOR Root File System"; + }; + + partition@1f00000 { + /* This location must not be altered */ + /* 512KB for u-boot Bootloader Image */ + /* 512KB for u-boot Environment Variables */ + reg = <0x01f00000 0x00100000>; + label = "NOR U-Boot Image"; + read-only; + }; + }; +}; + +&soc { + i2c@3000 { + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mdio@24000 { + phy0: ethernet-phy@0 { + interrupts = <3 1 0 0>; + reg = <0x0>; + }; + phy1: ethernet-phy@1 { + interrupts = <2 1 0 0>; + reg = <0x1>; + }; + phy2: ethernet-phy@2 { + interrupts = <1 1 0 0>; + reg = <0x2>; + }; + }; + + mdio@25000 { + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@26000 { + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet0: ethernet@b0000 { + phy-handle = <&phy2>; + phy-connection-type = "rgmii-id"; + }; + + enet1: ethernet@b1000 { + phy-handle = <&phy0>; + tbi-handle = <&tbi1>; + phy-connection-type = "sgmii"; + }; + + enet2: ethernet@b2000 { + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + }; + + usb@22000 { + phy_type = "ulpi"; + }; + + /* USB2 is shared with localbus, so it must be disabled + by default. We can't put 'status = "disabled";' here + since U-Boot doesn't clear the status property when + it enables USB2. OTOH, U-Boot does create a new node + when there isn't any. So, just comment it out. + */ + usb@23000 { + status = "disabled"; + phy_type = "ulpi"; + }; +}; diff --git a/arch/powerpc/boot/dts/p1020utm-pc_32b.dts b/arch/powerpc/boot/dts/p1020utm-pc_32b.dts new file mode 100644 index 000000000000..4bfdd8971cdb --- /dev/null +++ b/arch/powerpc/boot/dts/p1020utm-pc_32b.dts @@ -0,0 +1,89 @@ +/* + * P1020 UTM-PC Device Tree Source (32-bit address map) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1020si-pre.dtsi" +/ { + model = "fsl,P1020UTM-PC"; + compatible = "fsl,P1020UTM-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@ffe05000 { + reg = <0x0 0xffe05000 0x0 0x1000>; + + /* NOR */ + ranges = <0x0 0x0 0x0 0xec000000 0x02000000 + 0x1 0x0 0x0 0xffa00000 0x00040000 + 0x2 0x0 0x0 0xffb00000 0x00020000>; + }; + + soc: soc@ffe00000 { + ranges = <0x0 0x0 0xffe00000 0x100000>; + }; + + pci0: pcie@ffe09000 { + reg = <0x0 0xffe09000 0x0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0x0 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0x0 0xffc10000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@ffe0a000 { + reg = <0x0 0xffe0a000 0x0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0x0 0x80000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0x0 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p1020utm-pc.dtsi" +/include/ "fsl/p1020si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p1020utm-pc_36b.dts b/arch/powerpc/boot/dts/p1020utm-pc_36b.dts new file mode 100644 index 000000000000..abec53557501 --- /dev/null +++ b/arch/powerpc/boot/dts/p1020utm-pc_36b.dts @@ -0,0 +1,89 @@ +/* + * P1020 UTM-PC Device Tree Source (36-bit address map) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1020si-pre.dtsi" +/ { + model = "fsl,P1020UTM-PC"; + compatible = "fsl,P1020UTM-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@fffe05000 { + reg = <0xf 0xffe05000 0x0 0x1000>; + + /* NOR */ + ranges = <0x0 0x0 0xf 0xec000000 0x02000000 + 0x1 0x0 0xf 0xffa00000 0x00040000 + 0x2 0x0 0xf 0xffb00000 0x00020000>; + }; + + soc: soc@fffe00000 { + ranges = <0x0 0xf 0xffe00000 0x100000>; + }; + + pci0: pcie@fffe09000 { + reg = <0xf 0xffe09000 0x0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc10000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@fffe0a000 { + reg = <0xf 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p1020utm-pc.dtsi" +/include/ "fsl/p1020si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p2041rdb.dts b/arch/powerpc/boot/dts/p2041rdb.dts index 4f957db01230..285213976a7f 100644 --- a/arch/powerpc/boot/dts/p2041rdb.dts +++ b/arch/powerpc/boot/dts/p2041rdb.dts @@ -135,7 +135,6 @@ reg = <0xf 0xfe200000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>; - fsl,msi = <&msi0>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -151,7 +150,6 @@ reg = <0xf 0xfe201000 0 0x1000>; ranges = <0x02000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>; - fsl,msi = <&msi1>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -167,7 +165,6 @@ reg = <0xf 0xfe202000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x40000000 0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>; - fsl,msi = <&msi2>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 diff --git a/arch/powerpc/boot/dts/p3041ds.dts b/arch/powerpc/boot/dts/p3041ds.dts index f469145abaeb..22a215e94162 100644 --- a/arch/powerpc/boot/dts/p3041ds.dts +++ b/arch/powerpc/boot/dts/p3041ds.dts @@ -173,7 +173,6 @@ reg = <0xf 0xfe200000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>; - fsl,msi = <&msi0>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -189,7 +188,6 @@ reg = <0xf 0xfe201000 0 0x1000>; ranges = <0x02000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>; - fsl,msi = <&msi1>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -205,7 +203,6 @@ reg = <0xf 0xfe202000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x40000000 0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>; - fsl,msi = <&msi2>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -221,7 +218,6 @@ reg = <0xf 0xfe203000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x60000000 0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8030000 0 0x00010000>; - fsl,msi = <&msi2>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 diff --git a/arch/powerpc/boot/dts/p3060qds.dts b/arch/powerpc/boot/dts/p3060qds.dts index 529042e4b9a2..9ae875c8a211 100644 --- a/arch/powerpc/boot/dts/p3060qds.dts +++ b/arch/powerpc/boot/dts/p3060qds.dts @@ -212,7 +212,6 @@ reg = <0xf 0xfe200000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>; - fsl,msi = <&msi0>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -228,7 +227,6 @@ reg = <0xf 0xfe201000 0 0x1000>; ranges = <0x02000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>; - fsl,msi = <&msi1>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts index 6d60e54e50a0..3e204609d02e 100644 --- a/arch/powerpc/boot/dts/p4080ds.dts +++ b/arch/powerpc/boot/dts/p4080ds.dts @@ -141,7 +141,6 @@ reg = <0xf 0xfe200000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>; - fsl,msi = <&msi0>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -157,7 +156,6 @@ reg = <0xf 0xfe201000 0 0x1000>; ranges = <0x02000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>; - fsl,msi = <&msi1>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -173,7 +171,6 @@ reg = <0xf 0xfe202000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x40000000 0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>; - fsl,msi = <&msi2>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 diff --git a/arch/powerpc/boot/dts/p5020ds.dts b/arch/powerpc/boot/dts/p5020ds.dts index 1c250684c902..27c07ed6adc1 100644 --- a/arch/powerpc/boot/dts/p5020ds.dts +++ b/arch/powerpc/boot/dts/p5020ds.dts @@ -173,7 +173,6 @@ reg = <0xf 0xfe200000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>; - fsl,msi = <&msi0>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -189,7 +188,6 @@ reg = <0xf 0xfe201000 0 0x1000>; ranges = <0x02000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>; - fsl,msi = <&msi1>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -205,7 +203,6 @@ reg = <0xf 0xfe202000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x40000000 0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>; - fsl,msi = <&msi2>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 @@ -221,7 +218,6 @@ reg = <0xf 0xfe203000 0 0x1000>; ranges = <0x02000000 0 0xe0000000 0xc 0x60000000 0 0x20000000 0x01000000 0 0x00000000 0xf 0xf8030000 0 0x00010000>; - fsl,msi = <&msi2>; pcie@0 { ranges = <0x02000000 0 0xe0000000 0x02000000 0 0xe0000000 diff --git a/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig b/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig index 0db9ba0423ff..c09598b31de1 100644 --- a/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig +++ b/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig @@ -100,6 +100,7 @@ CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y # CONFIG_SND_SUPPORT_OLD_API is not set CONFIG_SND_SOC=y +CONFIG_SND_POWERPC_SOC=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_CMOS=y CONFIG_EXT2_FS=y diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig index f8aef205d222..91db656294e8 100644 --- a/arch/powerpc/configs/corenet32_smp_defconfig +++ b/arch/powerpc/configs/corenet32_smp_defconfig @@ -116,6 +116,7 @@ CONFIG_SERIAL_8250_RSA=y CONFIG_HW_RANDOM=y CONFIG_NVRAM=y CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y CONFIG_I2C_MPC=y CONFIG_SPI=y CONFIG_SPI_GPIO=y diff --git a/arch/powerpc/configs/corenet64_smp_defconfig b/arch/powerpc/configs/corenet64_smp_defconfig index 82b13bfcf3c0..6798343580f0 100644 --- a/arch/powerpc/configs/corenet64_smp_defconfig +++ b/arch/powerpc/configs/corenet64_smp_defconfig @@ -71,6 +71,8 @@ CONFIG_SERIAL_8250_MANY_PORTS=y CONFIG_SERIAL_8250_DETECT_IRQ=y CONFIG_SERIAL_8250_RSA=y CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MPC=y # CONFIG_HWMON is not set CONFIG_VIDEO_OUTPUT_CONTROL=y # CONFIG_HID_SUPPORT is not set diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index cc87a8441566..62bb723c5b54 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -117,6 +117,7 @@ CONFIG_SERIAL_8250_RSA=y CONFIG_SERIAL_QE=m CONFIG_NVRAM=y CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y CONFIG_I2C_CPM=m CONFIG_I2C_MPC=y CONFIG_SPI=y @@ -140,6 +141,7 @@ CONFIG_SND_INTEL8X0=y # CONFIG_SND_PPC is not set # CONFIG_SND_USB is not set CONFIG_SND_SOC=y +CONFIG_SND_POWERPC_SOC=y CONFIG_HID_A4TECH=y CONFIG_HID_APPLE=y CONFIG_HID_BELKIN=y diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index 48d6682f2434..d1828427ae55 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -119,6 +119,7 @@ CONFIG_SERIAL_8250_RSA=y CONFIG_SERIAL_QE=m CONFIG_NVRAM=y CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y CONFIG_I2C_CPM=m CONFIG_I2C_MPC=y CONFIG_SPI=y @@ -142,6 +143,7 @@ CONFIG_SND_INTEL8X0=y # CONFIG_SND_PPC is not set # CONFIG_SND_USB is not set CONFIG_SND_SOC=y +CONFIG_SND_POWERPC_SOC=y CONFIG_HID_A4TECH=y CONFIG_HID_APPLE=y CONFIG_HID_BELKIN=y diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h index dd70fac57ec8..62678e365ca0 100644 --- a/arch/powerpc/include/asm/dma-mapping.h +++ b/arch/powerpc/include/asm/dma-mapping.h @@ -22,9 +22,11 @@ /* Some dma direct funcs must be visible for use in other dma_ops */ extern void *dma_direct_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag); + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs); extern void dma_direct_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle); + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs); #ifdef CONFIG_NOT_COHERENT_CACHE @@ -130,23 +132,29 @@ static inline int dma_supported(struct device *dev, u64 mask) extern int dma_set_mask(struct device *dev, u64 dma_mask); -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { struct dma_map_ops *dma_ops = get_dma_ops(dev); void *cpu_addr; BUG_ON(!dma_ops); - cpu_addr = dma_ops->alloc_coherent(dev, size, dma_handle, flag); + cpu_addr = dma_ops->alloc(dev, size, dma_handle, flag, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr); return cpu_addr; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { struct dma_map_ops *dma_ops = get_dma_ops(dev); @@ -154,7 +162,7 @@ static inline void dma_free_coherent(struct device *dev, size_t size, debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); - dma_ops->free_coherent(dev, size, cpu_addr, dma_handle); + dma_ops->free(dev, size, cpu_addr, dma_handle, attrs); } static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) diff --git a/arch/powerpc/include/asm/epapr_hcalls.h b/arch/powerpc/include/asm/epapr_hcalls.h index f3b0c2cc9fea..976835d8f22e 100644 --- a/arch/powerpc/include/asm/epapr_hcalls.h +++ b/arch/powerpc/include/asm/epapr_hcalls.h @@ -134,10 +134,15 @@ * whether they will be clobbered. * * Note that r11 can be used as an output parameter. + * + * The "memory" clobber is only necessary for hcalls where the Hypervisor + * will read or write guest memory. However, we add it to all hcalls because + * the impact is minimal, and we want to ensure that it's present for the + * hcalls that need it. */ /* List of common clobbered registers. Do not use this macro. */ -#define EV_HCALL_CLOBBERS "r0", "r12", "xer", "ctr", "lr", "cc" +#define EV_HCALL_CLOBBERS "r0", "r12", "xer", "ctr", "lr", "cc", "memory" #define EV_HCALL_CLOBBERS8 EV_HCALL_CLOBBERS #define EV_HCALL_CLOBBERS7 EV_HCALL_CLOBBERS8, "r10" diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 548da3aa0a30..d58fc4e4149c 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -288,13 +288,6 @@ label##_hv: \ /* Exception addition: Hard disable interrupts */ #define DISABLE_INTS SOFT_DISABLE_INTS(r10,r11) -/* Exception addition: Keep interrupt state */ -#define ENABLE_INTS \ - ld r11,PACAKMSR(r13); \ - ld r12,_MSR(r1); \ - rlwimi r11,r12,0,MSR_EE; \ - mtmsrd r11,1 - #define ADD_NVGPRS \ bl .save_nvgprs diff --git a/arch/powerpc/include/asm/fsl_guts.h b/arch/powerpc/include/asm/fsl_guts.h index ce04530d2000..aa4c488589ce 100644 --- a/arch/powerpc/include/asm/fsl_guts.h +++ b/arch/powerpc/include/asm/fsl_guts.h @@ -16,15 +16,6 @@ #define __ASM_POWERPC_FSL_GUTS_H__ #ifdef __KERNEL__ -/* - * These #ifdefs are safe because it's not possible to build a kernel that - * runs on e500 and e600 cores. - */ - -#if !defined(CONFIG_PPC_85xx) && !defined(CONFIG_PPC_86xx) -#error Only 85xx and 86xx SOCs are supported -#endif - /** * Global Utility Registers. * @@ -36,11 +27,7 @@ * different names. In these cases, one name is chosen to avoid extraneous * #ifdefs. */ -#ifdef CONFIG_PPC_85xx -struct ccsr_guts_85xx { -#else -struct ccsr_guts_86xx { -#endif +struct ccsr_guts { __be32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */ __be32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */ __be32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */ @@ -77,11 +64,8 @@ struct ccsr_guts_86xx { u8 res0a8[0xb0 - 0xa8]; __be32 rstcr; /* 0x.00b0 - Reset Control Register */ u8 res0b4[0xc0 - 0xb4]; -#ifdef CONFIG_PPC_85xx - __be32 iovselsr; /* 0x.00c0 - I/O voltage select status register */ -#else - __be32 elbcvselcr; /* 0x.00c0 - eLBC Voltage Select Ctrl Reg */ -#endif + __be32 iovselsr; /* 0x.00c0 - I/O voltage select status register + Called 'elbcvselcr' on 86xx SOCs */ u8 res0c4[0x224 - 0xc4]; __be32 iodelay1; /* 0x.0224 - IO delay control register 1 */ __be32 iodelay2; /* 0x.0228 - IO delay control register 2 */ @@ -136,7 +120,7 @@ struct ccsr_guts_86xx { * ch: The channel on the DMA controller (0, 1, 2, or 3) * device: The device to set as the source (CCSR_GUTS_DMACR_DEV_xx) */ -static inline void guts_set_dmacr(struct ccsr_guts_86xx __iomem *guts, +static inline void guts_set_dmacr(struct ccsr_guts __iomem *guts, unsigned int co, unsigned int ch, unsigned int device) { unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch)); @@ -172,7 +156,7 @@ static inline void guts_set_dmacr(struct ccsr_guts_86xx __iomem *guts, * ch: The channel on the DMA controller (0, 1, 2, or 3) * value: the new value for the bit (0 or 1) */ -static inline void guts_set_pmuxcr_dma(struct ccsr_guts_86xx __iomem *guts, +static inline void guts_set_pmuxcr_dma(struct ccsr_guts __iomem *guts, unsigned int co, unsigned int ch, unsigned int value) { if ((ch == 0) || (ch == 3)) { diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h index cf417e510736..0e40843a1c6e 100644 --- a/arch/powerpc/include/asm/irq.h +++ b/arch/powerpc/include/asm/irq.h @@ -18,10 +18,6 @@ #include <linux/atomic.h> -/* Define a way to iterate across irqs. */ -#define for_each_irq(i) \ - for ((i) = 0; (i) < NR_IRQS; ++(i)) - extern atomic_t ppc_n_lost_interrupts; /* This number is used when no interrupt has been assigned */ @@ -33,8 +29,6 @@ extern atomic_t ppc_n_lost_interrupts; /* Same thing, used by the generic IRQ code */ #define NR_IRQS_LEGACY NUM_ISA_INTERRUPTS -struct irq_data; -extern irq_hw_number_t irqd_to_hwirq(struct irq_data *d); extern irq_hw_number_t virq_to_hw(unsigned int virq); /** diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index c65b9294376e..c9f698a994be 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -275,9 +275,6 @@ struct mpic unsigned int isu_mask; /* Number of sources */ unsigned int num_sources; - /* default senses array */ - unsigned char *senses; - unsigned int senses_count; /* vector numbers used for internal sources (ipi/timers) */ unsigned int ipi_vecs[4]; @@ -415,21 +412,6 @@ extern struct mpic *mpic_alloc(struct device_node *node, extern void mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, phys_addr_t phys_addr); -/* Set default sense codes - * - * @mpic: controller - * @senses: array of sense codes - * @count: size of above array - * - * Optionally provide an array (indexed on hardware interrupt numbers - * for this MPIC) of default sense codes for the chip. Those are linux - * sense codes IRQ_TYPE_* - * - * The driver gets ownership of the pointer, don't dispose of it or - * anything like that. __init only. - */ -extern void mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count); - /* Initialize the controller. After this has been called, none of the above * should be called again for this mpic diff --git a/arch/powerpc/include/asm/mpic_msgr.h b/arch/powerpc/include/asm/mpic_msgr.h index 3ec37dc9003e..326d33ca55cd 100644 --- a/arch/powerpc/include/asm/mpic_msgr.h +++ b/arch/powerpc/include/asm/mpic_msgr.h @@ -13,6 +13,7 @@ #include <linux/types.h> #include <linux/spinlock.h> +#include <asm/smp.h> struct mpic_msgr { u32 __iomem *base; diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h index b86faa9107da..8a97aa7289d3 100644 --- a/arch/powerpc/include/asm/reg_booke.h +++ b/arch/powerpc/include/asm/reg_booke.h @@ -15,11 +15,6 @@ #ifndef __ASM_POWERPC_REG_BOOKE_H__ #define __ASM_POWERPC_REG_BOOKE_H__ -#ifdef CONFIG_BOOKE_WDT -extern u32 booke_wdt_enabled; -extern u32 booke_wdt_period; -#endif /* CONFIG_BOOKE_WDT */ - /* Machine State Register (MSR) Fields */ #define MSR_GS (1<<28) /* Guest state */ #define MSR_UCLE (1<<26) /* User-mode cache lock enable */ diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index 3f6464b4d970..bcfdcd22c766 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -17,7 +17,8 @@ * to the dma address (mapping) of the first page. */ static void *dma_iommu_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { return iommu_alloc_coherent(dev, get_iommu_table_base(dev), size, dma_handle, dev->coherent_dma_mask, flag, @@ -25,7 +26,8 @@ static void *dma_iommu_alloc_coherent(struct device *dev, size_t size, } static void dma_iommu_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { iommu_free_coherent(get_iommu_table_base(dev), size, vaddr, dma_handle); } @@ -105,8 +107,8 @@ static u64 dma_iommu_get_required_mask(struct device *dev) } struct dma_map_ops dma_iommu_ops = { - .alloc_coherent = dma_iommu_alloc_coherent, - .free_coherent = dma_iommu_free_coherent, + .alloc = dma_iommu_alloc_coherent, + .free = dma_iommu_free_coherent, .map_sg = dma_iommu_map_sg, .unmap_sg = dma_iommu_unmap_sg, .dma_supported = dma_iommu_dma_supported, diff --git a/arch/powerpc/kernel/dma-swiotlb.c b/arch/powerpc/kernel/dma-swiotlb.c index 1ebc9189aada..4ab88dafb235 100644 --- a/arch/powerpc/kernel/dma-swiotlb.c +++ b/arch/powerpc/kernel/dma-swiotlb.c @@ -47,8 +47,8 @@ static u64 swiotlb_powerpc_get_required(struct device *dev) * for everything else. */ struct dma_map_ops swiotlb_dma_ops = { - .alloc_coherent = dma_direct_alloc_coherent, - .free_coherent = dma_direct_free_coherent, + .alloc = dma_direct_alloc_coherent, + .free = dma_direct_free_coherent, .map_sg = swiotlb_map_sg_attrs, .unmap_sg = swiotlb_unmap_sg_attrs, .dma_supported = swiotlb_dma_supported, diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index 7d0233c12ee3..b1ec983dcec8 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -26,7 +26,8 @@ void *dma_direct_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { void *ret; #ifdef CONFIG_NOT_COHERENT_CACHE @@ -54,7 +55,8 @@ void *dma_direct_alloc_coherent(struct device *dev, size_t size, } void dma_direct_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { #ifdef CONFIG_NOT_COHERENT_CACHE __dma_free_coherent(size, vaddr); @@ -150,8 +152,8 @@ static inline void dma_direct_sync_single(struct device *dev, #endif struct dma_map_ops dma_direct_ops = { - .alloc_coherent = dma_direct_alloc_coherent, - .free_coherent = dma_direct_free_coherent, + .alloc = dma_direct_alloc_coherent, + .free = dma_direct_free_coherent, .map_sg = dma_direct_map_sg, .unmap_sg = dma_direct_unmap_sg, .dma_supported = dma_direct_dma_supported, diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 3e57a00b8cba..ba3aeb4bc06a 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -206,40 +206,43 @@ reenable_mmu: /* re-enable mmu so we can */ andi. r10,r10,MSR_EE /* Did EE change? */ beq 1f - /* Save handler and return address into the 2 unused words - * of the STACK_FRAME_OVERHEAD (sneak sneak sneak). Everything - * else can be recovered from the pt_regs except r3 which for - * normal interrupts has been set to pt_regs and for syscalls - * is an argument, so we temporarily use ORIG_GPR3 to save it - */ - stw r9,8(r1) - stw r11,12(r1) - stw r3,ORIG_GPR3(r1) /* * The trace_hardirqs_off will use CALLER_ADDR0 and CALLER_ADDR1. * If from user mode there is only one stack frame on the stack, and * accessing CALLER_ADDR1 will cause oops. So we need create a dummy * stack frame to make trace_hardirqs_off happy. + * + * This is handy because we also need to save a bunch of GPRs, + * r3 can be different from GPR3(r1) at this point, r9 and r11 + * contains the old MSR and handler address respectively, + * r4 & r5 can contain page fault arguments that need to be passed + * along as well. r12, CCR, CTR, XER etc... are left clobbered as + * they aren't useful past this point (aren't syscall arguments), + * the rest is restored from the exception frame. */ + stwu r1,-32(r1) + stw r9,8(r1) + stw r11,12(r1) + stw r3,16(r1) + stw r4,20(r1) + stw r5,24(r1) andi. r12,r12,MSR_PR - beq 11f - stwu r1,-16(r1) + b 11f bl trace_hardirqs_off - addi r1,r1,16 b 12f - 11: bl trace_hardirqs_off 12: + lwz r5,24(r1) + lwz r4,20(r1) + lwz r3,16(r1) + lwz r11,12(r1) + lwz r9,8(r1) + addi r1,r1,32 lwz r0,GPR0(r1) - lwz r3,ORIG_GPR3(r1) - lwz r4,GPR4(r1) - lwz r5,GPR5(r1) lwz r6,GPR6(r1) lwz r7,GPR7(r1) lwz r8,GPR8(r1) - lwz r9,8(r1) - lwz r11,12(r1) 1: mtctr r11 mtlr r9 bctr /* jump to handler */ diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index f8a7a1a1a9f4..ef2074c3e906 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -588,23 +588,19 @@ _GLOBAL(ret_from_except_lite) fast_exc_return_irq: restore: /* - * This is the main kernel exit path, we first check if we - * have to change our interrupt state. + * This is the main kernel exit path. First we check if we + * are about to re-enable interrupts */ ld r5,SOFTE(r1) lbz r6,PACASOFTIRQEN(r13) - cmpwi cr1,r5,0 - cmpw cr0,r5,r6 - beq cr0,4f + cmpwi cr0,r5,0 + beq restore_irq_off - /* We do, handle disable first, which is easy */ - bne cr1,3f; - li r0,0 - stb r0,PACASOFTIRQEN(r13); - TRACE_DISABLE_INTS - b 4f + /* We are enabling, were we already enabled ? Yes, just return */ + cmpwi cr0,r6,1 + beq cr0,do_restore -3: /* + /* * We are about to soft-enable interrupts (we are hard disabled * at this point). We check if there's anything that needs to * be replayed first. @@ -626,7 +622,7 @@ restore_no_replay: /* * Final return path. BookE is handled in a different file */ -4: +do_restore: #ifdef CONFIG_PPC_BOOK3E b .exception_return_book3e #else @@ -700,6 +696,25 @@ fast_exception_return: #endif /* CONFIG_PPC_BOOK3E */ /* + * We are returning to a context with interrupts soft disabled. + * + * However, we may also about to hard enable, so we need to + * make sure that in this case, we also clear PACA_IRQ_HARD_DIS + * or that bit can get out of sync and bad things will happen + */ +restore_irq_off: + ld r3,_MSR(r1) + lbz r7,PACAIRQHAPPENED(r13) + andi. r0,r3,MSR_EE + beq 1f + rlwinm r7,r7,0,~PACA_IRQ_HARD_DIS + stb r7,PACAIRQHAPPENED(r13) +1: li r0,0 + stb r0,PACASOFTIRQEN(r13); + TRACE_DISABLE_INTS + b do_restore + + /* * Something did happen, check if a re-emit is needed * (this also clears paca->irq_happened) */ @@ -748,6 +763,9 @@ restore_check_irq_replay: #endif /* CONFIG_PPC_BOOK3E */ 1: b .ret_from_except /* What else to do here ? */ + + +3: do_work: #ifdef CONFIG_PREEMPT andi. r0,r3,MSR_PR /* Returning to user mode? */ @@ -767,16 +785,6 @@ do_work: SOFT_DISABLE_INTS(r3,r4) 1: bl .preempt_schedule_irq - /* Hard-disable interrupts again (and update PACA) */ -#ifdef CONFIG_PPC_BOOK3E - wrteei 0 -#else - ld r10,PACAKMSR(r13) /* Get kernel MSR without EE */ - mtmsrd r10,1 -#endif /* CONFIG_PPC_BOOK3E */ - li r0,PACA_IRQ_HARD_DIS - stb r0,PACAIRQHAPPENED(r13) - /* Re-test flags and eventually loop */ clrrdi r9,r1,THREAD_SHIFT ld r4,TI_FLAGS(r9) @@ -787,14 +795,6 @@ do_work: user_work: #endif /* CONFIG_PREEMPT */ - /* Enable interrupts */ -#ifdef CONFIG_PPC_BOOK3E - wrteei 1 -#else - ori r10,r10,MSR_EE - mtmsrd r10,1 -#endif /* CONFIG_PPC_BOOK3E */ - andi. r0,r4,_TIF_NEED_RESCHED beq 1f bl .restore_interrupts diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index cb705fdbb458..8f880bc77c56 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -768,8 +768,8 @@ alignment_common: std r3,_DAR(r1) std r4,_DSISR(r1) bl .save_nvgprs + DISABLE_INTS addi r3,r1,STACK_FRAME_OVERHEAD - ENABLE_INTS bl .alignment_exception b .ret_from_except diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index cfe7a38708c3..18bdf74fa164 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -40,6 +40,8 @@ #include <asm/prom.h> #include <asm/rtas.h> #include <asm/fadump.h> +#include <asm/debug.h> +#include <asm/setup.h> static struct fw_dump fw_dump; static struct fadump_mem_struct fdm; diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 79bb282e6501..b01d14eeca8d 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -65,7 +65,8 @@ static struct of_device_id __initdata ibmebus_matches[] = { static void *ibmebus_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, - gfp_t flag) + gfp_t flag, + struct dma_attrs *attrs) { void *mem; @@ -77,7 +78,8 @@ static void *ibmebus_alloc_coherent(struct device *dev, static void ibmebus_free_coherent(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle) + dma_addr_t dma_handle, + struct dma_attrs *attrs) { kfree(vaddr); } @@ -136,8 +138,8 @@ static u64 ibmebus_dma_get_required_mask(struct device *dev) } static struct dma_map_ops ibmebus_dma_ops = { - .alloc_coherent = ibmebus_alloc_coherent, - .free_coherent = ibmebus_free_coherent, + .alloc = ibmebus_alloc_coherent, + .free = ibmebus_free_coherent, .map_sg = ibmebus_map_sg, .unmap_sg = ibmebus_unmap_sg, .dma_supported = ibmebus_dma_supported, diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 243dbabfe74d..641da9e868ce 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -229,6 +229,19 @@ notrace void arch_local_irq_restore(unsigned long en) */ if (unlikely(irq_happened != PACA_IRQ_HARD_DIS)) __hard_irq_disable(); +#ifdef CONFIG_TRACE_IRQFLAG + else { + /* + * We should already be hard disabled here. We had bugs + * where that wasn't the case so let's dbl check it and + * warn if we are wrong. Only do that when IRQ tracing + * is enabled as mfmsr() can be costly. + */ + if (WARN_ON(mfmsr() & MSR_EE)) + __hard_irq_disable(); + } +#endif /* CONFIG_TRACE_IRQFLAG */ + set_soft_enabled(0); /* @@ -260,11 +273,17 @@ EXPORT_SYMBOL(arch_local_irq_restore); * if they are currently disabled. This is typically called before * schedule() or do_signal() when returning to userspace. We do it * in C to avoid the burden of dealing with lockdep etc... + * + * NOTE: This is called with interrupts hard disabled but not marked + * as such in paca->irq_happened, so we need to resync this. */ void restore_interrupts(void) { - if (irqs_disabled()) + if (irqs_disabled()) { + local_paca->irq_happened |= PACA_IRQ_HARD_DIS; local_irq_enable(); + } else + __hard_irq_enable(); } #endif /* CONFIG_PPC64 */ @@ -330,14 +349,10 @@ void migrate_irqs(void) alloc_cpumask_var(&mask, GFP_KERNEL); - for_each_irq(irq) { + for_each_irq_desc(irq, desc) { struct irq_data *data; struct irq_chip *chip; - desc = irq_to_desc(irq); - if (!desc) - continue; - data = irq_desc_get_irq_data(desc); if (irqd_is_per_cpu(data)) continue; @@ -560,12 +575,6 @@ void do_softirq(void) local_irq_restore(flags); } -irq_hw_number_t irqd_to_hwirq(struct irq_data *d) -{ - return d->hwirq; -} -EXPORT_SYMBOL_GPL(irqd_to_hwirq); - irq_hw_number_t virq_to_hw(unsigned int virq) { struct irq_data *irq_data = irq_get_irq_data(virq); diff --git a/arch/powerpc/kernel/kgdb.c b/arch/powerpc/kernel/kgdb.c index 76a6e40a6f7c..782bd0a3c2f0 100644 --- a/arch/powerpc/kernel/kgdb.c +++ b/arch/powerpc/kernel/kgdb.c @@ -24,6 +24,7 @@ #include <asm/current.h> #include <asm/processor.h> #include <asm/machdep.h> +#include <asm/debug.h> /* * This table contains the mapping between PowerPC hardware trap types, and diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index c957b1202bdc..5df777794403 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -23,14 +23,11 @@ void machine_kexec_mask_interrupts(void) { unsigned int i; + struct irq_desc *desc; - for_each_irq(i) { - struct irq_desc *desc = irq_to_desc(i); + for_each_irq_desc(i, desc) { struct irq_chip *chip; - if (!desc) - continue; - chip = irq_desc_get_chip(desc); if (!chip) continue; diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index f88698c0f332..4937c9690090 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -1235,7 +1235,7 @@ void __ppc64_runlatch_on(void) ctrl |= CTRL_RUNLATCH; mtspr(SPRN_CTRLT, ctrl); - ti->local_flags |= TLF_RUNLATCH; + ti->local_flags |= _TLF_RUNLATCH; } /* Called with hard IRQs off */ @@ -1244,7 +1244,7 @@ void __ppc64_runlatch_off(void) struct thread_info *ti = current_thread_info(); unsigned long ctrl; - ti->local_flags &= ~TLF_RUNLATCH; + ti->local_flags &= ~_TLF_RUNLATCH; ctrl = mfspr(SPRN_CTRLF); ctrl &= ~CTRL_RUNLATCH; diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index 9825f29d1faf..ec8a53fa9e8f 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -150,6 +150,9 @@ notrace void __init machine_init(u64 dt_ptr) } #ifdef CONFIG_BOOKE_WDT +extern u32 booke_wdt_enabled; +extern u32 booke_wdt_period; + /* Checks wdt=x and wdt_period=xx command-line option */ notrace int __init early_parse_wdt(char *p) { diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 6aa0c663e247..158972341a2d 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -248,7 +248,7 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) addr, regs->nip, regs->link, code); } - if (!arch_irq_disabled_regs(regs)) + if (arch_irqs_disabled() && !arch_irq_disabled_regs(regs)) local_irq_enable(); memset(&info, 0, sizeof(info)); @@ -1019,7 +1019,9 @@ void __kprobes program_check_exception(struct pt_regs *regs) return; } - local_irq_enable(); + /* We restore the interrupt state now */ + if (!arch_irq_disabled_regs(regs)) + local_irq_enable(); #ifdef CONFIG_MATH_EMULATION /* (reason & REASON_ILLEGAL) would be the obvious thing here, @@ -1069,6 +1071,10 @@ void alignment_exception(struct pt_regs *regs) { int sig, code, fixed = 0; + /* We restore the interrupt state now */ + if (!arch_irq_disabled_regs(regs)) + local_irq_enable(); + /* we don't implement logging of alignment exceptions */ if (!(current->thread.align_ctl & PR_UNALIGN_SIGBUS)) fixed = fix_alignment(regs); diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index b2f7c8480bf6..a3a99901c8ec 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -482,7 +482,8 @@ static void vio_cmo_balance(struct work_struct *work) } static void *vio_dma_iommu_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { struct vio_dev *viodev = to_vio_dev(dev); void *ret; @@ -492,7 +493,7 @@ static void *vio_dma_iommu_alloc_coherent(struct device *dev, size_t size, return NULL; } - ret = dma_iommu_ops.alloc_coherent(dev, size, dma_handle, flag); + ret = dma_iommu_ops.alloc(dev, size, dma_handle, flag, attrs); if (unlikely(ret == NULL)) { vio_cmo_dealloc(viodev, roundup(size, PAGE_SIZE)); atomic_inc(&viodev->cmo.allocs_failed); @@ -502,11 +503,12 @@ static void *vio_dma_iommu_alloc_coherent(struct device *dev, size_t size, } static void vio_dma_iommu_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { struct vio_dev *viodev = to_vio_dev(dev); - dma_iommu_ops.free_coherent(dev, size, vaddr, dma_handle); + dma_iommu_ops.free(dev, size, vaddr, dma_handle, attrs); vio_cmo_dealloc(viodev, roundup(size, PAGE_SIZE)); } @@ -607,8 +609,8 @@ static u64 vio_dma_get_required_mask(struct device *dev) } struct dma_map_ops vio_dma_mapping_ops = { - .alloc_coherent = vio_dma_iommu_alloc_coherent, - .free_coherent = vio_dma_iommu_free_coherent, + .alloc = vio_dma_iommu_alloc_coherent, + .free = vio_dma_iommu_free_coherent, .map_sg = vio_dma_iommu_map_sg, .unmap_sg = vio_dma_iommu_unmap_sg, .map_page = vio_dma_iommu_map_page, diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index ddc485a529f2..c3beaeef3f60 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -258,6 +258,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn, !(memslot->userspace_addr & (s - 1))) { start &= ~(s - 1); pgsize = s; + get_page(hpage); + put_page(page); page = hpage; } } @@ -281,11 +283,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn, err = 0; out: - if (got) { - if (PageHuge(page)) - page = compound_head(page); + if (got) put_page(page); - } return err; up_err: @@ -678,8 +677,15 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, SetPageDirty(page); out_put: - if (page) - put_page(page); + if (page) { + /* + * We drop pages[0] here, not page because page might + * have been set to the head page of a compound, but + * we have to drop the reference on the correct tail + * page to match the get inside gup() + */ + put_page(pages[0]); + } return ret; out_unlock: @@ -979,6 +985,7 @@ void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa, pa = *physp; } page = pfn_to_page(pa >> PAGE_SHIFT); + get_page(page); } else { hva = gfn_to_hva_memslot(memslot, gfn); npages = get_user_pages_fast(hva, 1, 1, pages); @@ -991,8 +998,6 @@ void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa, page = compound_head(page); psize <<= compound_order(page); } - if (!kvm->arch.using_mmu_notifiers) - get_page(page); offset = gpa & (psize - 1); if (nb_ret) *nb_ret = psize - offset; @@ -1003,7 +1008,6 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va) { struct page *page = virt_to_page(va); - page = compound_head(page); put_page(page); } diff --git a/arch/powerpc/kvm/book3s_emulate.c b/arch/powerpc/kvm/book3s_emulate.c index f1950d131827..135663a3e4fc 100644 --- a/arch/powerpc/kvm/book3s_emulate.c +++ b/arch/powerpc/kvm/book3s_emulate.c @@ -21,6 +21,7 @@ #include <asm/disassemble.h> #include <asm/kvm_book3s.h> #include <asm/reg.h> +#include <asm/switch_to.h> #define OP_19_XOP_RFID 18 #define OP_19_XOP_RFI 50 diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 01294a5099dd..108d1f580177 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1192,8 +1192,6 @@ static void unpin_slot(struct kvm *kvm, int slot_id) continue; pfn = physp[j] >> PAGE_SHIFT; page = pfn_to_page(pfn); - if (PageHuge(page)) - page = compound_head(page); SetPageDirty(page); put_page(page); } diff --git a/arch/powerpc/kvm/book3s_hv_builtin.c b/arch/powerpc/kvm/book3s_hv_builtin.c index bed1279aa6a8..e1b60f56f2a1 100644 --- a/arch/powerpc/kvm/book3s_hv_builtin.c +++ b/arch/powerpc/kvm/book3s_hv_builtin.c @@ -173,9 +173,9 @@ static void __init kvm_linear_init_one(ulong size, int count, int type) static struct kvmppc_linear_info *kvm_alloc_linear(int type) { - struct kvmppc_linear_info *ri; + struct kvmppc_linear_info *ri, *ret; - ri = NULL; + ret = NULL; spin_lock(&linear_lock); list_for_each_entry(ri, &free_linears, list) { if (ri->type != type) @@ -183,11 +183,12 @@ static struct kvmppc_linear_info *kvm_alloc_linear(int type) list_del(&ri->list); atomic_inc(&ri->use_count); + memset(ri->base_virt, 0, ri->npages << PAGE_SHIFT); + ret = ri; break; } spin_unlock(&linear_lock); - memset(ri->base_virt, 0, ri->npages << PAGE_SHIFT); - return ri; + return ret; } static void kvm_release_linear(struct kvmppc_linear_info *ri) diff --git a/arch/powerpc/kvm/book3s_hv_interrupts.S b/arch/powerpc/kvm/book3s_hv_interrupts.S index 3f7b674dd4bf..d3fb4df02c41 100644 --- a/arch/powerpc/kvm/book3s_hv_interrupts.S +++ b/arch/powerpc/kvm/book3s_hv_interrupts.S @@ -46,8 +46,10 @@ _GLOBAL(__kvmppc_vcore_entry) /* Save host state to the stack */ stdu r1, -SWITCH_FRAME_SIZE(r1) - /* Save non-volatile registers (r14 - r31) */ + /* Save non-volatile registers (r14 - r31) and CR */ SAVE_NVGPRS(r1) + mfcr r3 + std r3, _CCR(r1) /* Save host DSCR */ BEGIN_FTR_SECTION @@ -157,8 +159,10 @@ kvmppc_handler_highmem: * R13 = PACA */ - /* Restore non-volatile host registers (r14 - r31) */ + /* Restore non-volatile host registers (r14 - r31) and CR */ REST_NVGPRS(r1) + ld r4, _CCR(r1) + mtcr r4 addi r1, r1, SWITCH_FRAME_SIZE ld r0, PPC_LR_STKOFF(r1) diff --git a/arch/powerpc/kvm/book3s_interrupts.S b/arch/powerpc/kvm/book3s_interrupts.S index 0a8515a5c042..3e35383bdb21 100644 --- a/arch/powerpc/kvm/book3s_interrupts.S +++ b/arch/powerpc/kvm/book3s_interrupts.S @@ -84,6 +84,10 @@ kvm_start_entry: /* Save non-volatile registers (r14 - r31) */ SAVE_NVGPRS(r1) + /* Save CR */ + mfcr r14 + stw r14, _CCR(r1) + /* Save LR */ PPC_STL r0, _LINK(r1) @@ -165,6 +169,9 @@ kvm_exit_loop: PPC_LL r4, _LINK(r1) mtlr r4 + lwz r14, _CCR(r1) + mtcr r14 + /* Restore non-volatile host registers (r14 - r31) */ REST_NVGPRS(r1) diff --git a/arch/powerpc/kvm/book3s_paired_singles.c b/arch/powerpc/kvm/book3s_paired_singles.c index e70ef2d86431..a59a25a13218 100644 --- a/arch/powerpc/kvm/book3s_paired_singles.c +++ b/arch/powerpc/kvm/book3s_paired_singles.c @@ -24,6 +24,7 @@ #include <asm/kvm_fpu.h> #include <asm/reg.h> #include <asm/cacheflush.h> +#include <asm/switch_to.h> #include <linux/vmalloc.h> /* #define DEBUG */ diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 7340e1090b77..7759053d391b 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -33,6 +33,7 @@ #include <asm/kvm_ppc.h> #include <asm/kvm_book3s.h> #include <asm/mmu_context.h> +#include <asm/switch_to.h> #include <linux/gfp.h> #include <linux/sched.h> #include <linux/vmalloc.h> @@ -776,6 +777,7 @@ program_interrupt: } } + preempt_disable(); if (!(r & RESUME_HOST)) { /* To avoid clobbering exit_reason, only check for signals if * we aren't already exiting to userspace for some other @@ -797,8 +799,6 @@ program_interrupt: run->exit_reason = KVM_EXIT_INTR; r = -EINTR; } else { - preempt_disable(); - /* In case an interrupt came in that was triggered * from userspace (like DEC), we need to check what * to inject now! */ @@ -880,7 +880,8 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) switch (reg->id) { case KVM_REG_PPC_HIOR: - r = put_user(to_book3s(vcpu)->hior, (u64 __user *)reg->addr); + r = copy_to_user((u64 __user *)(long)reg->addr, + &to_book3s(vcpu)->hior, sizeof(u64)); break; default: break; @@ -895,7 +896,8 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) switch (reg->id) { case KVM_REG_PPC_HIOR: - r = get_user(to_book3s(vcpu)->hior, (u64 __user *)reg->addr); + r = copy_from_user(&to_book3s(vcpu)->hior, + (u64 __user *)(long)reg->addr, sizeof(u64)); if (!r) to_book3s(vcpu)->hior_explicit = true; break; diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S index 10d8ef602e5c..c8c4b878795a 100644 --- a/arch/powerpc/kvm/booke_interrupts.S +++ b/arch/powerpc/kvm/booke_interrupts.S @@ -34,7 +34,8 @@ /* r2 is special: it holds 'current', and it made nonvolatile in the * kernel with the -ffixed-r2 gcc option. */ #define HOST_R2 12 -#define HOST_NV_GPRS 16 +#define HOST_CR 16 +#define HOST_NV_GPRS 20 #define HOST_NV_GPR(n) (HOST_NV_GPRS + ((n - 14) * 4)) #define HOST_MIN_STACK_SIZE (HOST_NV_GPR(31) + 4) #define HOST_STACK_SIZE (((HOST_MIN_STACK_SIZE + 15) / 16) * 16) /* Align. */ @@ -296,8 +297,10 @@ heavyweight_exit: /* Return to kvm_vcpu_run(). */ lwz r4, HOST_STACK_LR(r1) + lwz r5, HOST_CR(r1) addi r1, r1, HOST_STACK_SIZE mtlr r4 + mtcr r5 /* r3 still contains the return code from kvmppc_handle_exit(). */ blr @@ -314,6 +317,8 @@ _GLOBAL(__kvmppc_vcpu_run) stw r3, HOST_RUN(r1) mflr r3 stw r3, HOST_STACK_LR(r1) + mfcr r5 + stw r5, HOST_CR(r1) /* Save host non-volatile register state to stack. */ stw r14, HOST_NV_GPR(r14)(r1) diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index af1ab5e9a691..5c3cf2d04e41 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -48,7 +48,13 @@ /* * Assembly helpers from arch/powerpc/net/bpf_jit.S: */ -extern u8 sk_load_word[], sk_load_half[], sk_load_byte[], sk_load_byte_msh[]; +#define DECLARE_LOAD_FUNC(func) \ + extern u8 func[], func##_negative_offset[], func##_positive_offset[] + +DECLARE_LOAD_FUNC(sk_load_word); +DECLARE_LOAD_FUNC(sk_load_half); +DECLARE_LOAD_FUNC(sk_load_byte); +DECLARE_LOAD_FUNC(sk_load_byte_msh); #define FUNCTION_DESCR_SIZE 24 diff --git a/arch/powerpc/net/bpf_jit_64.S b/arch/powerpc/net/bpf_jit_64.S index ff4506e85cce..55ba3855a97f 100644 --- a/arch/powerpc/net/bpf_jit_64.S +++ b/arch/powerpc/net/bpf_jit_64.S @@ -31,14 +31,13 @@ * then branch directly to slow_path_XXX if required. (In fact, could * load a spare GPR with the address of slow_path_generic and pass size * as an argument, making the call site a mtlr, li and bllr.) - * - * Technically, the "is addr < 0" check is unnecessary & slowing down - * the ABS path, as it's statically checked on generation. */ .globl sk_load_word sk_load_word: cmpdi r_addr, 0 - blt bpf_error + blt bpf_slow_path_word_neg + .globl sk_load_word_positive_offset +sk_load_word_positive_offset: /* Are we accessing past headlen? */ subi r_scratch1, r_HL, 4 cmpd r_scratch1, r_addr @@ -51,7 +50,9 @@ sk_load_word: .globl sk_load_half sk_load_half: cmpdi r_addr, 0 - blt bpf_error + blt bpf_slow_path_half_neg + .globl sk_load_half_positive_offset +sk_load_half_positive_offset: subi r_scratch1, r_HL, 2 cmpd r_scratch1, r_addr blt bpf_slow_path_half @@ -61,7 +62,9 @@ sk_load_half: .globl sk_load_byte sk_load_byte: cmpdi r_addr, 0 - blt bpf_error + blt bpf_slow_path_byte_neg + .globl sk_load_byte_positive_offset +sk_load_byte_positive_offset: cmpd r_HL, r_addr ble bpf_slow_path_byte lbzx r_A, r_D, r_addr @@ -69,22 +72,20 @@ sk_load_byte: /* * BPF_S_LDX_B_MSH: ldxb 4*([offset]&0xf) - * r_addr is the offset value, already known positive + * r_addr is the offset value */ .globl sk_load_byte_msh sk_load_byte_msh: + cmpdi r_addr, 0 + blt bpf_slow_path_byte_msh_neg + .globl sk_load_byte_msh_positive_offset +sk_load_byte_msh_positive_offset: cmpd r_HL, r_addr ble bpf_slow_path_byte_msh lbzx r_X, r_D, r_addr rlwinm r_X, r_X, 2, 32-4-2, 31-2 blr -bpf_error: - /* Entered with cr0 = lt */ - li r3, 0 - /* Generated code will 'blt epilogue', returning 0. */ - blr - /* Call out to skb_copy_bits: * We'll need to back up our volatile regs first; we have * local variable space at r1+(BPF_PPC_STACK_BASIC). @@ -136,3 +137,84 @@ bpf_slow_path_byte_msh: lbz r_X, BPF_PPC_STACK_BASIC+(2*8)(r1) rlwinm r_X, r_X, 2, 32-4-2, 31-2 blr + +/* Call out to bpf_internal_load_pointer_neg_helper: + * We'll need to back up our volatile regs first; we have + * local variable space at r1+(BPF_PPC_STACK_BASIC). + * Allocate a new stack frame here to remain ABI-compliant in + * stashing LR. + */ +#define sk_negative_common(SIZE) \ + mflr r0; \ + std r0, 16(r1); \ + /* R3 goes in parameter space of caller's frame */ \ + std r_skb, (BPF_PPC_STACKFRAME+48)(r1); \ + std r_A, (BPF_PPC_STACK_BASIC+(0*8))(r1); \ + std r_X, (BPF_PPC_STACK_BASIC+(1*8))(r1); \ + stdu r1, -BPF_PPC_SLOWPATH_FRAME(r1); \ + /* R3 = r_skb, as passed */ \ + mr r4, r_addr; \ + li r5, SIZE; \ + bl bpf_internal_load_pointer_neg_helper; \ + /* R3 != 0 on success */ \ + addi r1, r1, BPF_PPC_SLOWPATH_FRAME; \ + ld r0, 16(r1); \ + ld r_A, (BPF_PPC_STACK_BASIC+(0*8))(r1); \ + ld r_X, (BPF_PPC_STACK_BASIC+(1*8))(r1); \ + mtlr r0; \ + cmpldi r3, 0; \ + beq bpf_error_slow; /* cr0 = EQ */ \ + mr r_addr, r3; \ + ld r_skb, (BPF_PPC_STACKFRAME+48)(r1); \ + /* Great success! */ + +bpf_slow_path_word_neg: + lis r_scratch1,-32 /* SKF_LL_OFF */ + cmpd r_addr, r_scratch1 /* addr < SKF_* */ + blt bpf_error /* cr0 = LT */ + .globl sk_load_word_negative_offset +sk_load_word_negative_offset: + sk_negative_common(4) + lwz r_A, 0(r_addr) + blr + +bpf_slow_path_half_neg: + lis r_scratch1,-32 /* SKF_LL_OFF */ + cmpd r_addr, r_scratch1 /* addr < SKF_* */ + blt bpf_error /* cr0 = LT */ + .globl sk_load_half_negative_offset +sk_load_half_negative_offset: + sk_negative_common(2) + lhz r_A, 0(r_addr) + blr + +bpf_slow_path_byte_neg: + lis r_scratch1,-32 /* SKF_LL_OFF */ + cmpd r_addr, r_scratch1 /* addr < SKF_* */ + blt bpf_error /* cr0 = LT */ + .globl sk_load_byte_negative_offset +sk_load_byte_negative_offset: + sk_negative_common(1) + lbz r_A, 0(r_addr) + blr + +bpf_slow_path_byte_msh_neg: + lis r_scratch1,-32 /* SKF_LL_OFF */ + cmpd r_addr, r_scratch1 /* addr < SKF_* */ + blt bpf_error /* cr0 = LT */ + .globl sk_load_byte_msh_negative_offset +sk_load_byte_msh_negative_offset: + sk_negative_common(1) + lbz r_X, 0(r_addr) + rlwinm r_X, r_X, 2, 32-4-2, 31-2 + blr + +bpf_error_slow: + /* fabricate a cr0 = lt */ + li r_scratch1, -1 + cmpdi r_scratch1, 0 +bpf_error: + /* Entered with cr0 = lt */ + li r3, 0 + /* Generated code will 'blt epilogue', returning 0. */ + blr diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 73619d3aeb6c..2dc8b1484845 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -127,6 +127,9 @@ static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) PPC_BLR(); } +#define CHOOSE_LOAD_FUNC(K, func) \ + ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) + /* Assemble the body code between the prologue & epilogue. */ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, struct codegen_context *ctx, @@ -391,21 +394,16 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, /*** Absolute loads from packet header/data ***/ case BPF_S_LD_W_ABS: - func = sk_load_word; + func = CHOOSE_LOAD_FUNC(K, sk_load_word); goto common_load; case BPF_S_LD_H_ABS: - func = sk_load_half; + func = CHOOSE_LOAD_FUNC(K, sk_load_half); goto common_load; case BPF_S_LD_B_ABS: - func = sk_load_byte; + func = CHOOSE_LOAD_FUNC(K, sk_load_byte); common_load: - /* - * Load from [K]. Reference with the (negative) - * SKF_NET_OFF/SKF_LL_OFF offsets is unsupported. - */ + /* Load from [K]. */ ctx->seen |= SEEN_DATAREF; - if ((int)K < 0) - return -ENOTSUPP; PPC_LI64(r_scratch1, func); PPC_MTLR(r_scratch1); PPC_LI32(r_addr, K); @@ -429,7 +427,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, common_load_ind: /* * Load from [X + K]. Negative offsets are tested for - * in the helper functions, and result in a 'ret 0'. + * in the helper functions. */ ctx->seen |= SEEN_DATAREF | SEEN_XREG; PPC_LI64(r_scratch1, func); @@ -443,13 +441,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, break; case BPF_S_LDX_B_MSH: - /* - * x86 version drops packet (RET 0) when K<0, whereas - * interpreter does allow K<0 (__load_pointer, special - * ancillary data). common_load returns ENOTSUPP if K<0, - * so we fall back to interpreter & filter works. - */ - func = sk_load_byte_msh; + func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh); goto common_load; break; diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c index bfb11e01133e..e2d401ad8fbb 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pci.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c @@ -93,7 +93,7 @@ struct mpc52xx_pci { }; /* MPC5200 device tree match tables */ -const struct of_device_id mpc52xx_pci_ids[] __initdata = { +const struct of_device_id mpc52xx_pci_ids[] __initconst = { { .type = "pci", .compatible = "fsl,mpc5200-pci", }, { .type = "pci", .compatible = "mpc5200-pci", }, {} diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c index 9fef5302adc1..67dac22b4363 100644 --- a/arch/powerpc/platforms/85xx/common.c +++ b/arch/powerpc/platforms/85xx/common.c @@ -21,6 +21,12 @@ static struct of_device_id __initdata mpc85xx_common_ids[] = { { .compatible = "fsl,qe", }, { .compatible = "fsl,cpm2", }, { .compatible = "fsl,srio", }, + /* So that the DMA channel nodes can be probed individually: */ + { .compatible = "fsl,eloplus-dma", }, + /* For the PMC driver */ + { .compatible = "fsl,mpc8548-guts", }, + /* Probably unnecessary? */ + { .compatible = "gpio-leds", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index 3754ddc00af7..d208ebccb91c 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -270,7 +270,7 @@ static void __init mpc85xx_mds_qe_init(void) if (machine_is(p1021_mds)) { - struct ccsr_guts_85xx __iomem *guts; + struct ccsr_guts __iomem *guts; np = of_find_node_by_name(NULL, "global-utilities"); if (np) { @@ -399,12 +399,6 @@ static int __init board_fixups(void) machine_arch_initcall(mpc8568_mds, board_fixups); machine_arch_initcall(mpc8569_mds, board_fixups); -static struct of_device_id mpc85xx_ids[] = { - { .compatible = "fsl,mpc8548-guts", }, - { .compatible = "gpio-leds", }, - {}, -}; - static int __init mpc85xx_publish_devices(void) { if (machine_is(mpc8568_mds)) @@ -412,10 +406,7 @@ static int __init mpc85xx_publish_devices(void) if (machine_is(mpc8569_mds)) simple_gpiochip_init("fsl,mpc8569mds-bcsr-gpio"); - mpc85xx_common_publish_devices(); - of_platform_bus_probe(NULL, mpc85xx_ids, NULL); - - return 0; + return mpc85xx_common_publish_devices(); } machine_device_initcall(mpc8568_mds, mpc85xx_publish_devices); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index 9848f9e39853..313fce4f5574 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -127,7 +127,7 @@ static void __init mpc85xx_rdb_setup_arch(void) #if defined(CONFIG_UCC_GETH) || defined(CONFIG_SERIAL_QE) if (machine_is(p1025_rdb)) { - struct ccsr_guts_85xx __iomem *guts; + struct ccsr_guts __iomem *guts; np = of_find_node_by_name(NULL, "global-utilities"); if (np) { diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index 0fe88e39945e..f700c81a1321 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -150,7 +150,7 @@ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port) { struct device_node *guts_node; struct device_node *indirect_node = NULL; - struct ccsr_guts_85xx __iomem *guts; + struct ccsr_guts __iomem *guts; u8 __iomem *lbc_lcs0_ba = NULL; u8 __iomem *lbc_lcs1_ba = NULL; u8 b; @@ -269,7 +269,7 @@ exit: void p1022ds_set_pixel_clock(unsigned int pixclock) { struct device_node *guts_np = NULL; - struct ccsr_guts_85xx __iomem *guts; + struct ccsr_guts __iomem *guts; unsigned long freq; u64 temp; u32 pxclk; @@ -460,18 +460,7 @@ static void __init p1022_ds_setup_arch(void) pr_info("Freescale P1022 DS reference board\n"); } -static struct of_device_id __initdata p1022_ds_ids[] = { - /* So that the DMA channel nodes can be probed individually: */ - { .compatible = "fsl,eloplus-dma", }, - {}, -}; - -static int __init p1022_ds_publish_devices(void) -{ - mpc85xx_common_publish_devices(); - return of_platform_bus_probe(NULL, p1022_ds_ids, NULL); -} -machine_device_initcall(p1022_ds, p1022_ds_publish_devices); +machine_device_initcall(p1022_ds, mpc85xx_common_publish_devices); machine_arch_initcall(p1022_ds, swiotlb_setup_bus_notifier); diff --git a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c index bbc615206c67..62cd3c555bfb 100644 --- a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c +++ b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c @@ -225,7 +225,7 @@ void mpc8610hpcd_set_monitor_port(enum fsl_diu_monitor_port port) void mpc8610hpcd_set_pixel_clock(unsigned int pixclock) { struct device_node *guts_np = NULL; - struct ccsr_guts_86xx __iomem *guts; + struct ccsr_guts __iomem *guts; unsigned long freq; u64 temp; u32 pxclk; diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c index db360fc4cf0e..85825b5401e5 100644 --- a/arch/powerpc/platforms/cell/axon_msi.c +++ b/arch/powerpc/platforms/cell/axon_msi.c @@ -114,7 +114,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc) pr_devel("axon_msi: woff %x roff %x msi %x\n", write_offset, msic->read_offset, msi); - if (msi < NR_IRQS && irq_get_chip_data(msi) == msic) { + if (msi < nr_irqs && irq_get_chip_data(msi) == msic) { generic_handle_irq(msi); msic->fifo_virt[idx] = cpu_to_le32(0xffffffff); } else { @@ -276,9 +276,6 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) if (rc) return rc; - /* We rely on being able to stash a virq in a u16 */ - BUILD_BUG_ON(NR_IRQS > 65536); - list_for_each_entry(entry, &dev->msi_list, list) { virq = irq_create_direct_mapping(msic->irq_domain); if (virq == NO_IRQ) { @@ -392,7 +389,8 @@ static int axon_msi_probe(struct platform_device *device) } memset(msic->fifo_virt, 0xff, MSIC_FIFO_SIZE_BYTES); - msic->irq_domain = irq_domain_add_nomap(dn, &msic_host_ops, msic); + /* We rely on being able to stash a virq in a u16, so limit irqs to < 65536 */ + msic->irq_domain = irq_domain_add_nomap(dn, 65536, &msic_host_ops, msic); if (!msic->irq_domain) { printk(KERN_ERR "axon_msi: couldn't allocate irq_domain for %s\n", dn->full_name); diff --git a/arch/powerpc/platforms/cell/beat_interrupt.c b/arch/powerpc/platforms/cell/beat_interrupt.c index e5c3a2c6090d..8c6dc42ecf65 100644 --- a/arch/powerpc/platforms/cell/beat_interrupt.c +++ b/arch/powerpc/platforms/cell/beat_interrupt.c @@ -239,7 +239,7 @@ void __init beatic_init_IRQ(void) ppc_md.get_irq = beatic_get_irq; /* Allocate an irq host */ - beatic_host = irq_domain_add_nomap(NULL, &beatic_pic_host_ops, NULL); + beatic_host = irq_domain_add_nomap(NULL, 0, &beatic_pic_host_ops, NULL); BUG_ON(beatic_host == NULL); irq_set_default_host(beatic_host); } @@ -248,6 +248,6 @@ void beatic_deinit_IRQ(void) { int i; - for (i = 1; i < NR_IRQS; i++) + for (i = 1; i < nr_irqs; i++) beat_destruct_irq_plug(i); } diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index ae9fc7bc17d6..b9f509a34c01 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -564,7 +564,8 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev) /* A coherent allocation implies strong ordering */ static void *dma_fixed_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { if (iommu_fixed_is_weak) return iommu_alloc_coherent(dev, cell_get_iommu_table(dev), @@ -572,18 +573,19 @@ static void *dma_fixed_alloc_coherent(struct device *dev, size_t size, device_to_mask(dev), flag, dev_to_node(dev)); else - return dma_direct_ops.alloc_coherent(dev, size, dma_handle, - flag); + return dma_direct_ops.alloc(dev, size, dma_handle, flag, + attrs); } static void dma_fixed_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { if (iommu_fixed_is_weak) iommu_free_coherent(cell_get_iommu_table(dev), size, vaddr, dma_handle); else - dma_direct_ops.free_coherent(dev, size, vaddr, dma_handle); + dma_direct_ops.free(dev, size, vaddr, dma_handle, attrs); } static dma_addr_t dma_fixed_map_page(struct device *dev, struct page *page, @@ -642,8 +644,8 @@ static int dma_fixed_dma_supported(struct device *dev, u64 mask) static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask); struct dma_map_ops dma_iommu_fixed_ops = { - .alloc_coherent = dma_fixed_alloc_coherent, - .free_coherent = dma_fixed_free_coherent, + .alloc = dma_fixed_alloc_coherent, + .free = dma_fixed_free_coherent, .map_sg = dma_fixed_map_sg, .unmap_sg = dma_fixed_unmap_sg, .dma_supported = dma_fixed_dma_supported, diff --git a/arch/powerpc/platforms/cell/qpace_setup.c b/arch/powerpc/platforms/cell/qpace_setup.c index 7f9b6742f8b6..6e3409d590ac 100644 --- a/arch/powerpc/platforms/cell/qpace_setup.c +++ b/arch/powerpc/platforms/cell/qpace_setup.c @@ -61,7 +61,7 @@ static void qpace_progress(char *s, unsigned short hex) printk("*** %04x : %s\n", hex, s ? s : ""); } -static const struct of_device_id qpace_bus_ids[] __initdata = { +static const struct of_device_id qpace_bus_ids[] __initconst = { { .type = "soc", }, { .compatible = "soc", }, { .type = "spider", }, diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index fa3e294fd343..4ab087671185 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -140,7 +140,7 @@ static int __devinit cell_setup_phb(struct pci_controller *phb) return 0; } -static const struct of_device_id cell_bus_ids[] __initdata = { +static const struct of_device_id cell_bus_ids[] __initconst = { { .type = "soc", }, { .compatible = "soc", }, { .type = "spider", }, diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 996c5ff7824b..03685a329d7d 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -366,11 +366,20 @@ static void kw_i2c_timeout(unsigned long data) unsigned long flags; spin_lock_irqsave(&host->lock, flags); + + /* + * If the timer is pending, that means we raced with the + * irq, in which case we just return + */ + if (timer_pending(&host->timeout_timer)) + goto skip; + kw_i2c_handle_interrupt(host, kw_read_reg(reg_isr)); if (host->state != state_idle) { host->timeout_timer.expires = jiffies + KW_POLL_TIMEOUT; add_timer(&host->timeout_timer); } + skip: spin_unlock_irqrestore(&host->lock, flags); } diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 66ad93de1d55..c4e630576ff2 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -57,9 +57,9 @@ static int max_real_irqs; static DEFINE_RAW_SPINLOCK(pmac_pic_lock); -#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -static unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; -static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; +/* The max irq number this driver deals with is 128; see max_irqs */ +static DECLARE_BITMAP(ppc_lost_interrupts, 128); +static DECLARE_BITMAP(ppc_cached_irq_mask, 128); static int pmac_irq_cascade = -1; static struct irq_domain *pmac_pic_host; diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index a81e5a88fbdf..b4ddaa3fbb29 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -192,7 +192,7 @@ static int psurge_secondary_ipi_init(void) { int rc = -ENOMEM; - psurge_host = irq_domain_add_nomap(NULL, &psurge_host_ops, NULL); + psurge_host = irq_domain_add_nomap(NULL, 0, &psurge_host_ops, NULL); if (psurge_host) psurge_secondary_virq = irq_create_direct_mapping(psurge_host); diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index 2a4ff86cc21f..5f3b23220b8e 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -753,9 +753,8 @@ void __init ps3_init_IRQ(void) unsigned cpu; struct irq_domain *host; - host = irq_domain_add_nomap(NULL, &ps3_host_ops, NULL); + host = irq_domain_add_nomap(NULL, PS3_PLUG_MAX + 1, &ps3_host_ops, NULL); irq_set_default_host(host); - irq_set_virq_count(PS3_PLUG_MAX + 1); for_each_possible_cpu(cpu) { struct ps3_private *pd = &per_cpu(ps3_private, cpu); diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c index 880eb9ce22c5..5606fe36faf2 100644 --- a/arch/powerpc/platforms/ps3/system-bus.c +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -515,7 +515,8 @@ core_initcall(ps3_system_bus_init); * to the dma address (mapping) of the first page. */ static void * ps3_alloc_coherent(struct device *_dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { int result; struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); @@ -552,7 +553,7 @@ clean_none: } static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, - dma_addr_t dma_handle) + dma_addr_t dma_handle, struct dma_attrs *attrs) { struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); @@ -701,8 +702,8 @@ static u64 ps3_dma_get_required_mask(struct device *_dev) } static struct dma_map_ops ps3_sb_dma_ops = { - .alloc_coherent = ps3_alloc_coherent, - .free_coherent = ps3_free_coherent, + .alloc = ps3_alloc_coherent, + .free = ps3_free_coherent, .map_sg = ps3_sb_map_sg, .unmap_sg = ps3_sb_unmap_sg, .dma_supported = ps3_dma_supported, @@ -712,8 +713,8 @@ static struct dma_map_ops ps3_sb_dma_ops = { }; static struct dma_map_ops ps3_ioc0_dma_ops = { - .alloc_coherent = ps3_alloc_coherent, - .free_coherent = ps3_free_coherent, + .alloc = ps3_alloc_coherent, + .free = ps3_free_coherent, .map_sg = ps3_ioc0_map_sg, .unmap_sg = ps3_ioc0_unmap_sg, .dma_supported = ps3_dma_supported, diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index aadbe4f6d537..178a5f300bc9 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -30,9 +30,9 @@ config PPC_SPLPAR two or more partitions. config EEH - bool "PCI Extended Error Handling (EEH)" if EXPERT + bool depends on PPC_PSERIES && PCI - default y if !EXPERT + default y config PSERIES_MSI bool diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 309d38ef7322..a75e37dc41aa 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -1076,7 +1076,7 @@ static void eeh_add_device_late(struct pci_dev *dev) pr_debug("EEH: Adding device %s\n", pci_name(dev)); dn = pci_device_to_OF_node(dev); - edev = pci_dev_to_eeh_dev(dev); + edev = of_node_to_eeh_dev(dn); if (edev->pdev == dev) { pr_debug("EEH: Already referenced !\n"); return; diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c index 4a4752565856..4cb375c0f8d1 100644 --- a/arch/powerpc/platforms/pseries/eeh_event.c +++ b/arch/powerpc/platforms/pseries/eeh_event.c @@ -59,8 +59,7 @@ static int eeh_event_handler(void * dummy) struct eeh_event *event; struct eeh_dev *edev; - daemonize("eehd"); - set_current_state(TASK_INTERRUPTIBLE); + set_task_comm(current, "eehd"); spin_lock_irqsave(&eeh_eventlist_lock, flags); event = NULL; @@ -83,6 +82,7 @@ static int eeh_event_handler(void * dummy) printk(KERN_INFO "EEH: Detected PCI bus error on device %s\n", eeh_pci_name(edev->pdev)); + set_current_state(TASK_INTERRUPTIBLE); /* Don't add to load average */ edev = handle_eeh_events(event); eeh_clear_slot(eeh_dev_to_of_node(edev), EEH_MODE_RECOVERING); diff --git a/arch/powerpc/sysdev/cpm2_pic.c b/arch/powerpc/sysdev/cpm2_pic.c index d3be961e2ae7..10386b676d87 100644 --- a/arch/powerpc/sysdev/cpm2_pic.c +++ b/arch/powerpc/sysdev/cpm2_pic.c @@ -51,8 +51,7 @@ static intctl_cpm2_t __iomem *cpm2_intctl; static struct irq_domain *cpm2_pic_host; -#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; +static unsigned long ppc_cached_irq_mask[2]; /* 2 32-bit registers */ static const u_char irq_to_siureg[] = { 1, 1, 1, 1, 1, 1, 1, 1, diff --git a/arch/powerpc/sysdev/mpc8xx_pic.c b/arch/powerpc/sysdev/mpc8xx_pic.c index d5f5416be310..b724622c3a0b 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.c +++ b/arch/powerpc/sysdev/mpc8xx_pic.c @@ -18,69 +18,45 @@ extern int cpm_get_irq(struct pt_regs *regs); static struct irq_domain *mpc8xx_pic_host; -#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; +static unsigned long mpc8xx_cached_irq_mask; static sysconf8xx_t __iomem *siu_reg; -int cpm_get_irq(struct pt_regs *regs); +static inline unsigned long mpc8xx_irqd_to_bit(struct irq_data *d) +{ + return 0x80000000 >> irqd_to_hwirq(d); +} static void mpc8xx_unmask_irq(struct irq_data *d) { - int bit, word; - unsigned int irq_nr = (unsigned int)irqd_to_hwirq(d); - - bit = irq_nr & 0x1f; - word = irq_nr >> 5; - - ppc_cached_irq_mask[word] |= (1 << (31-bit)); - out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]); + mpc8xx_cached_irq_mask |= mpc8xx_irqd_to_bit(d); + out_be32(&siu_reg->sc_simask, mpc8xx_cached_irq_mask); } static void mpc8xx_mask_irq(struct irq_data *d) { - int bit, word; - unsigned int irq_nr = (unsigned int)irqd_to_hwirq(d); - - bit = irq_nr & 0x1f; - word = irq_nr >> 5; - - ppc_cached_irq_mask[word] &= ~(1 << (31-bit)); - out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]); + mpc8xx_cached_irq_mask &= ~mpc8xx_irqd_to_bit(d); + out_be32(&siu_reg->sc_simask, mpc8xx_cached_irq_mask); } static void mpc8xx_ack(struct irq_data *d) { - int bit; - unsigned int irq_nr = (unsigned int)irqd_to_hwirq(d); - - bit = irq_nr & 0x1f; - out_be32(&siu_reg->sc_sipend, 1 << (31-bit)); + out_be32(&siu_reg->sc_sipend, mpc8xx_irqd_to_bit(d)); } static void mpc8xx_end_irq(struct irq_data *d) { - int bit, word; - unsigned int irq_nr = (unsigned int)irqd_to_hwirq(d); - - bit = irq_nr & 0x1f; - word = irq_nr >> 5; - - ppc_cached_irq_mask[word] |= (1 << (31-bit)); - out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]); + mpc8xx_cached_irq_mask |= mpc8xx_irqd_to_bit(d); + out_be32(&siu_reg->sc_simask, mpc8xx_cached_irq_mask); } static int mpc8xx_set_irq_type(struct irq_data *d, unsigned int flow_type) { - if (flow_type & IRQ_TYPE_EDGE_FALLING) { - irq_hw_number_t hw = (unsigned int)irqd_to_hwirq(d); + /* only external IRQ senses are programmable */ + if ((flow_type & IRQ_TYPE_EDGE_FALLING) && !(irqd_to_hwirq(d) & 1)) { unsigned int siel = in_be32(&siu_reg->sc_siel); - - /* only external IRQ senses are programmable */ - if ((hw & 1) == 0) { - siel |= (0x80000000 >> hw); - out_be32(&siu_reg->sc_siel, siel); - __irq_set_handler_locked(d->irq, handle_edge_irq); - } + siel |= mpc8xx_irqd_to_bit(d); + out_be32(&siu_reg->sc_siel, siel); + __irq_set_handler_locked(d->irq, handle_edge_irq); } return 0; } @@ -132,6 +108,9 @@ static int mpc8xx_pic_host_xlate(struct irq_domain *h, struct device_node *ct, IRQ_TYPE_EDGE_FALLING, }; + if (intspec[0] > 0x1f) + return 0; + *out_hwirq = intspec[0]; if (intsize > 1 && intspec[1] < 4) *out_flags = map_pic_senses[intspec[1]]; diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 9ac71ebd2c40..395af1347749 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -604,18 +604,14 @@ static struct mpic *mpic_find(unsigned int irq) } /* Determine if the linux irq is an IPI */ -static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int irq) +static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int src) { - unsigned int src = virq_to_hw(irq); - return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]); } /* Determine if the linux irq is a timer */ -static unsigned int mpic_is_tm(struct mpic *mpic, unsigned int irq) +static unsigned int mpic_is_tm(struct mpic *mpic, unsigned int src) { - unsigned int src = virq_to_hw(irq); - return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]); } @@ -876,21 +872,45 @@ int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type) if (src >= mpic->num_sources) return -EINVAL; + vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); + + /* We don't support "none" type */ if (flow_type == IRQ_TYPE_NONE) - if (mpic->senses && src < mpic->senses_count) - flow_type = mpic->senses[src]; - if (flow_type == IRQ_TYPE_NONE) - flow_type = IRQ_TYPE_LEVEL_LOW; + flow_type = IRQ_TYPE_DEFAULT; + + /* Default: read HW settings */ + if (flow_type == IRQ_TYPE_DEFAULT) { + switch(vold & (MPIC_INFO(VECPRI_POLARITY_MASK) | + MPIC_INFO(VECPRI_SENSE_MASK))) { + case MPIC_INFO(VECPRI_SENSE_EDGE) | + MPIC_INFO(VECPRI_POLARITY_POSITIVE): + flow_type = IRQ_TYPE_EDGE_RISING; + break; + case MPIC_INFO(VECPRI_SENSE_EDGE) | + MPIC_INFO(VECPRI_POLARITY_NEGATIVE): + flow_type = IRQ_TYPE_EDGE_FALLING; + break; + case MPIC_INFO(VECPRI_SENSE_LEVEL) | + MPIC_INFO(VECPRI_POLARITY_POSITIVE): + flow_type = IRQ_TYPE_LEVEL_HIGH; + break; + case MPIC_INFO(VECPRI_SENSE_LEVEL) | + MPIC_INFO(VECPRI_POLARITY_NEGATIVE): + flow_type = IRQ_TYPE_LEVEL_LOW; + break; + } + } + /* Apply to irq desc */ irqd_set_trigger_type(d, flow_type); + /* Apply to HW */ if (mpic_is_ht_interrupt(mpic, src)) vecpri = MPIC_VECPRI_POLARITY_POSITIVE | MPIC_VECPRI_SENSE_EDGE; else vecpri = mpic_type_to_vecpri(mpic, flow_type); - vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) | MPIC_INFO(VECPRI_SENSE_MASK)); vnew |= vecpri; @@ -1026,7 +1046,7 @@ static int mpic_host_map(struct irq_domain *h, unsigned int virq, irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq); /* Set default irq type */ - irq_set_irq_type(virq, IRQ_TYPE_NONE); + irq_set_irq_type(virq, IRQ_TYPE_DEFAULT); /* If the MPIC was reset, then all vectors have already been * initialized. Otherwise, a per source lazy initialization @@ -1417,12 +1437,6 @@ void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, mpic->num_sources = isu_first + mpic->isu_size; } -void __init mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count) -{ - mpic->senses = senses; - mpic->senses_count = count; -} - void __init mpic_init(struct mpic *mpic) { int i, cpu; @@ -1555,12 +1569,12 @@ void mpic_irq_set_priority(unsigned int irq, unsigned int pri) return; raw_spin_lock_irqsave(&mpic_lock, flags); - if (mpic_is_ipi(mpic, irq)) { + if (mpic_is_ipi(mpic, src)) { reg = mpic_ipi_read(src - mpic->ipi_vecs[0]) & ~MPIC_VECPRI_PRIORITY_MASK; mpic_ipi_write(src - mpic->ipi_vecs[0], reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); - } else if (mpic_is_tm(mpic, irq)) { + } else if (mpic_is_tm(mpic, src)) { reg = mpic_tm_read(src - mpic->timer_vecs[0]) & ~MPIC_VECPRI_PRIORITY_MASK; mpic_tm_write(src - mpic->timer_vecs[0], diff --git a/arch/powerpc/sysdev/mpic_msgr.c b/arch/powerpc/sysdev/mpic_msgr.c index 6e7fa386e76a..483d8fa72e8b 100644 --- a/arch/powerpc/sysdev/mpic_msgr.c +++ b/arch/powerpc/sysdev/mpic_msgr.c @@ -27,6 +27,7 @@ static struct mpic_msgr **mpic_msgrs; static unsigned int mpic_msgr_count; +static DEFINE_RAW_SPINLOCK(msgrs_lock); static inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value) { @@ -56,12 +57,11 @@ struct mpic_msgr *mpic_msgr_get(unsigned int reg_num) if (reg_num >= mpic_msgr_count) return ERR_PTR(-ENODEV); - raw_spin_lock_irqsave(&msgr->lock, flags); - if (mpic_msgrs[reg_num]->in_use == MSGR_FREE) { - msgr = mpic_msgrs[reg_num]; + raw_spin_lock_irqsave(&msgrs_lock, flags); + msgr = mpic_msgrs[reg_num]; + if (msgr->in_use == MSGR_FREE) msgr->in_use = MSGR_INUSE; - } - raw_spin_unlock_irqrestore(&msgr->lock, flags); + raw_spin_unlock_irqrestore(&msgrs_lock, flags); return msgr; } @@ -228,7 +228,7 @@ static __devinit int mpic_msgr_probe(struct platform_device *dev) reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i; msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE; - msgr->mer = msgr->base + MPIC_MSGR_MER_OFFSET; + msgr->mer = (u32 *)((u8 *)msgr->base + MPIC_MSGR_MER_OFFSET); msgr->in_use = MSGR_FREE; msgr->num = i; raw_spin_lock_init(&msgr->lock); diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c index ceb09cbd2329..818e763f8265 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/arch/powerpc/sysdev/qe_lib/qe.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. + * Copyright (C) 2006-2010 Freescale Semicondutor, Inc. All rights reserved. * * Authors: Shlomi Gridish <gridish@freescale.com> * Li Yang <leoli@freescale.com> @@ -266,7 +266,19 @@ EXPORT_SYMBOL(qe_clock_source); static void qe_snums_init(void) { int i; - static const u8 snum_init[] = { + static const u8 snum_init_76[] = { + 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, + 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, + 0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9, + 0xD8, 0xD9, 0xE8, 0xE9, 0x44, 0x45, 0x4C, 0x4D, + 0x54, 0x55, 0x5C, 0x5D, 0x64, 0x65, 0x6C, 0x6D, + 0x74, 0x75, 0x7C, 0x7D, 0x84, 0x85, 0x8C, 0x8D, + 0x94, 0x95, 0x9C, 0x9D, 0xA4, 0xA5, 0xAC, 0xAD, + 0xB4, 0xB5, 0xBC, 0xBD, 0xC4, 0xC5, 0xCC, 0xCD, + 0xD4, 0xD5, 0xDC, 0xDD, 0xE4, 0xE5, 0xEC, 0xED, + 0xF4, 0xF5, 0xFC, 0xFD, + }; + static const u8 snum_init_46[] = { 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, 0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9, @@ -274,9 +286,15 @@ static void qe_snums_init(void) 0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59, 0x68, 0x69, 0x78, 0x79, 0x80, 0x81, }; + static const u8 *snum_init; qe_num_of_snum = qe_get_num_of_snums(); + if (qe_num_of_snum == 76) + snum_init = snum_init_76; + else + snum_init = snum_init_46; + for (i = 0; i < qe_num_of_snum; i++) { snums[i].num = snum_init[i]; snums[i].state = QE_SNUM_STATE_FREE; diff --git a/arch/powerpc/sysdev/scom.c b/arch/powerpc/sysdev/scom.c index 49a3ece1c6b3..702256a1ca11 100644 --- a/arch/powerpc/sysdev/scom.c +++ b/arch/powerpc/sysdev/scom.c @@ -22,6 +22,7 @@ #include <linux/debugfs.h> #include <linux/slab.h> #include <linux/export.h> +#include <asm/debug.h> #include <asm/prom.h> #include <asm/scom.h> diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c index ea5e204e3450..cd1d18db92c6 100644 --- a/arch/powerpc/sysdev/xics/xics-common.c +++ b/arch/powerpc/sysdev/xics/xics-common.c @@ -188,6 +188,7 @@ void xics_migrate_irqs_away(void) { int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id(); unsigned int irq, virq; + struct irq_desc *desc; /* If we used to be the default server, move to the new "boot_cpuid" */ if (hw_cpu == xics_default_server) @@ -202,8 +203,7 @@ void xics_migrate_irqs_away(void) /* Allow IPIs again... */ icp_ops->set_priority(DEFAULT_PRIORITY); - for_each_irq(virq) { - struct irq_desc *desc; + for_each_irq_desc(virq, desc) { struct irq_chip *chip; long server; unsigned long flags; @@ -212,9 +212,8 @@ void xics_migrate_irqs_away(void) /* We can't set affinity on ISA interrupts */ if (virq < NUM_ISA_INTERRUPTS) continue; - desc = irq_to_desc(virq); /* We only need to migrate enabled IRQS */ - if (!desc || !desc->action) + if (!desc->action) continue; if (desc->irq_data.domain != xics_host) continue; diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 2b7c0fbe578e..9015060919a0 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -90,7 +90,6 @@ config S390 select HAVE_KERNEL_XZ select HAVE_ARCH_MUTEX_CPU_RELAX select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 - select HAVE_RCU_TABLE_FREE if SMP select ARCH_SAVE_PAGE_KEYS if HIBERNATION select HAVE_MEMBLOCK select HAVE_MEMBLOCK_NODE_MAP diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 6cf8e26b3137..1957a9dd256d 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -1,8 +1,12 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y +CONFIG_FHANDLE=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_AUDIT=y -CONFIG_RCU_TRACE=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y @@ -14,16 +18,22 @@ CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y +CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +CONFIG_EXPERT=y # CONFIG_COMPAT_BRK is not set -CONFIG_SLAB=y CONFIG_PROFILING=y CONFIG_OPROFILE=y CONFIG_KPROBES=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_IBM_PARTITION=y CONFIG_DEFAULT_DEADLINE=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y @@ -34,18 +44,15 @@ CONFIG_KSM=y CONFIG_BINFMT_MISC=m CONFIG_CMM=m CONFIG_HZ_100=y -CONFIG_KEXEC=y -CONFIG_PM=y +CONFIG_CRASH_DUMP=y CONFIG_HIBERNATION=y CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_NET_KEY=y -CONFIG_AFIUCV=m CONFIG_INET=y CONFIG_IP_MULTICAST=y # CONFIG_INET_LRO is not set CONFIG_IPV6=y -CONFIG_NET_SCTPPROBE=m CONFIG_L2TP=m CONFIG_L2TP_DEBUGFS=m CONFIG_VLAN_8021Q=y @@ -84,15 +91,14 @@ CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_LOGGING=y CONFIG_SCSI_SCAN_ASYNC=y CONFIG_ZFCP=y -CONFIG_ZFCP_DIF=y CONFIG_NETDEVICES=y -CONFIG_DUMMY=m CONFIG_BONDING=m +CONFIG_DUMMY=m CONFIG_EQUALIZER=m CONFIG_TUN=m -CONFIG_NET_ETHERNET=y CONFIG_VIRTIO_NET=y CONFIG_RAW_DRIVER=m +CONFIG_VIRTIO_BALLOON=y CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set @@ -103,27 +109,21 @@ CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y # CONFIG_NETWORK_FILESYSTEMS is not set -CONFIG_PARTITION_ADVANCED=y -CONFIG_IBM_PARTITION=y -CONFIG_DLM=m CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_KERNEL=y CONFIG_TIMER_STATS=y CONFIG_PROVE_LOCKING=y CONFIG_PROVE_RCU=y CONFIG_LOCK_STAT=y CONFIG_DEBUG_LOCKDEP=y -CONFIG_DEBUG_SPINLOCK_SLEEP=y CONFIG_DEBUG_LIST=y CONFIG_DEBUG_NOTIFIERS=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_RCU_TRACE=y CONFIG_KPROBES_SANITY_TEST=y CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y CONFIG_CPU_NOTIFIER_ERROR_INJECT=m CONFIG_LATENCYTOP=y -CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_DEBUG_PAGEALLOC=y -# CONFIG_FTRACE is not set +CONFIG_BLK_DEV_IO_TRACE=y # CONFIG_STRICT_DEVMEM is not set CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m @@ -173,4 +173,3 @@ CONFIG_CRYPTO_SHA512_S390=m CONFIG_CRYPTO_DES_S390=m CONFIG_CRYPTO_AES_S390=m CONFIG_CRC7=m -CONFIG_VIRTIO_BALLOON=y diff --git a/arch/s390/include/asm/facility.h b/arch/s390/include/asm/facility.h index 1e5b27edc0c9..2ee66a65f2d4 100644 --- a/arch/s390/include/asm/facility.h +++ b/arch/s390/include/asm/facility.h @@ -38,12 +38,11 @@ static inline void stfle(u64 *stfle_fac_list, int size) unsigned long nr; preempt_disable(); - S390_lowcore.stfl_fac_list = 0; asm volatile( " .insn s,0xb2b10000,0(0)\n" /* stfl */ "0:\n" EX_TABLE(0b, 0b) - : "=m" (S390_lowcore.stfl_fac_list)); + : "+m" (S390_lowcore.stfl_fac_list)); nr = 4; /* bytes stored by stfl */ memcpy(stfle_fac_list, &S390_lowcore.stfl_fac_list, 4); if (S390_lowcore.stfl_fac_list & 0x01000000) { diff --git a/arch/s390/include/asm/pgalloc.h b/arch/s390/include/asm/pgalloc.h index 8eef9b5b3cf4..78e3041919de 100644 --- a/arch/s390/include/asm/pgalloc.h +++ b/arch/s390/include/asm/pgalloc.h @@ -22,10 +22,7 @@ void crst_table_free(struct mm_struct *, unsigned long *); unsigned long *page_table_alloc(struct mm_struct *, unsigned long); void page_table_free(struct mm_struct *, unsigned long *); -#ifdef CONFIG_HAVE_RCU_TABLE_FREE void page_table_free_rcu(struct mmu_gather *, unsigned long *); -void __tlb_remove_table(void *_table); -#endif static inline void clear_table(unsigned long *s, unsigned long val, size_t n) { diff --git a/arch/s390/include/asm/swab.h b/arch/s390/include/asm/swab.h index 6bdee21c077e..a3e4ebb32090 100644 --- a/arch/s390/include/asm/swab.h +++ b/arch/s390/include/asm/swab.h @@ -77,7 +77,7 @@ static inline __u16 __arch_swab16p(const __u16 *x) asm volatile( #ifndef __s390x__ - " icm %0,2,%O+1(%R1)\n" + " icm %0,2,%O1+1(%R1)\n" " ic %0,%1\n" : "=&d" (result) : "Q" (*x) : "cc"); #else /* __s390x__ */ diff --git a/arch/s390/include/asm/tlb.h b/arch/s390/include/asm/tlb.h index c687a2c83462..775a5eea8f9e 100644 --- a/arch/s390/include/asm/tlb.h +++ b/arch/s390/include/asm/tlb.h @@ -30,14 +30,10 @@ struct mmu_gather { struct mm_struct *mm; -#ifdef CONFIG_HAVE_RCU_TABLE_FREE struct mmu_table_batch *batch; -#endif unsigned int fullmm; - unsigned int need_flush; }; -#ifdef CONFIG_HAVE_RCU_TABLE_FREE struct mmu_table_batch { struct rcu_head rcu; unsigned int nr; @@ -49,7 +45,6 @@ struct mmu_table_batch { extern void tlb_table_flush(struct mmu_gather *tlb); extern void tlb_remove_table(struct mmu_gather *tlb, void *table); -#endif static inline void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, @@ -57,29 +52,20 @@ static inline void tlb_gather_mmu(struct mmu_gather *tlb, { tlb->mm = mm; tlb->fullmm = full_mm_flush; - tlb->need_flush = 0; -#ifdef CONFIG_HAVE_RCU_TABLE_FREE tlb->batch = NULL; -#endif if (tlb->fullmm) __tlb_flush_mm(mm); } static inline void tlb_flush_mmu(struct mmu_gather *tlb) { - if (!tlb->need_flush) - return; - tlb->need_flush = 0; - __tlb_flush_mm(tlb->mm); -#ifdef CONFIG_HAVE_RCU_TABLE_FREE tlb_table_flush(tlb); -#endif } static inline void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) { - tlb_flush_mmu(tlb); + tlb_table_flush(tlb); } /* @@ -105,10 +91,8 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, unsigned long address) { -#ifdef CONFIG_HAVE_RCU_TABLE_FREE if (!tlb->fullmm) return page_table_free_rcu(tlb, (unsigned long *) pte); -#endif page_table_free(tlb->mm, (unsigned long *) pte); } @@ -125,10 +109,8 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd, #ifdef __s390x__ if (tlb->mm->context.asce_limit <= (1UL << 31)) return; -#ifdef CONFIG_HAVE_RCU_TABLE_FREE if (!tlb->fullmm) return tlb_remove_table(tlb, pmd); -#endif crst_table_free(tlb->mm, (unsigned long *) pmd); #endif } @@ -146,10 +128,8 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud, #ifdef __s390x__ if (tlb->mm->context.asce_limit <= (1UL << 42)) return; -#ifdef CONFIG_HAVE_RCU_TABLE_FREE if (!tlb->fullmm) return tlb_remove_table(tlb, pud); -#endif crst_table_free(tlb->mm, (unsigned long *) pud); #endif } diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index c27a0727f930..adccd908ebc7 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S @@ -474,9 +474,9 @@ ENTRY(startup_kdump) stck __LC_LAST_UPDATE_CLOCK spt 5f-.LPG0(%r13) mvc __LC_LAST_UPDATE_TIMER(8),5f-.LPG0(%r13) + xc __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST #ifndef CONFIG_MARCH_G5 # check capabilities against MARCH_{G5,Z900,Z990,Z9_109,Z10} - xc __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST .insn s,0xb2b10000,__LC_STFL_FAC_LIST # store facility list tm __LC_STFL_FAC_LIST,0x01 # stfle available ? jz 0f diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 1c2cdd59ccd0..8a22c27219dd 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -118,9 +118,10 @@ asmlinkage void do_softirq(void) "a" (__do_softirq) : "0", "1", "2", "3", "4", "5", "14", "cc", "memory" ); - } else + } else { /* We are already on the async stack. */ __do_softirq(); + } } local_irq_restore(flags); @@ -192,11 +193,12 @@ int unregister_external_interrupt(u16 code, ext_int_handler_t handler) int index = ext_hash(code); spin_lock_irqsave(&ext_int_hash_lock, flags); - list_for_each_entry_rcu(p, &ext_int_hash[index], entry) + list_for_each_entry_rcu(p, &ext_int_hash[index], entry) { if (p->code == code && p->handler == handler) { list_del_rcu(&p->entry); kfree_rcu(p, rcu); } + } spin_unlock_irqrestore(&ext_int_hash_lock, flags); return 0; } @@ -211,9 +213,10 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, old_regs = set_irq_regs(regs); irq_enter(); - if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) + if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) { /* Serve timer interrupts first. */ clock_comparator_work(); + } kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++; if (ext_code.code != 0x1004) __get_cpu_var(s390_idle).nohz_delay = 1; diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 46405086479c..cb019f429e88 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -178,7 +178,7 @@ static void cpumf_pmu_enable(struct pmu *pmu) err = lcctl(cpuhw->state); if (err) { pr_err("Enabling the performance measuring unit " - "failed with rc=%lx\n", err); + "failed with rc=%x\n", err); return; } @@ -203,7 +203,7 @@ static void cpumf_pmu_disable(struct pmu *pmu) err = lcctl(inactive); if (err) { pr_err("Disabling the performance measuring unit " - "failed with rc=%lx\n", err); + "failed with rc=%x\n", err); return; } diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index 7bb15fcca75e..e1335dc2b1b7 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c @@ -61,21 +61,14 @@ long probe_kernel_write(void *dst, const void *src, size_t size) return copied < 0 ? -EFAULT : 0; } -/* - * Copy memory in real mode (kernel to kernel) - */ -int memcpy_real(void *dest, void *src, size_t count) +static int __memcpy_real(void *dest, void *src, size_t count) { register unsigned long _dest asm("2") = (unsigned long) dest; register unsigned long _len1 asm("3") = (unsigned long) count; register unsigned long _src asm("4") = (unsigned long) src; register unsigned long _len2 asm("5") = (unsigned long) count; - unsigned long flags; int rc = -EFAULT; - if (!count) - return 0; - flags = __arch_local_irq_stnsm(0xf8UL); asm volatile ( "0: mvcle %1,%2,0x0\n" "1: jo 0b\n" @@ -86,7 +79,23 @@ int memcpy_real(void *dest, void *src, size_t count) "+d" (_len2), "=m" (*((long *) dest)) : "m" (*((long *) src)) : "cc", "memory"); - arch_local_irq_restore(flags); + return rc; +} + +/* + * Copy memory in real mode (kernel to kernel) + */ +int memcpy_real(void *dest, void *src, size_t count) +{ + unsigned long flags; + int rc; + + if (!count) + return 0; + local_irq_save(flags); + __arch_local_irq_stnsm(0xfbUL); + rc = __memcpy_real(dest, src, count); + local_irq_restore(flags); return rc; } diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 373adf69b01c..6e765bf00670 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -678,8 +678,6 @@ void page_table_free(struct mm_struct *mm, unsigned long *table) } } -#ifdef CONFIG_HAVE_RCU_TABLE_FREE - static void __page_table_free_rcu(void *table, unsigned bit) { struct page *page; @@ -733,7 +731,66 @@ void __tlb_remove_table(void *_table) free_pages((unsigned long) table, ALLOC_ORDER); } -#endif +static void tlb_remove_table_smp_sync(void *arg) +{ + /* Simply deliver the interrupt */ +} + +static void tlb_remove_table_one(void *table) +{ + /* + * This isn't an RCU grace period and hence the page-tables cannot be + * assumed to be actually RCU-freed. + * + * It is however sufficient for software page-table walkers that rely + * on IRQ disabling. See the comment near struct mmu_table_batch. + */ + smp_call_function(tlb_remove_table_smp_sync, NULL, 1); + __tlb_remove_table(table); +} + +static void tlb_remove_table_rcu(struct rcu_head *head) +{ + struct mmu_table_batch *batch; + int i; + + batch = container_of(head, struct mmu_table_batch, rcu); + + for (i = 0; i < batch->nr; i++) + __tlb_remove_table(batch->tables[i]); + + free_page((unsigned long)batch); +} + +void tlb_table_flush(struct mmu_gather *tlb) +{ + struct mmu_table_batch **batch = &tlb->batch; + + if (*batch) { + __tlb_flush_mm(tlb->mm); + call_rcu_sched(&(*batch)->rcu, tlb_remove_table_rcu); + *batch = NULL; + } +} + +void tlb_remove_table(struct mmu_gather *tlb, void *table) +{ + struct mmu_table_batch **batch = &tlb->batch; + + if (*batch == NULL) { + *batch = (struct mmu_table_batch *) + __get_free_page(GFP_NOWAIT | __GFP_NOWARN); + if (*batch == NULL) { + __tlb_flush_mm(tlb->mm); + tlb_remove_table_one(table); + return; + } + (*batch)->nr = 0; + } + (*batch)->tables[(*batch)->nr++] = table; + if ((*batch)->nr == MAX_TABLE_BATCH) + tlb_table_flush(tlb); +} /* * switch on pgstes for its userspace process (for kvm) diff --git a/arch/sh/Kconfig.debug b/arch/sh/Kconfig.debug index c1d5a820b1aa..5f2bb4242c0f 100644 --- a/arch/sh/Kconfig.debug +++ b/arch/sh/Kconfig.debug @@ -61,6 +61,7 @@ config DUMP_CODE config DWARF_UNWINDER bool "Enable the DWARF unwinder for stacktraces" select FRAME_POINTER + depends on SUPERH32 default n help Enabling this option will make stacktraces more accurate, at diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig index d893411022d5..c0241bd584f4 100644 --- a/arch/sh/boards/Kconfig +++ b/arch/sh/boards/Kconfig @@ -54,6 +54,7 @@ config SH_7724_SOLUTION_ENGINE select SOLUTION_ENGINE depends on CPU_SUBTYPE_SH7724 select ARCH_REQUIRE_GPIOLIB + select SND_SOC_AK4642 if SND_SIMPLE_CARD help Select 7724 SolutionEngine if configuring for a Hitachi SH7724 evaluation board. @@ -223,6 +224,7 @@ config SH_ECOVEC bool "EcoVec" depends on CPU_SUBTYPE_SH7724 select ARCH_REQUIRE_GPIOLIB + select SND_SOC_DA7210 if SND_SIMPLE_CARD help Renesas "R0P7724LC0011/21RL (EcoVec)" support. diff --git a/arch/sh/boards/board-sh7785lcr.c b/arch/sh/boards/board-sh7785lcr.c index d879848f3cdd..d0d6221d7c2e 100644 --- a/arch/sh/boards/board-sh7785lcr.c +++ b/arch/sh/boards/board-sh7785lcr.c @@ -28,6 +28,7 @@ #include <cpu/sh7785.h> #include <asm/heartbeat.h> #include <asm/clock.h> +#include <asm/bl_bit.h> /* * NOTE: This board has 2 physical memory maps. diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index d12fe9ddf3da..299a40a44c92 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -32,6 +32,7 @@ #include <linux/videodev2.h> #include <video/sh_mobile_lcdc.h> #include <sound/sh_fsi.h> +#include <sound/simple_card.h> #include <media/sh_mobile_ceu.h> #include <media/soc_camera.h> #include <media/tw9910.h> @@ -809,6 +810,30 @@ static struct platform_device fsi_device = { }, }; +static struct asoc_simple_dai_init_info fsi_da7210_init_info = { + .fmt = SND_SOC_DAIFMT_I2S, + .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, + .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct asoc_simple_card_info fsi_da7210_info = { + .name = "DA7210", + .card = "FSIB-DA7210", + .cpu_dai = "fsib-dai", + .codec = "da7210.0-001a", + .platform = "sh_fsi.0", + .codec_dai = "da7210-hifi", + .init = &fsi_da7210_init_info, +}; + +static struct platform_device fsi_da7210_device = { + .name = "asoc-simple-card", + .dev = { + .platform_data = &fsi_da7210_info, + }, +}; + + /* IrDA */ static struct resource irda_resources[] = { [0] = { @@ -945,6 +970,7 @@ static struct platform_device *ecovec_devices[] __initdata = { &camera_devices[1], &camera_devices[2], &fsi_device, + &fsi_da7210_device, &irda_device, &vou_device, #if defined(CONFIG_MMC_SH_MMCIF) || defined(CONFIG_MMC_SH_MMCIF_MODULE) diff --git a/arch/sh/boards/mach-hp6xx/pm.c b/arch/sh/boards/mach-hp6xx/pm.c index adc9b4bba828..8b50cf763c06 100644 --- a/arch/sh/boards/mach-hp6xx/pm.c +++ b/arch/sh/boards/mach-hp6xx/pm.c @@ -14,6 +14,7 @@ #include <linux/gfp.h> #include <asm/io.h> #include <asm/hd64461.h> +#include <asm/bl_bit.h> #include <mach/hp6xx.h> #include <cpu/dac.h> #include <asm/freq.h> diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index c540b16547c3..5cc5ed44d9af 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -28,6 +28,7 @@ #include <video/sh_mobile_lcdc.h> #include <media/sh_mobile_ceu.h> #include <sound/sh_fsi.h> +#include <sound/simple_card.h> #include <asm/io.h> #include <asm/heartbeat.h> #include <asm/clock.h> @@ -304,17 +305,25 @@ static struct platform_device fsi_device = { }, }; -static struct fsi_ak4642_info fsi_ak4642_info = { +static struct asoc_simple_dai_init_info fsi2_ak4642_init_info = { + .fmt = SND_SOC_DAIFMT_LEFT_J, + .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, + .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, + .sysclk = 11289600, +}; + +static struct asoc_simple_card_info fsi_ak4642_info = { .name = "AK4642", .card = "FSIA-AK4642", .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi.0", - .id = FSI_PORT_A, + .codec_dai = "ak4642-hifi", + .init = &fsi2_ak4642_init_info, }; static struct platform_device fsi_ak4642_device = { - .name = "fsi-ak4642-audio", + .name = "asoc-simple-card", .dev = { .platform_data = &fsi_ak4642_info, }, diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c index b1cb2715ad6e..67ee95603813 100644 --- a/arch/sh/drivers/dma/dma-sysfs.c +++ b/arch/sh/drivers/dma/dma-sysfs.c @@ -54,7 +54,7 @@ static int __init dma_subsys_init(void) if (unlikely(ret)) return ret; - return device_create_file(dma_subsys.dev_root, &dev_attr_devices.attr); + return device_create_file(dma_subsys.dev_root, &dev_attr_devices); } postcore_initcall(dma_subsys_init); diff --git a/arch/sh/include/asm/atomic.h b/arch/sh/include/asm/atomic.h index 37f2f4a55231..f4c1c20bcdf6 100644 --- a/arch/sh/include/asm/atomic.h +++ b/arch/sh/include/asm/atomic.h @@ -11,7 +11,7 @@ #include <linux/types.h> #include <asm/cmpxchg.h> -#define ATOMIC_INIT(i) ( (atomic_t) { (i) } ) +#define ATOMIC_INIT(i) { (i) } #define atomic_read(v) (*(volatile int *)&(v)->counter) #define atomic_set(v,i) ((v)->counter = (i)) diff --git a/arch/sh/include/asm/dma-mapping.h b/arch/sh/include/asm/dma-mapping.h index 1a73c3e759a7..8bd965e00a15 100644 --- a/arch/sh/include/asm/dma-mapping.h +++ b/arch/sh/include/asm/dma-mapping.h @@ -52,25 +52,31 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) return dma_addr == 0; } -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); void *memory; if (dma_alloc_from_coherent(dev, size, dma_handle, &memory)) return memory; - if (!ops->alloc_coherent) + if (!ops->alloc) return NULL; - memory = ops->alloc_coherent(dev, size, dma_handle, gfp); + memory = ops->alloc(dev, size, dma_handle, gfp, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, memory); return memory; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); @@ -78,14 +84,16 @@ static inline void dma_free_coherent(struct device *dev, size_t size, return; debug_dma_free_coherent(dev, size, vaddr, dma_handle); - if (ops->free_coherent) - ops->free_coherent(dev, size, vaddr, dma_handle); + if (ops->free) + ops->free(dev, size, vaddr, dma_handle, attrs); } /* arch/sh/mm/consistent.c */ extern void *dma_generic_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_addr, gfp_t flag); + dma_addr_t *dma_addr, gfp_t flag, + struct dma_attrs *attrs); extern void dma_generic_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle); + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs); #endif /* __ASM_SH_DMA_MAPPING_H */ diff --git a/arch/sh/kernel/cpu/fpu.c b/arch/sh/kernel/cpu/fpu.c index 7f1b70cace35..f8f7af51c128 100644 --- a/arch/sh/kernel/cpu/fpu.c +++ b/arch/sh/kernel/cpu/fpu.c @@ -2,6 +2,7 @@ #include <linux/slab.h> #include <asm/processor.h> #include <asm/fpu.h> +#include <asm/traps.h> int init_fpu(struct task_struct *tsk) { diff --git a/arch/sh/kernel/cpu/sh2a/fpu.c b/arch/sh/kernel/cpu/sh2a/fpu.c index 488d24e0cdf0..98bbaa447c93 100644 --- a/arch/sh/kernel/cpu/sh2a/fpu.c +++ b/arch/sh/kernel/cpu/sh2a/fpu.c @@ -14,6 +14,7 @@ #include <asm/processor.h> #include <asm/io.h> #include <asm/fpu.h> +#include <asm/traps.h> /* The PR (precision) bit in the FP Status Register must be clear when * an frchg instruction is executed, otherwise the instruction is undefined. diff --git a/arch/sh/kernel/cpu/sh4/fpu.c b/arch/sh/kernel/cpu/sh4/fpu.c index e74cd6c0f10d..69ab4d3c8d41 100644 --- a/arch/sh/kernel/cpu/sh4/fpu.c +++ b/arch/sh/kernel/cpu/sh4/fpu.c @@ -16,6 +16,7 @@ #include <cpu/fpu.h> #include <asm/processor.h> #include <asm/fpu.h> +#include <asm/traps.h> /* The PR (precision) bit in the FP Status Register must be clear when * an frchg instruction is executed, otherwise the instruction is undefined. diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c index 5853989586ed..04ab5aeaf920 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c @@ -113,7 +113,7 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), /* MSTP32 clocks */ - CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP004]), + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP004]), CLKDEV_CON_ID("riic0", &mstp_clks[MSTP000]), CLKDEV_CON_ID("riic1", &mstp_clks[MSTP000]), CLKDEV_CON_ID("riic2", &mstp_clks[MSTP000]), diff --git a/arch/sh/kernel/cpu/shmobile/pm.c b/arch/sh/kernel/cpu/shmobile/pm.c index a6f95ae4aae7..08d27fac8d08 100644 --- a/arch/sh/kernel/cpu/shmobile/pm.c +++ b/arch/sh/kernel/cpu/shmobile/pm.c @@ -16,6 +16,7 @@ #include <asm/suspend.h> #include <asm/uaccess.h> #include <asm/cacheflush.h> +#include <asm/bl_bit.h> /* * Notifier lists for pre/post sleep notification diff --git a/arch/sh/kernel/dma-nommu.c b/arch/sh/kernel/dma-nommu.c index 3c55b87f8b63..5b0bfcda6d0b 100644 --- a/arch/sh/kernel/dma-nommu.c +++ b/arch/sh/kernel/dma-nommu.c @@ -63,8 +63,8 @@ static void nommu_sync_sg(struct device *dev, struct scatterlist *sg, #endif struct dma_map_ops nommu_dma_ops = { - .alloc_coherent = dma_generic_alloc_coherent, - .free_coherent = dma_generic_free_coherent, + .alloc = dma_generic_alloc_coherent, + .free = dma_generic_free_coherent, .map_page = nommu_map_page, .map_sg = nommu_map_sg, #ifdef CONFIG_DMA_NONCOHERENT diff --git a/arch/sh/kernel/idle.c b/arch/sh/kernel/idle.c index 64852ecc6881..ee226e20c20c 100644 --- a/arch/sh/kernel/idle.c +++ b/arch/sh/kernel/idle.c @@ -17,8 +17,8 @@ #include <linux/irqflags.h> #include <linux/smp.h> #include <linux/cpuidle.h> -#include <asm/pgalloc.h> #include <linux/atomic.h> +#include <asm/pgalloc.h> #include <asm/smp.h> #include <asm/bl_bit.h> diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c index efb6d398dec3..b117781bfea2 100644 --- a/arch/sh/kernel/kgdb.c +++ b/arch/sh/kernel/kgdb.c @@ -14,6 +14,7 @@ #include <linux/irq.h> #include <linux/io.h> #include <asm/cacheflush.h> +#include <asm/traps.h> /* Macros for single step instruction identification */ #define OPCODE_BT(op) (((op) & 0xff00) == 0x8900) diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index f72e3a951588..94273aaf78c1 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -26,6 +26,7 @@ #include <asm/mmu_context.h> #include <asm/fpu.h> #include <asm/syscalls.h> +#include <asm/switch_to.h> void show_regs(struct pt_regs * regs) { diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index a17a14d32340..eaebdf6a5c77 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -27,6 +27,7 @@ #include <asm/smp.h> #include <asm/cacheflush.h> #include <asm/sections.h> +#include <asm/setup.h> int __cpu_number_map[NR_CPUS]; /* Map physical to logical */ int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */ diff --git a/arch/sh/kernel/vsyscall/vsyscall-sigreturn.S b/arch/sh/kernel/vsyscall/vsyscall-sigreturn.S index 555a64f124ca..23af17584054 100644 --- a/arch/sh/kernel/vsyscall/vsyscall-sigreturn.S +++ b/arch/sh/kernel/vsyscall/vsyscall-sigreturn.S @@ -34,6 +34,41 @@ __kernel_rt_sigreturn: 1: .short __NR_rt_sigreturn .LEND_rt_sigreturn: .size __kernel_rt_sigreturn,.-.LSTART_rt_sigreturn + .previous .section .eh_frame,"a",@progbits +.LCIE1: + .ualong .LCIE1_end - .LCIE1_start +.LCIE1_start: + .ualong 0 /* CIE ID */ + .byte 0x1 /* Version number */ + .string "zRS" /* NUL-terminated augmentation string */ + .uleb128 0x1 /* Code alignment factor */ + .sleb128 -4 /* Data alignment factor */ + .byte 0x11 /* Return address register column */ + .uleb128 0x1 /* Augmentation length and data */ + .byte 0x1b /* DW_EH_PE_pcrel | DW_EH_PE_sdata4. */ + .byte 0xc, 0xf, 0x0 /* DW_CFA_def_cfa: r15 ofs 0 */ + + .align 2 +.LCIE1_end: + + .ualong .LFDE0_end-.LFDE0_start /* Length FDE0 */ +.LFDE0_start: + .ualong .LFDE0_start-.LCIE1 /* CIE pointer */ + .ualong .LSTART_sigreturn-. /* PC-relative start address */ + .ualong .LEND_sigreturn-.LSTART_sigreturn + .uleb128 0 /* Augmentation */ + .align 2 +.LFDE0_end: + + .ualong .LFDE1_end-.LFDE1_start /* Length FDE1 */ +.LFDE1_start: + .ualong .LFDE1_start-.LCIE1 /* CIE pointer */ + .ualong .LSTART_rt_sigreturn-. /* PC-relative start address */ + .ualong .LEND_rt_sigreturn-.LSTART_rt_sigreturn + .uleb128 0 /* Augmentation */ + .align 2 +.LFDE1_end: + .previous diff --git a/arch/sh/kernel/vsyscall/vsyscall-trapa.S b/arch/sh/kernel/vsyscall/vsyscall-trapa.S index 3e70f851cdc6..0eb74d00690a 100644 --- a/arch/sh/kernel/vsyscall/vsyscall-trapa.S +++ b/arch/sh/kernel/vsyscall/vsyscall-trapa.S @@ -3,37 +3,34 @@ .type __kernel_vsyscall,@function __kernel_vsyscall: .LSTART_vsyscall: - /* XXX: We'll have to do something here once we opt to use the vDSO - * page for something other than the signal trampoline.. as well as - * fill out .eh_frame -- PFM. */ + trapa #0x10 + nop .LEND_vsyscall: .size __kernel_vsyscall,.-.LSTART_vsyscall + .previous .section .eh_frame,"a",@progbits - .previous .LCIE: .ualong .LCIE_end - .LCIE_start .LCIE_start: .ualong 0 /* CIE ID */ .byte 0x1 /* Version number */ - .string "zRS" /* NUL-terminated augmentation string */ + .string "zR" /* NUL-terminated augmentation string */ .uleb128 0x1 /* Code alignment factor */ .sleb128 -4 /* Data alignment factor */ .byte 0x11 /* Return address register column */ - /* Augmentation length and data (none) */ - .byte 0xc /* DW_CFA_def_cfa */ - .uleb128 0xf /* r15 */ - .uleb128 0x0 /* offset 0 */ - + .uleb128 0x1 /* Augmentation length and data */ + .byte 0x1b /* DW_EH_PE_pcrel | DW_EH_PE_sdata4. */ + .byte 0xc,0xf,0x0 /* DW_CFA_def_cfa: r15 ofs 0 */ .align 2 .LCIE_end: .ualong .LFDE_end-.LFDE_start /* Length FDE */ .LFDE_start: - .ualong .LCIE /* CIE pointer */ - .ualong .LSTART_vsyscall-. /* start address */ + .ualong .LFDE_start-.LCIE /* CIE pointer */ + .ualong .LSTART_vsyscall-. /* PC-relative start address */ .ualong .LEND_vsyscall-.LSTART_vsyscall - .uleb128 0 + .uleb128 0 /* Augmentation */ .align 2 .LFDE_end: .previous diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c index 112fea12522a..0e529285b28d 100644 --- a/arch/sh/mm/cache-sh4.c +++ b/arch/sh/mm/cache-sh4.c @@ -18,6 +18,7 @@ #include <linux/highmem.h> #include <asm/pgtable.h> #include <asm/mmu_context.h> +#include <asm/cache_insns.h> #include <asm/cacheflush.h> /* diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c index f251b5f27652..b81d9dbf9fef 100644 --- a/arch/sh/mm/consistent.c +++ b/arch/sh/mm/consistent.c @@ -33,7 +33,8 @@ static int __init dma_init(void) fs_initcall(dma_init); void *dma_generic_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs) { void *ret, *ret_nocache; int order = get_order(size); @@ -64,7 +65,8 @@ void *dma_generic_alloc_coherent(struct device *dev, size_t size, } void dma_generic_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { int order = get_order(size); unsigned long pfn = dma_handle >> PAGE_SHIFT; diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index 324eef93c900..e99b104d967a 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c @@ -86,7 +86,7 @@ static noinline int vmalloc_fault(unsigned long address) pte_t *pte_k; /* Make sure we are in vmalloc/module/P3 area: */ - if (!(address >= VMALLOC_START && address < P3_ADDR_MAX)) + if (!(address >= P3SEG && address < P3_ADDR_MAX)) return -1; /* diff --git a/arch/sh/mm/flush-sh4.c b/arch/sh/mm/flush-sh4.c index 75a17f5bfa14..0b85dd9dd3a7 100644 --- a/arch/sh/mm/flush-sh4.c +++ b/arch/sh/mm/flush-sh4.c @@ -1,5 +1,6 @@ #include <linux/mm.h> #include <asm/mmu_context.h> +#include <asm/cache_insns.h> #include <asm/cacheflush.h> #include <asm/traps.h> diff --git a/arch/sh/mm/sram.c b/arch/sh/mm/sram.c index bc156ec4545e..2d8fa718d55e 100644 --- a/arch/sh/mm/sram.c +++ b/arch/sh/mm/sram.c @@ -9,6 +9,7 @@ */ #include <linux/init.h> #include <linux/kernel.h> +#include <linux/errno.h> #include <asm/sram.h> /* diff --git a/arch/sparc/include/asm/dma-mapping.h b/arch/sparc/include/asm/dma-mapping.h index 8c0e4f7bb204..48a7c65731d2 100644 --- a/arch/sparc/include/asm/dma-mapping.h +++ b/arch/sparc/include/asm/dma-mapping.h @@ -26,24 +26,30 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev) #include <asm-generic/dma-mapping-common.h> -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); void *cpu_addr; - cpu_addr = ops->alloc_coherent(dev, size, dma_handle, flag); + cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr); return cpu_addr; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); - ops->free_coherent(dev, size, cpu_addr, dma_handle); + ops->free(dev, size, cpu_addr, dma_handle, attrs); } static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h index 6fa2f7980e6b..76e4a52aa85e 100644 --- a/arch/sparc/include/asm/pgtable_64.h +++ b/arch/sparc/include/asm/pgtable_64.h @@ -12,8 +12,6 @@ * the SpitFire page tables. */ -#include <asm-generic/pgtable-nopud.h> - #include <linux/compiler.h> #include <linux/const.h> #include <asm/types.h> @@ -22,6 +20,8 @@ #include <asm/page.h> #include <asm/processor.h> +#include <asm-generic/pgtable-nopud.h> + /* The kernel image occupies 0x4000000 to 0x6000000 (4MB --> 96MB). * The page copy blockops can use 0x6000000 to 0x8000000. * The TSB is mapped in the 0x8000000 to 0xa000000 range. diff --git a/arch/sparc/kernel/central.c b/arch/sparc/kernel/central.c index 38d48a59879c..9708851a8b9f 100644 --- a/arch/sparc/kernel/central.c +++ b/arch/sparc/kernel/central.c @@ -269,4 +269,4 @@ static int __init sunfire_init(void) return 0; } -subsys_initcall(sunfire_init); +fs_initcall(sunfire_init); diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c index fea13c7b1aee..b93c2c9ccb1d 100644 --- a/arch/sparc/kernel/ds.c +++ b/arch/sparc/kernel/ds.c @@ -1264,4 +1264,4 @@ static int __init ds_init(void) return vio_register_driver(&ds_driver); } -subsys_initcall(ds_init); +fs_initcall(ds_init); diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c index 4643d68713fa..070ed141aac7 100644 --- a/arch/sparc/kernel/iommu.c +++ b/arch/sparc/kernel/iommu.c @@ -280,7 +280,8 @@ static inline void iommu_free_ctx(struct iommu *iommu, int ctx) } static void *dma_4u_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_addrp, gfp_t gfp) + dma_addr_t *dma_addrp, gfp_t gfp, + struct dma_attrs *attrs) { unsigned long flags, order, first_page; struct iommu *iommu; @@ -330,7 +331,8 @@ static void *dma_4u_alloc_coherent(struct device *dev, size_t size, } static void dma_4u_free_coherent(struct device *dev, size_t size, - void *cpu, dma_addr_t dvma) + void *cpu, dma_addr_t dvma, + struct dma_attrs *attrs) { struct iommu *iommu; unsigned long flags, order, npages; @@ -825,8 +827,8 @@ static void dma_4u_sync_sg_for_cpu(struct device *dev, } static struct dma_map_ops sun4u_dma_ops = { - .alloc_coherent = dma_4u_alloc_coherent, - .free_coherent = dma_4u_free_coherent, + .alloc = dma_4u_alloc_coherent, + .free = dma_4u_free_coherent, .map_page = dma_4u_map_page, .unmap_page = dma_4u_unmap_page, .map_sg = dma_4u_map_sg, diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index d0479e2163fa..21bd73943f7f 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -261,7 +261,8 @@ EXPORT_SYMBOL(sbus_set_sbus64); * CPU may access them without any explicit flushing. */ static void *sbus_alloc_coherent(struct device *dev, size_t len, - dma_addr_t *dma_addrp, gfp_t gfp) + dma_addr_t *dma_addrp, gfp_t gfp, + struct dma_attrs *attrs) { struct platform_device *op = to_platform_device(dev); unsigned long len_total = PAGE_ALIGN(len); @@ -315,7 +316,7 @@ err_nopages: } static void sbus_free_coherent(struct device *dev, size_t n, void *p, - dma_addr_t ba) + dma_addr_t ba, struct dma_attrs *attrs) { struct resource *res; struct page *pgv; @@ -407,8 +408,8 @@ static void sbus_sync_sg_for_device(struct device *dev, struct scatterlist *sg, } struct dma_map_ops sbus_dma_ops = { - .alloc_coherent = sbus_alloc_coherent, - .free_coherent = sbus_free_coherent, + .alloc = sbus_alloc_coherent, + .free = sbus_free_coherent, .map_page = sbus_map_page, .unmap_page = sbus_unmap_page, .map_sg = sbus_map_sg, @@ -436,7 +437,8 @@ arch_initcall(sparc_register_ioport); * hwdev should be valid struct pci_dev pointer for PCI devices. */ static void *pci32_alloc_coherent(struct device *dev, size_t len, - dma_addr_t *pba, gfp_t gfp) + dma_addr_t *pba, gfp_t gfp, + struct dma_attrs *attrs) { unsigned long len_total = PAGE_ALIGN(len); void *va; @@ -489,7 +491,7 @@ err_nopages: * past this call are illegal. */ static void pci32_free_coherent(struct device *dev, size_t n, void *p, - dma_addr_t ba) + dma_addr_t ba, struct dma_attrs *attrs) { struct resource *res; @@ -645,8 +647,8 @@ static void pci32_sync_sg_for_device(struct device *device, struct scatterlist * } struct dma_map_ops pci32_dma_ops = { - .alloc_coherent = pci32_alloc_coherent, - .free_coherent = pci32_free_coherent, + .alloc = pci32_alloc_coherent, + .free = pci32_free_coherent, .map_page = pci32_map_page, .unmap_page = pci32_unmap_page, .map_sg = pci32_map_sg, diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c index a19c8a063683..35e43673c453 100644 --- a/arch/sparc/kernel/leon_kernel.c +++ b/arch/sparc/kernel/leon_kernel.c @@ -104,11 +104,11 @@ static int irq_choose_cpu(const struct cpumask *affinity) { cpumask_t mask; - cpus_and(mask, cpu_online_map, *affinity); - if (cpus_equal(mask, cpu_online_map) || cpus_empty(mask)) + cpumask_and(&mask, cpu_online_mask, affinity); + if (cpumask_equal(&mask, cpu_online_mask) || cpumask_empty(&mask)) return boot_cpu_id; else - return first_cpu(mask); + return cpumask_first(&mask); } #else #define irq_choose_cpu(affinity) boot_cpu_id diff --git a/arch/sparc/kernel/leon_pci.c b/arch/sparc/kernel/leon_pci.c index aba6b958b2a5..19f56058742b 100644 --- a/arch/sparc/kernel/leon_pci.c +++ b/arch/sparc/kernel/leon_pci.c @@ -45,7 +45,6 @@ void leon_pci_init(struct platform_device *ofdev, struct leon_pci_info *info) void __devinit pcibios_fixup_bus(struct pci_bus *pbus) { - struct leon_pci_info *info = pbus->sysdata; struct pci_dev *dev; int i, has_io, has_mem; u16 cmd; @@ -111,18 +110,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pci_enable_resources(dev, mask); } -struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) -{ - /* - * Currently the OpenBoot nodes are not connected with the PCI device, - * this is because the LEON PROM does not create PCI nodes. Eventually - * this will change and the same approach as pcic.c can be used to - * match PROM nodes with pci devices. - */ - return NULL; -} -EXPORT_SYMBOL(pci_device_to_OF_node); - void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) { #ifdef CONFIG_PCI_DEBUG diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index 1210fde18740..160cac9c4036 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c @@ -23,6 +23,7 @@ #include <linux/pm.h> #include <linux/delay.h> #include <linux/gfp.h> +#include <linux/cpu.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> @@ -78,6 +79,8 @@ void __cpuinit leon_callin(void) local_flush_tlb_all(); leon_configure_cache_smp(); + notify_cpu_starting(cpuid); + /* Get our local ticker going. */ smp_setup_percpu_timer(); diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c index af5755d20fbe..7661e84a05a0 100644 --- a/arch/sparc/kernel/pci_sun4v.c +++ b/arch/sparc/kernel/pci_sun4v.c @@ -128,7 +128,8 @@ static inline long iommu_batch_end(void) } static void *dma_4v_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_addrp, gfp_t gfp) + dma_addr_t *dma_addrp, gfp_t gfp, + struct dma_attrs *attrs) { unsigned long flags, order, first_page, npages, n; struct iommu *iommu; @@ -198,7 +199,7 @@ range_alloc_fail: } static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu, - dma_addr_t dvma) + dma_addr_t dvma, struct dma_attrs *attrs) { struct pci_pbm_info *pbm; struct iommu *iommu; @@ -527,8 +528,8 @@ static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist, } static struct dma_map_ops sun4v_dma_ops = { - .alloc_coherent = dma_4v_alloc_coherent, - .free_coherent = dma_4v_free_coherent, + .alloc = dma_4v_alloc_coherent, + .free = dma_4v_free_coherent, .map_page = dma_4v_map_page, .unmap_page = dma_4v_unmap_page, .map_sg = dma_4v_map_sg, diff --git a/arch/sparc/kernel/rtrap_64.S b/arch/sparc/kernel/rtrap_64.S index 77f1b95e0806..9171fc238def 100644 --- a/arch/sparc/kernel/rtrap_64.S +++ b/arch/sparc/kernel/rtrap_64.S @@ -20,11 +20,6 @@ .text .align 32 -__handle_softirq: - call do_softirq - nop - ba,a,pt %xcc, __handle_softirq_continue - nop __handle_preemption: call schedule wrpr %g0, RTRAP_PSTATE, %pstate @@ -89,9 +84,7 @@ rtrap: cmp %l1, 0 /* mm/ultra.S:xcall_report_regs KNOWS about this load. */ - bne,pn %icc, __handle_softirq ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 -__handle_softirq_continue: rtrap_xcall: sethi %hi(0xf << 20), %l4 and %l1, %l4, %l4 diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index 232df9949530..3ee51f189a55 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -566,15 +566,10 @@ out: SYSCALL_DEFINE2(64_munmap, unsigned long, addr, size_t, len) { - long ret; - if (invalid_64bit_range(addr, len)) return -EINVAL; - down_write(¤t->mm->mmap_sem); - ret = do_munmap(current->mm, addr, len); - up_write(¤t->mm->mmap_sem); - return ret; + return vm_munmap(addr, len); } extern unsigned long do_mremap(unsigned long addr, diff --git a/arch/sparc/mm/fault_32.c b/arch/sparc/mm/fault_32.c index 7705c6731e28..df3155a17991 100644 --- a/arch/sparc/mm/fault_32.c +++ b/arch/sparc/mm/fault_32.c @@ -225,6 +225,8 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, unsigned long g2; int from_user = !(regs->psr & PSR_PS); int fault, code; + unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | + (write ? FAULT_FLAG_WRITE : 0)); if(text_fault) address = regs->pc; @@ -251,6 +253,7 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); +retry: down_read(&mm->mmap_sem); /* @@ -289,7 +292,11 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -297,13 +304,29 @@ good_area: goto do_sigbus; BUG(); } - if (fault & VM_FAULT_MAJOR) { - current->maj_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, address); - } else { - current->min_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address); + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) { + current->maj_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, + 1, regs, address); + } else { + current->min_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, + 1, regs, address); + } + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + + goto retry; + } } + up_read(&mm->mmap_sem); return; diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c index 504c0622f729..1fe0429b6314 100644 --- a/arch/sparc/mm/fault_64.c +++ b/arch/sparc/mm/fault_64.c @@ -279,6 +279,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) unsigned int insn = 0; int si_code, fault_code, fault; unsigned long address, mm_rss; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; fault_code = get_thread_fault_code(); @@ -333,6 +334,8 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) insn = get_fault_insn(regs, insn); goto handle_kernel_fault; } + +retry: down_read(&mm->mmap_sem); } @@ -423,7 +426,12 @@ good_area: goto bad_area; } - fault = handle_mm_fault(mm, vma, address, (fault_code & FAULT_CODE_WRITE) ? FAULT_FLAG_WRITE : 0); + flags |= ((fault_code & FAULT_CODE_WRITE) ? FAULT_FLAG_WRITE : 0); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -431,12 +439,27 @@ good_area: goto do_sigbus; BUG(); } - if (fault & VM_FAULT_MAJOR) { - current->maj_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, address); - } else { - current->min_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address); + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) { + current->maj_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, + 1, regs, address); + } else { + current->min_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, + 1, regs, address); + } + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + + goto retry; + } } up_read(&mm->mmap_sem); diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S index b57a5942ba64..874162a11ceb 100644 --- a/arch/sparc/mm/ultra.S +++ b/arch/sparc/mm/ultra.S @@ -495,11 +495,11 @@ xcall_fetch_glob_regs: stx %o7, [%g1 + GR_SNAP_O7] stx %i7, [%g1 + GR_SNAP_I7] /* Don't try this at home kids... */ - rdpr %cwp, %g2 - sub %g2, 1, %g7 + rdpr %cwp, %g3 + sub %g3, 1, %g7 wrpr %g7, %cwp mov %i7, %g7 - wrpr %g2, %cwp + wrpr %g3, %cwp stx %g7, [%g1 + GR_SNAP_RPC] sethi %hi(trap_block), %g7 or %g7, %lo(trap_block), %g7 diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index 11270ca22c0a..96033e2d6845 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -12,7 +12,7 @@ config TILE select GENERIC_PENDING_IRQ if SMP select GENERIC_IRQ_SHOW select SYS_HYPERVISOR - select ARCH_HAVE_NMI_SAFE_CMPXCHG if !M386 + select ARCH_HAVE_NMI_SAFE_CMPXCHG # FIXME: investigate whether we need/want these options. # select HAVE_IOREMAP_PROT @@ -69,6 +69,9 @@ config ARCH_PHYS_ADDR_T_64BIT config ARCH_DMA_ADDR_T_64BIT def_bool y +config NEED_DMA_MAP_STATE + def_bool y + config LOCKDEP_SUPPORT def_bool y @@ -118,7 +121,7 @@ config 64BIT config ARCH_DEFCONFIG string - default "arch/tile/configs/tile_defconfig" if !TILEGX + default "arch/tile/configs/tilepro_defconfig" if !TILEGX default "arch/tile/configs/tilegx_defconfig" if TILEGX source "init/Kconfig" @@ -240,6 +243,7 @@ endchoice config PAGE_OFFSET hex + depends on !64BIT default 0xF0000000 if VMSPLIT_3_75G default 0xE0000000 if VMSPLIT_3_5G default 0xB0000000 if VMSPLIT_2_75G diff --git a/arch/tile/Makefile b/arch/tile/Makefile index 17acce70569b..9520bc5a4b7f 100644 --- a/arch/tile/Makefile +++ b/arch/tile/Makefile @@ -30,7 +30,8 @@ ifneq ($(CONFIG_DEBUG_EXTRA_FLAGS),"") KBUILD_CFLAGS += $(CONFIG_DEBUG_EXTRA_FLAGS) endif -LIBGCC_PATH := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name) +LIBGCC_PATH := \ + $(shell $(CC) $(KBUILD_CFLAGS) $(KCFLAGS) -print-libgcc-file-name) # Provide the path to use for "make defconfig". KBUILD_DEFCONFIG := $(ARCH)_defconfig @@ -53,8 +54,6 @@ libs-y += $(LIBGCC_PATH) # See arch/tile/Kbuild for content of core part of the kernel core-y += arch/tile/ -core-$(CONFIG_KVM) += arch/tile/kvm/ - ifdef TILERA_ROOT INSTALL_PATH ?= $(TILERA_ROOT)/tile/boot endif diff --git a/arch/tile/include/arch/spr_def.h b/arch/tile/include/arch/spr_def.h index f548efeb2de3..d6ba449b5363 100644 --- a/arch/tile/include/arch/spr_def.h +++ b/arch/tile/include/arch/spr_def.h @@ -60,8 +60,8 @@ _concat4(SPR_IPI_EVENT_, CONFIG_KERNEL_PL,,) #define SPR_IPI_EVENT_RESET_K \ _concat4(SPR_IPI_EVENT_RESET_, CONFIG_KERNEL_PL,,) -#define SPR_IPI_MASK_SET_K \ - _concat4(SPR_IPI_MASK_SET_, CONFIG_KERNEL_PL,,) +#define SPR_IPI_EVENT_SET_K \ + _concat4(SPR_IPI_EVENT_SET_, CONFIG_KERNEL_PL,,) #define INT_IPI_K \ _concat4(INT_IPI_, CONFIG_KERNEL_PL,,) diff --git a/arch/tile/include/asm/atomic.h b/arch/tile/include/asm/atomic.h index bb696da5d7cd..f2461429a4a4 100644 --- a/arch/tile/include/asm/atomic.h +++ b/arch/tile/include/asm/atomic.h @@ -17,6 +17,8 @@ #ifndef _ASM_TILE_ATOMIC_H #define _ASM_TILE_ATOMIC_H +#include <asm/cmpxchg.h> + #ifndef __ASSEMBLY__ #include <linux/compiler.h> @@ -121,54 +123,6 @@ static inline int atomic_read(const atomic_t *v) */ #define atomic_add_negative(i, v) (atomic_add_return((i), (v)) < 0) -/* Nonexistent functions intended to cause link errors. */ -extern unsigned long __xchg_called_with_bad_pointer(void); -extern unsigned long __cmpxchg_called_with_bad_pointer(void); - -#define xchg(ptr, x) \ - ({ \ - typeof(*(ptr)) __x; \ - switch (sizeof(*(ptr))) { \ - case 4: \ - __x = (typeof(__x))(typeof(__x-__x))atomic_xchg( \ - (atomic_t *)(ptr), \ - (u32)(typeof((x)-(x)))(x)); \ - break; \ - case 8: \ - __x = (typeof(__x))(typeof(__x-__x))atomic64_xchg( \ - (atomic64_t *)(ptr), \ - (u64)(typeof((x)-(x)))(x)); \ - break; \ - default: \ - __xchg_called_with_bad_pointer(); \ - } \ - __x; \ - }) - -#define cmpxchg(ptr, o, n) \ - ({ \ - typeof(*(ptr)) __x; \ - switch (sizeof(*(ptr))) { \ - case 4: \ - __x = (typeof(__x))(typeof(__x-__x))atomic_cmpxchg( \ - (atomic_t *)(ptr), \ - (u32)(typeof((o)-(o)))(o), \ - (u32)(typeof((n)-(n)))(n)); \ - break; \ - case 8: \ - __x = (typeof(__x))(typeof(__x-__x))atomic64_cmpxchg( \ - (atomic64_t *)(ptr), \ - (u64)(typeof((o)-(o)))(o), \ - (u64)(typeof((n)-(n)))(n)); \ - break; \ - default: \ - __cmpxchg_called_with_bad_pointer(); \ - } \ - __x; \ - }) - -#define tas(ptr) (xchg((ptr), 1)) - #endif /* __ASSEMBLY__ */ #ifndef __tilegx__ diff --git a/arch/tile/include/asm/atomic_32.h b/arch/tile/include/asm/atomic_32.h index 466dc4a39a4f..54d1da826f93 100644 --- a/arch/tile/include/asm/atomic_32.h +++ b/arch/tile/include/asm/atomic_32.h @@ -200,7 +200,7 @@ static inline u64 atomic64_add_return(u64 i, atomic64_t *v) * @u: ...unless v is equal to u. * * Atomically adds @a to @v, so long as @v was not already @u. - * Returns the old value of @v. + * Returns non-zero if @v was not @u, and zero otherwise. */ static inline u64 atomic64_add_unless(atomic64_t *v, u64 a, u64 u) { diff --git a/arch/tile/include/asm/bitops_64.h b/arch/tile/include/asm/bitops_64.h index 58d021a9834f..60b87ee54fb8 100644 --- a/arch/tile/include/asm/bitops_64.h +++ b/arch/tile/include/asm/bitops_64.h @@ -38,10 +38,10 @@ static inline void clear_bit(unsigned nr, volatile unsigned long *addr) static inline void change_bit(unsigned nr, volatile unsigned long *addr) { - unsigned long old, mask = (1UL << (nr % BITS_PER_LONG)); - long guess, oldval; + unsigned long mask = (1UL << (nr % BITS_PER_LONG)); + unsigned long guess, oldval; addr += nr / BITS_PER_LONG; - old = *addr; + oldval = *addr; do { guess = oldval; oldval = atomic64_cmpxchg((atomic64_t *)addr, @@ -85,7 +85,7 @@ static inline int test_and_change_bit(unsigned nr, volatile unsigned long *addr) { unsigned long mask = (1UL << (nr % BITS_PER_LONG)); - long guess, oldval = *addr; + unsigned long guess, oldval; addr += nr / BITS_PER_LONG; oldval = *addr; do { diff --git a/arch/tile/include/asm/cmpxchg.h b/arch/tile/include/asm/cmpxchg.h new file mode 100644 index 000000000000..276f067e3640 --- /dev/null +++ b/arch/tile/include/asm/cmpxchg.h @@ -0,0 +1,73 @@ +/* + * cmpxchg.h -- forked from asm/atomic.h with this copyright: + * + * Copyright 2010 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + */ + +#ifndef _ASM_TILE_CMPXCHG_H +#define _ASM_TILE_CMPXCHG_H + +#ifndef __ASSEMBLY__ + +/* Nonexistent functions intended to cause link errors. */ +extern unsigned long __xchg_called_with_bad_pointer(void); +extern unsigned long __cmpxchg_called_with_bad_pointer(void); + +#define xchg(ptr, x) \ + ({ \ + typeof(*(ptr)) __x; \ + switch (sizeof(*(ptr))) { \ + case 4: \ + __x = (typeof(__x))(typeof(__x-__x))atomic_xchg( \ + (atomic_t *)(ptr), \ + (u32)(typeof((x)-(x)))(x)); \ + break; \ + case 8: \ + __x = (typeof(__x))(typeof(__x-__x))atomic64_xchg( \ + (atomic64_t *)(ptr), \ + (u64)(typeof((x)-(x)))(x)); \ + break; \ + default: \ + __xchg_called_with_bad_pointer(); \ + } \ + __x; \ + }) + +#define cmpxchg(ptr, o, n) \ + ({ \ + typeof(*(ptr)) __x; \ + switch (sizeof(*(ptr))) { \ + case 4: \ + __x = (typeof(__x))(typeof(__x-__x))atomic_cmpxchg( \ + (atomic_t *)(ptr), \ + (u32)(typeof((o)-(o)))(o), \ + (u32)(typeof((n)-(n)))(n)); \ + break; \ + case 8: \ + __x = (typeof(__x))(typeof(__x-__x))atomic64_cmpxchg( \ + (atomic64_t *)(ptr), \ + (u64)(typeof((o)-(o)))(o), \ + (u64)(typeof((n)-(n)))(n)); \ + break; \ + default: \ + __cmpxchg_called_with_bad_pointer(); \ + } \ + __x; \ + }) + +#define tas(ptr) (xchg((ptr), 1)) + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_TILE_CMPXCHG_H */ diff --git a/arch/tile/include/asm/irq.h b/arch/tile/include/asm/irq.h index f80f8ceabc67..33cff9a3058b 100644 --- a/arch/tile/include/asm/irq.h +++ b/arch/tile/include/asm/irq.h @@ -21,7 +21,7 @@ #define NR_IRQS 32 /* IRQ numbers used for linux IPIs. */ -#define IRQ_RESCHEDULE 1 +#define IRQ_RESCHEDULE 0 #define irq_canonicalize(irq) (irq) diff --git a/arch/tile/include/asm/pci.h b/arch/tile/include/asm/pci.h index 5d5a635530bd..32e6cbe8dff3 100644 --- a/arch/tile/include/asm/pci.h +++ b/arch/tile/include/asm/pci.h @@ -47,8 +47,8 @@ struct pci_controller { */ #define PCI_DMA_BUS_IS_PHYS 1 -int __devinit tile_pci_init(void); -int __devinit pcibios_init(void); +int __init tile_pci_init(void); +int __init pcibios_init(void); static inline void pci_iounmap(struct pci_dev *dev, void __iomem *addr) {} diff --git a/arch/tile/include/asm/spinlock_64.h b/arch/tile/include/asm/spinlock_64.h index 72be5904e020..5f8b6a095fd8 100644 --- a/arch/tile/include/asm/spinlock_64.h +++ b/arch/tile/include/asm/spinlock_64.h @@ -137,7 +137,7 @@ static inline void arch_read_unlock(arch_rwlock_t *rw) static inline void arch_write_unlock(arch_rwlock_t *rw) { __insn_mf(); - rw->lock = 0; + __insn_exch4(&rw->lock, 0); /* Avoid waiting in the write buffer. */ } static inline int arch_read_trylock(arch_rwlock_t *rw) diff --git a/arch/tile/include/asm/stack.h b/arch/tile/include/asm/stack.h index 4d97a2db932e..0e9d382a2d45 100644 --- a/arch/tile/include/asm/stack.h +++ b/arch/tile/include/asm/stack.h @@ -25,7 +25,6 @@ struct KBacktraceIterator { BacktraceIterator it; struct task_struct *task; /* task we are backtracing */ - pte_t *pgtable; /* page table for user space access */ int end; /* iteration complete. */ int new_context; /* new context is starting */ int profile; /* profiling, so stop on async intrpt */ diff --git a/arch/tile/include/asm/traps.h b/arch/tile/include/asm/traps.h index 5f20f920f932..e28c3df4176a 100644 --- a/arch/tile/include/asm/traps.h +++ b/arch/tile/include/asm/traps.h @@ -64,7 +64,11 @@ void do_breakpoint(struct pt_regs *, int fault_num); #ifdef __tilegx__ +/* kernel/single_step.c */ void gx_singlestep_handle(struct pt_regs *, int fault_num); + +/* kernel/intvec_64.S */ +void fill_ra_stack(void); #endif -#endif /* _ASM_TILE_SYSCALLS_H */ +#endif /* _ASM_TILE_TRAPS_H */ diff --git a/arch/tile/kernel/entry.S b/arch/tile/kernel/entry.S index 431e9ae60488..ec91568df880 100644 --- a/arch/tile/kernel/entry.S +++ b/arch/tile/kernel/entry.S @@ -85,6 +85,7 @@ STD_ENTRY(cpu_idle_on_new_stack) /* Loop forever on a nap during SMP boot. */ STD_ENTRY(smp_nap) nap + nop /* avoid provoking the icache prefetch with a jump */ j smp_nap /* we are not architecturally guaranteed not to exit nap */ jrp lr /* clue in the backtracer */ STD_ENDPROC(smp_nap) @@ -105,5 +106,6 @@ STD_ENTRY(_cpu_idle) .global _cpu_idle_nap _cpu_idle_nap: nap + nop /* avoid provoking the icache prefetch with a jump */ jrp lr STD_ENDPROC(_cpu_idle) diff --git a/arch/tile/kernel/intvec_32.S b/arch/tile/kernel/intvec_32.S index aecc8ed5f39b..5d56a1ef5ba5 100644 --- a/arch/tile/kernel/intvec_32.S +++ b/arch/tile/kernel/intvec_32.S @@ -799,6 +799,10 @@ handle_interrupt: * This routine takes a boolean in r30 indicating if this is an NMI. * If so, we also expect a boolean in r31 indicating whether to * re-enable the oprofile interrupts. + * + * Note that .Lresume_userspace is jumped to directly in several + * places, and we need to make sure r30 is set correctly in those + * callers as well. */ STD_ENTRY(interrupt_return) /* If we're resuming to kernel space, don't check thread flags. */ @@ -1237,7 +1241,10 @@ handle_syscall: bzt r30, 1f jal do_syscall_trace FEEDBACK_REENTER(handle_syscall) -1: j .Lresume_userspace /* jump into middle of interrupt_return */ +1: { + movei r30, 0 /* not an NMI */ + j .Lresume_userspace /* jump into middle of interrupt_return */ + } .Linvalid_syscall: /* Report an invalid syscall back to the user program */ @@ -1246,7 +1253,10 @@ handle_syscall: movei r28, -ENOSYS } sw r29, r28 - j .Lresume_userspace /* jump into middle of interrupt_return */ + { + movei r30, 0 /* not an NMI */ + j .Lresume_userspace /* jump into middle of interrupt_return */ + } STD_ENDPROC(handle_syscall) /* Return the address for oprofile to suppress in backtraces. */ @@ -1262,7 +1272,10 @@ STD_ENTRY(ret_from_fork) jal sim_notify_fork jal schedule_tail FEEDBACK_REENTER(ret_from_fork) - j .Lresume_userspace /* jump into middle of interrupt_return */ + { + movei r30, 0 /* not an NMI */ + j .Lresume_userspace /* jump into middle of interrupt_return */ + } STD_ENDPROC(ret_from_fork) /* @@ -1376,7 +1389,10 @@ handle_ill: jal send_sigtrap /* issue a SIGTRAP */ FEEDBACK_REENTER(handle_ill) - j .Lresume_userspace /* jump into middle of interrupt_return */ + { + movei r30, 0 /* not an NMI */ + j .Lresume_userspace /* jump into middle of interrupt_return */ + } .Ldispatch_normal_ill: { diff --git a/arch/tile/kernel/intvec_64.S b/arch/tile/kernel/intvec_64.S index 79c93e10ba27..49d9d6621682 100644 --- a/arch/tile/kernel/intvec_64.S +++ b/arch/tile/kernel/intvec_64.S @@ -22,6 +22,7 @@ #include <asm/irqflags.h> #include <asm/asm-offsets.h> #include <asm/types.h> +#include <asm/signal.h> #include <hv/hypervisor.h> #include <arch/abi.h> #include <arch/interrupts.h> @@ -605,6 +606,10 @@ handle_interrupt: * This routine takes a boolean in r30 indicating if this is an NMI. * If so, we also expect a boolean in r31 indicating whether to * re-enable the oprofile interrupts. + * + * Note that .Lresume_userspace is jumped to directly in several + * places, and we need to make sure r30 is set correctly in those + * callers as well. */ STD_ENTRY(interrupt_return) /* If we're resuming to kernel space, don't check thread flags. */ @@ -1039,11 +1044,28 @@ handle_syscall: /* Do syscall trace again, if requested. */ ld r30, r31 - andi r30, r30, _TIF_SYSCALL_TRACE - beqzt r30, 1f + andi r0, r30, _TIF_SYSCALL_TRACE + { + andi r0, r30, _TIF_SINGLESTEP + beqzt r0, 1f + } jal do_syscall_trace FEEDBACK_REENTER(handle_syscall) -1: j .Lresume_userspace /* jump into middle of interrupt_return */ + andi r0, r30, _TIF_SINGLESTEP + +1: beqzt r0, 2f + + /* Single stepping -- notify ptrace. */ + { + movei r0, SIGTRAP + jal ptrace_notify + } + FEEDBACK_REENTER(handle_syscall) + +2: { + movei r30, 0 /* not an NMI */ + j .Lresume_userspace /* jump into middle of interrupt_return */ + } .Lcompat_syscall: /* @@ -1077,7 +1099,10 @@ handle_syscall: movei r28, -ENOSYS } st r29, r28 - j .Lresume_userspace /* jump into middle of interrupt_return */ + { + movei r30, 0 /* not an NMI */ + j .Lresume_userspace /* jump into middle of interrupt_return */ + } STD_ENDPROC(handle_syscall) /* Return the address for oprofile to suppress in backtraces. */ @@ -1093,7 +1118,10 @@ STD_ENTRY(ret_from_fork) jal sim_notify_fork jal schedule_tail FEEDBACK_REENTER(ret_from_fork) - j .Lresume_userspace + { + movei r30, 0 /* not an NMI */ + j .Lresume_userspace /* jump into middle of interrupt_return */ + } STD_ENDPROC(ret_from_fork) /* Various stub interrupt handlers and syscall handlers */ @@ -1156,6 +1184,18 @@ int_unalign: push_extra_callee_saves r0 j do_trap +/* Fill the return address stack with nonzero entries. */ +STD_ENTRY(fill_ra_stack) + { + move r0, lr + jal 1f + } +1: jal 2f +2: jal 3f +3: jal 4f +4: jrp r0 + STD_ENDPROC(fill_ra_stack) + /* Include .intrpt1 array of interrupt vectors */ .section ".intrpt1", "ax" @@ -1166,7 +1206,7 @@ int_unalign: #define do_hardwall_trap bad_intr #endif - int_hand INT_MEM_ERROR, MEM_ERROR, bad_intr + int_hand INT_MEM_ERROR, MEM_ERROR, do_trap int_hand INT_SINGLE_STEP_3, SINGLE_STEP_3, bad_intr #if CONFIG_KERNEL_PL == 2 int_hand INT_SINGLE_STEP_2, SINGLE_STEP_2, gx_singlestep_handle diff --git a/arch/tile/kernel/module.c b/arch/tile/kernel/module.c index b90ab9925674..98d476920106 100644 --- a/arch/tile/kernel/module.c +++ b/arch/tile/kernel/module.c @@ -67,6 +67,8 @@ void *module_alloc(unsigned long size) area = __get_vm_area(size, VM_ALLOC, MEM_MODULE_START, MEM_MODULE_END); if (!area) goto error; + area->nr_pages = npages; + area->pages = pages; if (map_vm_area(area, prot_rwx, &pages)) { vunmap(area->addr); diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c index a1bb59eecc18..b56d12bf5900 100644 --- a/arch/tile/kernel/pci.c +++ b/arch/tile/kernel/pci.c @@ -141,7 +141,7 @@ static int __devinit tile_init_irqs(int controller_id, * * Returns the number of controllers discovered. */ -int __devinit tile_pci_init(void) +int __init tile_pci_init(void) { int i; @@ -287,7 +287,7 @@ static void __devinit fixup_read_and_payload_sizes(void) * The controllers have been set up by the time we get here, by a call to * tile_pci_init. */ -int __devinit pcibios_init(void) +int __init pcibios_init(void) { int i; diff --git a/arch/tile/kernel/proc.c b/arch/tile/kernel/proc.c index 7a9327046404..446a7f52cc11 100644 --- a/arch/tile/kernel/proc.c +++ b/arch/tile/kernel/proc.c @@ -146,7 +146,6 @@ static ctl_table unaligned_table[] = { }, {} }; -#endif static struct ctl_path tile_path[] = { { .procname = "tile" }, @@ -155,10 +154,9 @@ static struct ctl_path tile_path[] = { static int __init proc_sys_tile_init(void) { -#ifndef __tilegx__ /* FIXME: GX: no support for unaligned access yet */ register_sysctl_paths(tile_path, unaligned_table); -#endif return 0; } arch_initcall(proc_sys_tile_init); +#endif diff --git a/arch/tile/kernel/process.c b/arch/tile/kernel/process.c index 30caecac94dc..2d5ef617bb39 100644 --- a/arch/tile/kernel/process.c +++ b/arch/tile/kernel/process.c @@ -28,6 +28,7 @@ #include <linux/tracehook.h> #include <linux/signal.h> #include <asm/stack.h> +#include <asm/switch_to.h> #include <asm/homecache.h> #include <asm/syscalls.h> #include <asm/traps.h> @@ -285,7 +286,7 @@ struct task_struct *validate_current(void) static struct task_struct corrupt = { .comm = "<corrupt>" }; struct task_struct *tsk = current; if (unlikely((unsigned long)tsk < PAGE_OFFSET || - (void *)tsk > high_memory || + (high_memory && (void *)tsk > high_memory) || ((unsigned long)tsk & (__alignof__(*tsk) - 1)) != 0)) { pr_err("Corrupt 'current' %p (sp %#lx)\n", tsk, stack_pointer); tsk = &corrupt; diff --git a/arch/tile/kernel/setup.c b/arch/tile/kernel/setup.c index 5f85d8b34dbb..bff23f476110 100644 --- a/arch/tile/kernel/setup.c +++ b/arch/tile/kernel/setup.c @@ -103,13 +103,11 @@ unsigned long __initdata pci_reserve_end_pfn = -1U; static int __init setup_maxmem(char *str) { - long maxmem_mb; - if (str == NULL || strict_strtol(str, 0, &maxmem_mb) != 0 || - maxmem_mb == 0) + unsigned long long maxmem; + if (str == NULL || (maxmem = memparse(str, NULL)) == 0) return -EINVAL; - maxmem_pfn = (maxmem_mb >> (HPAGE_SHIFT - 20)) << - (HPAGE_SHIFT - PAGE_SHIFT); + maxmem_pfn = (maxmem >> HPAGE_SHIFT) << (HPAGE_SHIFT - PAGE_SHIFT); pr_info("Forcing RAM used to no more than %dMB\n", maxmem_pfn >> (20 - PAGE_SHIFT)); return 0; @@ -119,14 +117,15 @@ early_param("maxmem", setup_maxmem); static int __init setup_maxnodemem(char *str) { char *endp; - long maxnodemem_mb, node; + unsigned long long maxnodemem; + long node; node = str ? simple_strtoul(str, &endp, 0) : INT_MAX; - if (node >= MAX_NUMNODES || *endp != ':' || - strict_strtol(endp+1, 0, &maxnodemem_mb) != 0) + if (node >= MAX_NUMNODES || *endp != ':') return -EINVAL; - maxnodemem_pfn[node] = (maxnodemem_mb >> (HPAGE_SHIFT - 20)) << + maxnodemem = memparse(endp+1, NULL); + maxnodemem_pfn[node] = (maxnodemem >> HPAGE_SHIFT) << (HPAGE_SHIFT - PAGE_SHIFT); pr_info("Forcing RAM used on node %ld to no more than %dMB\n", node, maxnodemem_pfn[node] >> (20 - PAGE_SHIFT)); @@ -913,6 +912,13 @@ void __cpuinit setup_cpu(int boot) #ifdef CONFIG_BLK_DEV_INITRD +/* + * Note that the kernel can potentially support other compression + * techniques than gz, though we don't do so by default. If we ever + * decide to do so we can either look for other filename extensions, + * or just allow a file with this name to be compressed with an + * arbitrary compressor (somewhat counterintuitively). + */ static int __initdata set_initramfs_file; static char __initdata initramfs_file[128] = "initramfs.cpio.gz"; @@ -928,9 +934,9 @@ static int __init setup_initramfs_file(char *str) early_param("initramfs_file", setup_initramfs_file); /* - * We look for an additional "initramfs.cpio.gz" file in the hvfs. + * We look for an "initramfs.cpio.gz" file in the hvfs. * If there is one, we allocate some memory for it and it will be - * unpacked to the initramfs after any built-in initramfs_data. + * unpacked to the initramfs. */ static void __init load_hv_initrd(void) { @@ -1100,7 +1106,7 @@ EXPORT_SYMBOL(hash_for_home_map); /* * cpu_cacheable_map lists all the cpus whose caches the hypervisor can - * flush on our behalf. It is set to cpu_possible_map OR'ed with + * flush on our behalf. It is set to cpu_possible_mask OR'ed with * hash_for_home_map, and it is what should be passed to * hv_flush_remote() to flush all caches. Note that if there are * dedicated hypervisor driver tiles that have authorized use of their @@ -1186,7 +1192,7 @@ static void __init setup_cpu_maps(void) sizeof(cpu_lotar_map)); if (rc < 0) { pr_err("warning: no HV_INQ_TILES_LOTAR; using AVAIL\n"); - cpu_lotar_map = cpu_possible_map; + cpu_lotar_map = *cpu_possible_mask; } #if CHIP_HAS_CBOX_HOME_MAP() @@ -1196,9 +1202,9 @@ static void __init setup_cpu_maps(void) sizeof(hash_for_home_map)); if (rc < 0) early_panic("hv_inquire_tiles(HFH_CACHE) failed: rc %d\n", rc); - cpumask_or(&cpu_cacheable_map, &cpu_possible_map, &hash_for_home_map); + cpumask_or(&cpu_cacheable_map, cpu_possible_mask, &hash_for_home_map); #else - cpu_cacheable_map = cpu_possible_map; + cpu_cacheable_map = *cpu_possible_mask; #endif } diff --git a/arch/tile/kernel/single_step.c b/arch/tile/kernel/single_step.c index bc1eb586e24d..89529c9f0605 100644 --- a/arch/tile/kernel/single_step.c +++ b/arch/tile/kernel/single_step.c @@ -153,6 +153,25 @@ static tile_bundle_bits rewrite_load_store_unaligned( if (((unsigned long)addr % size) == 0) return bundle; + /* + * Return SIGBUS with the unaligned address, if requested. + * Note that we return SIGBUS even for completely invalid addresses + * as long as they are in fact unaligned; this matches what the + * tilepro hardware would be doing, if it could provide us with the + * actual bad address in an SPR, which it doesn't. + */ + if (unaligned_fixup == 0) { + siginfo_t info = { + .si_signo = SIGBUS, + .si_code = BUS_ADRALN, + .si_addr = addr + }; + trace_unhandled_signal("unaligned trap", regs, + (unsigned long)addr, SIGBUS); + force_sig_info(info.si_signo, &info, current); + return (tilepro_bundle_bits) 0; + } + #ifndef __LITTLE_ENDIAN # error We assume little-endian representation with copy_xx_user size 2 here #endif @@ -192,18 +211,6 @@ static tile_bundle_bits rewrite_load_store_unaligned( return (tile_bundle_bits) 0; } - if (unaligned_fixup == 0) { - siginfo_t info = { - .si_signo = SIGBUS, - .si_code = BUS_ADRALN, - .si_addr = addr - }; - trace_unhandled_signal("unaligned trap", regs, - (unsigned long)addr, SIGBUS); - force_sig_info(info.si_signo, &info, current); - return (tile_bundle_bits) 0; - } - if (unaligned_printk || unaligned_fixup_count == 0) { pr_info("Process %d/%s: PC %#lx: Fixup of" " unaligned %s at %#lx.\n", @@ -339,12 +346,10 @@ void single_step_once(struct pt_regs *regs) } /* allocate a cache line of writable, executable memory */ - down_write(¤t->mm->mmap_sem); - buffer = (void __user *) do_mmap(NULL, 0, 64, + buffer = (void __user *) vm_mmap(NULL, 0, 64, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0); - up_write(¤t->mm->mmap_sem); if (IS_ERR((void __force *)buffer)) { kfree(state); diff --git a/arch/tile/kernel/smp.c b/arch/tile/kernel/smp.c index a44e103c5a63..91da0f721958 100644 --- a/arch/tile/kernel/smp.c +++ b/arch/tile/kernel/smp.c @@ -103,7 +103,7 @@ static void smp_stop_cpu_interrupt(void) set_cpu_online(smp_processor_id(), 0); arch_local_irq_disable_all(); for (;;) - asm("nap"); + asm("nap; nop"); } /* This function calls the 'stop' function on all other CPUs in the system. */ @@ -113,6 +113,12 @@ void smp_send_stop(void) send_IPI_allbutself(MSG_TAG_STOP_CPU); } +/* On panic, just wait; we may get an smp_send_stop() later on. */ +void panic_smp_self_stop(void) +{ + while (1) + asm("nap; nop"); +} /* * Dispatch code called from hv_message_intr() for HV_MSG_TILE hv messages. diff --git a/arch/tile/kernel/smpboot.c b/arch/tile/kernel/smpboot.c index b949edcec200..172aef7d3159 100644 --- a/arch/tile/kernel/smpboot.c +++ b/arch/tile/kernel/smpboot.c @@ -196,6 +196,8 @@ void __cpuinit online_secondary(void) /* This must be done before setting cpu_online_mask */ wmb(); + notify_cpu_starting(smp_processor_id()); + /* * We need to hold call_lock, so there is no inconsistency * between the time smp_call_function() determines number of diff --git a/arch/tile/kernel/stack.c b/arch/tile/kernel/stack.c index 37ee4d037e0b..b2f44c28dda6 100644 --- a/arch/tile/kernel/stack.c +++ b/arch/tile/kernel/stack.c @@ -21,10 +21,12 @@ #include <linux/stacktrace.h> #include <linux/uaccess.h> #include <linux/mmzone.h> +#include <linux/dcache.h> +#include <linux/fs.h> #include <asm/backtrace.h> #include <asm/page.h> -#include <asm/tlbflush.h> #include <asm/ucontext.h> +#include <asm/switch_to.h> #include <asm/sigframe.h> #include <asm/stack.h> #include <arch/abi.h> @@ -44,72 +46,23 @@ static int in_kernel_stack(struct KBacktraceIterator *kbt, unsigned long sp) return sp >= kstack_base && sp < kstack_base + THREAD_SIZE; } -/* Is address valid for reading? */ -static int valid_address(struct KBacktraceIterator *kbt, unsigned long address) -{ - HV_PTE *l1_pgtable = kbt->pgtable; - HV_PTE *l2_pgtable; - unsigned long pfn; - HV_PTE pte; - struct page *page; - - if (l1_pgtable == NULL) - return 0; /* can't read user space in other tasks */ - -#ifdef CONFIG_64BIT - /* Find the real l1_pgtable by looking in the l0_pgtable. */ - pte = l1_pgtable[HV_L0_INDEX(address)]; - if (!hv_pte_get_present(pte)) - return 0; - pfn = hv_pte_get_pfn(pte); - if (pte_huge(pte)) { - if (!pfn_valid(pfn)) { - pr_err("L0 huge page has bad pfn %#lx\n", pfn); - return 0; - } - return hv_pte_get_present(pte) && hv_pte_get_readable(pte); - } - page = pfn_to_page(pfn); - BUG_ON(PageHighMem(page)); /* No HIGHMEM on 64-bit. */ - l1_pgtable = (HV_PTE *)pfn_to_kaddr(pfn); -#endif - pte = l1_pgtable[HV_L1_INDEX(address)]; - if (!hv_pte_get_present(pte)) - return 0; - pfn = hv_pte_get_pfn(pte); - if (pte_huge(pte)) { - if (!pfn_valid(pfn)) { - pr_err("huge page has bad pfn %#lx\n", pfn); - return 0; - } - return hv_pte_get_present(pte) && hv_pte_get_readable(pte); - } - - page = pfn_to_page(pfn); - if (PageHighMem(page)) { - pr_err("L2 page table not in LOWMEM (%#llx)\n", - HV_PFN_TO_CPA(pfn)); - return 0; - } - l2_pgtable = (HV_PTE *)pfn_to_kaddr(pfn); - pte = l2_pgtable[HV_L2_INDEX(address)]; - return hv_pte_get_present(pte) && hv_pte_get_readable(pte); -} - /* Callback for backtracer; basically a glorified memcpy */ static bool read_memory_func(void *result, unsigned long address, unsigned int size, void *vkbt) { int retval; struct KBacktraceIterator *kbt = (struct KBacktraceIterator *)vkbt; + + if (address == 0) + return 0; if (__kernel_text_address(address)) { /* OK to read kernel code. */ } else if (address >= PAGE_OFFSET) { /* We only tolerate kernel-space reads of this task's stack */ if (!in_kernel_stack(kbt, address)) return 0; - } else if (!valid_address(kbt, address)) { - return 0; /* invalid user-space address */ + } else if (!kbt->is_current) { + return 0; /* can't read from other user address spaces */ } pagefault_disable(); retval = __copy_from_user_inatomic(result, @@ -127,6 +80,8 @@ static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt) unsigned long sp = kbt->it.sp; struct pt_regs *p; + if (sp % sizeof(long) != 0) + return NULL; if (!in_kernel_stack(kbt, sp)) return NULL; if (!in_kernel_stack(kbt, sp + C_ABI_SAVE_AREA_SIZE + PTREGS_SIZE-1)) @@ -169,27 +124,27 @@ static int is_sigreturn(unsigned long pc) } /* Return a pt_regs pointer for a valid signal handler frame */ -static struct pt_regs *valid_sigframe(struct KBacktraceIterator* kbt) +static struct pt_regs *valid_sigframe(struct KBacktraceIterator* kbt, + struct rt_sigframe* kframe) { BacktraceIterator *b = &kbt->it; - if (b->pc == VDSO_BASE) { - struct rt_sigframe *frame; - unsigned long sigframe_top = - b->sp + sizeof(struct rt_sigframe) - 1; - if (!valid_address(kbt, b->sp) || - !valid_address(kbt, sigframe_top)) { - if (kbt->verbose) - pr_err(" (odd signal: sp %#lx?)\n", - (unsigned long)(b->sp)); + if (b->pc == VDSO_BASE && b->sp < PAGE_OFFSET && + b->sp % sizeof(long) == 0) { + int retval; + pagefault_disable(); + retval = __copy_from_user_inatomic( + kframe, (void __user __force *)b->sp, + sizeof(*kframe)); + pagefault_enable(); + if (retval != 0 || + (unsigned int)(kframe->info.si_signo) >= _NSIG) return NULL; - } - frame = (struct rt_sigframe *)b->sp; if (kbt->verbose) { pr_err(" <received signal %d>\n", - frame->info.si_signo); + kframe->info.si_signo); } - return (struct pt_regs *)&frame->uc.uc_mcontext; + return (struct pt_regs *)&kframe->uc.uc_mcontext; } return NULL; } @@ -202,10 +157,11 @@ static int KBacktraceIterator_is_sigreturn(struct KBacktraceIterator *kbt) static int KBacktraceIterator_restart(struct KBacktraceIterator *kbt) { struct pt_regs *p; + struct rt_sigframe kframe; p = valid_fault_handler(kbt); if (p == NULL) - p = valid_sigframe(kbt); + p = valid_sigframe(kbt, &kframe); if (p == NULL) return 0; backtrace_init(&kbt->it, read_memory_func, kbt, @@ -265,41 +221,19 @@ void KBacktraceIterator_init(struct KBacktraceIterator *kbt, /* * Set up callback information. We grab the kernel stack base - * so we will allow reads of that address range, and if we're - * asking about the current process we grab the page table - * so we can check user accesses before trying to read them. - * We flush the TLB to avoid any weird skew issues. + * so we will allow reads of that address range. */ - is_current = (t == NULL); + is_current = (t == NULL || t == current); kbt->is_current = is_current; if (is_current) t = validate_current(); kbt->task = t; - kbt->pgtable = NULL; kbt->verbose = 0; /* override in caller if desired */ kbt->profile = 0; /* override in caller if desired */ kbt->end = KBT_ONGOING; - kbt->new_context = 0; - if (is_current) { - HV_PhysAddr pgdir_pa = hv_inquire_context().page_table; - if (pgdir_pa == (unsigned long)swapper_pg_dir - PAGE_OFFSET) { - /* - * Not just an optimization: this also allows - * this to work at all before va/pa mappings - * are set up. - */ - kbt->pgtable = swapper_pg_dir; - } else { - struct page *page = pfn_to_page(PFN_DOWN(pgdir_pa)); - if (!PageHighMem(page)) - kbt->pgtable = __va(pgdir_pa); - else - pr_err("page table not in LOWMEM" - " (%#llx)\n", pgdir_pa); - } - local_flush_tlb_all(); + kbt->new_context = 1; + if (is_current) validate_stack(regs); - } if (regs == NULL) { if (is_current || t->state == TASK_RUNNING) { @@ -345,6 +279,78 @@ void KBacktraceIterator_next(struct KBacktraceIterator *kbt) } EXPORT_SYMBOL(KBacktraceIterator_next); +static void describe_addr(struct KBacktraceIterator *kbt, + unsigned long address, + int have_mmap_sem, char *buf, size_t bufsize) +{ + struct vm_area_struct *vma; + size_t namelen, remaining; + unsigned long size, offset, adjust; + char *p, *modname; + const char *name; + int rc; + + /* + * Look one byte back for every caller frame (i.e. those that + * aren't a new context) so we look up symbol data for the + * call itself, not the following instruction, which may be on + * a different line (or in a different function). + */ + adjust = !kbt->new_context; + address -= adjust; + + if (address >= PAGE_OFFSET) { + /* Handle kernel symbols. */ + BUG_ON(bufsize < KSYM_NAME_LEN); + name = kallsyms_lookup(address, &size, &offset, + &modname, buf); + if (name == NULL) { + buf[0] = '\0'; + return; + } + namelen = strlen(buf); + remaining = (bufsize - 1) - namelen; + p = buf + namelen; + rc = snprintf(p, remaining, "+%#lx/%#lx ", + offset + adjust, size); + if (modname && rc < remaining) + snprintf(p + rc, remaining - rc, "[%s] ", modname); + buf[bufsize-1] = '\0'; + return; + } + + /* If we don't have the mmap_sem, we can't show any more info. */ + buf[0] = '\0'; + if (!have_mmap_sem) + return; + + /* Find vma info. */ + vma = find_vma(kbt->task->mm, address); + if (vma == NULL || address < vma->vm_start) { + snprintf(buf, bufsize, "[unmapped address] "); + return; + } + + if (vma->vm_file) { + char *s; + p = d_path(&vma->vm_file->f_path, buf, bufsize); + if (IS_ERR(p)) + p = "?"; + s = strrchr(p, '/'); + if (s) + p = s+1; + } else { + p = "anon"; + } + + /* Generate a string description of the vma info. */ + namelen = strlen(p); + remaining = (bufsize - 1) - namelen; + memmove(buf, p, namelen); + snprintf(buf + namelen, remaining, "[%lx+%lx] ", + vma->vm_start, vma->vm_end - vma->vm_start); +} + /* * This method wraps the backtracer's more generic support. * It is only invoked from the architecture-specific code; show_stack() @@ -353,6 +359,7 @@ EXPORT_SYMBOL(KBacktraceIterator_next); void tile_show_stack(struct KBacktraceIterator *kbt, int headers) { int i; + int have_mmap_sem = 0; if (headers) { /* @@ -369,31 +376,16 @@ void tile_show_stack(struct KBacktraceIterator *kbt, int headers) kbt->verbose = 1; i = 0; for (; !KBacktraceIterator_end(kbt); KBacktraceIterator_next(kbt)) { - char *modname; - const char *name; - unsigned long address = kbt->it.pc; - unsigned long offset, size; char namebuf[KSYM_NAME_LEN+100]; + unsigned long address = kbt->it.pc; - if (address >= PAGE_OFFSET) - name = kallsyms_lookup(address, &size, &offset, - &modname, namebuf); - else - name = NULL; - - if (!name) - namebuf[0] = '\0'; - else { - size_t namelen = strlen(namebuf); - size_t remaining = (sizeof(namebuf) - 1) - namelen; - char *p = namebuf + namelen; - int rc = snprintf(p, remaining, "+%#lx/%#lx ", - offset, size); - if (modname && rc < remaining) - snprintf(p + rc, remaining - rc, - "[%s] ", modname); - namebuf[sizeof(namebuf)-1] = '\0'; - } + /* Try to acquire the mmap_sem as we pass into userspace. */ + if (address < PAGE_OFFSET && !have_mmap_sem && kbt->task->mm) + have_mmap_sem = + down_read_trylock(&kbt->task->mm->mmap_sem); + + describe_addr(kbt, address, have_mmap_sem, + namebuf, sizeof(namebuf)); pr_err(" frame %d: 0x%lx %s(sp 0x%lx)\n", i++, address, namebuf, (unsigned long)(kbt->it.sp)); @@ -408,6 +400,8 @@ void tile_show_stack(struct KBacktraceIterator *kbt, int headers) pr_err("Stack dump stopped; next frame identical to this one\n"); if (headers) pr_err("Stack dump complete\n"); + if (have_mmap_sem) + up_read(&kbt->task->mm->mmap_sem); } EXPORT_SYMBOL(tile_show_stack); diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c index 2bb6602a1ee7..73cff814ac57 100644 --- a/arch/tile/kernel/traps.c +++ b/arch/tile/kernel/traps.c @@ -200,7 +200,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, { siginfo_t info = { 0 }; int signo, code; - unsigned long address; + unsigned long address = 0; bundle_bits instr; /* Re-enable interrupts. */ @@ -223,6 +223,10 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, } switch (fault_num) { + case INT_MEM_ERROR: + signo = SIGBUS; + code = BUS_OBJERR; + break; case INT_ILL: if (copy_from_user(&instr, (void __user *)regs->pc, sizeof(instr))) { @@ -289,7 +293,10 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, address = regs->pc; break; #ifdef __tilegx__ - case INT_ILL_TRANS: + case INT_ILL_TRANS: { + /* Avoid a hardware erratum with the return address stack. */ + fill_ra_stack(); + signo = SIGSEGV; code = SEGV_MAPERR; if (reason & SPR_ILL_TRANS_REASON__I_STREAM_VA_RMASK) @@ -297,6 +304,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, else address = 0; /* FIXME: GX: single-step for address */ break; + } #endif default: panic("Unexpected do_trap interrupt number %d", fault_num); @@ -308,7 +316,8 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, info.si_addr = (void __user *)address; if (signo == SIGILL) info.si_trapno = fault_num; - trace_unhandled_signal("trap", regs, address, signo); + if (signo != SIGTRAP) + trace_unhandled_signal("trap", regs, address, signo); force_sig_info(signo, &info, current); } diff --git a/arch/tile/lib/Makefile b/arch/tile/lib/Makefile index 0c26086ecbef..985f59858234 100644 --- a/arch/tile/lib/Makefile +++ b/arch/tile/lib/Makefile @@ -7,6 +7,7 @@ lib-y = cacheflush.o checksum.o cpumask.o delay.o uaccess.o \ strchr_$(BITS).o strlen_$(BITS).o ifeq ($(CONFIG_TILEGX),y) +CFLAGS_REMOVE_memcpy_user_64.o = -fno-omit-frame-pointer lib-y += memcpy_user_64.o else lib-y += atomic_32.o atomic_asm_32.o memcpy_tile64.o diff --git a/arch/tile/lib/cacheflush.c b/arch/tile/lib/cacheflush.c index 8928aace7a64..db4fb89e12d8 100644 --- a/arch/tile/lib/cacheflush.c +++ b/arch/tile/lib/cacheflush.c @@ -39,7 +39,21 @@ void finv_buffer_remote(void *buffer, size_t size, int hfh) { char *p, *base; size_t step_size, load_count; + + /* + * On TILEPro the striping granularity is a fixed 8KB; on + * TILE-Gx it is configurable, and we rely on the fact that + * the hypervisor always configures maximum striping, so that + * bits 9 and 10 of the PA are part of the stripe function, so + * every 512 bytes we hit a striping boundary. + * + */ +#ifdef __tilegx__ + const unsigned long STRIPE_WIDTH = 512; +#else const unsigned long STRIPE_WIDTH = 8192; +#endif + #ifdef __tilegx__ /* * On TILE-Gx, we must disable the dstream prefetcher before doing @@ -74,7 +88,7 @@ void finv_buffer_remote(void *buffer, size_t size, int hfh) * memory, that one load would be sufficient, but since we may * be, we also need to back up to the last load issued to * another memory controller, which would be the point where - * we crossed an 8KB boundary (the granularity of striping + * we crossed a "striping" boundary (the granularity of striping * across memory controllers). Keep backing up and doing this * until we are before the beginning of the buffer, or have * hit all the controllers. @@ -88,12 +102,22 @@ void finv_buffer_remote(void *buffer, size_t size, int hfh) * every cache line on a full memory stripe on each * controller" that we simply do that, to simplify the logic. * - * FIXME: See bug 9535 for some issues with this code. + * On TILE-Gx the hash-for-home function is much more complex, + * with the upshot being we can't readily guarantee we have + * hit both entries in the 128-entry AMT that were hit by any + * load in the entire range, so we just re-load them all. + * With larger buffers, we may want to consider using a hypervisor + * trap to issue loads directly to each hash-for-home tile for + * each controller (doing it from Linux would trash the TLB). */ if (hfh) { step_size = L2_CACHE_BYTES; +#ifdef __tilegx__ + load_count = (size + L2_CACHE_BYTES - 1) / L2_CACHE_BYTES; +#else load_count = (STRIPE_WIDTH / L2_CACHE_BYTES) * (1 << CHIP_LOG_NUM_MSHIMS()); +#endif } else { step_size = STRIPE_WIDTH; load_count = (1 << CHIP_LOG_NUM_MSHIMS()); @@ -109,7 +133,7 @@ void finv_buffer_remote(void *buffer, size_t size, int hfh) /* Figure out how far back we need to go. */ base = p - (step_size * (load_count - 2)); - if ((long)base < (long)buffer) + if ((unsigned long)base < (unsigned long)buffer) base = buffer; /* diff --git a/arch/tile/lib/memcpy_user_64.c b/arch/tile/lib/memcpy_user_64.c index 4763b3aff1cc..37440caa7370 100644 --- a/arch/tile/lib/memcpy_user_64.c +++ b/arch/tile/lib/memcpy_user_64.c @@ -14,7 +14,13 @@ * Do memcpy(), but trap and return "n" when a load or store faults. * * Note: this idiom only works when memcpy() compiles to a leaf function. - * If "sp" is updated during memcpy, the "jrp lr" will be incorrect. + * Here leaf function not only means it does not have calls, but also + * requires no stack operations (sp, stack frame pointer) and no + * use of callee-saved registers, else "jrp lr" will be incorrect since + * unwinding stack frame is bypassed. Since memcpy() is not complex so + * these conditions are satisfied here, but we need to be careful when + * modifying this file. This is not a clean solution but is the best + * one so far. * * Also note that we are capturing "n" from the containing scope here. */ diff --git a/arch/tile/lib/spinlock_common.h b/arch/tile/lib/spinlock_common.h index c10109809132..6ac37509faca 100644 --- a/arch/tile/lib/spinlock_common.h +++ b/arch/tile/lib/spinlock_common.h @@ -60,5 +60,5 @@ static void delay_backoff(int iterations) loops += __insn_crc32_32(stack_pointer, get_cycles_low()) & (loops - 1); - relax(1 << exponent); + relax(loops); } diff --git a/arch/tile/mm/fault.c b/arch/tile/mm/fault.c index cba30e9547b4..22e58f51ed23 100644 --- a/arch/tile/mm/fault.c +++ b/arch/tile/mm/fault.c @@ -130,7 +130,7 @@ static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address) } /* - * Handle a fault on the vmalloc or module mapping area + * Handle a fault on the vmalloc area. */ static inline int vmalloc_fault(pgd_t *pgd, unsigned long address) { @@ -203,9 +203,14 @@ static pgd_t *get_current_pgd(void) * interrupt or a critical region, and must do as little as possible. * Similarly, we can't use atomic ops here, since we may be handling a * fault caused by an atomic op access. + * + * If we find a migrating PTE while we're in an NMI context, and we're + * at a PC that has a registered exception handler, we don't wait, + * since this thread may (e.g.) have been interrupted while migrating + * its own stack, which would then cause us to self-deadlock. */ static int handle_migrating_pte(pgd_t *pgd, int fault_num, - unsigned long address, + unsigned long address, unsigned long pc, int is_kernel_mode, int write) { pud_t *pud; @@ -227,6 +232,8 @@ static int handle_migrating_pte(pgd_t *pgd, int fault_num, pte_offset_kernel(pmd, address); pteval = *pte; if (pte_migrating(pteval)) { + if (in_nmi() && search_exception_tables(pc)) + return 0; wait_for_migration(pte); return 1; } @@ -300,7 +307,7 @@ static int handle_page_fault(struct pt_regs *regs, * rather than trying to patch up the existing PTE. */ pgd = get_current_pgd(); - if (handle_migrating_pte(pgd, fault_num, address, + if (handle_migrating_pte(pgd, fault_num, address, regs->pc, is_kernel_mode, write)) return 1; @@ -335,9 +342,12 @@ static int handle_page_fault(struct pt_regs *regs, /* * If we're trying to touch user-space addresses, we must * be either at PL0, or else with interrupts enabled in the - * kernel, so either way we can re-enable interrupts here. + * kernel, so either way we can re-enable interrupts here + * unless we are doing atomic access to user space with + * interrupts disabled. */ - local_irq_enable(); + if (!(regs->flags & PT_FLAGS_DISABLE_IRQ)) + local_irq_enable(); mm = tsk->mm; @@ -665,7 +675,7 @@ struct intvec_state do_page_fault_ics(struct pt_regs *regs, int fault_num, */ if (fault_num == INT_DTLB_ACCESS) write = 1; - if (handle_migrating_pte(pgd, fault_num, address, 1, write)) + if (handle_migrating_pte(pgd, fault_num, address, pc, 1, write)) return state; /* Return zero so that we continue on with normal fault handling. */ diff --git a/arch/tile/mm/homecache.c b/arch/tile/mm/homecache.c index 1cc6ae477c98..499f73770b05 100644 --- a/arch/tile/mm/homecache.c +++ b/arch/tile/mm/homecache.c @@ -394,6 +394,7 @@ int page_home(struct page *page) return pte_to_home(*virt_to_pte(NULL, kva)); } } +EXPORT_SYMBOL(page_home); void homecache_change_page_home(struct page *page, int order, int home) { diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c index 830c4908ea76..6a9d20ddc34f 100644 --- a/arch/tile/mm/init.c +++ b/arch/tile/mm/init.c @@ -254,11 +254,6 @@ static pgprot_t __init init_pgprot(ulong address) return construct_pgprot(PAGE_KERNEL_RO, PAGE_HOME_IMMUTABLE); } - /* As a performance optimization, keep the boot init stack here. */ - if (address >= (ulong)&init_thread_union && - address < (ulong)&init_thread_union + THREAD_SIZE) - return construct_pgprot(PAGE_KERNEL, smp_processor_id()); - #ifndef __tilegx__ #if !ATOMIC_LOCKS_FOUND_VIA_TABLE() /* Force the atomic_locks[] array page to be hash-for-home. */ @@ -557,6 +552,7 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base) address = MEM_SV_INTRPT; pmd = get_pmd(pgtables, address); + pfn = 0; /* code starts at PA 0 */ if (ktext_small) { /* Allocate an L2 PTE for the kernel text */ int cpu = 0; @@ -579,10 +575,15 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base) } BUG_ON(address != (unsigned long)_stext); - pfn = 0; /* code starts at PA 0 */ - pte = alloc_pte(); - for (pte_ofs = 0; address < (unsigned long)_einittext; - pfn++, pte_ofs++, address += PAGE_SIZE) { + pte = NULL; + for (; address < (unsigned long)_einittext; + pfn++, address += PAGE_SIZE) { + pte_ofs = pte_index(address); + if (pte_ofs == 0) { + if (pte) + assign_pte(pmd++, pte); + pte = alloc_pte(); + } if (!ktext_local) { prot = set_remote_cache_cpu(prot, cpu); cpu = cpumask_next(cpu, &ktext_mask); @@ -591,7 +592,8 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base) } pte[pte_ofs] = pfn_pte(pfn, prot); } - assign_pte(pmd, pte); + if (pte) + assign_pte(pmd, pte); } else { pte_t pteval = pfn_pte(0, PAGE_KERNEL_EXEC); pteval = pte_mkhuge(pteval); @@ -614,7 +616,9 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base) else pteval = hv_pte_set_mode(pteval, HV_PTE_MODE_CACHE_NO_L3); - *(pte_t *)pmd = pteval; + for (; address < (unsigned long)_einittext; + pfn += PFN_DOWN(HPAGE_SIZE), address += HPAGE_SIZE) + *(pte_t *)(pmd++) = pfn_pte(pfn, pteval); } /* Set swapper_pgprot here so it is flushed to memory right away. */ diff --git a/arch/tile/mm/pgtable.c b/arch/tile/mm/pgtable.c index 87303693a072..2410aa899b3e 100644 --- a/arch/tile/mm/pgtable.c +++ b/arch/tile/mm/pgtable.c @@ -177,14 +177,10 @@ void shatter_huge_page(unsigned long addr) if (!pmd_huge_page(*pmd)) return; - /* - * Grab the pgd_lock, since we may need it to walk the pgd_list, - * and since we need some kind of lock here to avoid races. - */ - spin_lock_irqsave(&pgd_lock, flags); + spin_lock_irqsave(&init_mm.page_table_lock, flags); if (!pmd_huge_page(*pmd)) { /* Lost the race to convert the huge page. */ - spin_unlock_irqrestore(&pgd_lock, flags); + spin_unlock_irqrestore(&init_mm.page_table_lock, flags); return; } @@ -194,6 +190,7 @@ void shatter_huge_page(unsigned long addr) #ifdef __PAGETABLE_PMD_FOLDED /* Walk every pgd on the system and update the pmd there. */ + spin_lock(&pgd_lock); list_for_each(pos, &pgd_list) { pmd_t *copy_pmd; pgd = list_to_pgd(pos) + pgd_index(addr); @@ -201,6 +198,7 @@ void shatter_huge_page(unsigned long addr) copy_pmd = pmd_offset(pud, addr); __set_pmd(copy_pmd, *pmd); } + spin_unlock(&pgd_lock); #endif /* Tell every cpu to notice the change. */ @@ -208,7 +206,7 @@ void shatter_huge_page(unsigned long addr) cpu_possible_mask, NULL, 0); /* Hold the lock until the TLB flush is finished to avoid races. */ - spin_unlock_irqrestore(&pgd_lock, flags); + spin_unlock_irqrestore(&init_mm.page_table_lock, flags); } /* @@ -217,9 +215,13 @@ void shatter_huge_page(unsigned long addr) * against pageattr.c; it is the unique case in which a valid change * of kernel pagetables can't be lazily synchronized by vmalloc faults. * vmalloc faults work because attached pagetables are never freed. - * The locking scheme was chosen on the basis of manfred's - * recommendations and having no core impact whatsoever. - * -- wli + * + * The lock is always taken with interrupts disabled, unlike on x86 + * and other platforms, because we need to take the lock in + * shatter_huge_page(), which may be called from an interrupt context. + * We are not at risk from the tlbflush IPI deadlock that was seen on + * x86, since we use the flush_remote() API to have the hypervisor do + * the TLB flushes regardless of irq disabling. */ DEFINE_SPINLOCK(pgd_lock); LIST_HEAD(pgd_list); @@ -469,10 +471,18 @@ void __set_pte(pte_t *ptep, pte_t pte) void set_pte(pte_t *ptep, pte_t pte) { - struct page *page = pfn_to_page(pte_pfn(pte)); - - /* Update the home of a PTE if necessary */ - pte = pte_set_home(pte, page_home(page)); + if (pte_present(pte) && + (!CHIP_HAS_MMIO() || hv_pte_get_mode(pte) != HV_PTE_MODE_MMIO)) { + /* The PTE actually references physical memory. */ + unsigned long pfn = pte_pfn(pte); + if (pfn_valid(pfn)) { + /* Update the home of the PTE from the struct page. */ + pte = pte_set_home(pte, page_home(pfn_to_page(pfn))); + } else if (hv_pte_get_mode(pte) == 0) { + /* remap_pfn_range(), etc, must supply PTE mode. */ + panic("set_pte(): out-of-range PFN and mode 0\n"); + } + } __set_pte(ptep, pte); } diff --git a/arch/um/drivers/cow.h b/arch/um/drivers/cow.h index dc36b222100b..6673508f3426 100644 --- a/arch/um/drivers/cow.h +++ b/arch/um/drivers/cow.h @@ -3,41 +3,6 @@ #include <asm/types.h> -#if defined(__KERNEL__) - -# include <asm/byteorder.h> - -# if defined(__BIG_ENDIAN) -# define ntohll(x) (x) -# define htonll(x) (x) -# elif defined(__LITTLE_ENDIAN) -# define ntohll(x) be64_to_cpu(x) -# define htonll(x) cpu_to_be64(x) -# else -# error "Could not determine byte order" -# endif - -#else -/* For the definition of ntohl, htonl and __BYTE_ORDER */ -#include <endian.h> -#include <netinet/in.h> -#if defined(__BYTE_ORDER) - -# if __BYTE_ORDER == __BIG_ENDIAN -# define ntohll(x) (x) -# define htonll(x) (x) -# elif __BYTE_ORDER == __LITTLE_ENDIAN -# define ntohll(x) bswap_64(x) -# define htonll(x) bswap_64(x) -# else -# error "Could not determine byte order: __BYTE_ORDER uncorrectly defined" -# endif - -#else /* ! defined(__BYTE_ORDER) */ -# error "Could not determine byte order: __BYTE_ORDER not defined" -#endif -#endif /* ! defined(__KERNEL__) */ - extern int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize, int alignment, int *bitmap_offset_out, unsigned long *bitmap_len_out, int *data_offset_out); diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c index 9cbb426c0b91..0ee9cc6cc4c7 100644 --- a/arch/um/drivers/cow_user.c +++ b/arch/um/drivers/cow_user.c @@ -8,11 +8,10 @@ * that. */ #include <unistd.h> -#include <byteswap.h> #include <errno.h> #include <string.h> #include <arpa/inet.h> -#include <asm/types.h> +#include <endian.h> #include "cow.h" #include "cow_sys.h" @@ -214,8 +213,8 @@ int write_cow_header(char *cow_file, int fd, char *backing_file, "header\n"); goto out; } - header->magic = htonl(COW_MAGIC); - header->version = htonl(COW_VERSION); + header->magic = htobe32(COW_MAGIC); + header->version = htobe32(COW_VERSION); err = -EINVAL; if (strlen(backing_file) > sizeof(header->backing_file) - 1) { @@ -246,10 +245,10 @@ int write_cow_header(char *cow_file, int fd, char *backing_file, goto out_free; } - header->mtime = htonl(modtime); - header->size = htonll(*size); - header->sectorsize = htonl(sectorsize); - header->alignment = htonl(alignment); + header->mtime = htobe32(modtime); + header->size = htobe64(*size); + header->sectorsize = htobe32(sectorsize); + header->alignment = htobe32(alignment); header->cow_format = COW_BITMAP; err = cow_write_file(fd, header, sizeof(*header)); @@ -301,8 +300,8 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, magic = header->v1.magic; if (magic == COW_MAGIC) version = header->v1.version; - else if (magic == ntohl(COW_MAGIC)) - version = ntohl(header->v1.version); + else if (magic == be32toh(COW_MAGIC)) + version = be32toh(header->v1.version); /* No error printed because the non-COW case comes through here */ else goto out; @@ -327,9 +326,9 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, "header\n"); goto out; } - *mtime_out = ntohl(header->v2.mtime); - *size_out = ntohll(header->v2.size); - *sectorsize_out = ntohl(header->v2.sectorsize); + *mtime_out = be32toh(header->v2.mtime); + *size_out = be64toh(header->v2.size); + *sectorsize_out = be32toh(header->v2.sectorsize); *bitmap_offset_out = sizeof(header->v2); *align_out = *sectorsize_out; file = header->v2.backing_file; @@ -341,10 +340,10 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, "header\n"); goto out; } - *mtime_out = ntohl(header->v3.mtime); - *size_out = ntohll(header->v3.size); - *sectorsize_out = ntohl(header->v3.sectorsize); - *align_out = ntohl(header->v3.alignment); + *mtime_out = be32toh(header->v3.mtime); + *size_out = be64toh(header->v3.size); + *sectorsize_out = be32toh(header->v3.sectorsize); + *align_out = be32toh(header->v3.alignment); if (*align_out == 0) { cow_printf("read_cow_header - invalid COW header, " "align == 0\n"); @@ -366,16 +365,16 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, * this was used until Dec2005 - 64bits are needed to represent * 2038+. I.e. we can safely do this truncating cast. * - * Additionally, we must use ntohl() instead of ntohll(), since + * Additionally, we must use be32toh() instead of be64toh(), since * the program used to use the former (tested - I got mtime * mismatch "0 vs whatever"). * * Ever heard about bug-to-bug-compatibility ? ;-) */ - *mtime_out = (time32_t) ntohl(header->v3_b.mtime); + *mtime_out = (time32_t) be32toh(header->v3_b.mtime); - *size_out = ntohll(header->v3_b.size); - *sectorsize_out = ntohl(header->v3_b.sectorsize); - *align_out = ntohl(header->v3_b.alignment); + *size_out = be64toh(header->v3_b.size); + *sectorsize_out = be32toh(header->v3_b.sectorsize); + *align_out = be32toh(header->v3_b.alignment); if (*align_out == 0) { cow_printf("read_cow_header - invalid COW header, " "align == 0\n"); diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index e672bd6d43e3..43b39d61b538 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -22,6 +22,7 @@ #include <linux/workqueue.h> #include <linux/mutex.h> #include <asm/uaccess.h> +#include <asm/switch_to.h> #include "init.h" #include "irq_kern.h" diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild index 8419f5cf2ac7..fff24352255d 100644 --- a/arch/um/include/asm/Kbuild +++ b/arch/um/include/asm/Kbuild @@ -1,3 +1,4 @@ generic-y += bug.h cputime.h device.h emergency-restart.h futex.h hardirq.h generic-y += hw_irq.h irq_regs.h kdebug.h percpu.h sections.h topology.h xor.h -generic-y += ftrace.h pci.h io.h param.h delay.h mutex.h current.h +generic-y += ftrace.h pci.h io.h param.h delay.h mutex.h current.h exec.h +generic-y += switch_to.h diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index 492bc4c1b62b..65a1c3d690ea 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile @@ -3,9 +3,10 @@ # Licensed under the GPL # -CPPFLAGS_vmlinux.lds := -DSTART=$(LDS_START) \ - -DELF_ARCH=$(LDS_ELF_ARCH) \ - -DELF_FORMAT=$(LDS_ELF_FORMAT) +CPPFLAGS_vmlinux.lds := -DSTART=$(LDS_START) \ + -DELF_ARCH=$(LDS_ELF_ARCH) \ + -DELF_FORMAT=$(LDS_ELF_FORMAT) \ + $(LDS_EXTRA) extra-y := vmlinux.lds clean-files := diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index f386d04a84a5..2b73dedb44ca 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -88,11 +88,8 @@ static inline void set_current(struct task_struct *task) extern void arch_switch_to(struct task_struct *to); -void *_switch_to(void *prev, void *next, void *last) +void *__switch_to(struct task_struct *from, struct task_struct *to) { - struct task_struct *from = prev; - struct task_struct *to = next; - to->thread.prev_sched = from; set_current(to); @@ -111,7 +108,6 @@ void *_switch_to(void *prev, void *next, void *last) } while (current->thread.saved_task); return current->thread.prev_sched; - } void interrupt_end(void) diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 4947b319f53a..0a49ef0c2bf4 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -103,7 +103,6 @@ int init_new_context(struct task_struct *task, struct mm_struct *mm) void uml_setup_stubs(struct mm_struct *mm) { - struct page **pages; int err, ret; if (!skas_needs_stub) diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index 2e9852c0d487..0a9e57e7446b 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -41,7 +41,7 @@ static int __init start_kernel_proc(void *unused) cpu_tasks[0].pid = pid; cpu_tasks[0].task = current; #ifdef CONFIG_SMP - cpu_online_map = cpumask_of_cpu(0); + init_cpu_online(get_cpu_mask(0)); #endif start_kernel(); return 0; diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c index 155206a66908..6f588e160fb0 100644 --- a/arch/um/kernel/smp.c +++ b/arch/um/kernel/smp.c @@ -76,7 +76,7 @@ static int idle_proc(void *cpup) cpu_relax(); notify_cpu_starting(cpu); - cpu_set(cpu, cpu_online_map); + set_cpu_online(cpu, true); default_idle(); return 0; } @@ -110,8 +110,7 @@ void smp_prepare_cpus(unsigned int maxcpus) for (i = 0; i < ncpus; ++i) set_cpu_possible(i, true); - cpu_clear(me, cpu_online_map); - cpu_set(me, cpu_online_map); + set_cpu_online(me, true); cpu_set(me, cpu_callin_map); err = os_pipe(cpu_data[me].ipi_pipe, 1, 1); @@ -138,13 +137,13 @@ void smp_prepare_cpus(unsigned int maxcpus) void smp_prepare_boot_cpu(void) { - cpu_set(smp_processor_id(), cpu_online_map); + set_cpu_online(smp_processor_id(), true); } int __cpu_up(unsigned int cpu) { cpu_set(cpu, smp_commenced_mask); - while (!cpu_isset(cpu, cpu_online_map)) + while (!cpu_online(cpu)) mb(); return 0; } diff --git a/arch/unicore32/include/asm/dma-mapping.h b/arch/unicore32/include/asm/dma-mapping.h index 9258e592f414..366460a81796 100644 --- a/arch/unicore32/include/asm/dma-mapping.h +++ b/arch/unicore32/include/asm/dma-mapping.h @@ -82,20 +82,26 @@ static inline int dma_set_mask(struct device *dev, u64 dma_mask) return 0; } -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { struct dma_map_ops *dma_ops = get_dma_ops(dev); - return dma_ops->alloc_coherent(dev, size, dma_handle, flag); + return dma_ops->alloc(dev, size, dma_handle, flag, attrs); } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { struct dma_map_ops *dma_ops = get_dma_ops(dev); - dma_ops->free_coherent(dev, size, cpu_addr, dma_handle); + dma_ops->free(dev, size, cpu_addr, dma_handle, attrs); } #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) diff --git a/arch/unicore32/mm/dma-swiotlb.c b/arch/unicore32/mm/dma-swiotlb.c index bfa9fbb2bbb1..16c08b2143a7 100644 --- a/arch/unicore32/mm/dma-swiotlb.c +++ b/arch/unicore32/mm/dma-swiotlb.c @@ -17,9 +17,23 @@ #include <asm/dma.h> +static void *unicore_swiotlb_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs) +{ + return swiotlb_alloc_coherent(dev, size, dma_handle, flags); +} + +static void unicore_swiotlb_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_addr, + struct dma_attrs *attrs) +{ + swiotlb_free_coherent(dev, size, vaddr, dma_addr); +} + struct dma_map_ops swiotlb_dma_map_ops = { - .alloc_coherent = swiotlb_alloc_coherent, - .free_coherent = swiotlb_free_coherent, + .alloc = unicore_swiotlb_alloc_coherent, + .free = unicore_swiotlb_free_coherent, .map_sg = swiotlb_map_sg_attrs, .unmap_sg = swiotlb_unmap_sg_attrs, .dma_supported = swiotlb_dma_supported, diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1d14cc6b79ad..c9866b0b77d8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -81,7 +81,7 @@ config X86 select CLKEVT_I8253 select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_IOMAP - select DCACHE_WORD_ACCESS if !DEBUG_PAGEALLOC + select DCACHE_WORD_ACCESS config INSTRUCTION_DECODER def_bool (KPROBES || PERF_EVENTS) diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 968dbe24a255..41a7237606a3 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -129,6 +129,7 @@ KBUILD_CFLAGS += -Wno-sign-compare KBUILD_CFLAGS += -fno-asynchronous-unwind-tables # prevent gcc from generating any FP code by mistake KBUILD_CFLAGS += $(call cc-option,-mno-sse -mno-mmx -mno-sse2 -mno-3dnow,) +KBUILD_CFLAGS += $(call cc-option,-mno-avx,) KBUILD_CFLAGS += $(mflags-y) KBUILD_AFLAGS += $(mflags-y) diff --git a/arch/x86/Makefile.um b/arch/x86/Makefile.um index 4be406abeefd..36b62bc52638 100644 --- a/arch/x86/Makefile.um +++ b/arch/x86/Makefile.um @@ -14,6 +14,9 @@ LINK-y += $(call cc-option,-m32) export LDFLAGS +LDS_EXTRA := -Ui386 +export LDS_EXTRA + # First of all, tune CFLAGS for the specific CPU. This actually sets cflags-y. include $(srctree)/arch/x86/Makefile_32.cpu diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S index a0559930a180..c85e3ac99bba 100644 --- a/arch/x86/boot/compressed/head_32.S +++ b/arch/x86/boot/compressed/head_32.S @@ -33,6 +33,9 @@ __HEAD ENTRY(startup_32) #ifdef CONFIG_EFI_STUB + jmp preferred_addr + + .balign 0x10 /* * We don't need the return address, so set up the stack so * efi_main() can find its arugments. @@ -41,12 +44,17 @@ ENTRY(startup_32) call efi_main cmpl $0, %eax - je preferred_addr movl %eax, %esi - call 1f + jne 2f 1: + /* EFI init failed, so hang. */ + hlt + jmp 1b +2: + call 3f +3: popl %eax - subl $1b, %eax + subl $3b, %eax subl BP_pref_address(%esi), %eax add BP_code32_start(%esi), %eax leal preferred_addr(%eax), %eax diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index 558d76ce23bc..87e03a13d8e3 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -200,18 +200,28 @@ ENTRY(startup_64) * entire text+data+bss and hopefully all of memory. */ #ifdef CONFIG_EFI_STUB - pushq %rsi + /* + * The entry point for the PE/COFF executable is 0x210, so only + * legacy boot loaders will execute this jmp. + */ + jmp preferred_addr + + .org 0x210 mov %rcx, %rdi mov %rdx, %rsi call efi_main - popq %rsi - cmpq $0,%rax - je preferred_addr movq %rax,%rsi - call 1f + cmpq $0,%rax + jne 2f 1: + /* EFI init failed, so hang. */ + hlt + jmp 1b +2: + call 3f +3: popq %rax - subq $1b, %rax + subq $3b, %rax subq BP_pref_address(%rsi), %rax add BP_code32_start(%esi), %eax leaq preferred_addr(%rax), %rax diff --git a/arch/x86/boot/compressed/relocs.c b/arch/x86/boot/compressed/relocs.c index d3c0b0277666..fb7117a4ade1 100644 --- a/arch/x86/boot/compressed/relocs.c +++ b/arch/x86/boot/compressed/relocs.c @@ -403,13 +403,11 @@ static void print_absolute_symbols(void) for (i = 0; i < ehdr.e_shnum; i++) { struct section *sec = &secs[i]; char *sym_strtab; - Elf32_Sym *sh_symtab; int j; if (sec->shdr.sh_type != SHT_SYMTAB) { continue; } - sh_symtab = sec->symtab; sym_strtab = sec->link->strtab; for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { Elf32_Sym *sym; diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c index ed549767a231..24443a332083 100644 --- a/arch/x86/boot/tools/build.c +++ b/arch/x86/boot/tools/build.c @@ -205,8 +205,13 @@ int main(int argc, char ** argv) put_unaligned_le32(file_sz, &buf[pe_header + 0x50]); #ifdef CONFIG_X86_32 - /* Address of entry point */ - put_unaligned_le32(i, &buf[pe_header + 0x28]); + /* + * Address of entry point. + * + * The EFI stub entry point is +16 bytes from the start of + * the .text section. + */ + put_unaligned_le32(i + 16, &buf[pe_header + 0x28]); /* .text size */ put_unaligned_le32(file_sz, &buf[pe_header + 0xb0]); @@ -217,9 +222,11 @@ int main(int argc, char ** argv) /* * Address of entry point. startup_32 is at the beginning and * the 64-bit entry point (startup_64) is always 512 bytes - * after. + * after. The EFI stub entry point is 16 bytes after that, as + * the first instruction allows legacy loaders to jump over + * the EFI stub initialisation */ - put_unaligned_le32(i + 512, &buf[pe_header + 0x28]); + put_unaligned_le32(i + 528, &buf[pe_header + 0x28]); /* .text size */ put_unaligned_le32(file_sz, &buf[pe_header + 0xc0]); diff --git a/arch/x86/ia32/ia32_aout.c b/arch/x86/ia32/ia32_aout.c index d511d951a052..07b3a68d2d29 100644 --- a/arch/x86/ia32/ia32_aout.c +++ b/arch/x86/ia32/ia32_aout.c @@ -119,9 +119,7 @@ static void set_brk(unsigned long start, unsigned long end) end = PAGE_ALIGN(end); if (end <= start) return; - down_write(¤t->mm->mmap_sem); - do_brk(start, end - start); - up_write(¤t->mm->mmap_sem); + vm_brk(start, end - start); } #ifdef CORE_DUMP @@ -296,8 +294,7 @@ static int load_aout_binary(struct linux_binprm *bprm, struct pt_regs *regs) /* OK, This is the point of no return */ set_personality(PER_LINUX); - set_thread_flag(TIF_IA32); - current->mm->context.ia32_compat = 1; + set_personality_ia32(false); setup_new_exec(bprm); @@ -332,9 +329,7 @@ static int load_aout_binary(struct linux_binprm *bprm, struct pt_regs *regs) pos = 32; map_size = ex.a_text+ex.a_data; - down_write(¤t->mm->mmap_sem); - error = do_brk(text_addr & PAGE_MASK, map_size); - up_write(¤t->mm->mmap_sem); + error = vm_brk(text_addr & PAGE_MASK, map_size); if (error != (text_addr & PAGE_MASK)) { send_sig(SIGKILL, current, 0); @@ -373,9 +368,7 @@ static int load_aout_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (!bprm->file->f_op->mmap || (fd_offset & ~PAGE_MASK) != 0) { loff_t pos = fd_offset; - down_write(¤t->mm->mmap_sem); - do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data); - up_write(¤t->mm->mmap_sem); + vm_brk(N_TXTADDR(ex), ex.a_text+ex.a_data); bprm->file->f_op->read(bprm->file, (char __user *)N_TXTADDR(ex), ex.a_text+ex.a_data, &pos); @@ -385,26 +378,22 @@ static int load_aout_binary(struct linux_binprm *bprm, struct pt_regs *regs) goto beyond_if; } - down_write(¤t->mm->mmap_sem); - error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text, + error = vm_mmap(bprm->file, N_TXTADDR(ex), ex.a_text, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE | MAP_32BIT, fd_offset); - up_write(¤t->mm->mmap_sem); if (error != N_TXTADDR(ex)) { send_sig(SIGKILL, current, 0); return error; } - down_write(¤t->mm->mmap_sem); - error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data, + error = vm_mmap(bprm->file, N_DATADDR(ex), ex.a_data, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE | MAP_32BIT, fd_offset + ex.a_text); - up_write(¤t->mm->mmap_sem); if (error != N_DATADDR(ex)) { send_sig(SIGKILL, current, 0); return error; @@ -476,9 +465,7 @@ static int load_aout_library(struct file *file) error_time = jiffies; } #endif - down_write(¤t->mm->mmap_sem); - do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss); - up_write(¤t->mm->mmap_sem); + vm_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss); file->f_op->read(file, (char __user *)start_addr, ex.a_text + ex.a_data, &pos); @@ -490,12 +477,10 @@ static int load_aout_library(struct file *file) goto out; } /* Now use mmap to map the library into memory. */ - down_write(¤t->mm->mmap_sem); - error = do_mmap(file, start_addr, ex.a_text + ex.a_data, + error = vm_mmap(file, start_addr, ex.a_text + ex.a_data, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_32BIT, N_TXTOFF(ex)); - up_write(¤t->mm->mmap_sem); retval = error; if (error != start_addr) goto out; @@ -503,9 +488,7 @@ static int load_aout_library(struct file *file) len = PAGE_ALIGN(ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss; if (bss > len) { - down_write(¤t->mm->mmap_sem); - error = do_brk(start_addr + len, bss - len); - up_write(¤t->mm->mmap_sem); + error = vm_brk(start_addr + len, bss - len); retval = error; if (error != start_addr + len) goto out; diff --git a/arch/x86/include/asm/cmpxchg.h b/arch/x86/include/asm/cmpxchg.h index b3b733262909..99480e55973d 100644 --- a/arch/x86/include/asm/cmpxchg.h +++ b/arch/x86/include/asm/cmpxchg.h @@ -43,7 +43,7 @@ extern void __add_wrong_size(void) switch (sizeof(*(ptr))) { \ case __X86_CASE_B: \ asm volatile (lock #op "b %b0, %1\n" \ - : "+r" (__ret), "+m" (*(ptr)) \ + : "+q" (__ret), "+m" (*(ptr)) \ : : "memory", "cc"); \ break; \ case __X86_CASE_W: \ @@ -173,7 +173,7 @@ extern void __add_wrong_size(void) switch (sizeof(*(ptr))) { \ case __X86_CASE_B: \ asm volatile (lock "addb %b1, %0\n" \ - : "+m" (*(ptr)) : "ri" (inc) \ + : "+m" (*(ptr)) : "qi" (inc) \ : "memory", "cc"); \ break; \ case __X86_CASE_W: \ diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h index ed3065fd6314..4b4331d71935 100644 --- a/arch/x86/include/asm/dma-mapping.h +++ b/arch/x86/include/asm/dma-mapping.h @@ -59,7 +59,8 @@ extern int dma_supported(struct device *hwdev, u64 mask); extern int dma_set_mask(struct device *dev, u64 mask); extern void *dma_generic_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_addr, gfp_t flag); + dma_addr_t *dma_addr, gfp_t flag, + struct dma_attrs *attrs); static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) { @@ -111,9 +112,11 @@ static inline gfp_t dma_alloc_coherent_gfp_flags(struct device *dev, gfp_t gfp) return gfp; } +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + static inline void * -dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, - gfp_t gfp) +dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle, + gfp_t gfp, struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); void *memory; @@ -129,18 +132,21 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, if (!is_device_dma_capable(dev)) return NULL; - if (!ops->alloc_coherent) + if (!ops->alloc) return NULL; - memory = ops->alloc_coherent(dev, size, dma_handle, - dma_alloc_coherent_gfp_flags(dev, gfp)); + memory = ops->alloc(dev, size, dma_handle, + dma_alloc_coherent_gfp_flags(dev, gfp), attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, memory); return memory; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t bus) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *vaddr, dma_addr_t bus, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); @@ -150,8 +156,8 @@ static inline void dma_free_coherent(struct device *dev, size_t size, return; debug_dma_free_coherent(dev, size, vaddr, bus); - if (ops->free_coherent) - ops->free_coherent(dev, size, vaddr, bus); + if (ops->free) + ops->free(dev, size, vaddr, bus, attrs); } #endif diff --git a/arch/x86/include/asm/posix_types.h b/arch/x86/include/asm/posix_types.h index 3427b7798dbc..7ef7c3020e5c 100644 --- a/arch/x86/include/asm/posix_types.h +++ b/arch/x86/include/asm/posix_types.h @@ -7,9 +7,9 @@ #else # ifdef __i386__ # include "posix_types_32.h" -# elif defined(__LP64__) -# include "posix_types_64.h" -# else +# elif defined(__ILP32__) # include "posix_types_x32.h" +# else +# include "posix_types_64.h" # endif #endif diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 7284c9a6a0b5..4fa7dcceb6c0 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -974,16 +974,6 @@ extern bool cpu_has_amd_erratum(const int *); #define cpu_has_amd_erratum(x) (false) #endif /* CONFIG_CPU_SUP_AMD */ -#ifdef CONFIG_X86_32 -/* - * disable hlt during certain critical i/o operations - */ -#define HAVE_DISABLE_HLT -#endif - -void disable_hlt(void); -void enable_hlt(void); - void cpu_idle_wait(void); extern unsigned long arch_align_stack(unsigned long sp); diff --git a/arch/x86/include/asm/sigcontext.h b/arch/x86/include/asm/sigcontext.h index 4a085383af27..5ca71c065eef 100644 --- a/arch/x86/include/asm/sigcontext.h +++ b/arch/x86/include/asm/sigcontext.h @@ -257,7 +257,7 @@ struct sigcontext { __u64 oldmask; __u64 cr2; struct _fpstate __user *fpstate; /* zero when no FPU context */ -#ifndef __LP64__ +#ifdef __ILP32__ __u32 __fpstate_pad; #endif __u64 reserved1[8]; diff --git a/arch/x86/include/asm/siginfo.h b/arch/x86/include/asm/siginfo.h index fc1aa5535646..34c47b3341c0 100644 --- a/arch/x86/include/asm/siginfo.h +++ b/arch/x86/include/asm/siginfo.h @@ -2,7 +2,13 @@ #define _ASM_X86_SIGINFO_H #ifdef __x86_64__ -# define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int)) +# ifdef __ILP32__ /* x32 */ +typedef long long __kernel_si_clock_t __attribute__((aligned(4))); +# define __ARCH_SI_CLOCK_T __kernel_si_clock_t +# define __ARCH_SI_ATTRIBUTES __attribute__((aligned(8))) +# else /* x86-64 */ +# define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int)) +# endif #endif #include <asm-generic/siginfo.h> diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 8be5f54d9360..e0544597cfe7 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -557,6 +557,8 @@ struct __large_struct { unsigned long buf[100]; }; extern unsigned long copy_from_user_nmi(void *to, const void __user *from, unsigned long n); +extern __must_check long +strncpy_from_user(char *dst, const char __user *src, long count); /* * movsl can be slow when source and dest are not both 8-byte aligned diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h index 566e803cc602..8084bc73b18c 100644 --- a/arch/x86/include/asm/uaccess_32.h +++ b/arch/x86/include/asm/uaccess_32.h @@ -213,11 +213,6 @@ static inline unsigned long __must_check copy_from_user(void *to, return n; } -long __must_check strncpy_from_user(char *dst, const char __user *src, - long count); -long __must_check __strncpy_from_user(char *dst, - const char __user *src, long count); - /** * strlen_user: - Get the size of a string in user space. * @str: The string to measure. diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index 1c66d30971ad..fcd4b6f3ef02 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -208,10 +208,6 @@ int __copy_in_user(void __user *dst, const void __user *src, unsigned size) } } -__must_check long -strncpy_from_user(char *dst, const char __user *src, long count); -__must_check long -__strncpy_from_user(char *dst, const char __user *src, long count); __must_check long strnlen_user(const char __user *str, long n); __must_check long __strnlen_user(const char __user *str, long n); __must_check long strlen_user(const char __user *str); diff --git a/arch/x86/include/asm/unistd.h b/arch/x86/include/asm/unistd.h index 37cdc9d99bb1..4437001d8e3d 100644 --- a/arch/x86/include/asm/unistd.h +++ b/arch/x86/include/asm/unistd.h @@ -63,10 +63,10 @@ #else # ifdef __i386__ # include <asm/unistd_32.h> -# elif defined(__LP64__) -# include <asm/unistd_64.h> -# else +# elif defined(__ILP32__) # include <asm/unistd_x32.h> +# else +# include <asm/unistd_64.h> # endif #endif diff --git a/arch/x86/include/asm/word-at-a-time.h b/arch/x86/include/asm/word-at-a-time.h new file mode 100644 index 000000000000..e58f03b206c3 --- /dev/null +++ b/arch/x86/include/asm/word-at-a-time.h @@ -0,0 +1,79 @@ +#ifndef _ASM_WORD_AT_A_TIME_H +#define _ASM_WORD_AT_A_TIME_H + +/* + * This is largely generic for little-endian machines, but the + * optimal byte mask counting is probably going to be something + * that is architecture-specific. If you have a reliably fast + * bit count instruction, that might be better than the multiply + * and shift, for example. + */ + +#ifdef CONFIG_64BIT + +/* + * Jan Achrenius on G+: microoptimized version of + * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56" + * that works for the bytemasks without having to + * mask them first. + */ +static inline long count_masked_bytes(unsigned long mask) +{ + return mask*0x0001020304050608ul >> 56; +} + +#else /* 32-bit case */ + +/* Carl Chatfield / Jan Achrenius G+ version for 32-bit */ +static inline long count_masked_bytes(long mask) +{ + /* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */ + long a = (0x0ff0001+mask) >> 23; + /* Fix the 1 for 00 case */ + return a & mask; +} + +#endif + +#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) + +/* Return the high bit set in the first byte that is a zero */ +static inline unsigned long has_zero(unsigned long a) +{ + return ((a - REPEAT_BYTE(0x01)) & ~a) & REPEAT_BYTE(0x80); +} + +/* + * Load an unaligned word from kernel space. + * + * In the (very unlikely) case of the word being a page-crosser + * and the next page not being mapped, take the exception and + * return zeroes in the non-existing part. + */ +static inline unsigned long load_unaligned_zeropad(const void *addr) +{ + unsigned long ret, dummy; + + asm( + "1:\tmov %2,%0\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3:\t" + "lea %2,%1\n\t" + "and %3,%1\n\t" + "mov (%1),%0\n\t" + "leal %2,%%ecx\n\t" + "andl %4,%%ecx\n\t" + "shll $3,%%ecx\n\t" + "shr %%cl,%0\n\t" + "jmp 2b\n" + ".previous\n" + _ASM_EXTABLE(1b, 3b) + :"=&r" (ret),"=&c" (dummy) + :"m" (*(unsigned long *)addr), + "i" (-sizeof(unsigned long)), + "i" (sizeof(unsigned long)-1)); + return ret; +} + +#endif /* _ASM_WORD_AT_A_TIME_H */ diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index baaca8defec8..764b66a4cf89 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -195,6 +195,5 @@ extern struct x86_msi_ops x86_msi; extern void x86_init_noop(void); extern void x86_init_uint_noop(unsigned int unused); -extern void x86_default_fixup_cpu_id(struct cpuinfo_x86 *c, int node); #endif diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index 103b6ab368d3..146a49c763a4 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -24,6 +24,10 @@ unsigned long acpi_realmode_flags; static char temp_stack[4096]; #endif +asmlinkage void acpi_enter_s3(void) +{ + acpi_enter_sleep_state(3, wake_sleep_flags); +} /** * acpi_suspend_lowlevel - save kernel state * diff --git a/arch/x86/kernel/acpi/sleep.h b/arch/x86/kernel/acpi/sleep.h index 416d4be13fef..d68677a2a010 100644 --- a/arch/x86/kernel/acpi/sleep.h +++ b/arch/x86/kernel/acpi/sleep.h @@ -3,12 +3,16 @@ */ #include <asm/trampoline.h> +#include <linux/linkage.h> extern unsigned long saved_video_mode; extern long saved_magic; extern int wakeup_pmode_return; +extern u8 wake_sleep_flags; +extern asmlinkage void acpi_enter_s3(void); + extern unsigned long acpi_copy_wakeup_routine(unsigned long); extern void wakeup_long64(void); diff --git a/arch/x86/kernel/acpi/wakeup_32.S b/arch/x86/kernel/acpi/wakeup_32.S index 13ab720573e3..72610839f03b 100644 --- a/arch/x86/kernel/acpi/wakeup_32.S +++ b/arch/x86/kernel/acpi/wakeup_32.S @@ -74,9 +74,7 @@ restore_registers: ENTRY(do_suspend_lowlevel) call save_processor_state call save_registers - pushl $3 - call acpi_enter_sleep_state - addl $4, %esp + call acpi_enter_s3 # In case of S3 failure, we'll emerge here. Jump # to ret_point to recover diff --git a/arch/x86/kernel/acpi/wakeup_64.S b/arch/x86/kernel/acpi/wakeup_64.S index 8ea5164cbd04..014d1d28c397 100644 --- a/arch/x86/kernel/acpi/wakeup_64.S +++ b/arch/x86/kernel/acpi/wakeup_64.S @@ -71,9 +71,7 @@ ENTRY(do_suspend_lowlevel) movq %rsi, saved_rsi addq $8, %rsp - movl $3, %edi - xorl %eax, %eax - call acpi_enter_sleep_state + call acpi_enter_s3 /* in case something went wrong, restore the machine status and go on */ jmp resume_point diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c index b1e7c7f7a0af..e66311200cbd 100644 --- a/arch/x86/kernel/amd_gart_64.c +++ b/arch/x86/kernel/amd_gart_64.c @@ -477,7 +477,7 @@ error: /* allocate and map a coherent mapping */ static void * gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr, - gfp_t flag) + gfp_t flag, struct dma_attrs *attrs) { dma_addr_t paddr; unsigned long align_mask; @@ -500,7 +500,8 @@ gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr, } __free_pages(page, get_order(size)); } else - return dma_generic_alloc_coherent(dev, size, dma_addr, flag); + return dma_generic_alloc_coherent(dev, size, dma_addr, flag, + attrs); return NULL; } @@ -508,7 +509,7 @@ gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr, /* free a coherent mapping */ static void gart_free_coherent(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_addr) + dma_addr_t dma_addr, struct dma_attrs *attrs) { gart_unmap_page(dev, dma_addr, size, DMA_BIDIRECTIONAL, NULL); free_pages((unsigned long)vaddr, get_order(size)); @@ -700,8 +701,8 @@ static struct dma_map_ops gart_dma_ops = { .unmap_sg = gart_unmap_sg, .map_page = gart_map_page, .unmap_page = gart_unmap_page, - .alloc_coherent = gart_alloc_coherent, - .free_coherent = gart_free_coherent, + .alloc = gart_alloc_coherent, + .free = gart_free_coherent, .mapping_error = gart_mapping_error, }; diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index 11544d8f1e97..edc24480469f 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -1637,9 +1637,11 @@ static int __init apic_verify(void) mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; /* The BIOS may have set up the APIC at some other address */ - rdmsr(MSR_IA32_APICBASE, l, h); - if (l & MSR_IA32_APICBASE_ENABLE) - mp_lapic_addr = l & MSR_IA32_APICBASE_BASE; + if (boot_cpu_data.x86 >= 6) { + rdmsr(MSR_IA32_APICBASE, l, h); + if (l & MSR_IA32_APICBASE_ENABLE) + mp_lapic_addr = l & MSR_IA32_APICBASE_BASE; + } pr_info("Found and enabled local APIC!\n"); return 0; @@ -1657,13 +1659,15 @@ int __init apic_force_enable(unsigned long addr) * MSR. This can only be done in software for Intel P6 or later * and AMD K7 (Model > 1) or later. */ - rdmsr(MSR_IA32_APICBASE, l, h); - if (!(l & MSR_IA32_APICBASE_ENABLE)) { - pr_info("Local APIC disabled by BIOS -- reenabling.\n"); - l &= ~MSR_IA32_APICBASE_BASE; - l |= MSR_IA32_APICBASE_ENABLE | addr; - wrmsr(MSR_IA32_APICBASE, l, h); - enabled_via_apicbase = 1; + if (boot_cpu_data.x86 >= 6) { + rdmsr(MSR_IA32_APICBASE, l, h); + if (!(l & MSR_IA32_APICBASE_ENABLE)) { + pr_info("Local APIC disabled by BIOS -- reenabling.\n"); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | addr; + wrmsr(MSR_IA32_APICBASE, l, h); + enabled_via_apicbase = 1; + } } return apic_verify(); } @@ -2209,10 +2213,12 @@ static void lapic_resume(void) * FIXME! This will be wrong if we ever support suspend on * SMP! We'll need to do this as part of the CPU restore! */ - rdmsr(MSR_IA32_APICBASE, l, h); - l &= ~MSR_IA32_APICBASE_BASE; - l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; - wrmsr(MSR_IA32_APICBASE, l, h); + if (boot_cpu_data.x86 >= 6) { + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; + wrmsr(MSR_IA32_APICBASE, l, h); + } } maxlvt = lapic_get_maxlvt(); diff --git a/arch/x86/kernel/apic/apic_numachip.c b/arch/x86/kernel/apic/apic_numachip.c index 899803e03214..23e75422e013 100644 --- a/arch/x86/kernel/apic/apic_numachip.c +++ b/arch/x86/kernel/apic/apic_numachip.c @@ -207,8 +207,11 @@ static void __init map_csrs(void) static void fixup_cpu_id(struct cpuinfo_x86 *c, int node) { - c->phys_proc_id = node; - per_cpu(cpu_llc_id, smp_processor_id()) = node; + + if (c->phys_proc_id != node) { + c->phys_proc_id = node; + per_cpu(cpu_llc_id, smp_processor_id()) = node; + } } static int __init numachip_system_init(void) diff --git a/arch/x86/kernel/apic/x2apic_phys.c b/arch/x86/kernel/apic/x2apic_phys.c index 8a778db45e3a..991e315f4227 100644 --- a/arch/x86/kernel/apic/x2apic_phys.c +++ b/arch/x86/kernel/apic/x2apic_phys.c @@ -24,6 +24,12 @@ static int x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id) { if (x2apic_phys) return x2apic_enabled(); + else if ((acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID) && + (acpi_gbl_FADT.flags & ACPI_FADT_APIC_PHYSICAL) && + x2apic_enabled()) { + printk(KERN_DEBUG "System requires x2apic physical mode\n"); + return 1; + } else return 0; } diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 0a44b90602b0..146bb6218eec 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -26,7 +26,8 @@ * contact AMD for precise details and a CPU swap. * * See http://www.multimania.com/poulot/k6bug.html - * http://www.amd.com/K6/k6docs/revgd.html + * and section 2.6.2 of "AMD-K6 Processor Revision Guide - Model 6" + * (Publication # 21266 Issue Date: August 1998) * * The following test is erm.. interesting. AMD neglected to up * the chip setting when fixing the bug but they also tweaked some @@ -94,7 +95,6 @@ static void __cpuinit init_amd_k6(struct cpuinfo_x86 *c) "system stability may be impaired when more than 32 MB are used.\n"); else printk(KERN_CONT "probably OK (after B9730xxxx).\n"); - printk(KERN_INFO "Please see http://membres.lycos.fr/poulot/k6bug.html\n"); } /* K6 with old style WHCR */ @@ -353,10 +353,11 @@ static void __cpuinit srat_detect_node(struct cpuinfo_x86 *c) node = per_cpu(cpu_llc_id, cpu); /* - * If core numbers are inconsistent, it's likely a multi-fabric platform, - * so invoke platform-specific handler + * On multi-fabric platform (e.g. Numascale NumaChip) a + * platform-specific handler needs to be called to fixup some + * IDs of the CPU. */ - if (c->phys_proc_id != node) + if (x86_cpuinit.fixup_cpu_id) x86_cpuinit.fixup_cpu_id(c, node); if (!node_online(node)) { @@ -579,6 +580,24 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) } } + /* re-enable TopologyExtensions if switched off by BIOS */ + if ((c->x86 == 0x15) && + (c->x86_model >= 0x10) && (c->x86_model <= 0x1f) && + !cpu_has(c, X86_FEATURE_TOPOEXT)) { + u64 val; + + if (!rdmsrl_amd_safe(0xc0011005, &val)) { + val |= 1ULL << 54; + wrmsrl_amd_safe(0xc0011005, val); + rdmsrl(0xc0011005, val); + if (val & (1ULL << 54)) { + set_cpu_cap(c, X86_FEATURE_TOPOEXT); + printk(KERN_INFO FW_INFO "CPU: Re-enabling " + "disabled Topology Extensions Support\n"); + } + } + } + cpu_detect_cache_sizes(c); /* Multi core CPU? */ diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 67e258362a3d..cf79302198a6 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1163,15 +1163,6 @@ static void dbg_restore_debug_regs(void) #endif /* ! CONFIG_KGDB */ /* - * Prints an error where the NUMA and configured core-number mismatch and the - * platform didn't override this to fix it up - */ -void __cpuinit x86_default_fixup_cpu_id(struct cpuinfo_x86 *c, int node) -{ - pr_err("NUMA core number %d differs from configured core number %d\n", node, c->phys_proc_id); -} - -/* * cpu_init() initializes state that is per-CPU. Some data is already * initialized (naturally) in the bootstrap process, such as the GDT * and IDT. We reload them nevertheless, this function acts as a diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index 73d08ed98a64..b8f3653dddbc 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -433,14 +433,14 @@ int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, unsigned slot, /* check if @slot is already used or the index is already disabled */ ret = amd_get_l3_disable_slot(nb, slot); if (ret >= 0) - return -EINVAL; + return -EEXIST; if (index > nb->l3_cache.indices) return -EINVAL; /* check whether the other slot has disabled the same index already */ if (index == amd_get_l3_disable_slot(nb, !slot)) - return -EINVAL; + return -EEXIST; amd_l3_disable_index(nb, cpu, slot, index); @@ -468,8 +468,8 @@ static ssize_t store_cache_disable(struct _cpuid4_info *this_leaf, err = amd_set_l3_disable_slot(this_leaf->base.nb, cpu, slot, val); if (err) { if (err == -EEXIST) - printk(KERN_WARNING "L3 disable slot %d in use!\n", - slot); + pr_warning("L3 slot %d in use/index already disabled!\n", + slot); return err; } return count; diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index ef484d9d0a25..a2dfacfd7103 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -1271,6 +1271,17 @@ done: return num ? -EINVAL : 0; } +PMU_FORMAT_ATTR(cccr, "config:0-31" ); +PMU_FORMAT_ATTR(escr, "config:32-62"); +PMU_FORMAT_ATTR(ht, "config:63" ); + +static struct attribute *intel_p4_formats_attr[] = { + &format_attr_cccr.attr, + &format_attr_escr.attr, + &format_attr_ht.attr, + NULL, +}; + static __initconst const struct x86_pmu p4_pmu = { .name = "Netburst P4/Xeon", .handle_irq = p4_pmu_handle_irq, @@ -1305,6 +1316,8 @@ static __initconst const struct x86_pmu p4_pmu = { * the former idea is taken from OProfile code */ .perfctr_second_write = 1, + + .format_attrs = intel_p4_formats_attr, }; __init int p4_pmu_init(void) diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index 7734bcbb5a3a..2d6e6498c176 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c @@ -235,6 +235,7 @@ int init_fpu(struct task_struct *tsk) if (tsk_used_math(tsk)) { if (HAVE_HWFP && tsk == current) unlazy_fpu(tsk); + tsk->thread.fpu.last_cpu = ~0; return 0; } diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 7943e0c21bde..3dafc6003b7c 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -282,8 +282,13 @@ void fixup_irqs(void) else if (!(warned++)) set_affinity = 0; + /* + * We unmask if the irq was not marked masked by the + * core code. That respects the lazy irq disable + * behaviour. + */ if (!irqd_can_move_in_process_context(data) && - !irqd_irq_disabled(data) && chip->irq_unmask) + !irqd_irq_masked(data) && chip->irq_unmask) chip->irq_unmask(data); raw_spin_unlock(&desc->lock); diff --git a/arch/x86/kernel/kdebugfs.c b/arch/x86/kernel/kdebugfs.c index 90fcf62854bb..1d5d31ea686b 100644 --- a/arch/x86/kernel/kdebugfs.c +++ b/arch/x86/kernel/kdebugfs.c @@ -68,16 +68,9 @@ static ssize_t setup_data_read(struct file *file, char __user *user_buf, return count; } -static int setup_data_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - - return 0; -} - static const struct file_operations fops_setup_data = { .read = setup_data_read, - .open = setup_data_open, + .open = simple_open, .llseek = default_llseek, }; diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index db6720edfdd0..8bfb6146f753 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c @@ -43,6 +43,8 @@ #include <linux/smp.h> #include <linux/nmi.h> #include <linux/hw_breakpoint.h> +#include <linux/uaccess.h> +#include <linux/memory.h> #include <asm/debugreg.h> #include <asm/apicdef.h> @@ -741,6 +743,64 @@ void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) regs->ip = ip; } +int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) +{ + int err; + char opc[BREAK_INSTR_SIZE]; + + bpt->type = BP_BREAKPOINT; + err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr, + BREAK_INSTR_SIZE); + if (err) + return err; + err = probe_kernel_write((char *)bpt->bpt_addr, + arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE); +#ifdef CONFIG_DEBUG_RODATA + if (!err) + return err; + /* + * It is safe to call text_poke() because normal kernel execution + * is stopped on all cores, so long as the text_mutex is not locked. + */ + if (mutex_is_locked(&text_mutex)) + return -EBUSY; + text_poke((void *)bpt->bpt_addr, arch_kgdb_ops.gdb_bpt_instr, + BREAK_INSTR_SIZE); + err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE); + if (err) + return err; + if (memcmp(opc, arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE)) + return -EINVAL; + bpt->type = BP_POKE_BREAKPOINT; +#endif /* CONFIG_DEBUG_RODATA */ + return err; +} + +int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) +{ +#ifdef CONFIG_DEBUG_RODATA + int err; + char opc[BREAK_INSTR_SIZE]; + + if (bpt->type != BP_POKE_BREAKPOINT) + goto knl_write; + /* + * It is safe to call text_poke() because normal kernel execution + * is stopped on all cores, so long as the text_mutex is not locked. + */ + if (mutex_is_locked(&text_mutex)) + goto knl_write; + text_poke((void *)bpt->bpt_addr, bpt->saved_instr, BREAK_INSTR_SIZE); + err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE); + if (err || memcmp(opc, bpt->saved_instr, BREAK_INSTR_SIZE)) + goto knl_write; + return err; +knl_write: +#endif /* CONFIG_DEBUG_RODATA */ + return probe_kernel_write((char *)bpt->bpt_addr, + (char *)bpt->saved_instr, BREAK_INSTR_SIZE); +} + struct kgdb_arch arch_kgdb_ops = { /* Breakpoint instruction: */ .gdb_bpt_instr = { 0xcc }, diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 694d801bf606..e554e5ad2fe8 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -38,6 +38,7 @@ #include <asm/traps.h> #include <asm/desc.h> #include <asm/tlbflush.h> +#include <asm/idle.h> static int kvmapf = 1; @@ -78,7 +79,6 @@ struct kvm_task_sleep_node { u32 token; int cpu; bool halted; - struct mm_struct *mm; }; static struct kvm_task_sleep_head { @@ -125,9 +125,7 @@ void kvm_async_pf_task_wait(u32 token) n.token = token; n.cpu = smp_processor_id(); - n.mm = current->active_mm; n.halted = idle || preempt_count() > 1; - atomic_inc(&n.mm->mm_count); init_waitqueue_head(&n.wq); hlist_add_head(&n.link, &b->list); spin_unlock(&b->lock); @@ -160,9 +158,6 @@ EXPORT_SYMBOL_GPL(kvm_async_pf_task_wait); static void apf_task_wake_one(struct kvm_task_sleep_node *n) { hlist_del_init(&n->link); - if (!n->mm) - return; - mmdrop(n->mm); if (n->halted) smp_send_reschedule(n->cpu); else if (waitqueue_active(&n->wq)) @@ -206,7 +201,7 @@ again: * async PF was not yet handled. * Add dummy entry for the token. */ - n = kmalloc(sizeof(*n), GFP_ATOMIC); + n = kzalloc(sizeof(*n), GFP_ATOMIC); if (!n) { /* * Allocation failed! Busy wait while other cpu @@ -218,7 +213,6 @@ again: } n->token = token; n->cpu = smp_processor_id(); - n->mm = NULL; init_waitqueue_head(&n->wq); hlist_add_head(&n->link, &b->list); } else @@ -253,7 +247,10 @@ do_async_page_fault(struct pt_regs *regs, unsigned long error_code) kvm_async_pf_task_wait((u32)read_cr2()); break; case KVM_PV_REASON_PAGE_READY: + rcu_irq_enter(); + exit_idle(); kvm_async_pf_task_wake((u32)read_cr2()); + rcu_irq_exit(); break; } } diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index 73465aab28f8..8a2ce8fd41c0 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c @@ -82,11 +82,6 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) { struct cpuinfo_x86 *c = &cpu_data(cpu); - if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { - pr_warning("CPU%d: family %d not supported\n", cpu, c->x86); - return -1; - } - csig->rev = c->microcode; pr_info("CPU%d: patch_level=0x%08x\n", cpu, csig->rev); @@ -380,6 +375,13 @@ static struct microcode_ops microcode_amd_ops = { struct microcode_ops * __init init_amd_microcode(void) { + struct cpuinfo_x86 *c = &cpu_data(0); + + if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { + pr_warning("AMD CPU family 0x%x not supported\n", c->x86); + return NULL; + } + patch = (void *)get_zeroed_page(GFP_KERNEL); if (!patch) return NULL; diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c index 87a0f8688301..c9bda6d6035c 100644 --- a/arch/x86/kernel/microcode_core.c +++ b/arch/x86/kernel/microcode_core.c @@ -419,10 +419,8 @@ static int mc_device_add(struct device *dev, struct subsys_interface *sif) if (err) return err; - if (microcode_init_cpu(cpu) == UCODE_ERROR) { - sysfs_remove_group(&dev->kobj, &mc_attr_group); + if (microcode_init_cpu(cpu) == UCODE_ERROR) return -EINVAL; - } return err; } @@ -528,11 +526,11 @@ static int __init microcode_init(void) microcode_ops = init_intel_microcode(); else if (c->x86_vendor == X86_VENDOR_AMD) microcode_ops = init_amd_microcode(); - - if (!microcode_ops) { + else pr_err("no support for this CPU vendor\n"); + + if (!microcode_ops) return -ENODEV; - } microcode_pdev = platform_device_register_simple("microcode", -1, NULL, 0); diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c index 6ac5782f4d6b..d0b2fb9ccbb1 100644 --- a/arch/x86/kernel/pci-calgary_64.c +++ b/arch/x86/kernel/pci-calgary_64.c @@ -430,7 +430,7 @@ static void calgary_unmap_page(struct device *dev, dma_addr_t dma_addr, } static void* calgary_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, struct dma_attrs *attrs) { void *ret = NULL; dma_addr_t mapping; @@ -463,7 +463,8 @@ error: } static void calgary_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { unsigned int npages; struct iommu_table *tbl = find_iommu_table(dev); @@ -476,8 +477,8 @@ static void calgary_free_coherent(struct device *dev, size_t size, } static struct dma_map_ops calgary_dma_ops = { - .alloc_coherent = calgary_alloc_coherent, - .free_coherent = calgary_free_coherent, + .alloc = calgary_alloc_coherent, + .free = calgary_free_coherent, .map_sg = calgary_map_sg, .unmap_sg = calgary_unmap_sg, .map_page = calgary_map_page, diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 28e5e06fcba4..3003250ac51d 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -96,7 +96,8 @@ void __init pci_iommu_alloc(void) } } void *dma_generic_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_addr, gfp_t flag) + dma_addr_t *dma_addr, gfp_t flag, + struct dma_attrs *attrs) { unsigned long dma_mask; struct page *page; diff --git a/arch/x86/kernel/pci-nommu.c b/arch/x86/kernel/pci-nommu.c index 3af4af810c07..f96050685b46 100644 --- a/arch/x86/kernel/pci-nommu.c +++ b/arch/x86/kernel/pci-nommu.c @@ -75,7 +75,7 @@ static int nommu_map_sg(struct device *hwdev, struct scatterlist *sg, } static void nommu_free_coherent(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_addr) + dma_addr_t dma_addr, struct dma_attrs *attrs) { free_pages((unsigned long)vaddr, get_order(size)); } @@ -96,8 +96,8 @@ static void nommu_sync_sg_for_device(struct device *dev, } struct dma_map_ops nommu_dma_ops = { - .alloc_coherent = dma_generic_alloc_coherent, - .free_coherent = nommu_free_coherent, + .alloc = dma_generic_alloc_coherent, + .free = nommu_free_coherent, .map_sg = nommu_map_sg, .map_page = nommu_map_page, .sync_single_for_device = nommu_sync_single_for_device, diff --git a/arch/x86/kernel/pci-swiotlb.c b/arch/x86/kernel/pci-swiotlb.c index 8f972cbddef0..6c483ba98b9c 100644 --- a/arch/x86/kernel/pci-swiotlb.c +++ b/arch/x86/kernel/pci-swiotlb.c @@ -15,21 +15,30 @@ int swiotlb __read_mostly; static void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, - dma_addr_t *dma_handle, gfp_t flags) + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs) { void *vaddr; - vaddr = dma_generic_alloc_coherent(hwdev, size, dma_handle, flags); + vaddr = dma_generic_alloc_coherent(hwdev, size, dma_handle, flags, + attrs); if (vaddr) return vaddr; return swiotlb_alloc_coherent(hwdev, size, dma_handle, flags); } +static void x86_swiotlb_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_addr, + struct dma_attrs *attrs) +{ + swiotlb_free_coherent(dev, size, vaddr, dma_addr); +} + static struct dma_map_ops swiotlb_dma_ops = { .mapping_error = swiotlb_dma_mapping_error, - .alloc_coherent = x86_swiotlb_alloc_coherent, - .free_coherent = swiotlb_free_coherent, + .alloc = x86_swiotlb_alloc_coherent, + .free = x86_swiotlb_free_coherent, .sync_single_for_cpu = swiotlb_sync_single_for_cpu, .sync_single_for_device = swiotlb_sync_single_for_device, .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index a33afaa5ddb7..1d92a5ab6e8b 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -362,34 +362,10 @@ void (*pm_idle)(void); EXPORT_SYMBOL(pm_idle); #endif -#ifdef CONFIG_X86_32 -/* - * This halt magic was a workaround for ancient floppy DMA - * wreckage. It should be safe to remove. - */ -static int hlt_counter; -void disable_hlt(void) -{ - hlt_counter++; -} -EXPORT_SYMBOL(disable_hlt); - -void enable_hlt(void) -{ - hlt_counter--; -} -EXPORT_SYMBOL(enable_hlt); - -static inline int hlt_use_halt(void) -{ - return (!hlt_counter && boot_cpu_data.hlt_works_ok); -} -#else static inline int hlt_use_halt(void) { return 1; } -#endif #ifndef CONFIG_SMP static inline void play_dead(void) diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 733ca39f367e..43d8b48b23e6 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -423,6 +423,7 @@ void set_personality_ia32(bool x32) current_thread_info()->status |= TS_COMPAT; } } +EXPORT_SYMBOL_GPL(set_personality_ia32); unsigned long get_wchan(struct task_struct *p) { diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index 71f4727da373..5a98aa272184 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c @@ -185,10 +185,22 @@ void __init setup_per_cpu_areas(void) #endif rc = -EINVAL; if (pcpu_chosen_fc != PCPU_FC_PAGE) { - const size_t atom_size = cpu_has_pse ? PMD_SIZE : PAGE_SIZE; const size_t dyn_size = PERCPU_MODULE_RESERVE + PERCPU_DYNAMIC_RESERVE - PERCPU_FIRST_CHUNK_RESERVE; + size_t atom_size; + /* + * On 64bit, use PMD_SIZE for atom_size so that embedded + * percpu areas are aligned to PMD. This, in the future, + * can also allow using PMD mappings in vmalloc area. Use + * PAGE_SIZE on 32bit as vmalloc space is highly contended + * and large vmalloc area allocs can easily fail. + */ +#ifdef CONFIG_X86_64 + atom_size = PMD_SIZE; +#else + atom_size = PAGE_SIZE; +#endif rc = pcpu_embed_first_chunk(PERCPU_FIRST_CHUNK_RESERVE, dyn_size, atom_size, pcpu_cpu_distance, diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index f386dc49f988..7515cf0e1805 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -216,9 +216,9 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) current_thread_info()->sig_on_uaccess_error = 1; /* - * 0 is a valid user pointer (in the access_ok sense) on 32-bit and + * NULL is a valid user pointer (in the access_ok sense) on 32-bit and * 64-bit, so we don't need to special-case it here. For all the - * vsyscalls, 0 means "don't write anything" not "write it at + * vsyscalls, NULL means "don't write anything" not "write it at * address 0". */ ret = -EFAULT; @@ -247,7 +247,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) ret = sys_getcpu((unsigned __user *)regs->di, (unsigned __user *)regs->si, - 0); + NULL); break; } diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index e9f265fd79ae..9cf71d0b2d37 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -93,7 +93,6 @@ struct x86_init_ops x86_init __initdata = { struct x86_cpuinit_ops x86_cpuinit __cpuinitdata = { .early_percpu_clock_init = x86_init_noop, .setup_percpu_clockev = setup_secondary_APIC_clock, - .fixup_cpu_id = x86_default_fixup_cpu_id, }; static void default_nmi_init(void) { }; diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index a73f0c104813..2e88438ffd83 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -369,7 +369,7 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data) case MSR_CORE_PERF_FIXED_CTR_CTRL: if (pmu->fixed_ctr_ctrl == data) return 0; - if (!(data & 0xfffffffffffff444)) { + if (!(data & 0xfffffffffffff444ull)) { reprogram_fixed_counters(pmu, data); return 0; } @@ -459,17 +459,17 @@ void kvm_pmu_cpuid_update(struct kvm_vcpu *vcpu) pmu->available_event_types = ~entry->ebx & ((1ull << bitmap_len) - 1); if (pmu->version == 1) { - pmu->global_ctrl = (1 << pmu->nr_arch_gp_counters) - 1; - return; + pmu->nr_arch_fixed_counters = 0; + } else { + pmu->nr_arch_fixed_counters = min((int)(entry->edx & 0x1f), + X86_PMC_MAX_FIXED); + pmu->counter_bitmask[KVM_PMC_FIXED] = + ((u64)1 << ((entry->edx >> 5) & 0xff)) - 1; } - pmu->nr_arch_fixed_counters = min((int)(entry->edx & 0x1f), - X86_PMC_MAX_FIXED); - pmu->counter_bitmask[KVM_PMC_FIXED] = - ((u64)1 << ((entry->edx >> 5) & 0xff)) - 1; - pmu->global_ctrl_mask = ~(((1 << pmu->nr_arch_gp_counters) - 1) - | (((1ull << pmu->nr_arch_fixed_counters) - 1) - << X86_PMC_IDX_FIXED)); + pmu->global_ctrl = ((1 << pmu->nr_arch_gp_counters) - 1) | + (((1ull << pmu->nr_arch_fixed_counters) - 1) << X86_PMC_IDX_FIXED); + pmu->global_ctrl_mask = ~pmu->global_ctrl; } void kvm_pmu_init(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 280751c84724..4ff0ab9bc3c8 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2210,9 +2210,12 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) msr = find_msr_entry(vmx, msr_index); if (msr) { msr->data = data; - if (msr - vmx->guest_msrs < vmx->save_nmsrs) + if (msr - vmx->guest_msrs < vmx->save_nmsrs) { + preempt_disable(); kvm_set_shared_msr(msr->index, msr->data, msr->mask); + preempt_enable(); + } break; } ret = kvm_set_msr_common(vcpu, msr_index, data); @@ -3906,7 +3909,9 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu) vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid); vmx->vcpu.arch.cr0 = X86_CR0_NW | X86_CR0_CD | X86_CR0_ET; + vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); vmx_set_cr0(&vmx->vcpu, kvm_read_cr0(vcpu)); /* enter rmode */ + srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); vmx_set_cr4(&vmx->vcpu, 0); vmx_set_efer(&vmx->vcpu, 0); vmx_fpu_activate(&vmx->vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4044ce0bf7c1..185a2b823a2d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6336,13 +6336,11 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, if (npages && !old.rmap) { unsigned long userspace_addr; - down_write(¤t->mm->mmap_sem); - userspace_addr = do_mmap(NULL, 0, + userspace_addr = vm_mmap(NULL, 0, npages * PAGE_SIZE, PROT_READ | PROT_WRITE, map_flags, 0); - up_write(¤t->mm->mmap_sem); if (IS_ERR((void *)userspace_addr)) return PTR_ERR((void *)userspace_addr); @@ -6366,10 +6364,8 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, if (!user_alloc && !old.user_alloc && old.rmap && !npages) { int ret; - down_write(¤t->mm->mmap_sem); - ret = do_munmap(current->mm, old.userspace_addr, + ret = vm_munmap(old.userspace_addr, old.npages * PAGE_SIZE); - up_write(¤t->mm->mmap_sem); if (ret < 0) printk(KERN_WARNING "kvm_vm_ioctl_set_memory_region: " @@ -6585,6 +6581,7 @@ void kvm_arch_async_page_present(struct kvm_vcpu *vcpu, kvm_inject_page_fault(vcpu, &fault); } vcpu->arch.apf.halted = false; + vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE; } bool kvm_arch_can_inject_async_page_present(struct kvm_vcpu *vcpu) diff --git a/arch/x86/lib/insn.c b/arch/x86/lib/insn.c index 25feb1ae71c5..b1e6c4b2e8eb 100644 --- a/arch/x86/lib/insn.c +++ b/arch/x86/lib/insn.c @@ -379,8 +379,8 @@ err_out: return; } -/* Decode moffset16/32/64 */ -static void __get_moffset(struct insn *insn) +/* Decode moffset16/32/64. Return 0 if failed */ +static int __get_moffset(struct insn *insn) { switch (insn->addr_bytes) { case 2: @@ -397,15 +397,19 @@ static void __get_moffset(struct insn *insn) insn->moffset2.value = get_next(int, insn); insn->moffset2.nbytes = 4; break; + default: /* opnd_bytes must be modified manually */ + goto err_out; } insn->moffset1.got = insn->moffset2.got = 1; + return 1; + err_out: - return; + return 0; } -/* Decode imm v32(Iz) */ -static void __get_immv32(struct insn *insn) +/* Decode imm v32(Iz). Return 0 if failed */ +static int __get_immv32(struct insn *insn) { switch (insn->opnd_bytes) { case 2: @@ -417,14 +421,18 @@ static void __get_immv32(struct insn *insn) insn->immediate.value = get_next(int, insn); insn->immediate.nbytes = 4; break; + default: /* opnd_bytes must be modified manually */ + goto err_out; } + return 1; + err_out: - return; + return 0; } -/* Decode imm v64(Iv/Ov) */ -static void __get_immv(struct insn *insn) +/* Decode imm v64(Iv/Ov), Return 0 if failed */ +static int __get_immv(struct insn *insn) { switch (insn->opnd_bytes) { case 2: @@ -441,15 +449,18 @@ static void __get_immv(struct insn *insn) insn->immediate2.value = get_next(int, insn); insn->immediate2.nbytes = 4; break; + default: /* opnd_bytes must be modified manually */ + goto err_out; } insn->immediate1.got = insn->immediate2.got = 1; + return 1; err_out: - return; + return 0; } /* Decode ptr16:16/32(Ap) */ -static void __get_immptr(struct insn *insn) +static int __get_immptr(struct insn *insn) { switch (insn->opnd_bytes) { case 2: @@ -462,14 +473,17 @@ static void __get_immptr(struct insn *insn) break; case 8: /* ptr16:64 is not exist (no segment) */ - return; + return 0; + default: /* opnd_bytes must be modified manually */ + goto err_out; } insn->immediate2.value = get_next(unsigned short, insn); insn->immediate2.nbytes = 2; insn->immediate1.got = insn->immediate2.got = 1; + return 1; err_out: - return; + return 0; } /** @@ -489,7 +503,8 @@ void insn_get_immediate(struct insn *insn) insn_get_displacement(insn); if (inat_has_moffset(insn->attr)) { - __get_moffset(insn); + if (!__get_moffset(insn)) + goto err_out; goto done; } @@ -517,16 +532,20 @@ void insn_get_immediate(struct insn *insn) insn->immediate2.nbytes = 4; break; case INAT_IMM_PTR: - __get_immptr(insn); + if (!__get_immptr(insn)) + goto err_out; break; case INAT_IMM_VWORD32: - __get_immv32(insn); + if (!__get_immv32(insn)) + goto err_out; break; case INAT_IMM_VWORD: - __get_immv(insn); + if (!__get_immv(insn)) + goto err_out; break; default: - break; + /* Here, insn must have an immediate, but failed */ + goto err_out; } if (inat_has_second_immediate(insn->attr)) { insn->immediate2.value = get_next(char, insn); diff --git a/arch/x86/lib/usercopy.c b/arch/x86/lib/usercopy.c index 97be9cb54483..d6ae30bbd7bb 100644 --- a/arch/x86/lib/usercopy.c +++ b/arch/x86/lib/usercopy.c @@ -7,6 +7,8 @@ #include <linux/highmem.h> #include <linux/module.h> +#include <asm/word-at-a-time.h> + /* * best effort, GUP based copy_from_user() that is NMI-safe */ @@ -41,3 +43,104 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n) return len; } EXPORT_SYMBOL_GPL(copy_from_user_nmi); + +static inline unsigned long count_bytes(unsigned long mask) +{ + mask = (mask - 1) & ~mask; + mask >>= 7; + return count_masked_bytes(mask); +} + +/* + * Do a strncpy, return length of string without final '\0'. + * 'count' is the user-supplied count (return 'count' if we + * hit it), 'max' is the address space maximum (and we return + * -EFAULT if we hit it). + */ +static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) +{ + long res = 0; + + /* + * Truncate 'max' to the user-specified limit, so that + * we only have one limit we need to check in the loop + */ + if (max > count) + max = count; + + while (max >= sizeof(unsigned long)) { + unsigned long c; + + /* Fall back to byte-at-a-time if we get a page fault */ + if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) + break; + /* This can write a few bytes past the NUL character, but that's ok */ + *(unsigned long *)(dst+res) = c; + c = has_zero(c); + if (c) + return res + count_bytes(c); + res += sizeof(unsigned long); + max -= sizeof(unsigned long); + } + + while (max) { + char c; + + if (unlikely(__get_user(c,src+res))) + return -EFAULT; + dst[res] = c; + if (!c) + return res; + res++; + max--; + } + + /* + * Uhhuh. We hit 'max'. But was that the user-specified maximum + * too? If so, that's ok - we got as much as the user asked for. + */ + if (res >= count) + return res; + + /* + * Nope: we hit the address space limit, and we still had more + * characters the caller would have wanted. That's an EFAULT. + */ + return -EFAULT; +} + +/** + * strncpy_from_user: - Copy a NUL terminated string from userspace. + * @dst: Destination address, in kernel space. This buffer must be at + * least @count bytes long. + * @src: Source address, in user space. + * @count: Maximum number of bytes to copy, including the trailing NUL. + * + * Copies a NUL-terminated string from userspace to kernel space. + * + * On success, returns the length of the string (not including the trailing + * NUL). + * + * If access to userspace fails, returns -EFAULT (some data may have been + * copied). + * + * If @count is smaller than the length of the string, copies @count bytes + * and returns @count. + */ +long +strncpy_from_user(char *dst, const char __user *src, long count) +{ + unsigned long max_addr, src_addr; + + if (unlikely(count <= 0)) + return 0; + + max_addr = current_thread_info()->addr_limit.seg; + src_addr = (unsigned long)src; + if (likely(src_addr < max_addr)) { + unsigned long max = max_addr - src_addr; + return do_strncpy_from_user(dst, src, count, max); + } + return -EFAULT; +} +EXPORT_SYMBOL(strncpy_from_user); diff --git a/arch/x86/lib/usercopy_32.c b/arch/x86/lib/usercopy_32.c index d9b094ca7aaa..ef2a6a5d78e3 100644 --- a/arch/x86/lib/usercopy_32.c +++ b/arch/x86/lib/usercopy_32.c @@ -33,93 +33,6 @@ static inline int __movsl_is_ok(unsigned long a1, unsigned long a2, unsigned lon __movsl_is_ok((unsigned long)(a1), (unsigned long)(a2), (n)) /* - * Copy a null terminated string from userspace. - */ - -#define __do_strncpy_from_user(dst, src, count, res) \ -do { \ - int __d0, __d1, __d2; \ - might_fault(); \ - __asm__ __volatile__( \ - " testl %1,%1\n" \ - " jz 2f\n" \ - "0: lodsb\n" \ - " stosb\n" \ - " testb %%al,%%al\n" \ - " jz 1f\n" \ - " decl %1\n" \ - " jnz 0b\n" \ - "1: subl %1,%0\n" \ - "2:\n" \ - ".section .fixup,\"ax\"\n" \ - "3: movl %5,%0\n" \ - " jmp 2b\n" \ - ".previous\n" \ - _ASM_EXTABLE(0b,3b) \ - : "=&d"(res), "=&c"(count), "=&a" (__d0), "=&S" (__d1), \ - "=&D" (__d2) \ - : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), "4"(dst) \ - : "memory"); \ -} while (0) - -/** - * __strncpy_from_user: - Copy a NUL terminated string from userspace, with less checking. - * @dst: Destination address, in kernel space. This buffer must be at - * least @count bytes long. - * @src: Source address, in user space. - * @count: Maximum number of bytes to copy, including the trailing NUL. - * - * Copies a NUL-terminated string from userspace to kernel space. - * Caller must check the specified block with access_ok() before calling - * this function. - * - * On success, returns the length of the string (not including the trailing - * NUL). - * - * If access to userspace fails, returns -EFAULT (some data may have been - * copied). - * - * If @count is smaller than the length of the string, copies @count bytes - * and returns @count. - */ -long -__strncpy_from_user(char *dst, const char __user *src, long count) -{ - long res; - __do_strncpy_from_user(dst, src, count, res); - return res; -} -EXPORT_SYMBOL(__strncpy_from_user); - -/** - * strncpy_from_user: - Copy a NUL terminated string from userspace. - * @dst: Destination address, in kernel space. This buffer must be at - * least @count bytes long. - * @src: Source address, in user space. - * @count: Maximum number of bytes to copy, including the trailing NUL. - * - * Copies a NUL-terminated string from userspace to kernel space. - * - * On success, returns the length of the string (not including the trailing - * NUL). - * - * If access to userspace fails, returns -EFAULT (some data may have been - * copied). - * - * If @count is smaller than the length of the string, copies @count bytes - * and returns @count. - */ -long -strncpy_from_user(char *dst, const char __user *src, long count) -{ - long res = -EFAULT; - if (access_ok(VERIFY_READ, src, 1)) - __do_strncpy_from_user(dst, src, count, res); - return res; -} -EXPORT_SYMBOL(strncpy_from_user); - -/* * Zero Userspace */ diff --git a/arch/x86/lib/usercopy_64.c b/arch/x86/lib/usercopy_64.c index b7c2849ffb66..0d0326f388c0 100644 --- a/arch/x86/lib/usercopy_64.c +++ b/arch/x86/lib/usercopy_64.c @@ -9,55 +9,6 @@ #include <asm/uaccess.h> /* - * Copy a null terminated string from userspace. - */ - -#define __do_strncpy_from_user(dst,src,count,res) \ -do { \ - long __d0, __d1, __d2; \ - might_fault(); \ - __asm__ __volatile__( \ - " testq %1,%1\n" \ - " jz 2f\n" \ - "0: lodsb\n" \ - " stosb\n" \ - " testb %%al,%%al\n" \ - " jz 1f\n" \ - " decq %1\n" \ - " jnz 0b\n" \ - "1: subq %1,%0\n" \ - "2:\n" \ - ".section .fixup,\"ax\"\n" \ - "3: movq %5,%0\n" \ - " jmp 2b\n" \ - ".previous\n" \ - _ASM_EXTABLE(0b,3b) \ - : "=&r"(res), "=&c"(count), "=&a" (__d0), "=&S" (__d1), \ - "=&D" (__d2) \ - : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), "4"(dst) \ - : "memory"); \ -} while (0) - -long -__strncpy_from_user(char *dst, const char __user *src, long count) -{ - long res; - __do_strncpy_from_user(dst, src, count, res); - return res; -} -EXPORT_SYMBOL(__strncpy_from_user); - -long -strncpy_from_user(char *dst, const char __user *src, long count) -{ - long res = -EFAULT; - if (access_ok(VERIFY_READ, src, 1)) - return __strncpy_from_user(dst, src, count); - return res; -} -EXPORT_SYMBOL(strncpy_from_user); - -/* * Zero Userspace */ diff --git a/arch/x86/net/bpf_jit.S b/arch/x86/net/bpf_jit.S index 66870223f8c5..877b9a1b2152 100644 --- a/arch/x86/net/bpf_jit.S +++ b/arch/x86/net/bpf_jit.S @@ -18,17 +18,17 @@ * r9d : hlen = skb->len - skb->data_len */ #define SKBDATA %r8 - -sk_load_word_ind: - .globl sk_load_word_ind - - add %ebx,%esi /* offset += X */ -# test %esi,%esi /* if (offset < 0) goto bpf_error; */ - js bpf_error +#define SKF_MAX_NEG_OFF $(-0x200000) /* SKF_LL_OFF from filter.h */ sk_load_word: .globl sk_load_word + test %esi,%esi + js bpf_slow_path_word_neg + +sk_load_word_positive_offset: + .globl sk_load_word_positive_offset + mov %r9d,%eax # hlen sub %esi,%eax # hlen - offset cmp $3,%eax @@ -37,16 +37,15 @@ sk_load_word: bswap %eax /* ntohl() */ ret - -sk_load_half_ind: - .globl sk_load_half_ind - - add %ebx,%esi /* offset += X */ - js bpf_error - sk_load_half: .globl sk_load_half + test %esi,%esi + js bpf_slow_path_half_neg + +sk_load_half_positive_offset: + .globl sk_load_half_positive_offset + mov %r9d,%eax sub %esi,%eax # hlen - offset cmp $1,%eax @@ -55,14 +54,15 @@ sk_load_half: rol $8,%ax # ntohs() ret -sk_load_byte_ind: - .globl sk_load_byte_ind - add %ebx,%esi /* offset += X */ - js bpf_error - sk_load_byte: .globl sk_load_byte + test %esi,%esi + js bpf_slow_path_byte_neg + +sk_load_byte_positive_offset: + .globl sk_load_byte_positive_offset + cmp %esi,%r9d /* if (offset >= hlen) goto bpf_slow_path_byte */ jle bpf_slow_path_byte movzbl (SKBDATA,%rsi),%eax @@ -73,25 +73,21 @@ sk_load_byte: * * Implements BPF_S_LDX_B_MSH : ldxb 4*([offset]&0xf) * Must preserve A accumulator (%eax) - * Inputs : %esi is the offset value, already known positive + * Inputs : %esi is the offset value */ -ENTRY(sk_load_byte_msh) - CFI_STARTPROC +sk_load_byte_msh: + .globl sk_load_byte_msh + test %esi,%esi + js bpf_slow_path_byte_msh_neg + +sk_load_byte_msh_positive_offset: + .globl sk_load_byte_msh_positive_offset cmp %esi,%r9d /* if (offset >= hlen) goto bpf_slow_path_byte_msh */ jle bpf_slow_path_byte_msh movzbl (SKBDATA,%rsi),%ebx and $15,%bl shl $2,%bl ret - CFI_ENDPROC -ENDPROC(sk_load_byte_msh) - -bpf_error: -# force a return 0 from jit handler - xor %eax,%eax - mov -8(%rbp),%rbx - leaveq - ret /* rsi contains offset and can be scratched */ #define bpf_slow_path_common(LEN) \ @@ -138,3 +134,67 @@ bpf_slow_path_byte_msh: shl $2,%al xchg %eax,%ebx ret + +#define sk_negative_common(SIZE) \ + push %rdi; /* save skb */ \ + push %r9; \ + push SKBDATA; \ +/* rsi already has offset */ \ + mov $SIZE,%ecx; /* size */ \ + call bpf_internal_load_pointer_neg_helper; \ + test %rax,%rax; \ + pop SKBDATA; \ + pop %r9; \ + pop %rdi; \ + jz bpf_error + + +bpf_slow_path_word_neg: + cmp SKF_MAX_NEG_OFF, %esi /* test range */ + jl bpf_error /* offset lower -> error */ +sk_load_word_negative_offset: + .globl sk_load_word_negative_offset + sk_negative_common(4) + mov (%rax), %eax + bswap %eax + ret + +bpf_slow_path_half_neg: + cmp SKF_MAX_NEG_OFF, %esi + jl bpf_error +sk_load_half_negative_offset: + .globl sk_load_half_negative_offset + sk_negative_common(2) + mov (%rax),%ax + rol $8,%ax + movzwl %ax,%eax + ret + +bpf_slow_path_byte_neg: + cmp SKF_MAX_NEG_OFF, %esi + jl bpf_error +sk_load_byte_negative_offset: + .globl sk_load_byte_negative_offset + sk_negative_common(1) + movzbl (%rax), %eax + ret + +bpf_slow_path_byte_msh_neg: + cmp SKF_MAX_NEG_OFF, %esi + jl bpf_error +sk_load_byte_msh_negative_offset: + .globl sk_load_byte_msh_negative_offset + xchg %eax,%ebx /* dont lose A , X is about to be scratched */ + sk_negative_common(1) + movzbl (%rax),%eax + and $15,%al + shl $2,%al + xchg %eax,%ebx + ret + +bpf_error: +# force a return 0 from jit handler + xor %eax,%eax + mov -8(%rbp),%rbx + leaveq + ret diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 5671752f8d9c..0597f95b6da6 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -30,7 +30,10 @@ int bpf_jit_enable __read_mostly; * assembly code in arch/x86/net/bpf_jit.S */ extern u8 sk_load_word[], sk_load_half[], sk_load_byte[], sk_load_byte_msh[]; -extern u8 sk_load_word_ind[], sk_load_half_ind[], sk_load_byte_ind[]; +extern u8 sk_load_word_positive_offset[], sk_load_half_positive_offset[]; +extern u8 sk_load_byte_positive_offset[], sk_load_byte_msh_positive_offset[]; +extern u8 sk_load_word_negative_offset[], sk_load_half_negative_offset[]; +extern u8 sk_load_byte_negative_offset[], sk_load_byte_msh_negative_offset[]; static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) { @@ -117,6 +120,8 @@ static inline void bpf_flush_icache(void *start, void *end) set_fs(old_fs); } +#define CHOOSE_LOAD_FUNC(K, func) \ + ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) void bpf_jit_compile(struct sk_filter *fp) { @@ -289,7 +294,7 @@ void bpf_jit_compile(struct sk_filter *fp) EMIT2(0x24, K & 0xFF); /* and imm8,%al */ } else if (K >= 0xFFFF0000) { EMIT2(0x66, 0x25); /* and imm16,%ax */ - EMIT2(K, 2); + EMIT(K, 2); } else { EMIT1_off32(0x25, K); /* and imm32,%eax */ } @@ -473,44 +478,46 @@ void bpf_jit_compile(struct sk_filter *fp) #endif break; case BPF_S_LD_W_ABS: - func = sk_load_word; + func = CHOOSE_LOAD_FUNC(K, sk_load_word); common_load: seen |= SEEN_DATAREF; - if ((int)K < 0) { - /* Abort the JIT because __load_pointer() is needed. */ - goto out; - } t_offset = func - (image + addrs[i]); EMIT1_off32(0xbe, K); /* mov imm32,%esi */ EMIT1_off32(0xe8, t_offset); /* call */ break; case BPF_S_LD_H_ABS: - func = sk_load_half; + func = CHOOSE_LOAD_FUNC(K, sk_load_half); goto common_load; case BPF_S_LD_B_ABS: - func = sk_load_byte; + func = CHOOSE_LOAD_FUNC(K, sk_load_byte); goto common_load; case BPF_S_LDX_B_MSH: - if ((int)K < 0) { - /* Abort the JIT because __load_pointer() is needed. */ - goto out; - } + func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh); seen |= SEEN_DATAREF | SEEN_XREG; - t_offset = sk_load_byte_msh - (image + addrs[i]); + t_offset = func - (image + addrs[i]); EMIT1_off32(0xbe, K); /* mov imm32,%esi */ EMIT1_off32(0xe8, t_offset); /* call sk_load_byte_msh */ break; case BPF_S_LD_W_IND: - func = sk_load_word_ind; + func = sk_load_word; common_load_ind: seen |= SEEN_DATAREF | SEEN_XREG; t_offset = func - (image + addrs[i]); - EMIT1_off32(0xbe, K); /* mov imm32,%esi */ + if (K) { + if (is_imm8(K)) { + EMIT3(0x8d, 0x73, K); /* lea imm8(%rbx), %esi */ + } else { + EMIT2(0x8d, 0xb3); /* lea imm32(%rbx),%esi */ + EMIT(K, 4); + } + } else { + EMIT2(0x89,0xde); /* mov %ebx,%esi */ + } EMIT1_off32(0xe8, t_offset); /* call sk_load_xxx_ind */ break; case BPF_S_LD_H_IND: - func = sk_load_half_ind; + func = sk_load_half; goto common_load_ind; case BPF_S_LD_B_IND: - func = sk_load_byte_ind; + func = sk_load_byte; goto common_load_ind; case BPF_S_JMP_JA: t_offset = addrs[i + K] - addrs[i]; diff --git a/arch/x86/platform/geode/net5501.c b/arch/x86/platform/geode/net5501.c index 66d377e334f7..646e3b5b4bb6 100644 --- a/arch/x86/platform/geode/net5501.c +++ b/arch/x86/platform/geode/net5501.c @@ -63,7 +63,7 @@ static struct gpio_led net5501_leds[] = { .name = "net5501:1", .gpio = 6, .default_trigger = "default-on", - .active_low = 1, + .active_low = 0, }, }; diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index e0a37233c0af..e31bcd8f2eee 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -805,7 +805,7 @@ void intel_scu_devices_create(void) } else i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); } - intel_scu_notifier_post(SCU_AVAILABLE, 0L); + intel_scu_notifier_post(SCU_AVAILABLE, NULL); } EXPORT_SYMBOL_GPL(intel_scu_devices_create); @@ -814,7 +814,7 @@ void intel_scu_devices_destroy(void) { int i; - intel_scu_notifier_post(SCU_DOWN, 0L); + intel_scu_notifier_post(SCU_DOWN, NULL); for (i = 0; i < ipc_next_dev; i++) platform_device_del(ipc_devs[i]); diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c index 47936830968c..218cdb16163c 100644 --- a/arch/x86/power/cpu.c +++ b/arch/x86/power/cpu.c @@ -225,13 +225,13 @@ static void __restore_processor_state(struct saved_context *ctxt) fix_processor_context(); do_fpu_end(); + x86_platform.restore_sched_clock_state(); mtrr_bp_restore(); } /* Needed by apm.c */ void restore_processor_state(void) { - x86_platform.restore_sched_clock_state(); __restore_processor_state(&saved_context); } #ifdef CONFIG_X86_32 diff --git a/arch/x86/um/asm/barrier.h b/arch/x86/um/asm/barrier.h new file mode 100644 index 000000000000..7d01b8c56c00 --- /dev/null +++ b/arch/x86/um/asm/barrier.h @@ -0,0 +1,75 @@ +#ifndef _ASM_UM_BARRIER_H_ +#define _ASM_UM_BARRIER_H_ + +#include <asm/asm.h> +#include <asm/segment.h> +#include <asm/cpufeature.h> +#include <asm/cmpxchg.h> +#include <asm/nops.h> + +#include <linux/kernel.h> +#include <linux/irqflags.h> + +/* + * Force strict CPU ordering. + * And yes, this is required on UP too when we're talking + * to devices. + */ +#ifdef CONFIG_X86_32 + +#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2) +#define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2) +#define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM) + +#else /* CONFIG_X86_32 */ + +#define mb() asm volatile("mfence" : : : "memory") +#define rmb() asm volatile("lfence" : : : "memory") +#define wmb() asm volatile("sfence" : : : "memory") + +#endif /* CONFIG_X86_32 */ + +#define read_barrier_depends() do { } while (0) + +#ifdef CONFIG_SMP + +#define smp_mb() mb() +#ifdef CONFIG_X86_PPRO_FENCE +#define smp_rmb() rmb() +#else /* CONFIG_X86_PPRO_FENCE */ +#define smp_rmb() barrier() +#endif /* CONFIG_X86_PPRO_FENCE */ + +#ifdef CONFIG_X86_OOSTORE +#define smp_wmb() wmb() +#else /* CONFIG_X86_OOSTORE */ +#define smp_wmb() barrier() +#endif /* CONFIG_X86_OOSTORE */ + +#define smp_read_barrier_depends() read_barrier_depends() +#define set_mb(var, value) do { (void)xchg(&var, value); } while (0) + +#else /* CONFIG_SMP */ + +#define smp_mb() barrier() +#define smp_rmb() barrier() +#define smp_wmb() barrier() +#define smp_read_barrier_depends() do { } while (0) +#define set_mb(var, value) do { var = value; barrier(); } while (0) + +#endif /* CONFIG_SMP */ + +/* + * Stop RDTSC speculation. This is needed when you need to use RDTSC + * (or get_cycles or vread that possibly accesses the TSC) in a defined + * code region. + * + * (Could use an alternative three way for this if there was one.) + */ +static inline void rdtsc_barrier(void) +{ + alternative(ASM_NOP3, "mfence", X86_FEATURE_MFENCE_RDTSC); + alternative(ASM_NOP3, "lfence", X86_FEATURE_LFENCE_RDTSC); +} + +#endif diff --git a/arch/x86/um/asm/system.h b/arch/x86/um/asm/system.h deleted file mode 100644 index a459fd9b7598..000000000000 --- a/arch/x86/um/asm/system.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef _ASM_X86_SYSTEM_H_ -#define _ASM_X86_SYSTEM_H_ - -#include <asm/asm.h> -#include <asm/segment.h> -#include <asm/cpufeature.h> -#include <asm/cmpxchg.h> -#include <asm/nops.h> - -#include <linux/kernel.h> -#include <linux/irqflags.h> - -/* entries in ARCH_DLINFO: */ -#ifdef CONFIG_IA32_EMULATION -# define AT_VECTOR_SIZE_ARCH 2 -#else -# define AT_VECTOR_SIZE_ARCH 1 -#endif - -extern unsigned long arch_align_stack(unsigned long sp); - -void default_idle(void); - -/* - * Force strict CPU ordering. - * And yes, this is required on UP too when we're talking - * to devices. - */ -#ifdef CONFIG_X86_32 -/* - * Some non-Intel clones support out of order store. wmb() ceases to be a - * nop for these. - */ -#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2) -#define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2) -#define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM) -#else -#define mb() asm volatile("mfence":::"memory") -#define rmb() asm volatile("lfence":::"memory") -#define wmb() asm volatile("sfence" ::: "memory") -#endif - -/** - * read_barrier_depends - Flush all pending reads that subsequents reads - * depend on. - * - * No data-dependent reads from memory-like regions are ever reordered - * over this barrier. All reads preceding this primitive are guaranteed - * to access memory (but not necessarily other CPUs' caches) before any - * reads following this primitive that depend on the data return by - * any of the preceding reads. This primitive is much lighter weight than - * rmb() on most CPUs, and is never heavier weight than is - * rmb(). - * - * These ordering constraints are respected by both the local CPU - * and the compiler. - * - * Ordering is not guaranteed by anything other than these primitives, - * not even by data dependencies. See the documentation for - * memory_barrier() for examples and URLs to more information. - * - * For example, the following code would force ordering (the initial - * value of "a" is zero, "b" is one, and "p" is "&a"): - * - * <programlisting> - * CPU 0 CPU 1 - * - * b = 2; - * memory_barrier(); - * p = &b; q = p; - * read_barrier_depends(); - * d = *q; - * </programlisting> - * - * because the read of "*q" depends on the read of "p" and these - * two reads are separated by a read_barrier_depends(). However, - * the following code, with the same initial values for "a" and "b": - * - * <programlisting> - * CPU 0 CPU 1 - * - * a = 2; - * memory_barrier(); - * b = 3; y = b; - * read_barrier_depends(); - * x = a; - * </programlisting> - * - * does not enforce ordering, since there is no data dependency between - * the read of "a" and the read of "b". Therefore, on some CPUs, such - * as Alpha, "y" could be set to 3 and "x" to 0. Use rmb() - * in cases like this where there are no data dependencies. - **/ - -#define read_barrier_depends() do { } while (0) - -#ifdef CONFIG_SMP -#define smp_mb() mb() -#ifdef CONFIG_X86_PPRO_FENCE -# define smp_rmb() rmb() -#else -# define smp_rmb() barrier() -#endif -#ifdef CONFIG_X86_OOSTORE -# define smp_wmb() wmb() -#else -# define smp_wmb() barrier() -#endif -#define smp_read_barrier_depends() read_barrier_depends() -#define set_mb(var, value) do { (void)xchg(&var, value); } while (0) -#else -#define smp_mb() barrier() -#define smp_rmb() barrier() -#define smp_wmb() barrier() -#define smp_read_barrier_depends() do { } while (0) -#define set_mb(var, value) do { var = value; barrier(); } while (0) -#endif - -/* - * Stop RDTSC speculation. This is needed when you need to use RDTSC - * (or get_cycles or vread that possibly accesses the TSC) in a defined - * code region. - * - * (Could use an alternative three way for this if there was one.) - */ -static inline void rdtsc_barrier(void) -{ - alternative(ASM_NOP3, "mfence", X86_FEATURE_MFENCE_RDTSC); - alternative(ASM_NOP3, "lfence", X86_FEATURE_LFENCE_RDTSC); -} - -extern void *_switch_to(void *prev, void *next, void *last); -#define switch_to(prev, next, last) prev = _switch_to(prev, next, last) - -#endif diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index b132ade26f77..95dccce8e979 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -63,6 +63,7 @@ #include <asm/stackprotector.h> #include <asm/hypervisor.h> #include <asm/mwait.h> +#include <asm/pci_x86.h> #ifdef CONFIG_ACPI #include <linux/acpi.h> @@ -261,7 +262,8 @@ static void xen_cpuid(unsigned int *ax, unsigned int *bx, static bool __init xen_check_mwait(void) { -#ifdef CONFIG_ACPI +#if defined(CONFIG_ACPI) && !defined(CONFIG_ACPI_PROCESSOR_AGGREGATOR) && \ + !defined(CONFIG_ACPI_PROCESSOR_AGGREGATOR_MODULE) struct xen_platform_op op = { .cmd = XENPF_set_processor_pminfo, .u.set_pminfo.id = -1, @@ -349,7 +351,6 @@ static void __init xen_init_cpuid_mask(void) /* Xen will set CR4.OSXSAVE if supported and not disabled by force */ if ((cx & xsave_mask) != xsave_mask) cpuid_leaf1_ecx_mask &= ~xsave_mask; /* disable XSAVE & OSXSAVE */ - if (xen_check_mwait()) cpuid_leaf1_ecx_set_mask = (1 << (X86_FEATURE_MWAIT % 32)); } @@ -809,9 +810,40 @@ static void xen_io_delay(void) } #ifdef CONFIG_X86_LOCAL_APIC +static unsigned long xen_set_apic_id(unsigned int x) +{ + WARN_ON(1); + return x; +} +static unsigned int xen_get_apic_id(unsigned long x) +{ + return ((x)>>24) & 0xFFu; +} static u32 xen_apic_read(u32 reg) { - return 0; + struct xen_platform_op op = { + .cmd = XENPF_get_cpuinfo, + .interface_version = XENPF_INTERFACE_VERSION, + .u.pcpu_info.xen_cpuid = 0, + }; + int ret = 0; + + /* Shouldn't need this as APIC is turned off for PV, and we only + * get called on the bootup processor. But just in case. */ + if (!xen_initial_domain() || smp_processor_id()) + return 0; + + if (reg == APIC_LVR) + return 0x10; + + if (reg != APIC_ID) + return 0; + + ret = HYPERVISOR_dom0_op(&op); + if (ret) + return 0; + + return op.u.pcpu_info.apic_id << 24; } static void xen_apic_write(u32 reg, u32 val) @@ -849,6 +881,8 @@ static void set_xen_basic_apic_ops(void) apic->icr_write = xen_apic_icr_write; apic->wait_icr_idle = xen_apic_wait_icr_idle; apic->safe_wait_icr_idle = xen_safe_apic_wait_icr_idle; + apic->set_apic_id = xen_set_apic_id; + apic->get_apic_id = xen_get_apic_id; } #endif @@ -967,7 +1001,7 @@ void xen_setup_shared_info(void) xen_setup_mfn_list_list(); } -/* This is called once we have the cpu_possible_map */ +/* This is called once we have the cpu_possible_mask */ void xen_setup_vcpu_info_placement(void) { int cpu; @@ -1365,8 +1399,10 @@ asmlinkage void __init xen_start_kernel(void) /* Make sure ACS will be enabled */ pci_request_acs(); } - - +#ifdef CONFIG_PCI + /* PCI BIOS service won't work from a PV guest. */ + pci_probe &= ~PCI_PROBE_BIOS; +#endif xen_raw_console_write("about to get started...\n"); xen_setup_runstate_info(0); diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 988828b479ed..69f5857660ac 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -353,8 +353,13 @@ static pteval_t pte_mfn_to_pfn(pteval_t val) { if (val & _PAGE_PRESENT) { unsigned long mfn = (val & PTE_PFN_MASK) >> PAGE_SHIFT; + unsigned long pfn = mfn_to_pfn(mfn); + pteval_t flags = val & PTE_FLAGS_MASK; - val = ((pteval_t)mfn_to_pfn(mfn) << PAGE_SHIFT) | flags; + if (unlikely(pfn == ~0)) + val = flags & ~_PAGE_PRESENT; + else + val = ((pteval_t)pfn << PAGE_SHIFT) | flags; } return val; @@ -1859,6 +1864,7 @@ pgd_t * __init xen_setup_kernel_pagetable(pgd_t *pgd, #endif /* CONFIG_X86_64 */ static unsigned char dummy_mapping[PAGE_SIZE] __page_aligned_bss; +static unsigned char fake_ioapic_mapping[PAGE_SIZE] __page_aligned_bss; static void xen_set_fixmap(unsigned idx, phys_addr_t phys, pgprot_t prot) { @@ -1899,7 +1905,7 @@ static void xen_set_fixmap(unsigned idx, phys_addr_t phys, pgprot_t prot) * We just don't map the IO APIC - all access is via * hypercalls. Keep the address in the pte for reference. */ - pte = pfn_pte(PFN_DOWN(__pa(dummy_mapping)), PAGE_KERNEL); + pte = pfn_pte(PFN_DOWN(__pa(fake_ioapic_mapping)), PAGE_KERNEL); break; #endif @@ -2064,6 +2070,7 @@ void __init xen_init_mmu_ops(void) pv_mmu_ops = xen_mmu_ops; memset(dummy_mapping, 0xff, PAGE_SIZE); + memset(fake_ioapic_mapping, 0xfd, PAGE_SIZE); } /* Protected by xen_reservation_lock. */ diff --git a/arch/x86/xen/pci-swiotlb-xen.c b/arch/x86/xen/pci-swiotlb-xen.c index b480d4207a4c..967633ad98c4 100644 --- a/arch/x86/xen/pci-swiotlb-xen.c +++ b/arch/x86/xen/pci-swiotlb-xen.c @@ -12,8 +12,8 @@ int xen_swiotlb __read_mostly; static struct dma_map_ops xen_swiotlb_dma_ops = { .mapping_error = xen_swiotlb_dma_mapping_error, - .alloc_coherent = xen_swiotlb_alloc_coherent, - .free_coherent = xen_swiotlb_free_coherent, + .alloc = xen_swiotlb_alloc_coherent, + .free = xen_swiotlb_free_coherent, .sync_single_for_cpu = xen_swiotlb_sync_single_for_cpu, .sync_single_for_device = xen_swiotlb_sync_single_for_device, .sync_sg_for_cpu = xen_swiotlb_sync_sg_for_cpu, diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index 02900e8ce26c..0503c0c493a9 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -59,7 +59,7 @@ static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id) static void __cpuinit cpu_bringup(void) { - int cpu = smp_processor_id(); + int cpu; cpu_init(); touch_softlockup_watchdog(); @@ -178,6 +178,7 @@ static void __init xen_fill_possible_map(void) static void __init xen_filter_cpu_maps(void) { int i, rc; + unsigned int subtract = 0; if (!xen_initial_domain()) return; @@ -192,8 +193,22 @@ static void __init xen_filter_cpu_maps(void) } else { set_cpu_possible(i, false); set_cpu_present(i, false); + subtract++; } } +#ifdef CONFIG_HOTPLUG_CPU + /* This is akin to using 'nr_cpus' on the Linux command line. + * Which is OK as when we use 'dom0_max_vcpus=X' we can only + * have up to X, while nr_cpu_ids is greater than X. This + * normally is not a problem, except when CPU hotplugging + * is involved and then there might be more than X CPUs + * in the guest - which will not work as there is no + * hypercall to expand the max number of VCPUs an already + * running guest has. So cap it up to X. */ + if (subtract) + nr_cpu_ids = nr_cpu_ids - subtract; +#endif + } static void __init xen_smp_prepare_boot_cpu(void) diff --git a/arch/x86/xen/xen-asm.S b/arch/x86/xen/xen-asm.S index 79d7362ad6d1..3e45aa000718 100644 --- a/arch/x86/xen/xen-asm.S +++ b/arch/x86/xen/xen-asm.S @@ -96,7 +96,7 @@ ENTRY(xen_restore_fl_direct) /* check for unmasked and pending */ cmpw $0x0001, PER_CPU_VAR(xen_vcpu_info) + XEN_vcpu_info_pending - jz 1f + jnz 1f 2: call check_events 1: ENDPATCH(xen_restore_fl_direct) diff --git a/arch/xtensa/include/asm/hardirq.h b/arch/xtensa/include/asm/hardirq.h index 26664cef8f11..91695a135498 100644 --- a/arch/xtensa/include/asm/hardirq.h +++ b/arch/xtensa/include/asm/hardirq.h @@ -11,9 +11,6 @@ #ifndef _XTENSA_HARDIRQ_H #define _XTENSA_HARDIRQ_H -void ack_bad_irq(unsigned int irq); -#define ack_bad_irq ack_bad_irq - #include <asm-generic/hardirq.h> #endif /* _XTENSA_HARDIRQ_H */ diff --git a/arch/xtensa/include/asm/io.h b/arch/xtensa/include/asm/io.h index d04cd3a625fa..4beb43c087d3 100644 --- a/arch/xtensa/include/asm/io.h +++ b/arch/xtensa/include/asm/io.h @@ -14,6 +14,7 @@ #ifdef __KERNEL__ #include <asm/byteorder.h> #include <asm/page.h> +#include <linux/bug.h> #include <linux/kernel.h> #include <linux/types.h> diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c index b69b000349fc..d78869a00b11 100644 --- a/arch/xtensa/kernel/signal.c +++ b/arch/xtensa/kernel/signal.c @@ -496,6 +496,7 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { + int ret; /* Are we from a system call? */ diff --git a/block/blk-core.c b/block/blk-core.c index 3a78b00edd71..1f61b74867e4 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -483,7 +483,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) if (!q) return NULL; - q->id = ida_simple_get(&blk_queue_ida, 0, 0, GFP_KERNEL); + q->id = ida_simple_get(&blk_queue_ida, 0, 0, gfp_mask); if (q->id < 0) goto fail_q; @@ -1277,7 +1277,8 @@ static bool attempt_plug_merge(struct request_queue *q, struct bio *bio, list_for_each_entry_reverse(rq, &plug->list, queuelist) { int el_ret; - (*request_count)++; + if (rq->q == q) + (*request_count)++; if (rq->q != q || !blk_rq_merge_ok(rq, bio)) continue; diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 5eed6a76721d..f2ddb94626bd 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1218,7 +1218,7 @@ void blk_throtl_drain(struct request_queue *q) struct bio_list bl; struct bio *bio; - WARN_ON_ONCE(!queue_is_locked(q)); + queue_lockdep_assert_held(q); bio_list_init(&bl); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 457295253566..3c38536bd52c 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -295,6 +295,7 @@ struct cfq_data { unsigned int cfq_slice_idle; unsigned int cfq_group_idle; unsigned int cfq_latency; + unsigned int cfq_target_latency; /* * Fallback dummy cfqq for extreme OOM conditions @@ -604,7 +605,7 @@ cfq_group_slice(struct cfq_data *cfqd, struct cfq_group *cfqg) { struct cfq_rb_root *st = &cfqd->grp_service_tree; - return cfq_target_latency * cfqg->weight / st->total_weight; + return cfqd->cfq_target_latency * cfqg->weight / st->total_weight; } static inline unsigned @@ -2271,7 +2272,8 @@ new_workload: * to have higher weight. A more accurate thing would be to * calculate system wide asnc/sync ratio. */ - tmp = cfq_target_latency * cfqg_busy_async_queues(cfqd, cfqg); + tmp = cfqd->cfq_target_latency * + cfqg_busy_async_queues(cfqd, cfqg); tmp = tmp/cfqd->busy_queues; slice = min_t(unsigned, slice, tmp); @@ -3737,6 +3739,7 @@ static void *cfq_init_queue(struct request_queue *q) cfqd->cfq_back_penalty = cfq_back_penalty; cfqd->cfq_slice[0] = cfq_slice_async; cfqd->cfq_slice[1] = cfq_slice_sync; + cfqd->cfq_target_latency = cfq_target_latency; cfqd->cfq_slice_async_rq = cfq_slice_async_rq; cfqd->cfq_slice_idle = cfq_slice_idle; cfqd->cfq_group_idle = cfq_group_idle; @@ -3788,6 +3791,7 @@ SHOW_FUNCTION(cfq_slice_sync_show, cfqd->cfq_slice[1], 1); SHOW_FUNCTION(cfq_slice_async_show, cfqd->cfq_slice[0], 1); SHOW_FUNCTION(cfq_slice_async_rq_show, cfqd->cfq_slice_async_rq, 0); SHOW_FUNCTION(cfq_low_latency_show, cfqd->cfq_latency, 0); +SHOW_FUNCTION(cfq_target_latency_show, cfqd->cfq_target_latency, 1); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ @@ -3821,6 +3825,7 @@ STORE_FUNCTION(cfq_slice_async_store, &cfqd->cfq_slice[0], 1, UINT_MAX, 1); STORE_FUNCTION(cfq_slice_async_rq_store, &cfqd->cfq_slice_async_rq, 1, UINT_MAX, 0); STORE_FUNCTION(cfq_low_latency_store, &cfqd->cfq_latency, 0, 1, 0); +STORE_FUNCTION(cfq_target_latency_store, &cfqd->cfq_target_latency, 1, UINT_MAX, 1); #undef STORE_FUNCTION #define CFQ_ATTR(name) \ @@ -3838,6 +3843,7 @@ static struct elv_fs_entry cfq_attrs[] = { CFQ_ATTR(slice_idle), CFQ_ATTR(group_idle), CFQ_ATTR(low_latency), + CFQ_ATTR(target_latency), __ATTR_NULL }; diff --git a/crypto/Kconfig b/crypto/Kconfig index 21ff9d015432..8e84225c096b 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -627,7 +627,7 @@ config CRYPTO_BLOWFISH_COMMON config CRYPTO_BLOWFISH_X86_64 tristate "Blowfish cipher algorithm (x86_64)" - depends on (X86 || UML_X86) && 64BIT + depends on X86 && 64BIT select CRYPTO_ALGAPI select CRYPTO_BLOWFISH_COMMON help @@ -657,7 +657,7 @@ config CRYPTO_CAMELLIA config CRYPTO_CAMELLIA_X86_64 tristate "Camellia cipher algorithm (x86_64)" - depends on (X86 || UML_X86) && 64BIT + depends on X86 && 64BIT depends on CRYPTO select CRYPTO_ALGAPI select CRYPTO_LRW @@ -893,7 +893,7 @@ config CRYPTO_TWOFISH_X86_64 config CRYPTO_TWOFISH_X86_64_3WAY tristate "Twofish cipher algorithm (x86_64, 3-way parallel)" - depends on (X86 || UML_X86) && 64BIT + depends on X86 && 64BIT select CRYPTO_ALGAPI select CRYPTO_TWOFISH_COMMON select CRYPTO_TWOFISH_X86_64 diff --git a/crypto/ablkcipher.c b/crypto/ablkcipher.c index a0f768c1d9aa..8d3a056ebeea 100644 --- a/crypto/ablkcipher.c +++ b/crypto/ablkcipher.c @@ -613,8 +613,7 @@ out: return err; } -static struct crypto_alg *crypto_lookup_skcipher(const char *name, u32 type, - u32 mask) +struct crypto_alg *crypto_lookup_skcipher(const char *name, u32 type, u32 mask) { struct crypto_alg *alg; @@ -652,6 +651,7 @@ static struct crypto_alg *crypto_lookup_skcipher(const char *name, u32 type, return ERR_PTR(crypto_givcipher_default(alg, type, mask)); } +EXPORT_SYMBOL_GPL(crypto_lookup_skcipher); int crypto_grab_skcipher(struct crypto_skcipher_spawn *spawn, const char *name, u32 type, u32 mask) diff --git a/crypto/aead.c b/crypto/aead.c index 04add3dca6fe..e4cb35159be4 100644 --- a/crypto/aead.c +++ b/crypto/aead.c @@ -470,8 +470,7 @@ out: return err; } -static struct crypto_alg *crypto_lookup_aead(const char *name, u32 type, - u32 mask) +struct crypto_alg *crypto_lookup_aead(const char *name, u32 type, u32 mask) { struct crypto_alg *alg; @@ -503,6 +502,7 @@ static struct crypto_alg *crypto_lookup_aead(const char *name, u32 type, return ERR_PTR(crypto_nivaead_default(alg, type, mask)); } +EXPORT_SYMBOL_GPL(crypto_lookup_aead); int crypto_grab_aead(struct crypto_aead_spawn *spawn, const char *name, u32 type, u32 mask) diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index f76e42bcc6e7..f1ea0a064135 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -21,9 +21,13 @@ #include <linux/module.h> #include <linux/crypto.h> #include <linux/cryptouser.h> +#include <linux/sched.h> #include <net/netlink.h> #include <linux/security.h> #include <net/net_namespace.h> +#include <crypto/internal/aead.h> +#include <crypto/internal/skcipher.h> + #include "internal.h" DEFINE_MUTEX(crypto_cfg_mutex); @@ -301,6 +305,60 @@ static int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh, return crypto_unregister_instance(alg); } +static struct crypto_alg *crypto_user_skcipher_alg(const char *name, u32 type, + u32 mask) +{ + int err; + struct crypto_alg *alg; + + type = crypto_skcipher_type(type); + mask = crypto_skcipher_mask(mask); + + for (;;) { + alg = crypto_lookup_skcipher(name, type, mask); + if (!IS_ERR(alg)) + return alg; + + err = PTR_ERR(alg); + if (err != -EAGAIN) + break; + if (signal_pending(current)) { + err = -EINTR; + break; + } + } + + return ERR_PTR(err); +} + +static struct crypto_alg *crypto_user_aead_alg(const char *name, u32 type, + u32 mask) +{ + int err; + struct crypto_alg *alg; + + type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV); + type |= CRYPTO_ALG_TYPE_AEAD; + mask &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV); + mask |= CRYPTO_ALG_TYPE_MASK; + + for (;;) { + alg = crypto_lookup_aead(name, type, mask); + if (!IS_ERR(alg)) + return alg; + + err = PTR_ERR(alg); + if (err != -EAGAIN) + break; + if (signal_pending(current)) { + err = -EINTR; + break; + } + } + + return ERR_PTR(err); +} + static int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs) { @@ -325,7 +383,19 @@ static int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh, else name = p->cru_name; - alg = crypto_alg_mod_lookup(name, p->cru_type, p->cru_mask); + switch (p->cru_type & p->cru_mask & CRYPTO_ALG_TYPE_MASK) { + case CRYPTO_ALG_TYPE_AEAD: + alg = crypto_user_aead_alg(name, p->cru_type, p->cru_mask); + break; + case CRYPTO_ALG_TYPE_GIVCIPHER: + case CRYPTO_ALG_TYPE_BLKCIPHER: + case CRYPTO_ALG_TYPE_ABLKCIPHER: + alg = crypto_user_skcipher_alg(name, p->cru_type, p->cru_mask); + break; + default: + alg = crypto_alg_mod_lookup(name, p->cru_type, p->cru_mask); + } + if (IS_ERR(alg)) return PTR_ERR(alg); @@ -387,12 +457,20 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if ((type == (CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE) && (nlh->nlmsg_flags & NLM_F_DUMP))) { + struct crypto_alg *alg; + u16 dump_alloc = 0; + if (link->dump == NULL) return -EINVAL; + + list_for_each_entry(alg, &crypto_alg_list, cra_list) + dump_alloc += CRYPTO_REPORT_MAXSIZE; + { struct netlink_dump_control c = { .dump = link->dump, .done = link->done, + .min_dump_alloc = dump_alloc, }; return netlink_dump_start(crypto_nlsk, skb, nlh, &c); } diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c index 29a89dad68b6..b2c99dc1c5e2 100644 --- a/crypto/pcrypt.c +++ b/crypto/pcrypt.c @@ -280,11 +280,11 @@ static int pcrypt_aead_init_tfm(struct crypto_tfm *tfm) ictx->tfm_count++; - cpu_index = ictx->tfm_count % cpumask_weight(cpu_active_mask); + cpu_index = ictx->tfm_count % cpumask_weight(cpu_online_mask); - ctx->cb_cpu = cpumask_first(cpu_active_mask); + ctx->cb_cpu = cpumask_first(cpu_online_mask); for (cpu = 0; cpu < cpu_index; cpu++) - ctx->cb_cpu = cpumask_next(ctx->cb_cpu, cpu_active_mask); + ctx->cb_cpu = cpumask_next(ctx->cb_cpu, cpu_online_mask); cipher = crypto_spawn_aead(crypto_instance_ctx(inst)); @@ -472,7 +472,7 @@ static int pcrypt_init_padata(struct padata_pcrypt *pcrypt, goto err_free_padata; } - cpumask_and(mask->mask, cpu_possible_mask, cpu_active_mask); + cpumask_and(mask->mask, cpu_possible_mask, cpu_online_mask); rcu_assign_pointer(pcrypt->cb_cpumask, mask); pcrypt->nblock.notifier_call = pcrypt_cpumask_change_notify; diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 107f6f7be5e1..dd30f40af9f5 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -174,7 +174,7 @@ sha512_update(struct shash_desc *desc, const u8 *data, unsigned int len) index = sctx->count[0] & 0x7f; /* Update number of bytes */ - if (!(sctx->count[0] += len)) + if ((sctx->count[0] += len) < len) sctx->count[1]++; part_len = 128 - index; diff --git a/drivers/Kconfig b/drivers/Kconfig index 6f0459cb745b..d236aef7e59f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -50,6 +50,8 @@ source "drivers/i2c/Kconfig" source "drivers/spi/Kconfig" +source "drivers/hsi/Kconfig" + source "drivers/pps/Kconfig" source "drivers/ptp/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 262b19d6b627..95952c82bf16 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_ATA) += ata/ obj-$(CONFIG_TARGET_CORE) += target/ obj-$(CONFIG_MTD) += mtd/ obj-$(CONFIG_SPI) += spi/ +obj-y += hsi/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index ab513a972c95..a716fede4f25 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -74,7 +74,8 @@ acpi_status acpi_reset(void) /* Check if the reset register is supported */ - if (!reset_reg->address) { + if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) || + !reset_reg->address) { return_ACPI_STATUS(AE_NOT_EXIST); } diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c index b258cab9061c..7586544fddb4 100644 --- a/drivers/acpi/ec_sys.c +++ b/drivers/acpi/ec_sys.c @@ -27,12 +27,6 @@ MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may " static struct dentry *acpi_ec_debugfs_dir; -static int acpi_ec_open_io(struct inode *i, struct file *f) -{ - f->private_data = i->i_private; - return 0; -} - static ssize_t acpi_ec_read_io(struct file *f, char __user *buf, size_t count, loff_t *off) { @@ -95,7 +89,7 @@ static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, static const struct file_operations acpi_ec_io_ops = { .owner = THIS_MODULE, - .open = acpi_ec_open_io, + .open = simple_open, .read = acpi_ec_read_io, .write = acpi_ec_write_io, .llseek = default_llseek, diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index ba14fb93c929..c3881b2eb8b2 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -607,8 +607,7 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_handler = handler; acpi_irq_context = context; - if (request_threaded_irq(irq, NULL, acpi_irq, IRQF_SHARED, "acpi", - acpi_irq)) { + if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) { printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq); acpi_irq_handler = NULL; return AE_NOT_ACQUIRED; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 7049a7d27c4f..330bb4d75852 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -631,7 +631,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) * We know a device's inferred power state when all the resources * required for a given D-state are 'on'. */ - for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) { + for (i = ACPI_STATE_D0; i < ACPI_STATE_D3_HOT; i++) { list = &device->power.states[i].resources; if (list->count < 1) continue; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index b3447f63e46b..f3decb30223f 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -786,7 +786,7 @@ static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) while (1) { if (cx->entry_method == ACPI_CSTATE_HALT) - halt(); + safe_halt(); else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) { inb(cx->address); /* See comment in acpi_idle_do_entry() */ diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c index c1d612435939..a6c77e8b37bd 100644 --- a/drivers/acpi/reboot.c +++ b/drivers/acpi/reboot.c @@ -23,7 +23,8 @@ void acpi_reboot(void) /* Is the reset register supported? The spec says we should be * checking the bit width and bit offset, but Windows ignores * these fields */ - /* Ignore also acpi_gbl_FADT.flags.ACPI_FADT_RESET_REGISTER */ + if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER)) + return; reset_value = acpi_gbl_FADT.reset_value; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 767e2dcb9616..7417267e88fa 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -869,7 +869,7 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) /* * Enumerate supported power management states */ - for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3; i++) { + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { struct acpi_device_power_state *ps = &device->power.states[i]; char object_name[5] = { '_', 'P', 'R', '0' + i, '\0' }; @@ -884,21 +884,18 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) acpi_bus_add_power_resource(ps->resources.handles[j]); } - /* The exist of _PR3 indicates D3Cold support */ - if (i == ACPI_STATE_D3) { - status = acpi_get_handle(device->handle, object_name, &handle); - if (ACPI_SUCCESS(status)) - device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; - } - /* Evaluate "_PSx" to see if we can do explicit sets */ object_name[2] = 'S'; status = acpi_get_handle(device->handle, object_name, &handle); if (ACPI_SUCCESS(status)) ps->flags.explicit_set = 1; - /* State is valid if we have some power control */ - if (ps->resources.count || ps->flags.explicit_set) + /* + * State is valid if there are means to put the device into it. + * D3hot is only valid if _PR3 present. + */ + if (ps->resources.count || + (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) ps->flags.valid = 1; ps->power = -1; /* Unknown - driver assigned */ diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 1d661b5c3287..eb6fd233764b 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -28,23 +28,33 @@ #include "internal.h" #include "sleep.h" +u8 wake_sleep_flags = ACPI_NO_OPTIONAL_METHODS; static unsigned int gts, bfs; -module_param(gts, uint, 0644); -module_param(bfs, uint, 0644); -MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend."); -MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".); - -static u8 wake_sleep_flags(void) +static int set_param_wake_flag(const char *val, struct kernel_param *kp) { - u8 flags = ACPI_NO_OPTIONAL_METHODS; + int ret = param_set_int(val, kp); - if (gts) - flags |= ACPI_EXECUTE_GTS; - if (bfs) - flags |= ACPI_EXECUTE_BFS; + if (ret) + return ret; - return flags; + if (kp->arg == (const char *)>s) { + if (gts) + wake_sleep_flags |= ACPI_EXECUTE_GTS; + else + wake_sleep_flags &= ~ACPI_EXECUTE_GTS; + } + if (kp->arg == (const char *)&bfs) { + if (bfs) + wake_sleep_flags |= ACPI_EXECUTE_BFS; + else + wake_sleep_flags &= ~ACPI_EXECUTE_BFS; + } + return ret; } +module_param_call(gts, set_param_wake_flag, param_get_int, >s, 0644); +module_param_call(bfs, set_param_wake_flag, param_get_int, &bfs, 0644); +MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend."); +MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".); static u8 sleep_states[ACPI_S_STATE_COUNT]; @@ -263,7 +273,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state) { acpi_status status = AE_OK; u32 acpi_state = acpi_target_sleep_state; - u8 flags = wake_sleep_flags(); int error; ACPI_FLUSH_CPU_CACHE(); @@ -271,7 +280,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) switch (acpi_state) { case ACPI_STATE_S1: barrier(); - status = acpi_enter_sleep_state(acpi_state, flags); + status = acpi_enter_sleep_state(acpi_state, wake_sleep_flags); break; case ACPI_STATE_S3: @@ -286,7 +295,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(acpi_state, flags); + acpi_leave_sleep_state_prep(acpi_state, wake_sleep_flags); /* ACPI 3.0 specs (P62) says that it's the responsibility * of the OSPM to clear the status bit [ implying that the @@ -550,30 +559,27 @@ static int acpi_hibernation_begin(void) static int acpi_hibernation_enter(void) { - u8 flags = wake_sleep_flags(); acpi_status status = AE_OK; ACPI_FLUSH_CPU_CACHE(); /* This shouldn't return. If it returns, we have a problem */ - status = acpi_enter_sleep_state(ACPI_STATE_S4, flags); + status = acpi_enter_sleep_state(ACPI_STATE_S4, wake_sleep_flags); /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(ACPI_STATE_S4, flags); + acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags); return ACPI_SUCCESS(status) ? 0 : -EFAULT; } static void acpi_hibernation_leave(void) { - u8 flags = wake_sleep_flags(); - /* * If ACPI is not enabled by the BIOS and the boot kernel, we need to * enable it here. */ acpi_enable(); /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(ACPI_STATE_S4, flags); + acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags); /* Check the hardware signature */ if (facs && s4_hardware_signature != facs->hardware_signature) { printk(KERN_EMERG "ACPI: Hardware changed while hibernated, " @@ -828,12 +834,10 @@ static void acpi_power_off_prepare(void) static void acpi_power_off(void) { - u8 flags = wake_sleep_flags(); - /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */ printk(KERN_DEBUG "%s called\n", __func__); local_irq_disable(); - acpi_enter_sleep_state(ACPI_STATE_S5, flags); + acpi_enter_sleep_state(ACPI_STATE_S5, wake_sleep_flags); } /* diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 01c2cf4efcdd..cc273226dbd0 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -247,8 +247,7 @@ static int amba_pm_restore(struct device *dev) /* * Hooks to provide runtime PM of the pclk (bus clock). It is safe to * enable/disable the bus clock at runtime PM suspend/resume as this - * does not result in loss of context. However, disabling vcore power - * would do, so we leave that to the driver. + * does not result in loss of context. */ static int amba_pm_runtime_suspend(struct device *dev) { @@ -354,39 +353,6 @@ static void amba_put_disable_pclk(struct amba_device *pcdev) clk_put(pclk); } -static int amba_get_enable_vcore(struct amba_device *pcdev) -{ - struct regulator *vcore = regulator_get(&pcdev->dev, "vcore"); - int ret; - - pcdev->vcore = vcore; - - if (IS_ERR(vcore)) { - /* It is OK not to supply a vcore regulator */ - if (PTR_ERR(vcore) == -ENODEV) - return 0; - return PTR_ERR(vcore); - } - - ret = regulator_enable(vcore); - if (ret) { - regulator_put(vcore); - pcdev->vcore = ERR_PTR(-ENODEV); - } - - return ret; -} - -static void amba_put_disable_vcore(struct amba_device *pcdev) -{ - struct regulator *vcore = pcdev->vcore; - - if (!IS_ERR(vcore)) { - regulator_disable(vcore); - regulator_put(vcore); - } -} - /* * These are the device model conversion veneers; they convert the * device model structures to our more specific structures. @@ -399,10 +365,6 @@ static int amba_probe(struct device *dev) int ret; do { - ret = amba_get_enable_vcore(pcdev); - if (ret) - break; - ret = amba_get_enable_pclk(pcdev); if (ret) break; @@ -420,7 +382,6 @@ static int amba_probe(struct device *dev) pm_runtime_put_noidle(dev); amba_put_disable_pclk(pcdev); - amba_put_disable_vcore(pcdev); } while (0); return ret; @@ -442,7 +403,6 @@ static int amba_remove(struct device *dev) pm_runtime_put_noidle(dev); amba_put_disable_pclk(pcdev); - amba_put_disable_vcore(pcdev); return ret; } diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 79a1e9dd56d9..ebaf67e4b2bc 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -394,6 +394,8 @@ static const struct pci_device_id ahci_pci_tbl[] = { .driver_data = board_ahci_yes_fbs }, /* 88se9128 */ { PCI_DEVICE(0x1b4b, 0x9125), .driver_data = board_ahci_yes_fbs }, /* 88se9125 */ + { PCI_DEVICE(0x1b4b, 0x917a), + .driver_data = board_ahci_yes_fbs }, /* 88se9172 */ { PCI_DEVICE(0x1b4b, 0x91a3), .driver_data = board_ahci_yes_fbs }, diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 0c86c77764bc..9e419e1c2006 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -280,6 +280,7 @@ static struct dev_pm_ops ahci_pm_ops = { static const struct of_device_id ahci_of_match[] = { { .compatible = "calxeda,hb-ahci", }, + { .compatible = "snps,spear-ahci", }, {}, }; MODULE_DEVICE_TABLE(of, ahci_of_match); diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 68013f96729f..7857e8fd0a3e 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -329,6 +329,8 @@ static const struct pci_device_id piix_pci_tbl[] = { { 0x8086, 0x8c08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, /* SATA Controller IDE (Lynx Point) */ { 0x8086, 0x8c09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (DH89xxCC) */ + { 0x8086, 0x2326, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, { } /* terminate list */ }; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index e0bda9ff89cd..23763a1ec570 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -95,7 +95,7 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev); static void ata_dev_xfermask(struct ata_device *dev); static unsigned long ata_dev_blacklisted(const struct ata_device *dev); -unsigned int ata_print_id = 1; +atomic_t ata_print_id = ATOMIC_INIT(0); struct ata_force_param { const char *name; @@ -6029,7 +6029,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) /* give ports names and add SCSI hosts */ for (i = 0; i < host->n_ports; i++) - host->ports[i]->print_id = ata_print_id++; + host->ports[i]->print_id = atomic_inc_return(&ata_print_id); /* Create associated sysfs transport objects */ diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index c61316e9d2f7..d1fbd59ead16 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -3501,7 +3501,8 @@ static int ata_count_probe_trials_cb(struct ata_ering_entry *ent, void *void_arg u64 now = get_jiffies_64(); int *trials = void_arg; - if (ent->timestamp < now - min(now, interval)) + if ((ent->eflags & ATA_EFLAG_OLD_ER) || + (ent->timestamp < now - min(now, interval))) return -1; (*trials)++; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 1ee00c8b5b04..22226350cd0c 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3399,7 +3399,8 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) */ shost->max_host_blocked = 1; - rc = scsi_add_host(ap->scsi_host, &ap->tdev); + rc = scsi_add_host_with_dma(ap->scsi_host, + &ap->tdev, ap->host->dev); if (rc) goto err_add; } @@ -3838,18 +3839,25 @@ void ata_sas_port_stop(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_sas_port_stop); -int ata_sas_async_port_init(struct ata_port *ap) +/** + * ata_sas_async_probe - simply schedule probing and return + * @ap: Port to probe + * + * For batch scheduling of probe for sas attached ata devices, assumes + * the port has already been through ata_sas_port_init() + */ +void ata_sas_async_probe(struct ata_port *ap) { - int rc = ap->ops->port_start(ap); - - if (!rc) { - ap->print_id = ata_print_id++; - __ata_port_probe(ap); - } + __ata_port_probe(ap); +} +EXPORT_SYMBOL_GPL(ata_sas_async_probe); - return rc; +int ata_sas_sync_probe(struct ata_port *ap) +{ + return ata_port_probe(ap); } -EXPORT_SYMBOL_GPL(ata_sas_async_port_init); +EXPORT_SYMBOL_GPL(ata_sas_sync_probe); + /** * ata_sas_port_init - Initialize a SATA device @@ -3866,12 +3874,10 @@ int ata_sas_port_init(struct ata_port *ap) { int rc = ap->ops->port_start(ap); - if (!rc) { - ap->print_id = ata_print_id++; - rc = ata_port_probe(ap); - } - - return rc; + if (rc) + return rc; + ap->print_id = atomic_inc_return(&ata_print_id); + return 0; } EXPORT_SYMBOL_GPL(ata_sas_port_init); diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 74aaee30e264..c34190485377 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -294,6 +294,7 @@ int ata_tport_add(struct device *parent, device_enable_async_suspend(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); + pm_runtime_forbid(dev); transport_add_device(dev); transport_configure_device(dev); diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 2e26fcaf635b..9d0fd0b71852 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -53,7 +53,7 @@ enum { ATA_DNXFER_QUIET = (1 << 31), }; -extern unsigned int ata_print_id; +extern atomic_t ata_print_id; extern int atapi_passthru16; extern int libata_fua; extern int libata_noacpi; diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index fc2db2a89a6b..3239517f4d90 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -943,9 +943,9 @@ static int arasan_cf_resume(struct device *dev) return 0; } +#endif static SIMPLE_DEV_PM_OPS(arasan_cf_pm_ops, arasan_cf_suspend, arasan_cf_resume); -#endif static struct platform_driver arasan_cf_driver = { .probe = arasan_cf_probe, @@ -953,9 +953,7 @@ static struct platform_driver arasan_cf_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &arasan_cf_pm_ops, -#endif }, }; diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 38950ea8398a..7336d4a7ab31 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4025,7 +4025,8 @@ static int mv_platform_probe(struct platform_device *pdev) struct ata_host *host; struct mv_host_priv *hpriv; struct resource *res; - int n_ports, rc; + int n_ports = 0; + int rc; ata_print_version_once(&pdev->dev, DRV_VERSION); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 6c9387d646ec..5401814c874d 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -16,10 +16,11 @@ #include <linux/interrupt.h> #include <linux/bitops.h> #include <linux/mutex.h> -#include <linux/kthread.h> +#include <linux/workqueue.h> #include <linux/highmem.h> #include <linux/firmware.h> #include <linux/slab.h> +#include <linux/sched.h> #define to_dev(obj) container_of(obj, struct device, kobj) @@ -81,6 +82,11 @@ enum { static int loading_timeout = 60; /* In seconds */ +static inline long firmware_loading_timeout(void) +{ + return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT; +} + /* fw_lock could be moved to 'struct firmware_priv' but since it is just * guarding for corner cases a global lock should be OK */ static DEFINE_MUTEX(fw_lock); @@ -440,13 +446,11 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, { struct firmware_priv *fw_priv; struct device *f_dev; - int error; fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL); if (!fw_priv) { dev_err(device, "%s: kmalloc failed\n", __func__); - error = -ENOMEM; - goto err_out; + return ERR_PTR(-ENOMEM); } fw_priv->fw = firmware; @@ -463,98 +467,80 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, f_dev->parent = device; f_dev->class = &firmware_class; - dev_set_uevent_suppress(f_dev, true); - - /* Need to pin this module until class device is destroyed */ - __module_get(THIS_MODULE); - - error = device_add(f_dev); - if (error) { - dev_err(device, "%s: device_register failed\n", __func__); - goto err_put_dev; - } - - error = device_create_bin_file(f_dev, &firmware_attr_data); - if (error) { - dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__); - goto err_del_dev; - } - - error = device_create_file(f_dev, &dev_attr_loading); - if (error) { - dev_err(device, "%s: device_create_file failed\n", __func__); - goto err_del_bin_attr; - } - - if (uevent) - dev_set_uevent_suppress(f_dev, false); - return fw_priv; - -err_del_bin_attr: - device_remove_bin_file(f_dev, &firmware_attr_data); -err_del_dev: - device_del(f_dev); -err_put_dev: - put_device(f_dev); -err_out: - return ERR_PTR(error); } -static void fw_destroy_instance(struct firmware_priv *fw_priv) -{ - struct device *f_dev = &fw_priv->dev; - - device_remove_file(f_dev, &dev_attr_loading); - device_remove_bin_file(f_dev, &firmware_attr_data); - device_unregister(f_dev); -} - -static int _request_firmware(const struct firmware **firmware_p, - const char *name, struct device *device, - bool uevent, bool nowait) +static struct firmware_priv * +_request_firmware_prepare(const struct firmware **firmware_p, const char *name, + struct device *device, bool uevent, bool nowait) { - struct firmware_priv *fw_priv; struct firmware *firmware; - int retval = 0; + struct firmware_priv *fw_priv; if (!firmware_p) - return -EINVAL; + return ERR_PTR(-EINVAL); *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { dev_err(device, "%s: kmalloc(struct firmware) failed\n", __func__); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } if (fw_get_builtin_firmware(firmware, name)) { dev_dbg(device, "firmware: using built-in firmware %s\n", name); - return 0; + return NULL; + } + + fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); + if (IS_ERR(fw_priv)) { + release_firmware(firmware); + *firmware_p = NULL; } + return fw_priv; +} - read_lock_usermodehelper(); +static void _request_firmware_cleanup(const struct firmware **firmware_p) +{ + release_firmware(*firmware_p); + *firmware_p = NULL; +} - if (WARN_ON(usermodehelper_is_disabled())) { - dev_err(device, "firmware: %s will not be loaded\n", name); - retval = -EBUSY; - goto out; +static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, + long timeout) +{ + int retval = 0; + struct device *f_dev = &fw_priv->dev; + + dev_set_uevent_suppress(f_dev, true); + + /* Need to pin this module until class device is destroyed */ + __module_get(THIS_MODULE); + + retval = device_add(f_dev); + if (retval) { + dev_err(f_dev, "%s: device_register failed\n", __func__); + goto err_put_dev; } - if (uevent) - dev_dbg(device, "firmware: requesting %s\n", name); + retval = device_create_bin_file(f_dev, &firmware_attr_data); + if (retval) { + dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__); + goto err_del_dev; + } - fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); - if (IS_ERR(fw_priv)) { - retval = PTR_ERR(fw_priv); - goto out; + retval = device_create_file(f_dev, &dev_attr_loading); + if (retval) { + dev_err(f_dev, "%s: device_create_file failed\n", __func__); + goto err_del_bin_attr; } if (uevent) { - if (loading_timeout > 0) + dev_set_uevent_suppress(f_dev, false); + dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id); + if (timeout != MAX_SCHEDULE_TIMEOUT) mod_timer(&fw_priv->timeout, - round_jiffies_up(jiffies + - loading_timeout * HZ)); + round_jiffies_up(jiffies + timeout)); kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); } @@ -570,16 +556,13 @@ static int _request_firmware(const struct firmware **firmware_p, fw_priv->fw = NULL; mutex_unlock(&fw_lock); - fw_destroy_instance(fw_priv); - -out: - read_unlock_usermodehelper(); - - if (retval) { - release_firmware(firmware); - *firmware_p = NULL; - } - + device_remove_file(f_dev, &dev_attr_loading); +err_del_bin_attr: + device_remove_bin_file(f_dev, &firmware_attr_data); +err_del_dev: + device_del(f_dev); +err_put_dev: + put_device(f_dev); return retval; } @@ -602,7 +585,26 @@ int request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { - return _request_firmware(firmware_p, name, device, true, false); + struct firmware_priv *fw_priv; + int ret; + + fw_priv = _request_firmware_prepare(firmware_p, name, device, true, + false); + if (IS_ERR_OR_NULL(fw_priv)) + return PTR_RET(fw_priv); + + ret = usermodehelper_read_trylock(); + if (WARN_ON(ret)) { + dev_err(device, "firmware: %s will not be loaded\n", name); + } else { + ret = _request_firmware_load(fw_priv, true, + firmware_loading_timeout()); + usermodehelper_read_unlock(); + } + if (ret) + _request_firmware_cleanup(firmware_p); + + return ret; } /** @@ -629,25 +631,39 @@ struct firmware_work { bool uevent; }; -static int request_firmware_work_func(void *arg) +static void request_firmware_work_func(struct work_struct *work) { - struct firmware_work *fw_work = arg; + struct firmware_work *fw_work; const struct firmware *fw; + struct firmware_priv *fw_priv; + long timeout; int ret; - if (!arg) { - WARN_ON(1); - return 0; + fw_work = container_of(work, struct firmware_work, work); + fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device, + fw_work->uevent, true); + if (IS_ERR_OR_NULL(fw_priv)) { + ret = PTR_RET(fw_priv); + goto out; + } + + timeout = usermodehelper_read_lock_wait(firmware_loading_timeout()); + if (timeout) { + ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout); + usermodehelper_read_unlock(); + } else { + dev_dbg(fw_work->device, "firmware: %s loading timed out\n", + fw_work->name); + ret = -EAGAIN; } + if (ret) + _request_firmware_cleanup(&fw); - ret = _request_firmware(&fw, fw_work->name, fw_work->device, - fw_work->uevent, true); + out: fw_work->cont(fw, fw_work->context); module_put(fw_work->module); kfree(fw_work); - - return ret; } /** @@ -673,7 +689,6 @@ request_firmware_nowait( const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)) { - struct task_struct *task; struct firmware_work *fw_work; fw_work = kzalloc(sizeof (struct firmware_work), gfp); @@ -692,15 +707,8 @@ request_firmware_nowait( return -EFAULT; } - task = kthread_run(request_firmware_work_func, fw_work, - "firmware/%s", name); - if (IS_ERR(task)) { - fw_work->cont(NULL, fw_work->context); - module_put(fw_work->module); - kfree(fw_work); - return PTR_ERR(task); - } - + INIT_WORK(&fw_work->work, request_firmware_work_func); + schedule_work(&fw_work->work); return 0; } diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 541f821d4ea6..bd0f3949bcf9 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -532,6 +532,8 @@ static int rpm_suspend(struct device *dev, int rpmflags) dev->power.suspend_time = ktime_set(0, 0); dev->power.max_time_suspended_ns = -1; dev->power.deferred_resume = false; + wake_up_all(&dev->power.wait_queue); + if (retval == -EAGAIN || retval == -EBUSY) { dev->power.runtime_error = 0; @@ -547,7 +549,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) } else { pm_runtime_cancel_pending(dev); } - wake_up_all(&dev->power.wait_queue); goto out; } diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 0f6c7fb418e8..9ef0a5326f17 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -14,5 +14,8 @@ config REGMAP_I2C config REGMAP_SPI tristate +config REGMAP_MMIO + tristate + config REGMAP_IRQ bool diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index defd57963c84..5e75d1b683e2 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -3,4 +3,5 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o +obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index fcafc5b2e651..d92e9b1cb83c 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -26,21 +26,29 @@ struct regmap_format { size_t val_bytes; void (*format_write)(struct regmap *map, unsigned int reg, unsigned int val); - void (*format_reg)(void *buf, unsigned int reg); - void (*format_val)(void *buf, unsigned int val); + void (*format_reg)(void *buf, unsigned int reg, unsigned int shift); + void (*format_val)(void *buf, unsigned int val, unsigned int shift); unsigned int (*parse_val)(void *buf); }; +typedef void (*regmap_lock)(struct regmap *map); +typedef void (*regmap_unlock)(struct regmap *map); + struct regmap { - struct mutex lock; + struct mutex mutex; + spinlock_t spinlock; + regmap_lock lock; + regmap_unlock unlock; struct device *dev; /* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ struct regmap_format format; /* Buffer format */ const struct regmap_bus *bus; + void *bus_context; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; + const char *debugfs_name; #endif unsigned int max_register; @@ -52,6 +60,10 @@ struct regmap { u8 read_flag_mask; u8 write_flag_mask; + /* number of bits to (left) shift the reg value when formatting*/ + int reg_shift; + int reg_stride; + /* regcache specific members */ const struct regcache_ops *cache_ops; enum regcache_type cache_type; @@ -101,11 +113,11 @@ int _regmap_write(struct regmap *map, unsigned int reg, #ifdef CONFIG_DEBUG_FS extern void regmap_debugfs_initcall(void); -extern void regmap_debugfs_init(struct regmap *map); +extern void regmap_debugfs_init(struct regmap *map, const char *name); extern void regmap_debugfs_exit(struct regmap *map); #else static inline void regmap_debugfs_initcall(void) { } -static inline void regmap_debugfs_init(struct regmap *map) { } +static inline void regmap_debugfs_init(struct regmap *map, const char *name) { } static inline void regmap_debugfs_exit(struct regmap *map) { } #endif diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 483b06d4a380..afd6aa91a0df 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -108,7 +108,7 @@ static int regcache_lzo_decompress_cache_block(struct regmap *map, static inline int regcache_lzo_get_blkindex(struct regmap *map, unsigned int reg) { - return (reg * map->cache_word_size) / + return ((reg / map->reg_stride) * map->cache_word_size) / DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count(map)); } @@ -116,9 +116,10 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map, static inline int regcache_lzo_get_blkpos(struct regmap *map, unsigned int reg) { - return reg % (DIV_ROUND_UP(map->cache_size_raw, - regcache_lzo_block_count(map)) / - map->cache_word_size); + return (reg / map->reg_stride) % + (DIV_ROUND_UP(map->cache_size_raw, + regcache_lzo_block_count(map)) / + map->cache_word_size); } static inline int regcache_lzo_get_blksize(struct regmap *map) @@ -322,7 +323,7 @@ static int regcache_lzo_write(struct regmap *map, } /* set the bit so we know we have to sync this register */ - set_bit(reg, lzo_block->sync_bmp); + set_bit(reg / map->reg_stride, lzo_block->sync_bmp); kfree(tmp_dst); kfree(lzo_block->src); return 0; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 5157fa04c2f0..e6732cf7c06e 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -39,11 +39,12 @@ struct regcache_rbtree_ctx { }; static inline void regcache_rbtree_get_base_top_reg( + struct regmap *map, struct regcache_rbtree_node *rbnode, unsigned int *base, unsigned int *top) { *base = rbnode->base_reg; - *top = rbnode->base_reg + rbnode->blklen - 1; + *top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride); } static unsigned int regcache_rbtree_get_register( @@ -70,7 +71,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, rbnode = rbtree_ctx->cached_rbnode; if (rbnode) { - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); if (reg >= base_reg && reg <= top_reg) return rbnode; } @@ -78,7 +80,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, node = rbtree_ctx->root.rb_node; while (node) { rbnode = container_of(node, struct regcache_rbtree_node, node); - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); if (reg >= base_reg && reg <= top_reg) { rbtree_ctx->cached_rbnode = rbnode; return rbnode; @@ -92,7 +95,7 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, return NULL; } -static int regcache_rbtree_insert(struct rb_root *root, +static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root, struct regcache_rbtree_node *rbnode) { struct rb_node **new, *parent; @@ -106,7 +109,7 @@ static int regcache_rbtree_insert(struct rb_root *root, rbnode_tmp = container_of(*new, struct regcache_rbtree_node, node); /* base and top registers of the current rbnode */ - regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp, + regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp, &top_reg_tmp); /* base register of the rbnode to be added */ base_reg = rbnode->base_reg; @@ -138,24 +141,31 @@ static int rbtree_show(struct seq_file *s, void *ignored) unsigned int base, top; int nodes = 0; int registers = 0; + int this_registers, average; - mutex_lock(&map->lock); + map->lock(map); for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { n = container_of(node, struct regcache_rbtree_node, node); - regcache_rbtree_get_base_top_reg(n, &base, &top); - seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1); + regcache_rbtree_get_base_top_reg(map, n, &base, &top); + this_registers = ((top - base) / map->reg_stride) + 1; + seq_printf(s, "%x-%x (%d)\n", base, top, this_registers); nodes++; - registers += top - base + 1; + registers += this_registers; } + if (nodes) + average = registers / nodes; + else + average = 0; + seq_printf(s, "%d nodes, %d registers, average %d registers\n", - nodes, registers, registers / nodes); + nodes, registers, average); - mutex_unlock(&map->lock); + map->unlock(map); return 0; } @@ -249,7 +259,7 @@ static int regcache_rbtree_read(struct regmap *map, rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { - reg_tmp = reg - rbnode->base_reg; + reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; *value = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); } else { @@ -304,7 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, */ rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { - reg_tmp = reg - rbnode->base_reg; + reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; val = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); if (val == value) @@ -315,13 +325,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, /* look for an adjacent register to the one we are about to add */ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { - rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node); + rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, + node); for (i = 0; i < rbnode_tmp->blklen; i++) { - reg_tmp = rbnode_tmp->base_reg + i; - if (abs(reg_tmp - reg) != 1) + reg_tmp = rbnode_tmp->base_reg + + (i * map->reg_stride); + if (abs(reg_tmp - reg) != map->reg_stride) continue; /* decide where in the block to place our register */ - if (reg_tmp + 1 == reg) + if (reg_tmp + map->reg_stride == reg) pos = i + 1; else pos = i; @@ -351,7 +363,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return -ENOMEM; } regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size); - regcache_rbtree_insert(&rbtree_ctx->root, rbnode); + regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } @@ -391,12 +403,12 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, end = rbnode->blklen; for (i = base; i < end; i++) { - regtmp = rbnode->base_reg + i; + regtmp = rbnode->base_reg + (i * map->reg_stride); val = regcache_rbtree_get_register(rbnode, i, map->cache_word_size); /* Is this the hardware default? If so skip. */ - ret = regcache_lookup_reg(map, i); + ret = regcache_lookup_reg(map, regtmp); if (ret >= 0 && val == map->reg_defaults[ret].def) continue; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 87f54dbf601b..835883bda977 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -59,7 +59,7 @@ static int regcache_hw_init(struct regmap *map) for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (regmap_volatile(map, i)) + if (regmap_volatile(map, i * map->reg_stride)) continue; count++; } @@ -76,9 +76,9 @@ static int regcache_hw_init(struct regmap *map) for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (regmap_volatile(map, i)) + if (regmap_volatile(map, i * map->reg_stride)) continue; - map->reg_defaults[j].reg = i; + map->reg_defaults[j].reg = i * map->reg_stride; map->reg_defaults[j].def = val; j++; } @@ -98,6 +98,10 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) int i; void *tmp_buf; + for (i = 0; i < config->num_reg_defaults; i++) + if (config->reg_defaults[i].reg % map->reg_stride) + return -EINVAL; + if (map->cache_type == REGCACHE_NONE) { map->cache_bypass = true; return 0; @@ -264,7 +268,7 @@ int regcache_sync(struct regmap *map) BUG_ON(!map->cache_ops || !map->cache_ops->sync); - mutex_lock(&map->lock); + map->lock(map); /* Remember the initial bypass state */ bypass = map->cache_bypass; dev_dbg(map->dev, "Syncing %s cache\n", @@ -278,6 +282,10 @@ int regcache_sync(struct regmap *map) /* Apply any patch first */ map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { + if (map->patch[i].reg % map->reg_stride) { + ret = -EINVAL; + goto out; + } ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { dev_err(map->dev, "Failed to write %x = %x: %d\n", @@ -296,7 +304,7 @@ out: trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -323,7 +331,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, BUG_ON(!map->cache_ops || !map->cache_ops->sync); - mutex_lock(&map->lock); + map->lock(map); /* Remember the initial bypass state */ bypass = map->cache_bypass; @@ -342,10 +350,11 @@ out: trace_regcache_sync(map->dev, name, "stop region"); /* Restore the bypass state */ map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } +EXPORT_SYMBOL_GPL(regcache_sync_region); /** * regcache_cache_only: Put a register map into cache only mode @@ -361,11 +370,11 @@ out: */ void regcache_cache_only(struct regmap *map, bool enable) { - mutex_lock(&map->lock); + map->lock(map); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; trace_regmap_cache_only(map->dev, enable); - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -380,9 +389,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only); */ void regcache_mark_dirty(struct regmap *map) { - mutex_lock(&map->lock); + map->lock(map); map->cache_dirty = true; - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_mark_dirty); @@ -399,11 +408,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty); */ void regcache_cache_bypass(struct regmap *map, bool enable) { - mutex_lock(&map->lock); + map->lock(map); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; trace_regmap_cache_bypass(map->dev, enable); - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 58517a5dac13..bb1ff175b962 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -27,12 +27,6 @@ static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size) return strlen(buf); } -static int regmap_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t regmap_name_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -57,7 +51,7 @@ static ssize_t regmap_name_read_file(struct file *file, } static const struct file_operations regmap_name_fops = { - .open = regmap_open_file, + .open = simple_open, .read = regmap_name_read_file, .llseek = default_llseek, }; @@ -86,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, val_len = 2 * map->format.val_bytes; tot_len = reg_len + val_len + 3; /* : \n */ - for (i = 0; i < map->max_register + 1; i++) { + for (i = 0; i <= map->max_register; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; @@ -174,7 +168,7 @@ static ssize_t regmap_map_write_file(struct file *file, #endif static const struct file_operations regmap_map_fops = { - .open = regmap_open_file, + .open = simple_open, .read = regmap_map_read_file, .write = regmap_map_write_file, .llseek = default_llseek, @@ -203,7 +197,7 @@ static ssize_t regmap_access_read_file(struct file *file, reg_len = regmap_calc_reg_len(map->max_register, buf, count); tot_len = reg_len + 10; /* ': R W V P\n' */ - for (i = 0; i < map->max_register + 1; i++) { + for (i = 0; i <= map->max_register; i += map->reg_stride) { /* Ignore registers which are neither readable nor writable */ if (!regmap_readable(map, i) && !regmap_writeable(map, i)) continue; @@ -243,15 +237,22 @@ out: } static const struct file_operations regmap_access_fops = { - .open = regmap_open_file, + .open = simple_open, .read = regmap_access_read_file, .llseek = default_llseek, }; -void regmap_debugfs_init(struct regmap *map) +void regmap_debugfs_init(struct regmap *map, const char *name) { - map->debugfs = debugfs_create_dir(dev_name(map->dev), - regmap_debugfs_root); + if (name) { + map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", + dev_name(map->dev), name); + name = map->debugfs_name; + } else { + name = dev_name(map->dev); + } + + map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); if (!map->debugfs) { dev_warn(map->dev, "Failed to create debugfs directory\n"); return; @@ -280,6 +281,7 @@ void regmap_debugfs_init(struct regmap *map) void regmap_debugfs_exit(struct regmap *map) { debugfs_remove_recursive(map->debugfs); + kfree(map->debugfs_name); } void regmap_debugfs_initcall(void) diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 9a3a8c564389..5f6b2478bf17 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -15,8 +15,9 @@ #include <linux/module.h> #include <linux/init.h> -static int regmap_i2c_write(struct device *dev, const void *data, size_t count) +static int regmap_i2c_write(void *context, const void *data, size_t count) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); int ret; @@ -29,10 +30,11 @@ static int regmap_i2c_write(struct device *dev, const void *data, size_t count) return -EIO; } -static int regmap_i2c_gather_write(struct device *dev, +static int regmap_i2c_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); struct i2c_msg xfer[2]; int ret; @@ -62,10 +64,11 @@ static int regmap_i2c_gather_write(struct device *dev, return -EIO; } -static int regmap_i2c_read(struct device *dev, +static int regmap_i2c_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); struct i2c_msg xfer[2]; int ret; @@ -107,7 +110,7 @@ static struct regmap_bus regmap_i2c = { struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { - return regmap_init(&i2c->dev, ®map_i2c, config); + return regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config); } EXPORT_SYMBOL_GPL(regmap_init_i2c); @@ -124,7 +127,7 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c); struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { - return devm_regmap_init(&i2c->dev, ®map_i2c, config); + return devm_regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config); } EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 1befaa7a31cb..fc69d29d272a 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -50,6 +50,7 @@ static void regmap_irq_lock(struct irq_data *data) static void regmap_irq_sync_unlock(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; int i, ret; /* @@ -58,11 +59,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * suppress pointless writes. */ for (i = 0; i < d->chip->num_regs; i++) { - ret = regmap_update_bits(d->map, d->chip->mask_base + i, + ret = regmap_update_bits(d->map, d->chip->mask_base + + (i * map->reg_stride), d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", - d->chip->mask_base + i); + d->chip->mask_base + (i * map->reg_stride)); } mutex_unlock(&d->lock); @@ -71,17 +73,19 @@ static void regmap_irq_sync_unlock(struct irq_data *data) static void regmap_irq_enable(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); - d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; } static void regmap_irq_disable(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); - d->mask_buf[irq_data->reg_offset] |= irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } static struct irq_chip regmap_irq_chip = { @@ -136,17 +140,19 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && chip->ack_base) { - ret = regmap_write(map, chip->ack_base + i, + ret = regmap_write(map, chip->ack_base + + (i * map->reg_stride), data->status_buf[i]); if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", - chip->ack_base + i, ret); + chip->ack_base + (i * map->reg_stride), + ret); } } for (i = 0; i < chip->num_irqs; i++) { - if (data->status_buf[chip->irqs[i].reg_offset] & - chip->irqs[i].mask) { + if (data->status_buf[chip->irqs[i].reg_offset / + map->reg_stride] & chip->irqs[i].mask) { handle_nested_irq(data->irq_base + i); handled = true; } @@ -181,6 +187,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int cur_irq, i; int ret = -ENOMEM; + for (i = 0; i < chip->num_irqs; i++) { + if (chip->irqs[i].reg_offset % map->reg_stride) + return -EINVAL; + if (chip->irqs[i].reg_offset / map->reg_stride >= + chip->num_regs) + return -EINVAL; + } + irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); if (irq_base < 0) { dev_warn(map->dev, "Failed to allocate IRQs: %d\n", @@ -218,16 +232,17 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) - d->mask_buf_def[chip->irqs[i].reg_offset] + d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] |= chip->irqs[i].mask; /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - ret = regmap_write(map, chip->mask_base + i, d->mask_buf[i]); + ret = regmap_write(map, chip->mask_base + (i * map->reg_stride), + d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", - chip->mask_base + i, ret); + chip->mask_base + (i * map->reg_stride), ret); goto err_alloc; } } diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c new file mode 100644 index 000000000000..febd6de6c8ac --- /dev/null +++ b/drivers/base/regmap/regmap-mmio.c @@ -0,0 +1,224 @@ +/* + * Register map access API - MMIO support + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +struct regmap_mmio_context { + void __iomem *regs; + unsigned val_bytes; +}; + +static int regmap_mmio_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct regmap_mmio_context *ctx = context; + u32 offset; + + BUG_ON(reg_size != 4); + + offset = be32_to_cpup(reg); + + while (val_size) { + switch (ctx->val_bytes) { + case 1: + writeb(*(u8 *)val, ctx->regs + offset); + break; + case 2: + writew(be16_to_cpup(val), ctx->regs + offset); + break; + case 4: + writel(be32_to_cpup(val), ctx->regs + offset); + break; +#ifdef CONFIG_64BIT + case 8: + writeq(be64_to_cpup(val), ctx->regs + offset); + break; +#endif + default: + /* Should be caught by regmap_mmio_check_config */ + BUG(); + } + val_size -= ctx->val_bytes; + val += ctx->val_bytes; + offset += ctx->val_bytes; + } + + return 0; +} + +static int regmap_mmio_write(void *context, const void *data, size_t count) +{ + BUG_ON(count < 4); + + return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); +} + +static int regmap_mmio_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct regmap_mmio_context *ctx = context; + u32 offset; + + BUG_ON(reg_size != 4); + + offset = be32_to_cpup(reg); + + while (val_size) { + switch (ctx->val_bytes) { + case 1: + *(u8 *)val = readb(ctx->regs + offset); + break; + case 2: + *(u16 *)val = cpu_to_be16(readw(ctx->regs + offset)); + break; + case 4: + *(u32 *)val = cpu_to_be32(readl(ctx->regs + offset)); + break; +#ifdef CONFIG_64BIT + case 8: + *(u64 *)val = cpu_to_be32(readq(ctx->regs + offset)); + break; +#endif + default: + /* Should be caught by regmap_mmio_check_config */ + BUG(); + } + val_size -= ctx->val_bytes; + val += ctx->val_bytes; + offset += ctx->val_bytes; + } + + return 0; +} + +static void regmap_mmio_free_context(void *context) +{ + kfree(context); +} + +static struct regmap_bus regmap_mmio = { + .fast_io = true, + .write = regmap_mmio_write, + .gather_write = regmap_mmio_gather_write, + .read = regmap_mmio_read, + .free_context = regmap_mmio_free_context, +}; + +struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + int min_stride; + + if (config->reg_bits != 32) + return ERR_PTR(-EINVAL); + + if (config->pad_bits) + return ERR_PTR(-EINVAL); + + switch (config->val_bits) { + case 8: + /* The core treats 0 as 1 */ + min_stride = 0; + break; + case 16: + min_stride = 2; + break; + case 32: + min_stride = 4; + break; +#ifdef CONFIG_64BIT + case 64: + min_stride = 8; + break; +#endif + break; + default: + return ERR_PTR(-EINVAL); + } + + if (config->reg_stride < min_stride) + return ERR_PTR(-EINVAL); + + ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->regs = regs; + ctx->val_bytes = config->val_bits / 8; + + return ctx; +} + +/** + * regmap_init_mmio(): Initialise register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(regmap_init_mmio); + +/** + * devm_regmap_init_mmio(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return devm_regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 7c0c35a39c33..ffa46a92ad33 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -15,17 +15,19 @@ #include <linux/init.h> #include <linux/module.h> -static int regmap_spi_write(struct device *dev, const void *data, size_t count) +static int regmap_spi_write(void *context, const void *data, size_t count) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); return spi_write(spi, data, count); } -static int regmap_spi_gather_write(struct device *dev, +static int regmap_spi_gather_write(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); struct spi_message m; struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, }, @@ -38,10 +40,11 @@ static int regmap_spi_gather_write(struct device *dev, return spi_sync(spi, &m); } -static int regmap_spi_read(struct device *dev, +static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); return spi_write_then_read(spi, reg, reg_size, val, val_size); @@ -66,7 +69,7 @@ static struct regmap_bus regmap_spi = { struct regmap *regmap_init_spi(struct spi_device *spi, const struct regmap_config *config) { - return regmap_init(&spi->dev, ®map_spi, config); + return regmap_init(&spi->dev, ®map_spi, &spi->dev, config); } EXPORT_SYMBOL_GPL(regmap_init_spi); @@ -83,7 +86,7 @@ EXPORT_SYMBOL_GPL(regmap_init_spi); struct regmap *devm_regmap_init_spi(struct spi_device *spi, const struct regmap_config *config) { - return devm_regmap_init(&spi->dev, ®map_spi, config); + return devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config); } EXPORT_SYMBOL_GPL(devm_regmap_init_spi); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7a3f535e481c..de9f79650e8f 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -112,25 +112,36 @@ static void regmap_format_10_14_write(struct regmap *map, out[0] = reg >> 2; } -static void regmap_format_8(void *buf, unsigned int val) +static void regmap_format_8(void *buf, unsigned int val, unsigned int shift) { u8 *b = buf; - b[0] = val; + b[0] = val << shift; } -static void regmap_format_16(void *buf, unsigned int val) +static void regmap_format_16(void *buf, unsigned int val, unsigned int shift) { __be16 *b = buf; - b[0] = cpu_to_be16(val); + b[0] = cpu_to_be16(val << shift); } -static void regmap_format_32(void *buf, unsigned int val) +static void regmap_format_24(void *buf, unsigned int val, unsigned int shift) +{ + u8 *b = buf; + + val <<= shift; + + b[0] = val >> 16; + b[1] = val >> 8; + b[2] = val; +} + +static void regmap_format_32(void *buf, unsigned int val, unsigned int shift) { __be32 *b = buf; - b[0] = cpu_to_be32(val); + b[0] = cpu_to_be32(val << shift); } static unsigned int regmap_parse_8(void *buf) @@ -149,6 +160,16 @@ static unsigned int regmap_parse_16(void *buf) return b[0]; } +static unsigned int regmap_parse_24(void *buf) +{ + u8 *b = buf; + unsigned int ret = b[2]; + ret |= ((unsigned int)b[1]) << 8; + ret |= ((unsigned int)b[0]) << 16; + + return ret; +} + static unsigned int regmap_parse_32(void *buf) { __be32 *b = buf; @@ -158,11 +179,32 @@ static unsigned int regmap_parse_32(void *buf) return b[0]; } +static void regmap_lock_mutex(struct regmap *map) +{ + mutex_lock(&map->mutex); +} + +static void regmap_unlock_mutex(struct regmap *map) +{ + mutex_unlock(&map->mutex); +} + +static void regmap_lock_spinlock(struct regmap *map) +{ + spin_lock(&map->spinlock); +} + +static void regmap_unlock_spinlock(struct regmap *map) +{ + spin_unlock(&map->spinlock); +} + /** * regmap_init(): Initialise register map * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to @@ -171,6 +213,7 @@ static unsigned int regmap_parse_32(void *buf) */ struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config) { struct regmap *map; @@ -185,14 +228,28 @@ struct regmap *regmap_init(struct device *dev, goto err; } - mutex_init(&map->lock); + if (bus->fast_io) { + spin_lock_init(&map->spinlock); + map->lock = regmap_lock_spinlock; + map->unlock = regmap_unlock_spinlock; + } else { + mutex_init(&map->mutex); + map->lock = regmap_lock_mutex; + map->unlock = regmap_unlock_mutex; + } map->format.buf_size = (config->reg_bits + config->val_bits) / 8; map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); map->format.buf_size += map->format.pad_bytes; + map->reg_shift = config->pad_bits % 8; + if (config->reg_stride) + map->reg_stride = config->reg_stride; + else + map->reg_stride = 1; map->dev = dev; map->bus = bus; + map->bus_context = bus_context; map->max_register = config->max_register; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; @@ -207,7 +264,7 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } - switch (config->reg_bits) { + switch (config->reg_bits + map->reg_shift) { case 2: switch (config->val_bits) { case 6: @@ -273,6 +330,10 @@ struct regmap *regmap_init(struct device *dev, map->format.format_val = regmap_format_16; map->format.parse_val = regmap_parse_16; break; + case 24: + map->format.format_val = regmap_format_24; + map->format.parse_val = regmap_parse_24; + break; case 32: map->format.format_val = regmap_format_32; map->format.parse_val = regmap_parse_32; @@ -289,7 +350,7 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - regmap_debugfs_init(map); + regmap_debugfs_init(map, config->name); ret = regcache_init(map, config); if (ret < 0) @@ -316,6 +377,7 @@ static void devm_regmap_release(struct device *dev, void *res) * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer @@ -325,6 +387,7 @@ static void devm_regmap_release(struct device *dev, void *res) */ struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config) { struct regmap **ptr, *regmap; @@ -333,7 +396,7 @@ struct regmap *devm_regmap_init(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - regmap = regmap_init(dev, bus, config); + regmap = regmap_init(dev, bus, bus_context, config); if (!IS_ERR(regmap)) { *ptr = regmap; devres_add(dev, ptr); @@ -360,7 +423,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) { int ret; - mutex_lock(&map->lock); + map->lock(map); regcache_exit(map); regmap_debugfs_exit(map); @@ -372,14 +435,14 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; - regmap_debugfs_init(map); + regmap_debugfs_init(map, config->name); map->cache_bypass = false; map->cache_only = false; ret = regcache_init(map, config); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -391,6 +454,8 @@ void regmap_exit(struct regmap *map) { regcache_exit(map); regmap_debugfs_exit(map); + if (map->bus->free_context) + map->bus->free_context(map->bus_context); kfree(map->work_buf); kfree(map); } @@ -408,7 +473,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, /* Check for unwritable registers before we start */ if (map->writeable_reg) for (i = 0; i < val_len / map->format.val_bytes; i++) - if (!map->writeable_reg(map->dev, reg + i)) + if (!map->writeable_reg(map->dev, + reg + (i * map->reg_stride))) return -EINVAL; if (!map->cache_bypass && map->format.parse_val) { @@ -417,7 +483,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, for (i = 0; i < val_len / val_bytes; i++) { memcpy(map->work_buf, val + (i * val_bytes), val_bytes); ival = map->format.parse_val(map->work_buf); - ret = regcache_write(map, reg + i, ival); + ret = regcache_write(map, reg + (i * map->reg_stride), + ival); if (ret) { dev_err(map->dev, "Error in caching of register: %u ret: %d\n", @@ -431,7 +498,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, } } - map->format.format_reg(map->work_buf, reg); + map->format.format_reg(map->work_buf, reg, map->reg_shift); u8[0] |= map->write_flag_mask; @@ -444,12 +511,12 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, */ if (val == (map->work_buf + map->format.pad_bytes + map->format.reg_bytes)) - ret = map->bus->write(map->dev, map->work_buf, + ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + val_len); else if (map->bus->gather_write) - ret = map->bus->gather_write(map->dev, map->work_buf, + ret = map->bus->gather_write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); @@ -464,7 +531,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, memcpy(buf, map->work_buf, map->format.reg_bytes); memcpy(buf + map->format.reg_bytes + map->format.pad_bytes, val, val_len); - ret = map->bus->write(map->dev, buf, len); + ret = map->bus->write(map->bus_context, buf, len); kfree(buf); } @@ -498,7 +565,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_hw_write_start(map->dev, reg, 1); - ret = map->bus->write(map->dev, map->work_buf, + ret = map->bus->write(map->bus_context, map->work_buf, map->format.buf_size); trace_regmap_hw_write_done(map->dev, reg, 1); @@ -506,7 +573,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, return ret; } else { map->format.format_val(map->work_buf + map->format.reg_bytes - + map->format.pad_bytes, val); + + map->format.pad_bytes, val, 0); return _regmap_raw_write(map, reg, map->work_buf + map->format.reg_bytes + @@ -529,11 +596,14 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; - mutex_lock(&map->lock); + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map); ret = _regmap_write(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -560,11 +630,16 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; - mutex_lock(&map->lock); + if (val_len % map->format.val_bytes) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map); ret = _regmap_raw_write(map, reg, val, val_len); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -593,8 +668,10 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->format.parse_val) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; - mutex_lock(&map->lock); + map->lock(map); /* No formatting is require if val_byte is 1 */ if (val_bytes == 1) { @@ -615,7 +692,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, kfree(wval); out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); @@ -626,7 +703,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, u8 *u8 = map->work_buf; int ret; - map->format.format_reg(map->work_buf, reg); + map->format.format_reg(map->work_buf, reg, map->reg_shift); /* * Some buses or devices flag reads by setting the high bits in the @@ -639,7 +716,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, trace_regmap_hw_read_start(map->dev, reg, val_len / map->format.val_bytes); - ret = map->bus->read(map->dev, map->work_buf, + ret = map->bus->read(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); @@ -689,11 +766,14 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; - mutex_lock(&map->lock); + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map); ret = _regmap_read(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -718,7 +798,12 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; - mutex_lock(&map->lock); + if (val_len % map->format.val_bytes) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map); if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { @@ -730,16 +815,17 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, * cost as we expect to hit the cache. */ for (i = 0; i < val_count; i++) { - ret = _regmap_read(map, reg + i, &v); + ret = _regmap_read(map, reg + (i * map->reg_stride), + &v); if (ret != 0) goto out; - map->format.format_val(val + (i * val_bytes), v); + map->format.format_val(val + (i * val_bytes), v, 0); } } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -765,6 +851,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (!map->format.parse_val) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; if (vol || map->cache_type == REGCACHE_NONE) { ret = regmap_raw_read(map, reg, val, val_bytes * val_count); @@ -775,9 +863,12 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, map->format.parse_val(val + i); } else { for (i = 0; i < val_count; i++) { - ret = regmap_read(map, reg + i, val + (i * val_bytes)); + unsigned int ival; + ret = regmap_read(map, reg + (i * map->reg_stride), + &ival); if (ret != 0) return ret; + memcpy(val + (i * val_bytes), &ival, val_bytes); } } @@ -792,7 +883,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_read(map, reg, &orig); if (ret != 0) @@ -809,7 +900,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -876,7 +967,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, if (map->patch) return -EBUSY; - mutex_lock(&map->lock); + map->lock(map); bypass = map->cache_bypass; @@ -904,7 +995,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, out: map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } diff --git a/drivers/base/soc.c b/drivers/base/soc.c index 05f150382da8..ba29b2e73d48 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -15,7 +15,7 @@ #include <linux/sys_soc.h> #include <linux/err.h> -static DEFINE_IDR(soc_ida); +static DEFINE_IDA(soc_ida); static DEFINE_SPINLOCK(soc_lock); static ssize_t soc_info_get(struct device *dev, @@ -168,8 +168,6 @@ void soc_device_unregister(struct soc_device *soc_dev) static int __init soc_bus_register(void) { - spin_lock_init(&soc_lock); - return bus_register(&soc_bus_type); } core_initcall(soc_bus_register); diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index c1172dafdffa..fb7c80fb721e 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -29,7 +29,7 @@ config BCMA_HOST_PCI config BCMA_DRIVER_PCI_HOSTMODE bool "Driver for PCI core working in hostmode" - depends on BCMA && MIPS + depends on BCMA && MIPS && BCMA_HOST_PCI help PCI core hostmode operation (external PCI bus). diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index 4e20bcfa7ec5..d2097a11c3c7 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -10,6 +10,7 @@ */ #include "bcma_private.h" +#include <linux/pci.h> #include <linux/export.h> #include <linux/bcma/bcma.h> #include <asm/paccess.h> diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index cdcf75c0954f..3e2a6002aae6 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -404,16 +404,19 @@ int bcma_sprom_get(struct bcma_bus *bus) return -EOPNOTSUPP; if (!bcma_sprom_ext_available(bus)) { + bool sprom_onchip; + /* * External SPROM takes precedence so check * on-chip OTP only when no external SPROM * is present. */ - if (bcma_sprom_onchip_available(bus)) { + sprom_onchip = bcma_sprom_onchip_available(bus); + if (sprom_onchip) { /* determine offset */ offset = bcma_sprom_onchip_offset(bus); } - if (!offset) { + if (!offset || !sprom_onchip) { /* * Maybe there is no SPROM on the device? * Now we ask the arch code if there is some sprom diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c index e820b68d2f6c..acda773b3720 100644 --- a/drivers/block/cciss_scsi.c +++ b/drivers/block/cciss_scsi.c @@ -866,6 +866,7 @@ cciss_scsi_detect(ctlr_info_t *h) sh->can_queue = cciss_tape_cmds; sh->sg_tablesize = h->maxsgentries; sh->max_cmd_len = MAX_COMMAND_SIZE; + sh->max_sectors = h->cciss_max_sectors; ((struct cciss_scsi_adapter_data_t *) h->scsi_ctlr)->scsi_host = sh; @@ -1410,7 +1411,7 @@ static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *c, /* track how many SG entries we are using */ if (request_nsgs > h->maxSG) h->maxSG = request_nsgs; - c->Header.SGTotal = (__u8) request_nsgs + chained; + c->Header.SGTotal = (u16) request_nsgs + chained; if (request_nsgs > h->max_cmd_sgentries) c->Header.SGList = h->max_cmd_sgentries; else diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index abfaacaaf346..946166e13953 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -2297,7 +2297,7 @@ static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms return; } - if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) { + if (!capable(CAP_SYS_ADMIN)) { retcode = ERR_PERM; goto fail; } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 76a08236430a..b0b00d70c166 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -1030,37 +1030,6 @@ static int fd_wait_for_completion(unsigned long delay, timeout_fn function) return 0; } -static DEFINE_SPINLOCK(floppy_hlt_lock); -static int hlt_disabled; -static void floppy_disable_hlt(void) -{ - unsigned long flags; - - WARN_ONCE(1, "floppy_disable_hlt() scheduled for removal in 2012"); - spin_lock_irqsave(&floppy_hlt_lock, flags); - if (!hlt_disabled) { - hlt_disabled = 1; -#ifdef HAVE_DISABLE_HLT - disable_hlt(); -#endif - } - spin_unlock_irqrestore(&floppy_hlt_lock, flags); -} - -static void floppy_enable_hlt(void) -{ - unsigned long flags; - - spin_lock_irqsave(&floppy_hlt_lock, flags); - if (hlt_disabled) { - hlt_disabled = 0; -#ifdef HAVE_DISABLE_HLT - enable_hlt(); -#endif - } - spin_unlock_irqrestore(&floppy_hlt_lock, flags); -} - static void setup_DMA(void) { unsigned long f; @@ -1105,7 +1074,6 @@ static void setup_DMA(void) fd_enable_dma(); release_dma_lock(f); #endif - floppy_disable_hlt(); } static void show_floppy(void); @@ -1707,7 +1675,6 @@ irqreturn_t floppy_interrupt(int irq, void *dev_id) fd_disable_dma(); release_dma_lock(f); - floppy_enable_hlt(); do_floppy = NULL; if (fdc >= N_FDC || FDCS->address == -1) { /* we don't even know which FDC is the culprit */ @@ -1856,8 +1823,6 @@ static void floppy_shutdown(unsigned long data) show_floppy(); cancel_activity(); - floppy_enable_hlt(); - flags = claim_dma_lock(); fd_disable_dma(); release_dma_lock(flags); @@ -4508,7 +4473,6 @@ static void floppy_release_irq_and_dma(void) #if N_FDC > 1 set_dor(1, ~8, 0); #endif - floppy_enable_hlt(); if (floppy_track_buffer && max_buffer_sectors) { tmpsize = max_buffer_sectors * 1024; diff --git a/drivers/block/mtip32xx/Kconfig b/drivers/block/mtip32xx/Kconfig index b5dd14e072f2..0ba837fc62a8 100644 --- a/drivers/block/mtip32xx/Kconfig +++ b/drivers/block/mtip32xx/Kconfig @@ -4,6 +4,6 @@ config BLK_DEV_PCIESSD_MTIP32XX tristate "Block Device Driver for Micron PCIe SSDs" - depends on HOTPLUG_PCI_PCIE + depends on PCI help This enables the block driver for Micron PCIe SSDs. diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 8eb81c96608f..00f9fc992090 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -36,6 +36,7 @@ #include <linux/idr.h> #include <linux/kthread.h> #include <../drivers/ata/ahci.h> +#include <linux/export.h> #include "mtip32xx.h" #define HW_CMD_SLOT_SZ (MTIP_MAX_COMMAND_SLOTS * 32) @@ -44,6 +45,7 @@ #define HW_PORT_PRIV_DMA_SZ \ (HW_CMD_SLOT_SZ + HW_CMD_TBL_AR_SZ + AHCI_RX_FIS_SZ) +#define HOST_CAP_NZDMA (1 << 19) #define HOST_HSORG 0xFC #define HSORG_DISABLE_SLOTGRP_INTR (1<<24) #define HSORG_DISABLE_SLOTGRP_PXIS (1<<16) @@ -139,6 +141,12 @@ static void mtip_command_cleanup(struct driver_data *dd) int group = 0, commandslot = 0, commandindex = 0; struct mtip_cmd *command; struct mtip_port *port = dd->port; + static int in_progress; + + if (in_progress) + return; + + in_progress = 1; for (group = 0; group < 4; group++) { for (commandslot = 0; commandslot < 32; commandslot++) { @@ -165,7 +173,8 @@ static void mtip_command_cleanup(struct driver_data *dd) up(&port->cmd_slot); - atomic_set(&dd->drv_cleanup_done, true); + set_bit(MTIP_DDF_CLEANUP_BIT, &dd->dd_flag); + in_progress = 0; } /* @@ -262,6 +271,9 @@ static int hba_reset_nosleep(struct driver_data *dd) && time_before(jiffies, timeout)) mdelay(1); + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) + return -1; + if (readl(dd->mmio + HOST_CTL) & HOST_RESET) return -1; @@ -294,6 +306,10 @@ static inline void mtip_issue_ncq_command(struct mtip_port *port, int tag) port->cmd_issue[MTIP_TAG_INDEX(tag)]); spin_unlock_irqrestore(&port->cmd_issue_lock, flags); + + /* Set the command's timeout value.*/ + port->commands[tag].comp_time = jiffies + msecs_to_jiffies( + MTIP_NCQ_COMMAND_TIMEOUT_MS); } /* @@ -420,7 +436,12 @@ static void mtip_init_port(struct mtip_port *port) writel(0xFFFFFFFF, port->completed[i]); /* Clear any pending interrupts for this port */ - writel(readl(port->mmio + PORT_IRQ_STAT), port->mmio + PORT_IRQ_STAT); + writel(readl(port->dd->mmio + PORT_IRQ_STAT), + port->dd->mmio + PORT_IRQ_STAT); + + /* Clear any pending interrupts on the HBA. */ + writel(readl(port->dd->mmio + HOST_IRQ_STAT), + port->dd->mmio + HOST_IRQ_STAT); /* Enable port interrupts */ writel(DEF_PORT_IRQ, port->mmio + PORT_IRQ_MASK); @@ -447,6 +468,9 @@ static void mtip_restart_port(struct mtip_port *port) && time_before(jiffies, timeout)) ; + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) + return; + /* * Chip quirk: escalate to hba reset if * PxCMD.CR not clear after 500 ms @@ -475,6 +499,9 @@ static void mtip_restart_port(struct mtip_port *port) while (time_before(jiffies, timeout)) ; + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) + return; + /* Clear PxSCTL.DET */ writel(readl(port->mmio + PORT_SCR_CTL) & ~1, port->mmio + PORT_SCR_CTL); @@ -486,15 +513,35 @@ static void mtip_restart_port(struct mtip_port *port) && time_before(jiffies, timeout)) ; + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) + return; + if ((readl(port->mmio + PORT_SCR_STAT) & 0x01) == 0) dev_warn(&port->dd->pdev->dev, "COM reset failed\n"); - /* Clear SError, the PxSERR.DIAG.x should be set so clear it */ - writel(readl(port->mmio + PORT_SCR_ERR), port->mmio + PORT_SCR_ERR); + mtip_init_port(port); + mtip_start_port(port); - /* Enable the DMA engine */ - mtip_enable_engine(port, 1); +} + +/* + * Helper function for tag logging + */ +static void print_tags(struct driver_data *dd, + char *msg, + unsigned long *tagbits, + int cnt) +{ + unsigned char tagmap[128]; + int group, tagmap_len = 0; + + memset(tagmap, 0, sizeof(tagmap)); + for (group = SLOTBITS_IN_LONGS; group > 0; group--) + tagmap_len = sprintf(tagmap + tagmap_len, "%016lX ", + tagbits[group-1]); + dev_warn(&dd->pdev->dev, + "%d command(s) %s: tagmap [%s]", cnt, msg, tagmap); } /* @@ -514,15 +561,18 @@ static void mtip_timeout_function(unsigned long int data) int tag, cmdto_cnt = 0; unsigned int bit, group; unsigned int num_command_slots = port->dd->slot_groups * 32; + unsigned long to, tagaccum[SLOTBITS_IN_LONGS]; if (unlikely(!port)) return; - if (atomic_read(&port->dd->resumeflag) == true) { + if (test_bit(MTIP_DDF_RESUME_BIT, &port->dd->dd_flag)) { mod_timer(&port->cmd_timer, jiffies + msecs_to_jiffies(30000)); return; } + /* clear the tag accumulator */ + memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long)); for (tag = 0; tag < num_command_slots; tag++) { /* @@ -540,12 +590,10 @@ static void mtip_timeout_function(unsigned long int data) command = &port->commands[tag]; fis = (struct host_to_dev_fis *) command->command; - dev_warn(&port->dd->pdev->dev, - "Timeout for command tag %d\n", tag); - + set_bit(tag, tagaccum); cmdto_cnt++; if (cmdto_cnt == 1) - set_bit(MTIP_FLAG_EH_ACTIVE_BIT, &port->flags); + set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); /* * Clear the completed bit. This should prevent @@ -578,15 +626,29 @@ static void mtip_timeout_function(unsigned long int data) } } - if (cmdto_cnt) { - dev_warn(&port->dd->pdev->dev, - "%d commands timed out: restarting port", - cmdto_cnt); + if (cmdto_cnt && !test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) { + print_tags(port->dd, "timed out", tagaccum, cmdto_cnt); + mtip_restart_port(port); - clear_bit(MTIP_FLAG_EH_ACTIVE_BIT, &port->flags); + clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); wake_up_interruptible(&port->svc_wait); } + if (port->ic_pause_timer) { + to = port->ic_pause_timer + msecs_to_jiffies(1000); + if (time_after(jiffies, to)) { + if (!test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) { + port->ic_pause_timer = 0; + clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags); + clear_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags); + clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); + wake_up_interruptible(&port->svc_wait); + } + + + } + } + /* Restart the timer */ mod_timer(&port->cmd_timer, jiffies + msecs_to_jiffies(MTIP_TIMEOUT_CHECK_PERIOD)); @@ -681,23 +743,18 @@ static void mtip_completion(struct mtip_port *port, complete(waiting); } -/* - * Helper function for tag logging - */ -static void print_tags(struct driver_data *dd, - char *msg, - unsigned long *tagbits) +static void mtip_null_completion(struct mtip_port *port, + int tag, + void *data, + int status) { - unsigned int tag, count = 0; - - for (tag = 0; tag < (dd->slot_groups) * 32; tag++) { - if (test_bit(tag, tagbits)) - count++; - } - if (count) - dev_info(&dd->pdev->dev, "%s [%i tags]\n", msg, count); + return; } +static int mtip_read_log_page(struct mtip_port *port, u8 page, u16 *buffer, + dma_addr_t buffer_dma, unsigned int sectors); +static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, + struct smart_attr *attrib); /* * Handle an error. * @@ -708,12 +765,16 @@ static void print_tags(struct driver_data *dd, */ static void mtip_handle_tfe(struct driver_data *dd) { - int group, tag, bit, reissue; + int group, tag, bit, reissue, rv; struct mtip_port *port; - struct mtip_cmd *command; + struct mtip_cmd *cmd; u32 completed; struct host_to_dev_fis *fis; unsigned long tagaccum[SLOTBITS_IN_LONGS]; + unsigned int cmd_cnt = 0; + unsigned char *buf; + char *fail_reason = NULL; + int fail_all_ncq_write = 0, fail_all_ncq_cmds = 0; dev_warn(&dd->pdev->dev, "Taskfile error\n"); @@ -722,8 +783,11 @@ static void mtip_handle_tfe(struct driver_data *dd) /* Stop the timer to prevent command timeouts. */ del_timer(&port->cmd_timer); + /* clear the tag accumulator */ + memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long)); + /* Set eh_active */ - set_bit(MTIP_FLAG_EH_ACTIVE_BIT, &port->flags); + set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); /* Loop through all the groups */ for (group = 0; group < dd->slot_groups; group++) { @@ -732,9 +796,6 @@ static void mtip_handle_tfe(struct driver_data *dd) /* clear completed status register in the hardware.*/ writel(completed, port->completed[group]); - /* clear the tag accumulator */ - memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long)); - /* Process successfully completed commands */ for (bit = 0; bit < 32 && completed; bit++) { if (!(completed & (1<<bit))) @@ -745,13 +806,14 @@ static void mtip_handle_tfe(struct driver_data *dd) if (tag == MTIP_TAG_INTERNAL) continue; - command = &port->commands[tag]; - if (likely(command->comp_func)) { + cmd = &port->commands[tag]; + if (likely(cmd->comp_func)) { set_bit(tag, tagaccum); - atomic_set(&port->commands[tag].active, 0); - command->comp_func(port, + cmd_cnt++; + atomic_set(&cmd->active, 0); + cmd->comp_func(port, tag, - command->comp_data, + cmd->comp_data, 0); } else { dev_err(&port->dd->pdev->dev, @@ -765,12 +827,45 @@ static void mtip_handle_tfe(struct driver_data *dd) } } } - print_tags(dd, "TFE tags completed:", tagaccum); + + print_tags(dd, "completed (TFE)", tagaccum, cmd_cnt); /* Restart the port */ mdelay(20); mtip_restart_port(port); + /* Trying to determine the cause of the error */ + rv = mtip_read_log_page(dd->port, ATA_LOG_SATA_NCQ, + dd->port->log_buf, + dd->port->log_buf_dma, 1); + if (rv) { + dev_warn(&dd->pdev->dev, + "Error in READ LOG EXT (10h) command\n"); + /* non-critical error, don't fail the load */ + } else { + buf = (unsigned char *)dd->port->log_buf; + if (buf[259] & 0x1) { + dev_info(&dd->pdev->dev, + "Write protect bit is set.\n"); + set_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag); + fail_all_ncq_write = 1; + fail_reason = "write protect"; + } + if (buf[288] == 0xF7) { + dev_info(&dd->pdev->dev, + "Exceeded Tmax, drive in thermal shutdown.\n"); + set_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag); + fail_all_ncq_cmds = 1; + fail_reason = "thermal shutdown"; + } + if (buf[288] == 0xBF) { + dev_info(&dd->pdev->dev, + "Drive indicates rebuild has failed.\n"); + fail_all_ncq_cmds = 1; + fail_reason = "rebuild failed"; + } + } + /* clear the tag accumulator */ memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long)); @@ -779,32 +874,47 @@ static void mtip_handle_tfe(struct driver_data *dd) for (bit = 0; bit < 32; bit++) { reissue = 1; tag = (group << 5) + bit; + cmd = &port->commands[tag]; /* If the active bit is set re-issue the command */ - if (atomic_read(&port->commands[tag].active) == 0) + if (atomic_read(&cmd->active) == 0) continue; - fis = (struct host_to_dev_fis *) - port->commands[tag].command; + fis = (struct host_to_dev_fis *)cmd->command; /* Should re-issue? */ if (tag == MTIP_TAG_INTERNAL || fis->command == ATA_CMD_SET_FEATURES) reissue = 0; + else { + if (fail_all_ncq_cmds || + (fail_all_ncq_write && + fis->command == ATA_CMD_FPDMA_WRITE)) { + dev_warn(&dd->pdev->dev, + " Fail: %s w/tag %d [%s].\n", + fis->command == ATA_CMD_FPDMA_WRITE ? + "write" : "read", + tag, + fail_reason != NULL ? + fail_reason : "unknown"); + atomic_set(&cmd->active, 0); + if (cmd->comp_func) { + cmd->comp_func(port, tag, + cmd->comp_data, + -ENODATA); + } + continue; + } + } /* * First check if this command has * exceeded its retries. */ - if (reissue && - (port->commands[tag].retries-- > 0)) { + if (reissue && (cmd->retries-- > 0)) { set_bit(tag, tagaccum); - /* Update the timeout value. */ - port->commands[tag].comp_time = - jiffies + msecs_to_jiffies( - MTIP_NCQ_COMMAND_TIMEOUT_MS); /* Re-issue the command. */ mtip_issue_ncq_command(port, tag); @@ -814,13 +924,13 @@ static void mtip_handle_tfe(struct driver_data *dd) /* Retire a command that will not be reissued */ dev_warn(&port->dd->pdev->dev, "retiring tag %d\n", tag); - atomic_set(&port->commands[tag].active, 0); + atomic_set(&cmd->active, 0); - if (port->commands[tag].comp_func) - port->commands[tag].comp_func( + if (cmd->comp_func) + cmd->comp_func( port, tag, - port->commands[tag].comp_data, + cmd->comp_data, PORT_IRQ_TF_ERR); else dev_warn(&port->dd->pdev->dev, @@ -828,10 +938,10 @@ static void mtip_handle_tfe(struct driver_data *dd) tag); } } - print_tags(dd, "TFE tags reissued:", tagaccum); + print_tags(dd, "reissued (TFE)", tagaccum, cmd_cnt); /* clear eh_active */ - clear_bit(MTIP_FLAG_EH_ACTIVE_BIT, &port->flags); + clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); wake_up_interruptible(&port->svc_wait); mod_timer(&port->cmd_timer, @@ -899,7 +1009,7 @@ static inline void mtip_process_legacy(struct driver_data *dd, u32 port_stat) struct mtip_port *port = dd->port; struct mtip_cmd *cmd = &port->commands[MTIP_TAG_INTERNAL]; - if (test_bit(MTIP_FLAG_IC_ACTIVE_BIT, &port->flags) && + if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags) && (cmd != NULL) && !(readl(port->cmd_issue[MTIP_TAG_INTERNAL]) & (1 << MTIP_TAG_INTERNAL))) { if (cmd->comp_func) { @@ -911,8 +1021,6 @@ static inline void mtip_process_legacy(struct driver_data *dd, u32 port_stat) } } - dev_warn(&dd->pdev->dev, "IRQ status 0x%x ignored.\n", port_stat); - return; } @@ -968,6 +1076,9 @@ static inline irqreturn_t mtip_handle_irq(struct driver_data *data) /* don't proceed further */ return IRQ_HANDLED; } + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, + &dd->dd_flag)) + return rv; mtip_process_errors(dd, port_stat & PORT_IRQ_ERR); } @@ -1015,6 +1126,39 @@ static void mtip_issue_non_ncq_command(struct mtip_port *port, int tag) port->cmd_issue[MTIP_TAG_INDEX(tag)]); } +static bool mtip_pause_ncq(struct mtip_port *port, + struct host_to_dev_fis *fis) +{ + struct host_to_dev_fis *reply; + unsigned long task_file_data; + + reply = port->rxfis + RX_FIS_D2H_REG; + task_file_data = readl(port->mmio+PORT_TFDATA); + + if ((task_file_data & 1) || (fis->command == ATA_CMD_SEC_ERASE_UNIT)) + return false; + + if (fis->command == ATA_CMD_SEC_ERASE_PREP) { + set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags); + port->ic_pause_timer = jiffies; + return true; + } else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) && + (fis->features == 0x03)) { + set_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags); + port->ic_pause_timer = jiffies; + return true; + } else if ((fis->command == ATA_CMD_SEC_ERASE_UNIT) || + ((fis->command == 0xFC) && + (fis->features == 0x27 || fis->features == 0x72 || + fis->features == 0x62 || fis->features == 0x26))) { + /* Com reset after secure erase or lowlevel format */ + mtip_restart_port(port); + return false; + } + + return false; +} + /* * Wait for port to quiesce * @@ -1033,11 +1177,13 @@ static int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout) to = jiffies + msecs_to_jiffies(timeout); do { - if (test_bit(MTIP_FLAG_SVC_THD_ACTIVE_BIT, &port->flags) && - test_bit(MTIP_FLAG_ISSUE_CMDS_BIT, &port->flags)) { + if (test_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags) && + test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) { msleep(20); continue; /* svc thd is actively issuing commands */ } + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) + return -EFAULT; /* * Ignore s_active bit 0 of array element 0. * This bit will always be set @@ -1074,7 +1220,7 @@ static int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout) * -EAGAIN Time out waiting for command to complete. */ static int mtip_exec_internal_command(struct mtip_port *port, - void *fis, + struct host_to_dev_fis *fis, int fis_len, dma_addr_t buffer, int buf_len, @@ -1084,8 +1230,9 @@ static int mtip_exec_internal_command(struct mtip_port *port, { struct mtip_cmd_sg *command_sg; DECLARE_COMPLETION_ONSTACK(wait); - int rv = 0; + int rv = 0, ready2go = 1; struct mtip_cmd *int_cmd = &port->commands[MTIP_TAG_INTERNAL]; + unsigned long to; /* Make sure the buffer is 8 byte aligned. This is asic specific. */ if (buffer & 0x00000007) { @@ -1094,23 +1241,38 @@ static int mtip_exec_internal_command(struct mtip_port *port, return -EFAULT; } - /* Only one internal command should be running at a time */ - if (test_and_set_bit(MTIP_TAG_INTERNAL, port->allocated)) { + to = jiffies + msecs_to_jiffies(timeout); + do { + ready2go = !test_and_set_bit(MTIP_TAG_INTERNAL, + port->allocated); + if (ready2go) + break; + mdelay(100); + } while (time_before(jiffies, to)); + if (!ready2go) { dev_warn(&port->dd->pdev->dev, - "Internal command already active\n"); + "Internal cmd active. new cmd [%02X]\n", fis->command); return -EBUSY; } - set_bit(MTIP_FLAG_IC_ACTIVE_BIT, &port->flags); + set_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); + port->ic_pause_timer = 0; + + if (fis->command == ATA_CMD_SEC_ERASE_UNIT) + clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags); + else if (fis->command == ATA_CMD_DOWNLOAD_MICRO) + clear_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags); if (atomic == GFP_KERNEL) { - /* wait for io to complete if non atomic */ - if (mtip_quiesce_io(port, 5000) < 0) { - dev_warn(&port->dd->pdev->dev, - "Failed to quiesce IO\n"); - release_slot(port, MTIP_TAG_INTERNAL); - clear_bit(MTIP_FLAG_IC_ACTIVE_BIT, &port->flags); - wake_up_interruptible(&port->svc_wait); - return -EBUSY; + if (fis->command != ATA_CMD_STANDBYNOW1) { + /* wait for io to complete if non atomic */ + if (mtip_quiesce_io(port, 5000) < 0) { + dev_warn(&port->dd->pdev->dev, + "Failed to quiesce IO\n"); + release_slot(port, MTIP_TAG_INTERNAL); + clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); + wake_up_interruptible(&port->svc_wait); + return -EBUSY; + } } /* Set the completion function and data for the command. */ @@ -1120,7 +1282,7 @@ static int mtip_exec_internal_command(struct mtip_port *port, } else { /* Clear completion - we're going to poll */ int_cmd->comp_data = NULL; - int_cmd->comp_func = NULL; + int_cmd->comp_func = mtip_null_completion; } /* Copy the command to the command table */ @@ -1159,6 +1321,12 @@ static int mtip_exec_internal_command(struct mtip_port *port, "Internal command did not complete [%d] " "within timeout of %lu ms\n", atomic, timeout); + if (mtip_check_surprise_removal(port->dd->pdev) || + test_bit(MTIP_DDF_REMOVE_PENDING_BIT, + &port->dd->dd_flag)) { + rv = -ENXIO; + goto exec_ic_exit; + } rv = -EAGAIN; } @@ -1166,31 +1334,59 @@ static int mtip_exec_internal_command(struct mtip_port *port, & (1 << MTIP_TAG_INTERNAL)) { dev_warn(&port->dd->pdev->dev, "Retiring internal command but CI is 1.\n"); + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, + &port->dd->dd_flag)) { + hba_reset_nosleep(port->dd); + rv = -ENXIO; + } else { + mtip_restart_port(port); + rv = -EAGAIN; + } + goto exec_ic_exit; } } else { /* Spin for <timeout> checking if command still outstanding */ timeout = jiffies + msecs_to_jiffies(timeout); - - while ((readl( - port->cmd_issue[MTIP_TAG_INTERNAL]) - & (1 << MTIP_TAG_INTERNAL)) - && time_before(jiffies, timeout)) - ; + while ((readl(port->cmd_issue[MTIP_TAG_INTERNAL]) + & (1 << MTIP_TAG_INTERNAL)) + && time_before(jiffies, timeout)) { + if (mtip_check_surprise_removal(port->dd->pdev)) { + rv = -ENXIO; + goto exec_ic_exit; + } + if ((fis->command != ATA_CMD_STANDBYNOW1) && + test_bit(MTIP_DDF_REMOVE_PENDING_BIT, + &port->dd->dd_flag)) { + rv = -ENXIO; + goto exec_ic_exit; + } + } if (readl(port->cmd_issue[MTIP_TAG_INTERNAL]) & (1 << MTIP_TAG_INTERNAL)) { dev_err(&port->dd->pdev->dev, - "Internal command did not complete [%d]\n", - atomic); + "Internal command did not complete [atomic]\n"); rv = -EAGAIN; + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, + &port->dd->dd_flag)) { + hba_reset_nosleep(port->dd); + rv = -ENXIO; + } else { + mtip_restart_port(port); + rv = -EAGAIN; + } } } - +exec_ic_exit: /* Clear the allocated and active bits for the internal command. */ atomic_set(&int_cmd->active, 0); release_slot(port, MTIP_TAG_INTERNAL); - clear_bit(MTIP_FLAG_IC_ACTIVE_BIT, &port->flags); + if (rv >= 0 && mtip_pause_ncq(port, fis)) { + /* NCQ paused */ + return rv; + } + clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); wake_up_interruptible(&port->svc_wait); return rv; @@ -1240,6 +1436,9 @@ static int mtip_get_identify(struct mtip_port *port, void __user *user_buffer) int rv = 0; struct host_to_dev_fis fis; + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) + return -EFAULT; + /* Build the FIS. */ memset(&fis, 0, sizeof(struct host_to_dev_fis)); fis.type = 0x27; @@ -1313,6 +1512,7 @@ static int mtip_standby_immediate(struct mtip_port *port) { int rv; struct host_to_dev_fis fis; + unsigned long start; /* Build the FIS. */ memset(&fis, 0, sizeof(struct host_to_dev_fis)); @@ -1320,15 +1520,150 @@ static int mtip_standby_immediate(struct mtip_port *port) fis.opts = 1 << 7; fis.command = ATA_CMD_STANDBYNOW1; - /* Execute the command. Use a 15-second timeout for large drives. */ + start = jiffies; rv = mtip_exec_internal_command(port, &fis, 5, 0, 0, 0, - GFP_KERNEL, + GFP_ATOMIC, 15000); + dbg_printk(MTIP_DRV_NAME "Time taken to complete standby cmd: %d ms\n", + jiffies_to_msecs(jiffies - start)); + if (rv) + dev_warn(&port->dd->pdev->dev, + "STANDBY IMMEDIATE command failed.\n"); + + return rv; +} + +/* + * Issue a READ LOG EXT command to the device. + * + * @port pointer to the port structure. + * @page page number to fetch + * @buffer pointer to buffer + * @buffer_dma dma address corresponding to @buffer + * @sectors page length to fetch, in sectors + * + * return value + * @rv return value from mtip_exec_internal_command() + */ +static int mtip_read_log_page(struct mtip_port *port, u8 page, u16 *buffer, + dma_addr_t buffer_dma, unsigned int sectors) +{ + struct host_to_dev_fis fis; + + memset(&fis, 0, sizeof(struct host_to_dev_fis)); + fis.type = 0x27; + fis.opts = 1 << 7; + fis.command = ATA_CMD_READ_LOG_EXT; + fis.sect_count = sectors & 0xFF; + fis.sect_cnt_ex = (sectors >> 8) & 0xFF; + fis.lba_low = page; + fis.lba_mid = 0; + fis.device = ATA_DEVICE_OBS; + + memset(buffer, 0, sectors * ATA_SECT_SIZE); + + return mtip_exec_internal_command(port, + &fis, + 5, + buffer_dma, + sectors * ATA_SECT_SIZE, + 0, + GFP_ATOMIC, + MTIP_INTERNAL_COMMAND_TIMEOUT_MS); +} + +/* + * Issue a SMART READ DATA command to the device. + * + * @port pointer to the port structure. + * @buffer pointer to buffer + * @buffer_dma dma address corresponding to @buffer + * + * return value + * @rv return value from mtip_exec_internal_command() + */ +static int mtip_get_smart_data(struct mtip_port *port, u8 *buffer, + dma_addr_t buffer_dma) +{ + struct host_to_dev_fis fis; + + memset(&fis, 0, sizeof(struct host_to_dev_fis)); + fis.type = 0x27; + fis.opts = 1 << 7; + fis.command = ATA_CMD_SMART; + fis.features = 0xD0; + fis.sect_count = 1; + fis.lba_mid = 0x4F; + fis.lba_hi = 0xC2; + fis.device = ATA_DEVICE_OBS; + + return mtip_exec_internal_command(port, + &fis, + 5, + buffer_dma, + ATA_SECT_SIZE, + 0, + GFP_ATOMIC, + 15000); +} + +/* + * Get the value of a smart attribute + * + * @port pointer to the port structure + * @id attribute number + * @attrib pointer to return attrib information corresponding to @id + * + * return value + * -EINVAL NULL buffer passed or unsupported attribute @id. + * -EPERM Identify data not valid, SMART not supported or not enabled + */ +static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, + struct smart_attr *attrib) +{ + int rv, i; + struct smart_attr *pattr; + + if (!attrib) + return -EINVAL; + + if (!port->identify_valid) { + dev_warn(&port->dd->pdev->dev, "IDENTIFY DATA not valid\n"); + return -EPERM; + } + if (!(port->identify[82] & 0x1)) { + dev_warn(&port->dd->pdev->dev, "SMART not supported\n"); + return -EPERM; + } + if (!(port->identify[85] & 0x1)) { + dev_warn(&port->dd->pdev->dev, "SMART not enabled\n"); + return -EPERM; + } + + memset(port->smart_buf, 0, ATA_SECT_SIZE); + rv = mtip_get_smart_data(port, port->smart_buf, port->smart_buf_dma); + if (rv) { + dev_warn(&port->dd->pdev->dev, "Failed to ge SMART data\n"); + return rv; + } + + pattr = (struct smart_attr *)(port->smart_buf + 2); + for (i = 0; i < 29; i++, pattr++) + if (pattr->attr_id == id) { + memcpy(attrib, pattr, sizeof(struct smart_attr)); + break; + } + + if (i == 29) { + dev_warn(&port->dd->pdev->dev, + "Query for invalid SMART attribute ID\n"); + rv = -EINVAL; + } return rv; } @@ -1504,10 +1839,7 @@ static int exec_drive_task(struct mtip_port *port, u8 *command) fis.cyl_hi = command[5]; fis.device = command[6] & ~0x10; /* Clear the dev bit*/ - - dbg_printk(MTIP_DRV_NAME "%s: User Command: cmd %x, feat %x, " - "nsect %x, sect %x, lcyl %x, " - "hcyl %x, sel %x\n", + dbg_printk(MTIP_DRV_NAME " %s: User Command: cmd %x, feat %x, nsect %x, sect %x, lcyl %x, hcyl %x, sel %x\n", __func__, command[0], command[1], @@ -1534,8 +1866,7 @@ static int exec_drive_task(struct mtip_port *port, u8 *command) command[4] = reply->cyl_low; command[5] = reply->cyl_hi; - dbg_printk(MTIP_DRV_NAME "%s: Completion Status: stat %x, " - "err %x , cyl_lo %x cyl_hi %x\n", + dbg_printk(MTIP_DRV_NAME " %s: Completion Status: stat %x, err %x , cyl_lo %x cyl_hi %x\n", __func__, command[0], command[1], @@ -1578,7 +1909,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command, } dbg_printk(MTIP_DRV_NAME - "%s: User Command: cmd %x, sect %x, " + " %s: User Command: cmd %x, sect %x, " "feat %x, sectcnt %x\n", __func__, command[0], @@ -1607,7 +1938,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command, command[2] = command[3]; dbg_printk(MTIP_DRV_NAME - "%s: Completion Status: stat %x, " + " %s: Completion Status: stat %x, " "err %x, cmd %x\n", __func__, command[0], @@ -1810,9 +2141,10 @@ static int exec_drive_taskfile(struct driver_data *dd, } dbg_printk(MTIP_DRV_NAME - "taskfile: cmd %x, feat %x, nsect %x," + " %s: cmd %x, feat %x, nsect %x," " sect/lbal %x, lcyl/lbam %x, hcyl/lbah %x," " head/dev %x\n", + __func__, fis.command, fis.features, fis.sect_count, @@ -1823,8 +2155,8 @@ static int exec_drive_taskfile(struct driver_data *dd, switch (fis.command) { case ATA_CMD_DOWNLOAD_MICRO: - /* Change timeout for Download Microcode to 60 seconds.*/ - timeout = 60000; + /* Change timeout for Download Microcode to 2 minutes */ + timeout = 120000; break; case ATA_CMD_SEC_ERASE_UNIT: /* Change timeout for Security Erase Unit to 4 minutes.*/ @@ -1840,8 +2172,8 @@ static int exec_drive_taskfile(struct driver_data *dd, timeout = 10000; break; case ATA_CMD_SMART: - /* Change timeout for vendor unique command to 10 secs */ - timeout = 10000; + /* Change timeout for vendor unique command to 15 secs */ + timeout = 15000; break; default: timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS; @@ -1903,18 +2235,8 @@ static int exec_drive_taskfile(struct driver_data *dd, req_task->hob_ports[1] = reply->features_ex; req_task->hob_ports[2] = reply->sect_cnt_ex; } - - /* Com rest after secure erase or lowlevel format */ - if (((fis.command == ATA_CMD_SEC_ERASE_UNIT) || - ((fis.command == 0xFC) && - (fis.features == 0x27 || fis.features == 0x72 || - fis.features == 0x62 || fis.features == 0x26))) && - !(reply->command & 1)) { - mtip_restart_port(dd->port); - } - dbg_printk(MTIP_DRV_NAME - "%s: Completion: stat %x," + " %s: Completion: stat %x," "err %x, sect_cnt %x, lbalo %x," "lbamid %x, lbahi %x, dev %x\n", __func__, @@ -2080,14 +2402,10 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t start, struct host_to_dev_fis *fis; struct mtip_port *port = dd->port; struct mtip_cmd *command = &port->commands[tag]; + int dma_dir = (dir == READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; /* Map the scatter list for DMA access */ - if (dir == READ) - nents = dma_map_sg(&dd->pdev->dev, command->sg, - nents, DMA_FROM_DEVICE); - else - nents = dma_map_sg(&dd->pdev->dev, command->sg, - nents, DMA_TO_DEVICE); + nents = dma_map_sg(&dd->pdev->dev, command->sg, nents, dma_dir); command->scatter_ents = nents; @@ -2127,7 +2445,7 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t start, */ command->comp_data = dd; command->comp_func = mtip_async_complete; - command->direction = (dir == READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + command->direction = dma_dir; /* * Set the completion function and data for the command passed @@ -2140,19 +2458,16 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t start, * To prevent this command from being issued * if an internal command is in progress or error handling is active. */ - if (unlikely(test_bit(MTIP_FLAG_IC_ACTIVE_BIT, &port->flags) || - test_bit(MTIP_FLAG_EH_ACTIVE_BIT, &port->flags))) { + if (port->flags & MTIP_PF_PAUSE_IO) { set_bit(tag, port->cmds_to_issue); - set_bit(MTIP_FLAG_ISSUE_CMDS_BIT, &port->flags); + set_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags); return; } /* Issue the command to the hardware */ mtip_issue_ncq_command(port, tag); - /* Set the command's timeout value.*/ - port->commands[tag].comp_time = jiffies + msecs_to_jiffies( - MTIP_NCQ_COMMAND_TIMEOUT_MS); + return; } /* @@ -2191,6 +2506,10 @@ static struct scatterlist *mtip_hw_get_scatterlist(struct driver_data *dd, down(&dd->port->cmd_slot); *tag = get_slot(dd->port); + if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) { + up(&dd->port->cmd_slot); + return NULL; + } if (unlikely(*tag < 0)) return NULL; @@ -2207,7 +2526,7 @@ static struct scatterlist *mtip_hw_get_scatterlist(struct driver_data *dd, * return value * The size, in bytes, of the data copied into buf. */ -static ssize_t hw_show_registers(struct device *dev, +static ssize_t mtip_hw_show_registers(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2216,7 +2535,7 @@ static ssize_t hw_show_registers(struct device *dev, int size = 0; int n; - size += sprintf(&buf[size], "%s:\ns_active:\n", __func__); + size += sprintf(&buf[size], "S ACTive:\n"); for (n = 0; n < dd->slot_groups; n++) size += sprintf(&buf[size], "0x%08x\n", @@ -2240,20 +2559,39 @@ static ssize_t hw_show_registers(struct device *dev, group_allocated); } - size += sprintf(&buf[size], "completed:\n"); + size += sprintf(&buf[size], "Completed:\n"); for (n = 0; n < dd->slot_groups; n++) size += sprintf(&buf[size], "0x%08x\n", readl(dd->port->completed[n])); - size += sprintf(&buf[size], "PORT_IRQ_STAT 0x%08x\n", + size += sprintf(&buf[size], "PORT IRQ STAT : 0x%08x\n", readl(dd->port->mmio + PORT_IRQ_STAT)); - size += sprintf(&buf[size], "HOST_IRQ_STAT 0x%08x\n", + size += sprintf(&buf[size], "HOST IRQ STAT : 0x%08x\n", readl(dd->mmio + HOST_IRQ_STAT)); return size; } -static DEVICE_ATTR(registers, S_IRUGO, hw_show_registers, NULL); + +static ssize_t mtip_hw_show_status(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct driver_data *dd = dev_to_disk(dev)->private_data; + int size = 0; + + if (test_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag)) + size += sprintf(buf, "%s", "thermal_shutdown\n"); + else if (test_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag)) + size += sprintf(buf, "%s", "write_protect\n"); + else + size += sprintf(buf, "%s", "online\n"); + + return size; +} + +static DEVICE_ATTR(registers, S_IRUGO, mtip_hw_show_registers, NULL); +static DEVICE_ATTR(status, S_IRUGO, mtip_hw_show_status, NULL); /* * Create the sysfs related attributes. @@ -2272,7 +2610,10 @@ static int mtip_hw_sysfs_init(struct driver_data *dd, struct kobject *kobj) if (sysfs_create_file(kobj, &dev_attr_registers.attr)) dev_warn(&dd->pdev->dev, - "Error creating registers sysfs entry\n"); + "Error creating 'registers' sysfs entry\n"); + if (sysfs_create_file(kobj, &dev_attr_status.attr)) + dev_warn(&dd->pdev->dev, + "Error creating 'status' sysfs entry\n"); return 0; } @@ -2292,6 +2633,7 @@ static int mtip_hw_sysfs_exit(struct driver_data *dd, struct kobject *kobj) return -EINVAL; sysfs_remove_file(kobj, &dev_attr_registers.attr); + sysfs_remove_file(kobj, &dev_attr_status.attr); return 0; } @@ -2384,10 +2726,12 @@ static int mtip_ftl_rebuild_poll(struct driver_data *dd) "FTL rebuild in progress. Polling for completion.\n"); start = jiffies; - dd->ftlrebuildflag = 1; timeout = jiffies + msecs_to_jiffies(MTIP_FTL_REBUILD_TIMEOUT_MS); do { + if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, + &dd->dd_flag))) + return -EFAULT; if (mtip_check_surprise_removal(dd->pdev)) return -EFAULT; @@ -2408,22 +2752,17 @@ static int mtip_ftl_rebuild_poll(struct driver_data *dd) dev_warn(&dd->pdev->dev, "FTL rebuild complete (%d secs).\n", jiffies_to_msecs(jiffies - start) / 1000); - dd->ftlrebuildflag = 0; mtip_block_initialize(dd); - break; + return 0; } ssleep(10); } while (time_before(jiffies, timeout)); /* Check for timeout */ - if (dd->ftlrebuildflag) { - dev_err(&dd->pdev->dev, + dev_err(&dd->pdev->dev, "Timed out waiting for FTL rebuild to complete (%d secs).\n", jiffies_to_msecs(jiffies - start) / 1000); - return -EFAULT; - } - - return 0; + return -EFAULT; } /* @@ -2448,14 +2787,17 @@ static int mtip_service_thread(void *data) * is in progress nor error handling is active */ wait_event_interruptible(port->svc_wait, (port->flags) && - !test_bit(MTIP_FLAG_IC_ACTIVE_BIT, &port->flags) && - !test_bit(MTIP_FLAG_EH_ACTIVE_BIT, &port->flags)); + !(port->flags & MTIP_PF_PAUSE_IO)); if (kthread_should_stop()) break; - set_bit(MTIP_FLAG_SVC_THD_ACTIVE_BIT, &port->flags); - if (test_bit(MTIP_FLAG_ISSUE_CMDS_BIT, &port->flags)) { + if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, + &dd->dd_flag))) + break; + + set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags); + if (test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) { slot = 1; /* used to restrict the loop to one iteration */ slot_start = num_cmd_slots; @@ -2480,21 +2822,19 @@ static int mtip_service_thread(void *data) /* Issue the command to the hardware */ mtip_issue_ncq_command(port, slot); - /* Set the command's timeout value.*/ - port->commands[slot].comp_time = jiffies + - msecs_to_jiffies(MTIP_NCQ_COMMAND_TIMEOUT_MS); - clear_bit(slot, port->cmds_to_issue); } - clear_bit(MTIP_FLAG_ISSUE_CMDS_BIT, &port->flags); - } else if (test_bit(MTIP_FLAG_REBUILD_BIT, &port->flags)) { - mtip_ftl_rebuild_poll(dd); - clear_bit(MTIP_FLAG_REBUILD_BIT, &port->flags); + clear_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags); + } else if (test_bit(MTIP_PF_REBUILD_BIT, &port->flags)) { + if (!mtip_ftl_rebuild_poll(dd)) + set_bit(MTIP_DDF_REBUILD_FAILED_BIT, + &dd->dd_flag); + clear_bit(MTIP_PF_REBUILD_BIT, &port->flags); } - clear_bit(MTIP_FLAG_SVC_THD_ACTIVE_BIT, &port->flags); + clear_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags); - if (test_bit(MTIP_FLAG_SVC_THD_SHOULD_STOP_BIT, &port->flags)) + if (test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags)) break; } return 0; @@ -2513,6 +2853,9 @@ static int mtip_hw_init(struct driver_data *dd) int i; int rv; unsigned int num_command_slots; + unsigned long timeout, timetaken; + unsigned char *buf; + struct smart_attr attr242; dd->mmio = pcim_iomap_table(dd->pdev)[MTIP_ABAR]; @@ -2547,7 +2890,7 @@ static int mtip_hw_init(struct driver_data *dd) /* Allocate memory for the command list. */ dd->port->command_list = dmam_alloc_coherent(&dd->pdev->dev, - HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 2), + HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4), &dd->port->command_list_dma, GFP_KERNEL); if (!dd->port->command_list) { @@ -2560,7 +2903,7 @@ static int mtip_hw_init(struct driver_data *dd) /* Clear the memory we have allocated. */ memset(dd->port->command_list, 0, - HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 2)); + HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4)); /* Setup the addresse of the RX FIS. */ dd->port->rxfis = dd->port->command_list + HW_CMD_SLOT_SZ; @@ -2576,10 +2919,19 @@ static int mtip_hw_init(struct driver_data *dd) dd->port->identify_dma = dd->port->command_tbl_dma + HW_CMD_TBL_AR_SZ; - /* Setup the address of the sector buffer. */ + /* Setup the address of the sector buffer - for some non-ncq cmds */ dd->port->sector_buffer = (void *) dd->port->identify + ATA_SECT_SIZE; dd->port->sector_buffer_dma = dd->port->identify_dma + ATA_SECT_SIZE; + /* Setup the address of the log buf - for read log command */ + dd->port->log_buf = (void *)dd->port->sector_buffer + ATA_SECT_SIZE; + dd->port->log_buf_dma = dd->port->sector_buffer_dma + ATA_SECT_SIZE; + + /* Setup the address of the smart buf - for smart read data command */ + dd->port->smart_buf = (void *)dd->port->log_buf + ATA_SECT_SIZE; + dd->port->smart_buf_dma = dd->port->log_buf_dma + ATA_SECT_SIZE; + + /* Point the command headers at the command tables. */ for (i = 0; i < num_command_slots; i++) { dd->port->commands[i].command_header = @@ -2623,14 +2975,43 @@ static int mtip_hw_init(struct driver_data *dd) dd->port->mmio + i*0x80 + PORT_SDBV; } - /* Reset the HBA. */ - if (mtip_hba_reset(dd) < 0) { - dev_err(&dd->pdev->dev, - "Card did not reset within timeout\n"); - rv = -EIO; + timetaken = jiffies; + timeout = jiffies + msecs_to_jiffies(30000); + while (((readl(dd->port->mmio + PORT_SCR_STAT) & 0x0F) != 0x03) && + time_before(jiffies, timeout)) { + mdelay(100); + } + if (unlikely(mtip_check_surprise_removal(dd->pdev))) { + timetaken = jiffies - timetaken; + dev_warn(&dd->pdev->dev, + "Surprise removal detected at %u ms\n", + jiffies_to_msecs(timetaken)); + rv = -ENODEV; + goto out2 ; + } + if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) { + timetaken = jiffies - timetaken; + dev_warn(&dd->pdev->dev, + "Removal detected at %u ms\n", + jiffies_to_msecs(timetaken)); + rv = -EFAULT; goto out2; } + /* Conditionally reset the HBA. */ + if (!(readl(dd->mmio + HOST_CAP) & HOST_CAP_NZDMA)) { + if (mtip_hba_reset(dd) < 0) { + dev_err(&dd->pdev->dev, + "Card did not reset within timeout\n"); + rv = -EIO; + goto out2; + } + } else { + /* Clear any pending interrupts on the HBA */ + writel(readl(dd->mmio + HOST_IRQ_STAT), + dd->mmio + HOST_IRQ_STAT); + } + mtip_init_port(dd->port); mtip_start_port(dd->port); @@ -2660,6 +3041,12 @@ static int mtip_hw_init(struct driver_data *dd) mod_timer(&dd->port->cmd_timer, jiffies + msecs_to_jiffies(MTIP_TIMEOUT_CHECK_PERIOD)); + + if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) { + rv = -EFAULT; + goto out3; + } + if (mtip_get_identify(dd->port, NULL) < 0) { rv = -EFAULT; goto out3; @@ -2667,10 +3054,47 @@ static int mtip_hw_init(struct driver_data *dd) if (*(dd->port->identify + MTIP_FTL_REBUILD_OFFSET) == MTIP_FTL_REBUILD_MAGIC) { - set_bit(MTIP_FLAG_REBUILD_BIT, &dd->port->flags); + set_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags); return MTIP_FTL_REBUILD_MAGIC; } mtip_dump_identify(dd->port); + + /* check write protect, over temp and rebuild statuses */ + rv = mtip_read_log_page(dd->port, ATA_LOG_SATA_NCQ, + dd->port->log_buf, + dd->port->log_buf_dma, 1); + if (rv) { + dev_warn(&dd->pdev->dev, + "Error in READ LOG EXT (10h) command\n"); + /* non-critical error, don't fail the load */ + } else { + buf = (unsigned char *)dd->port->log_buf; + if (buf[259] & 0x1) { + dev_info(&dd->pdev->dev, + "Write protect bit is set.\n"); + set_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag); + } + if (buf[288] == 0xF7) { + dev_info(&dd->pdev->dev, + "Exceeded Tmax, drive in thermal shutdown.\n"); + set_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag); + } + if (buf[288] == 0xBF) { + dev_info(&dd->pdev->dev, + "Drive indicates rebuild has failed.\n"); + /* TODO */ + } + } + + /* get write protect progess */ + memset(&attr242, 0, sizeof(struct smart_attr)); + if (mtip_get_smart_attr(dd->port, 242, &attr242)) + dev_warn(&dd->pdev->dev, + "Unable to check write protect progress\n"); + else + dev_info(&dd->pdev->dev, + "Write protect progress: %d%% (%d blocks)\n", + attr242.cur, attr242.data); return rv; out3: @@ -2688,7 +3112,7 @@ out2: /* Free the command/command header memory. */ dmam_free_coherent(&dd->pdev->dev, - HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 2), + HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4), dd->port->command_list, dd->port->command_list_dma); out1: @@ -2712,9 +3136,12 @@ static int mtip_hw_exit(struct driver_data *dd) * Send standby immediate (E0h) to the drive so that it * saves its state. */ - if (atomic_read(&dd->drv_cleanup_done) != true) { + if (!test_bit(MTIP_DDF_CLEANUP_BIT, &dd->dd_flag)) { - mtip_standby_immediate(dd->port); + if (!test_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags)) + if (mtip_standby_immediate(dd->port)) + dev_warn(&dd->pdev->dev, + "STANDBY IMMEDIATE failed\n"); /* de-initialize the port. */ mtip_deinit_port(dd->port); @@ -2734,7 +3161,7 @@ static int mtip_hw_exit(struct driver_data *dd) /* Free the command/command header memory. */ dmam_free_coherent(&dd->pdev->dev, - HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 2), + HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4), dd->port->command_list, dd->port->command_list_dma); /* Free the memory allocated for the for structure. */ @@ -2892,6 +3319,9 @@ static int mtip_block_ioctl(struct block_device *dev, if (!dd) return -ENOTTY; + if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) + return -ENOTTY; + switch (cmd) { case BLKFLSBUF: return -ENOTTY; @@ -2927,6 +3357,9 @@ static int mtip_block_compat_ioctl(struct block_device *dev, if (!dd) return -ENOTTY; + if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) + return -ENOTTY; + switch (cmd) { case BLKFLSBUF: return -ENOTTY; @@ -3049,6 +3482,24 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio) int nents = 0; int tag = 0; + if (unlikely(dd->dd_flag & MTIP_DDF_STOP_IO)) { + if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, + &dd->dd_flag))) { + bio_endio(bio, -ENXIO); + return; + } + if (unlikely(test_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag))) { + bio_endio(bio, -ENODATA); + return; + } + if (unlikely(test_bit(MTIP_DDF_WRITE_PROTECT_BIT, + &dd->dd_flag) && + bio_data_dir(bio))) { + bio_endio(bio, -ENODATA); + return; + } + } + if (unlikely(!bio_has_data(bio))) { blk_queue_flush(queue, 0); bio_endio(bio, 0); @@ -3061,7 +3512,7 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio) if (unlikely((bio)->bi_vcnt > MTIP_MAX_SG)) { dev_warn(&dd->pdev->dev, - "Maximum number of SGL entries exceeded"); + "Maximum number of SGL entries exceeded\n"); bio_io_error(bio); mtip_hw_release_scatterlist(dd, tag); return; @@ -3210,8 +3661,10 @@ skip_create_disk: kobject_put(kobj); } - if (dd->mtip_svc_handler) + if (dd->mtip_svc_handler) { + set_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag); return rv; /* service thread created for handling rebuild */ + } start_service_thread: sprintf(thd_name, "mtip_svc_thd_%02d", index); @@ -3220,12 +3673,15 @@ start_service_thread: dd, thd_name); if (IS_ERR(dd->mtip_svc_handler)) { - printk(KERN_ERR "mtip32xx: service thread failed to start\n"); + dev_err(&dd->pdev->dev, "service thread failed to start\n"); dd->mtip_svc_handler = NULL; rv = -EFAULT; goto kthread_run_error; } + if (wait_for_rebuild == MTIP_FTL_REBUILD_MAGIC) + rv = wait_for_rebuild; + return rv; kthread_run_error: @@ -3266,16 +3722,18 @@ static int mtip_block_remove(struct driver_data *dd) struct kobject *kobj; if (dd->mtip_svc_handler) { - set_bit(MTIP_FLAG_SVC_THD_SHOULD_STOP_BIT, &dd->port->flags); + set_bit(MTIP_PF_SVC_THD_STOP_BIT, &dd->port->flags); wake_up_interruptible(&dd->port->svc_wait); kthread_stop(dd->mtip_svc_handler); } - /* Clean up the sysfs attributes managed by the protocol layer. */ - kobj = kobject_get(&disk_to_dev(dd->disk)->kobj); - if (kobj) { - mtip_hw_sysfs_exit(dd, kobj); - kobject_put(kobj); + /* Clean up the sysfs attributes, if created */ + if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) { + kobj = kobject_get(&disk_to_dev(dd->disk)->kobj); + if (kobj) { + mtip_hw_sysfs_exit(dd, kobj); + kobject_put(kobj); + } } /* @@ -3283,6 +3741,11 @@ static int mtip_block_remove(struct driver_data *dd) * from /dev */ del_gendisk(dd->disk); + + spin_lock(&rssd_index_lock); + ida_remove(&rssd_index_ida, dd->index); + spin_unlock(&rssd_index_lock); + blk_cleanup_queue(dd->queue); dd->disk = NULL; dd->queue = NULL; @@ -3312,6 +3775,11 @@ static int mtip_block_shutdown(struct driver_data *dd) /* Delete our gendisk structure, and cleanup the blk queue. */ del_gendisk(dd->disk); + + spin_lock(&rssd_index_lock); + ida_remove(&rssd_index_ida, dd->index); + spin_unlock(&rssd_index_lock); + blk_cleanup_queue(dd->queue); dd->disk = NULL; dd->queue = NULL; @@ -3359,11 +3827,6 @@ static int mtip_pci_probe(struct pci_dev *pdev, return -ENOMEM; } - /* Set the atomic variable as 1 in case of SRSI */ - atomic_set(&dd->drv_cleanup_done, true); - - atomic_set(&dd->resumeflag, false); - /* Attach the private data to this PCI device. */ pci_set_drvdata(pdev, dd); @@ -3420,7 +3883,8 @@ static int mtip_pci_probe(struct pci_dev *pdev, * instance number. */ instance++; - + if (rv != MTIP_FTL_REBUILD_MAGIC) + set_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag); goto done; block_initialize_err: @@ -3434,9 +3898,6 @@ iomap_err: pci_set_drvdata(pdev, NULL); return rv; done: - /* Set the atomic variable as 0 in case of SRSI */ - atomic_set(&dd->drv_cleanup_done, true); - return rv; } @@ -3452,8 +3913,10 @@ static void mtip_pci_remove(struct pci_dev *pdev) struct driver_data *dd = pci_get_drvdata(pdev); int counter = 0; + set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag); + if (mtip_check_surprise_removal(pdev)) { - while (atomic_read(&dd->drv_cleanup_done) == false) { + while (!test_bit(MTIP_DDF_CLEANUP_BIT, &dd->dd_flag)) { counter++; msleep(20); if (counter == 10) { @@ -3463,8 +3926,6 @@ static void mtip_pci_remove(struct pci_dev *pdev) } } } - /* Set the atomic variable as 1 in case of SRSI */ - atomic_set(&dd->drv_cleanup_done, true); /* Clean up the block layer. */ mtip_block_remove(dd); @@ -3493,7 +3954,7 @@ static int mtip_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) return -EFAULT; } - atomic_set(&dd->resumeflag, true); + set_bit(MTIP_DDF_RESUME_BIT, &dd->dd_flag); /* Disable ports & interrupts then send standby immediate */ rv = mtip_block_suspend(dd); @@ -3559,7 +4020,7 @@ static int mtip_pci_resume(struct pci_dev *pdev) dev_err(&pdev->dev, "Unable to resume\n"); err: - atomic_set(&dd->resumeflag, false); + clear_bit(MTIP_DDF_RESUME_BIT, &dd->dd_flag); return rv; } @@ -3608,18 +4069,25 @@ MODULE_DEVICE_TABLE(pci, mtip_pci_tbl); */ static int __init mtip_init(void) { + int error; + printk(KERN_INFO MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n"); /* Allocate a major block device number to use with this driver. */ - mtip_major = register_blkdev(0, MTIP_DRV_NAME); - if (mtip_major < 0) { + error = register_blkdev(0, MTIP_DRV_NAME); + if (error <= 0) { printk(KERN_ERR "Unable to register block device (%d)\n", - mtip_major); + error); return -EBUSY; } + mtip_major = error; /* Register our PCI operations. */ - return pci_register_driver(&mtip_pci_driver); + error = pci_register_driver(&mtip_pci_driver); + if (error) + unregister_blkdev(mtip_major, MTIP_DRV_NAME); + + return error; } /* diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index e0554a8f2233..4ef58336310a 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -34,8 +34,8 @@ /* offset of Device Control register in PCIe extended capabilites space */ #define PCIE_CONFIG_EXT_DEVICE_CONTROL_OFFSET 0x48 -/* # of times to retry timed out IOs */ -#define MTIP_MAX_RETRIES 5 +/* # of times to retry timed out/failed IOs */ +#define MTIP_MAX_RETRIES 2 /* Various timeout values in ms */ #define MTIP_NCQ_COMMAND_TIMEOUT_MS 5000 @@ -114,12 +114,41 @@ #define __force_bit2int (unsigned int __force) /* below are bit numbers in 'flags' defined in mtip_port */ -#define MTIP_FLAG_IC_ACTIVE_BIT 0 -#define MTIP_FLAG_EH_ACTIVE_BIT 1 -#define MTIP_FLAG_SVC_THD_ACTIVE_BIT 2 -#define MTIP_FLAG_ISSUE_CMDS_BIT 4 -#define MTIP_FLAG_REBUILD_BIT 5 -#define MTIP_FLAG_SVC_THD_SHOULD_STOP_BIT 8 +#define MTIP_PF_IC_ACTIVE_BIT 0 /* pio/ioctl */ +#define MTIP_PF_EH_ACTIVE_BIT 1 /* error handling */ +#define MTIP_PF_SE_ACTIVE_BIT 2 /* secure erase */ +#define MTIP_PF_DM_ACTIVE_BIT 3 /* download microcde */ +#define MTIP_PF_PAUSE_IO ((1 << MTIP_PF_IC_ACTIVE_BIT) | \ + (1 << MTIP_PF_EH_ACTIVE_BIT) | \ + (1 << MTIP_PF_SE_ACTIVE_BIT) | \ + (1 << MTIP_PF_DM_ACTIVE_BIT)) + +#define MTIP_PF_SVC_THD_ACTIVE_BIT 4 +#define MTIP_PF_ISSUE_CMDS_BIT 5 +#define MTIP_PF_REBUILD_BIT 6 +#define MTIP_PF_SVC_THD_STOP_BIT 8 + +/* below are bit numbers in 'dd_flag' defined in driver_data */ +#define MTIP_DDF_REMOVE_PENDING_BIT 1 +#define MTIP_DDF_OVER_TEMP_BIT 2 +#define MTIP_DDF_WRITE_PROTECT_BIT 3 +#define MTIP_DDF_STOP_IO ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \ + (1 << MTIP_DDF_OVER_TEMP_BIT) | \ + (1 << MTIP_DDF_WRITE_PROTECT_BIT)) + +#define MTIP_DDF_CLEANUP_BIT 5 +#define MTIP_DDF_RESUME_BIT 6 +#define MTIP_DDF_INIT_DONE_BIT 7 +#define MTIP_DDF_REBUILD_FAILED_BIT 8 + +__packed struct smart_attr{ + u8 attr_id; + u16 flags; + u8 cur; + u8 worst; + u32 data; + u8 res[3]; +}; /* Register Frame Information Structure (FIS), host to device. */ struct host_to_dev_fis { @@ -345,6 +374,12 @@ struct mtip_port { * when the command slot and all associated data structures * are no longer needed. */ + u16 *log_buf; + dma_addr_t log_buf_dma; + + u8 *smart_buf; + dma_addr_t smart_buf_dma; + unsigned long allocated[SLOTBITS_IN_LONGS]; /* * used to queue commands when an internal command is in progress @@ -368,6 +403,7 @@ struct mtip_port { * Timer used to complete commands that have been active for too long. */ struct timer_list cmd_timer; + unsigned long ic_pause_timer; /* * Semaphore used to block threads if there are no * command slots available. @@ -404,13 +440,9 @@ struct driver_data { unsigned slot_groups; /* number of slot groups the product supports */ - atomic_t drv_cleanup_done; /* Atomic variable for SRSI */ - unsigned long index; /* Index to determine the disk name */ - unsigned int ftlrebuildflag; /* FTL rebuild flag */ - - atomic_t resumeflag; /* Atomic variable to track suspend/resume */ + unsigned long dd_flag; /* NOTE: use atomic bit operations on this */ struct task_struct *mtip_svc_handler; /* task_struct of svc thd */ }; diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index c4a60badf252..0d39f2f4294a 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -351,6 +351,7 @@ static void virtblk_config_changed_work(struct work_struct *work) cap_str_10, cap_str_2); set_capacity(vblk->disk, capacity); + revalidate_disk(vblk->disk); done: mutex_unlock(&vblk->config_lock); } @@ -374,6 +375,34 @@ static int init_vq(struct virtio_blk *vblk) return err; } +/* + * Legacy naming scheme used for virtio devices. We are stuck with it for + * virtio blk but don't ever use it for any new driver. + */ +static int virtblk_name_format(char *prefix, int index, char *buf, int buflen) +{ + const int base = 'z' - 'a' + 1; + char *begin = buf + strlen(prefix); + char *end = buf + buflen; + char *p; + int unit; + + p = end - 1; + *p = '\0'; + unit = base; + do { + if (p == begin) + return -EINVAL; + *--p = 'a' + (index % unit); + index = (index / unit) - 1; + } while (index >= 0); + + memmove(begin, p, end - p); + memcpy(buf, prefix, strlen(prefix)); + + return 0; +} + static int __devinit virtblk_probe(struct virtio_device *vdev) { struct virtio_blk *vblk; @@ -442,18 +471,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) q->queuedata = vblk; - if (index < 26) { - sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26); - } else if (index < (26 + 1) * 26) { - sprintf(vblk->disk->disk_name, "vd%c%c", - 'a' + index / 26 - 1, 'a' + index % 26); - } else { - const unsigned int m1 = (index / 26 - 1) / 26 - 1; - const unsigned int m2 = (index / 26 - 1) % 26; - const unsigned int m3 = index % 26; - sprintf(vblk->disk->disk_name, "vd%c%c%c", - 'a' + m1, 'a' + m2, 'a' + m3); - } + virtblk_name_format("vd", index, vblk->disk->disk_name, DISK_NAME_LEN); vblk->disk->major = major; vblk->disk->first_minor = index_to_minor(index); diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 0088bf60f368..73f196ca713f 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -321,6 +321,7 @@ struct seg_buf { static void xen_blkbk_unmap(struct pending_req *req) { struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + struct page *pages[BLKIF_MAX_SEGMENTS_PER_REQUEST]; unsigned int i, invcount = 0; grant_handle_t handle; int ret; @@ -332,25 +333,12 @@ static void xen_blkbk_unmap(struct pending_req *req) gnttab_set_unmap_op(&unmap[invcount], vaddr(req, i), GNTMAP_host_map, handle); pending_handle(req, i) = BLKBACK_INVALID_HANDLE; + pages[invcount] = virt_to_page(vaddr(req, i)); invcount++; } - ret = HYPERVISOR_grant_table_op( - GNTTABOP_unmap_grant_ref, unmap, invcount); + ret = gnttab_unmap_refs(unmap, pages, invcount, false); BUG_ON(ret); - /* - * Note, we use invcount, so nr->pages, so we can't index - * using vaddr(req, i). - */ - for (i = 0; i < invcount; i++) { - ret = m2p_remove_override( - virt_to_page(unmap[i].host_addr), false); - if (ret) { - pr_alert(DRV_PFX "Failed to remove M2P override for %lx\n", - (unsigned long)unmap[i].host_addr); - continue; - } - } } static int xen_blkbk_map(struct blkif_request *req, @@ -378,7 +366,7 @@ static int xen_blkbk_map(struct blkif_request *req, pending_req->blkif->domid); } - ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map, nseg); + ret = gnttab_map_refs(map, NULL, &blkbk->pending_page(pending_req, 0), nseg); BUG_ON(ret); /* @@ -398,15 +386,6 @@ static int xen_blkbk_map(struct blkif_request *req, if (ret) continue; - ret = m2p_add_override(PFN_DOWN(map[i].dev_bus_addr), - blkbk->pending_page(pending_req, i), NULL); - if (ret) { - pr_alert(DRV_PFX "Failed to install M2P override for %lx (ret: %d)\n", - (unsigned long)map[i].dev_bus_addr, ret); - /* We could switch over to GNTTABOP_copy */ - continue; - } - seg[i].buf = map[i].dev_bus_addr | (req->u.rw.seg[i].first_sect << 9); } @@ -419,21 +398,18 @@ static int dispatch_discard_io(struct xen_blkif *blkif, int err = 0; int status = BLKIF_RSP_OKAY; struct block_device *bdev = blkif->vbd.bdev; + unsigned long secure; blkif->st_ds_req++; xen_blkif_get(blkif); - if (blkif->blk_backend_type == BLKIF_BACKEND_PHY || - blkif->blk_backend_type == BLKIF_BACKEND_FILE) { - unsigned long secure = (blkif->vbd.discard_secure && - (req->u.discard.flag & BLKIF_DISCARD_SECURE)) ? - BLKDEV_DISCARD_SECURE : 0; - err = blkdev_issue_discard(bdev, - req->u.discard.sector_number, - req->u.discard.nr_sectors, - GFP_KERNEL, secure); - } else - err = -EOPNOTSUPP; + secure = (blkif->vbd.discard_secure && + (req->u.discard.flag & BLKIF_DISCARD_SECURE)) ? + BLKDEV_DISCARD_SECURE : 0; + + err = blkdev_issue_discard(bdev, req->u.discard.sector_number, + req->u.discard.nr_sectors, + GFP_KERNEL, secure); if (err == -EOPNOTSUPP) { pr_debug(DRV_PFX "discard op failed, not supported\n"); @@ -830,7 +806,7 @@ static int __init xen_blkif_init(void) int i, mmap_pages; int rc = 0; - if (!xen_pv_domain()) + if (!xen_domain()) return -ENODEV; blkbk = kzalloc(sizeof(struct xen_blkbk), GFP_KERNEL); diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index d0ee7edc9be8..773cf27dc23f 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -146,11 +146,6 @@ enum blkif_protocol { BLKIF_PROTOCOL_X86_64 = 3, }; -enum blkif_backend_type { - BLKIF_BACKEND_PHY = 1, - BLKIF_BACKEND_FILE = 2, -}; - struct xen_vbd { /* What the domain refers to this vbd as. */ blkif_vdev_t handle; @@ -177,7 +172,6 @@ struct xen_blkif { unsigned int irq; /* Comms information. */ enum blkif_protocol blk_protocol; - enum blkif_backend_type blk_backend_type; union blkif_back_rings blk_rings; void *blk_ring; /* The VBD attached to this interface. */ diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 24a2fb57e5d0..4f66171c6683 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -381,72 +381,49 @@ int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt, err = xenbus_printf(xbt, dev->nodename, "feature-flush-cache", "%d", state); if (err) - xenbus_dev_fatal(dev, err, "writing feature-flush-cache"); + dev_warn(&dev->dev, "writing feature-flush-cache (%d)", err); return err; } -int xen_blkbk_discard(struct xenbus_transaction xbt, struct backend_info *be) +static void xen_blkbk_discard(struct xenbus_transaction xbt, struct backend_info *be) { struct xenbus_device *dev = be->dev; struct xen_blkif *blkif = be->blkif; - char *type; int err; int state = 0; + struct block_device *bdev = be->blkif->vbd.bdev; + struct request_queue *q = bdev_get_queue(bdev); - type = xenbus_read(XBT_NIL, dev->nodename, "type", NULL); - if (!IS_ERR(type)) { - if (strncmp(type, "file", 4) == 0) { - state = 1; - blkif->blk_backend_type = BLKIF_BACKEND_FILE; + if (blk_queue_discard(q)) { + err = xenbus_printf(xbt, dev->nodename, + "discard-granularity", "%u", + q->limits.discard_granularity); + if (err) { + dev_warn(&dev->dev, "writing discard-granularity (%d)", err); + return; } - if (strncmp(type, "phy", 3) == 0) { - struct block_device *bdev = be->blkif->vbd.bdev; - struct request_queue *q = bdev_get_queue(bdev); - if (blk_queue_discard(q)) { - err = xenbus_printf(xbt, dev->nodename, - "discard-granularity", "%u", - q->limits.discard_granularity); - if (err) { - xenbus_dev_fatal(dev, err, - "writing discard-granularity"); - goto kfree; - } - err = xenbus_printf(xbt, dev->nodename, - "discard-alignment", "%u", - q->limits.discard_alignment); - if (err) { - xenbus_dev_fatal(dev, err, - "writing discard-alignment"); - goto kfree; - } - state = 1; - blkif->blk_backend_type = BLKIF_BACKEND_PHY; - } - /* Optional. */ - err = xenbus_printf(xbt, dev->nodename, - "discard-secure", "%d", - blkif->vbd.discard_secure); - if (err) { - xenbus_dev_fatal(dev, err, - "writting discard-secure"); - goto kfree; - } + err = xenbus_printf(xbt, dev->nodename, + "discard-alignment", "%u", + q->limits.discard_alignment); + if (err) { + dev_warn(&dev->dev, "writing discard-alignment (%d)", err); + return; + } + state = 1; + /* Optional. */ + err = xenbus_printf(xbt, dev->nodename, + "discard-secure", "%d", + blkif->vbd.discard_secure); + if (err) { + dev_warn(&dev->dev, "writing discard-secure (%d)", err); + return; } - } else { - err = PTR_ERR(type); - xenbus_dev_fatal(dev, err, "reading type"); - goto out; } - err = xenbus_printf(xbt, dev->nodename, "feature-discard", "%d", state); if (err) - xenbus_dev_fatal(dev, err, "writing feature-discard"); -kfree: - kfree(type); -out: - return err; + dev_warn(&dev->dev, "writing feature-discard (%d)", err); } int xen_blkbk_barrier(struct xenbus_transaction xbt, struct backend_info *be, int state) @@ -457,7 +434,7 @@ int xen_blkbk_barrier(struct xenbus_transaction xbt, err = xenbus_printf(xbt, dev->nodename, "feature-barrier", "%d", state); if (err) - xenbus_dev_fatal(dev, err, "writing feature-barrier"); + dev_warn(&dev->dev, "writing feature-barrier (%d)", err); return err; } @@ -689,14 +666,12 @@ again: return; } - err = xen_blkbk_flush_diskcache(xbt, be, be->blkif->vbd.flush_support); - if (err) - goto abort; + /* If we can't advertise it is OK. */ + xen_blkbk_flush_diskcache(xbt, be, be->blkif->vbd.flush_support); - err = xen_blkbk_discard(xbt, be); + xen_blkbk_discard(xbt, be); - /* If we can't advertise it is OK. */ - err = xen_blkbk_barrier(xbt, be, be->blkif->vbd.flush_support); + xen_blkbk_barrier(xbt, be, be->blkif->vbd.flush_support); err = xenbus_printf(xbt, dev->nodename, "sectors", "%llu", (unsigned long long)vbd_sz(&be->blkif->vbd)); diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index d5e1ab956740..4e86393a09cf 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -43,6 +43,7 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/scatterlist.h> +#include <linux/bitmap.h> #include <xen/xen.h> #include <xen/xenbus.h> @@ -81,6 +82,7 @@ static const struct block_device_operations xlvbd_block_fops; */ struct blkfront_info { + spinlock_t io_lock; struct mutex mutex; struct xenbus_device *xbdev; struct gendisk *gd; @@ -105,8 +107,6 @@ struct blkfront_info int is_ready; }; -static DEFINE_SPINLOCK(blkif_io_lock); - static unsigned int nr_minors; static unsigned long *minors; static DEFINE_SPINLOCK(minor_lock); @@ -177,8 +177,7 @@ static int xlbd_reserve_minors(unsigned int minor, unsigned int nr) spin_lock(&minor_lock); if (find_next_bit(minors, end, minor) >= end) { - for (; minor < end; ++minor) - __set_bit(minor, minors); + bitmap_set(minors, minor, nr); rc = 0; } else rc = -EBUSY; @@ -193,8 +192,7 @@ static void xlbd_release_minors(unsigned int minor, unsigned int nr) BUG_ON(end > nr_minors); spin_lock(&minor_lock); - for (; minor < end; ++minor) - __clear_bit(minor, minors); + bitmap_clear(minors, minor, nr); spin_unlock(&minor_lock); } @@ -419,7 +417,7 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) struct request_queue *rq; struct blkfront_info *info = gd->private_data; - rq = blk_init_queue(do_blkif_request, &blkif_io_lock); + rq = blk_init_queue(do_blkif_request, &info->io_lock); if (rq == NULL) return -1; @@ -636,14 +634,14 @@ static void xlvbd_release_gendisk(struct blkfront_info *info) if (info->rq == NULL) return; - spin_lock_irqsave(&blkif_io_lock, flags); + spin_lock_irqsave(&info->io_lock, flags); /* No more blkif_request(). */ blk_stop_queue(info->rq); /* No more gnttab callback work. */ gnttab_cancel_free_callback(&info->callback); - spin_unlock_irqrestore(&blkif_io_lock, flags); + spin_unlock_irqrestore(&info->io_lock, flags); /* Flush gnttab callback work. Must be done with no locks held. */ flush_work_sync(&info->work); @@ -675,16 +673,16 @@ static void blkif_restart_queue(struct work_struct *work) { struct blkfront_info *info = container_of(work, struct blkfront_info, work); - spin_lock_irq(&blkif_io_lock); + spin_lock_irq(&info->io_lock); if (info->connected == BLKIF_STATE_CONNECTED) kick_pending_request_queues(info); - spin_unlock_irq(&blkif_io_lock); + spin_unlock_irq(&info->io_lock); } static void blkif_free(struct blkfront_info *info, int suspend) { /* Prevent new requests being issued until we fix things up. */ - spin_lock_irq(&blkif_io_lock); + spin_lock_irq(&info->io_lock); info->connected = suspend ? BLKIF_STATE_SUSPENDED : BLKIF_STATE_DISCONNECTED; /* No more blkif_request(). */ @@ -692,7 +690,7 @@ static void blkif_free(struct blkfront_info *info, int suspend) blk_stop_queue(info->rq); /* No more gnttab callback work. */ gnttab_cancel_free_callback(&info->callback); - spin_unlock_irq(&blkif_io_lock); + spin_unlock_irq(&info->io_lock); /* Flush gnttab callback work. Must be done with no locks held. */ flush_work_sync(&info->work); @@ -728,10 +726,10 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) struct blkfront_info *info = (struct blkfront_info *)dev_id; int error; - spin_lock_irqsave(&blkif_io_lock, flags); + spin_lock_irqsave(&info->io_lock, flags); if (unlikely(info->connected != BLKIF_STATE_CONNECTED)) { - spin_unlock_irqrestore(&blkif_io_lock, flags); + spin_unlock_irqrestore(&info->io_lock, flags); return IRQ_HANDLED; } @@ -816,7 +814,7 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) kick_pending_request_queues(info); - spin_unlock_irqrestore(&blkif_io_lock, flags); + spin_unlock_irqrestore(&info->io_lock, flags); return IRQ_HANDLED; } @@ -991,6 +989,7 @@ static int blkfront_probe(struct xenbus_device *dev, } mutex_init(&info->mutex); + spin_lock_init(&info->io_lock); info->xbdev = dev; info->vdevice = vdevice; info->connected = BLKIF_STATE_DISCONNECTED; @@ -1068,7 +1067,7 @@ static int blkif_recover(struct blkfront_info *info) xenbus_switch_state(info->xbdev, XenbusStateConnected); - spin_lock_irq(&blkif_io_lock); + spin_lock_irq(&info->io_lock); /* Now safe for us to use the shared ring */ info->connected = BLKIF_STATE_CONNECTED; @@ -1079,7 +1078,7 @@ static int blkif_recover(struct blkfront_info *info) /* Kick any other new requests queued since we resumed */ kick_pending_request_queues(info); - spin_unlock_irq(&blkif_io_lock); + spin_unlock_irq(&info->io_lock); return 0; } @@ -1277,10 +1276,10 @@ static void blkfront_connect(struct blkfront_info *info) xenbus_switch_state(info->xbdev, XenbusStateConnected); /* Kick pending requests. */ - spin_lock_irq(&blkif_io_lock); + spin_lock_irq(&info->io_lock); info->connected = BLKIF_STATE_CONNECTED; kick_pending_request_queues(info); - spin_unlock_irq(&blkif_io_lock); + spin_unlock_irq(&info->io_lock); add_disk(info->gd); @@ -1410,7 +1409,6 @@ static int blkif_release(struct gendisk *disk, fmode_t mode) mutex_lock(&blkfront_mutex); bdev = bdget_disk(disk, 0); - bdput(bdev); if (bdev->bd_openers) goto out; @@ -1441,6 +1439,7 @@ static int blkif_release(struct gendisk *disk, fmode_t mode) } out: + bdput(bdev); mutex_unlock(&blkfront_mutex); return 0; } @@ -1475,7 +1474,7 @@ static int __init xlblk_init(void) if (!xen_domain()) return -ENODEV; - if (!xen_platform_pci_unplug) + if (xen_hvm_domain() && !xen_platform_pci_unplug) return -ENODEV; if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) { diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 48442476ec00..57fd867553d7 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -72,7 +72,11 @@ static struct usb_device_id ath3k_table[] = { /* Atheros AR3012 with sflash firmware*/ { USB_DEVICE(0x0CF3, 0x3004) }, + { USB_DEVICE(0x0CF3, 0x311D) }, { USB_DEVICE(0x13d3, 0x3375) }, + { USB_DEVICE(0x04CA, 0x3005) }, + { USB_DEVICE(0x13d3, 0x3362) }, + { USB_DEVICE(0x0CF3, 0xE004) }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xE02C) }, @@ -89,7 +93,11 @@ static struct usb_device_id ath3k_blist_tbl[] = { /* Atheros AR3012 with sflash firmware*/ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { } /* Terminating entry */ }; diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c index 6c20bbb54b71..428dbb7574bd 100644 --- a/drivers/bluetooth/btmrvl_debugfs.c +++ b/drivers/bluetooth/btmrvl_debugfs.c @@ -45,12 +45,6 @@ struct btmrvl_debugfs_data { struct dentry *txdnldready; }; -static int btmrvl_open_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t btmrvl_hscfgcmd_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { @@ -93,7 +87,7 @@ static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf, static const struct file_operations btmrvl_hscfgcmd_fops = { .read = btmrvl_hscfgcmd_read, .write = btmrvl_hscfgcmd_write, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -134,7 +128,7 @@ static ssize_t btmrvl_psmode_read(struct file *file, char __user *userbuf, static const struct file_operations btmrvl_psmode_fops = { .read = btmrvl_psmode_read, .write = btmrvl_psmode_write, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -180,7 +174,7 @@ static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf, static const struct file_operations btmrvl_pscmd_fops = { .read = btmrvl_pscmd_read, .write = btmrvl_pscmd_write, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -221,7 +215,7 @@ static ssize_t btmrvl_gpiogap_read(struct file *file, char __user *userbuf, static const struct file_operations btmrvl_gpiogap_fops = { .read = btmrvl_gpiogap_read, .write = btmrvl_gpiogap_write, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -265,7 +259,7 @@ static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf, static const struct file_operations btmrvl_hscmd_fops = { .read = btmrvl_hscmd_read, .write = btmrvl_hscmd_write, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -305,7 +299,7 @@ static ssize_t btmrvl_hsmode_read(struct file *file, char __user * userbuf, static const struct file_operations btmrvl_hsmode_fops = { .read = btmrvl_hsmode_read, .write = btmrvl_hsmode_write, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -323,7 +317,7 @@ static ssize_t btmrvl_curpsmode_read(struct file *file, char __user *userbuf, static const struct file_operations btmrvl_curpsmode_fops = { .read = btmrvl_curpsmode_read, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -341,7 +335,7 @@ static ssize_t btmrvl_psstate_read(struct file *file, char __user * userbuf, static const struct file_operations btmrvl_psstate_fops = { .read = btmrvl_psstate_read, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -359,7 +353,7 @@ static ssize_t btmrvl_hsstate_read(struct file *file, char __user *userbuf, static const struct file_operations btmrvl_hsstate_fops = { .read = btmrvl_hsstate_read, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -378,7 +372,7 @@ static ssize_t btmrvl_txdnldready_read(struct file *file, char __user *userbuf, static const struct file_operations btmrvl_txdnldready_fops = { .read = btmrvl_txdnldready_read, - .open = btmrvl_open_generic, + .open = simple_open, .llseek = default_llseek, }; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 480cad920048..9217121362e1 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -61,7 +61,7 @@ static struct usb_device_id btusb_table[] = { { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, /* Broadcom SoftSailing reporting vendor specific */ - { USB_DEVICE(0x05ac, 0x21e1) }, + { USB_DEVICE(0x0a5c, 0x21e1) }, /* Apple MacBookPro 7,1 */ { USB_DEVICE(0x05ac, 0x8213) }, @@ -101,11 +101,16 @@ static struct usb_device_id btusb_table[] = { { USB_DEVICE(0x0c10, 0x0000) }, /* Broadcom BCM20702A0 */ + { USB_DEVICE(0x0489, 0xe042) }, { USB_DEVICE(0x0a5c, 0x21e3) }, { USB_DEVICE(0x0a5c, 0x21e6) }, + { USB_DEVICE(0x0a5c, 0x21e8) }, { USB_DEVICE(0x0a5c, 0x21f3) }, { USB_DEVICE(0x413c, 0x8197) }, + /* Foxconn - Hon Hai */ + { USB_DEVICE(0x0489, 0xe033) }, + { } /* Terminating entry */ }; @@ -129,7 +134,11 @@ static struct usb_device_id blacklist_table[] = { /* Atheros 3012 with sflash firmware */ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index fd5adb408f44..98a8c05d4f23 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -299,11 +299,11 @@ static void hci_uart_tty_close(struct tty_struct *tty) hci_uart_close(hdev); if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { - hu->proto->close(hu); if (hdev) { hci_unregister_dev(hdev); hci_free_dev(hdev); } + hu->proto->close(hu); } kfree(hu); diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index 5da67f165afa..7ea18a5fe71c 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -234,6 +234,7 @@ #define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT2_IG 0x0166 #define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_HB 0x0158 /* Server */ #define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT1_IG 0x015A +#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT2_IG 0x016A int intel_gmch_probe(struct pci_dev *pdev, struct agp_bridge_data *bridge); diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 5cf47ac2d401..7f025fb620de 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1190,7 +1190,6 @@ static inline int needs_idle_maps(void) { #ifdef CONFIG_INTEL_IOMMU const unsigned short gpu_devid = intel_private.pcidev->device; - extern int intel_iommu_gfx_mapped; /* Query intel_iommu to see if we need the workaround. Presumably that * was loaded first. @@ -1459,6 +1458,8 @@ static const struct intel_gtt_driver_description { "Ivybridge", &sandybridge_gtt_driver }, { PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT1_IG, "Ivybridge", &sandybridge_gtt_driver }, + { PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT2_IG, + "Ivybridge", &sandybridge_gtt_driver }, { 0, NULL, NULL } }; diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index 57501ca9204b..46118f845948 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -301,7 +301,7 @@ apm_ioctl(struct file *filp, u_int cmd, u_long arg) * anything critical, chill a bit on each iteration. */ while (wait_event_freezable(apm_suspend_waitqueue, - as->suspend_state == SUSPEND_DONE)) + as->suspend_state != SUSPEND_ACKED)) msleep(10); break; case SUSPEND_ACKTO: diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 3845ab44c330..dfd7876f127c 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -906,8 +906,8 @@ int hpet_alloc(struct hpet_data *hdp) hpetp->hp_which, hdp->hd_phys_address, hpetp->hp_ntimer > 1 ? "s" : ""); for (i = 0; i < hpetp->hp_ntimer; i++) - printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); - printk("\n"); + printk(KERN_CONT "%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); + printk(KERN_CONT "\n"); temp = hpetp->hp_tick_freq; remainder = do_div(temp, 1000000); diff --git a/drivers/char/random.c b/drivers/char/random.c index 54ca8b23cde3..4ec04a754733 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1260,10 +1260,15 @@ static int proc_do_uuid(ctl_table *table, int write, uuid = table->data; if (!uuid) { uuid = tmp_uuid; - uuid[8] = 0; - } - if (uuid[8] == 0) generate_random_uuid(uuid); + } else { + static DEFINE_SPINLOCK(bootid_spinlock); + + spin_lock(&bootid_spinlock); + if (!uuid[8]) + generate_random_uuid(uuid); + spin_unlock(&bootid_spinlock); + } sprintf(buf, "%pU", uuid); diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 4dc019408fac..3b22a606f79d 100644 --- a/drivers/char/tile-srom.c +++ b/drivers/char/tile-srom.c @@ -194,17 +194,17 @@ static ssize_t srom_read(struct file *filp, char __user *buf, hv_retval = _srom_read(srom->hv_devhdl, kernbuf, *f_pos, bytes_this_pass); - if (hv_retval > 0) { - if (copy_to_user(buf, kernbuf, hv_retval) != 0) { - retval = -EFAULT; - break; - } - } else if (hv_retval <= 0) { + if (hv_retval <= 0) { if (retval == 0) retval = hv_retval; break; } + if (copy_to_user(buf, kernbuf, hv_retval) != 0) { + retval = -EFAULT; + break; + } + retval += hv_retval; *f_pos += hv_retval; buf += hv_retval; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index b58b56187065..ddf86b6500b7 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1038,12 +1038,6 @@ static struct attribute_group port_attribute_group = { .attrs = port_sysfs_entries, }; -static int debugfs_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - return 0; -} - static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count, loff_t *offp) { @@ -1087,7 +1081,7 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, static const struct file_operations port_debugfs_ops = { .owner = THIS_MODULE, - .open = debugfs_open, + .open = simple_open, .read = debugfs_read, }; diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c index 82e882028fcf..6b5cf02c35c8 100644 --- a/drivers/clocksource/acpi_pm.c +++ b/drivers/clocksource/acpi_pm.c @@ -23,7 +23,6 @@ #include <linux/init.h> #include <linux/pci.h> #include <linux/delay.h> -#include <linux/async.h> #include <asm/io.h> /* @@ -180,15 +179,17 @@ static int verify_pmtmr_rate(void) /* Number of reads we try to get two different values */ #define ACPI_PM_READ_CHECKS 10000 -static void __init acpi_pm_clocksource_async(void *unused, async_cookie_t cookie) +static int __init init_acpi_pm_clocksource(void) { cycle_t value1, value2; unsigned int i, j = 0; + if (!pmtmr_ioport) + return -ENODEV; /* "verify" this timing source: */ for (j = 0; j < ACPI_PM_MONOTONICITY_CHECKS; j++) { - usleep_range(100 * j, 100 * j + 100); + udelay(100 * j); value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm); for (i = 0; i < ACPI_PM_READ_CHECKS; i++) { value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm); @@ -202,34 +203,25 @@ static void __init acpi_pm_clocksource_async(void *unused, async_cookie_t cookie " 0x%#llx, 0x%#llx - aborting.\n", value1, value2); pmtmr_ioport = 0; - return; + return -EINVAL; } if (i == ACPI_PM_READ_CHECKS) { printk(KERN_INFO "PM-Timer failed consistency check " " (0x%#llx) - aborting.\n", value1); pmtmr_ioport = 0; - return; + return -ENODEV; } } if (verify_pmtmr_rate() != 0){ pmtmr_ioport = 0; - return; + return -ENODEV; } - clocksource_register_hz(&clocksource_acpi_pm, + return clocksource_register_hz(&clocksource_acpi_pm, PMTMR_TICKS_PER_SEC); } -static int __init init_acpi_pm_clocksource(void) -{ - if (!pmtmr_ioport) - return -ENODEV; - - async_schedule(acpi_pm_clocksource_async, NULL); - return 0; -} - /* We use fs_initcall because we want the PCI fixups to have run * but we still need to load before device_initcall */ diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 32d790dd8180..5961e6415f08 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -4,6 +4,7 @@ config ARM_OMAP2PLUS_CPUFREQ bool "TI OMAP2+" + depends on ARCH_OMAP2PLUS default ARCH_OMAP2PLUS select CPU_FREQ_TABLE @@ -51,9 +52,6 @@ config ARM_S5PV210_CPUFREQ config ARM_EXYNOS_CPUFREQ bool "SAMSUNG EXYNOS SoCs" depends on ARCH_EXYNOS - select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210 - select ARM_EXYNOS4X12_CPUFREQ if (SOC_EXYNOS4212 || SOC_EXYNOS4412) - select ARM_EXYNOS5250_CPUFREQ if SOC_EXYNOS5250 default y help This adds the CPUFreq driver common part for Samsung @@ -62,20 +60,19 @@ config ARM_EXYNOS_CPUFREQ If in doubt, say N. config ARM_EXYNOS4210_CPUFREQ - bool "Samsung EXYNOS4210" - depends on ARCH_EXYNOS + def_bool CPU_EXYNOS4210 help This adds the CPUFreq driver for Samsung EXYNOS4210 SoC (S5PV310 or S5PC210). config ARM_EXYNOS4X12_CPUFREQ - bool "Samsung EXYNOS4X12" + def_bool (SOC_EXYNOS4212 || SOC_EXYNOS4412) help This adds the CPUFreq driver for Samsung EXYNOS4X12 SoC (EXYNOS4212 or EXYNOS4412). config ARM_EXYNOS5250_CPUFREQ - bool "Samsung EXYNOS5250" + def_bool SOC_EXYNOS5250 help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c index a22ffa5bff9f..0bf1b8910eeb 100644 --- a/drivers/cpufreq/db8500-cpufreq.c +++ b/drivers/cpufreq/db8500-cpufreq.c @@ -142,7 +142,7 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */ /* policy sharing between dual CPUs */ - cpumask_copy(policy->cpus, &cpu_present_map); + cpumask_copy(policy->cpus, cpu_present_mask); policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 87411cebc577..2f0083a51a9a 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -74,7 +74,7 @@ static cpuidle_enter_t cpuidle_enter_ops; /** * cpuidle_play_dead - cpu off-lining * - * Only returns in case of an error + * Returns in case of an error or no driver */ int cpuidle_play_dead(void) { @@ -83,6 +83,9 @@ int cpuidle_play_dead(void) int i, dead_state = -1; int power_usage = -1; + if (!drv) + return -ENODEV; + /* Find lowest-power state that supports long-term idle */ for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { struct cpuidle_state *s = &drv->states[i]; diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c index 0053d7ebb5ca..8f3f74ce8c7f 100644 --- a/drivers/crypto/ixp4xx_crypto.c +++ b/drivers/crypto/ixp4xx_crypto.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/gfp.h> +#include <linux/module.h> #include <crypto/ctr.h> #include <crypto/des.h> diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index dc641c796526..921039e56f87 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -124,6 +124,9 @@ struct talitos_private { void __iomem *reg; int irq[2]; + /* SEC global registers lock */ + spinlock_t reg_lock ____cacheline_aligned; + /* SEC version geometry (from device tree node) */ unsigned int num_channels; unsigned int chfifo_len; @@ -412,6 +415,7 @@ static void talitos_done_##name(unsigned long data) \ { \ struct device *dev = (struct device *)data; \ struct talitos_private *priv = dev_get_drvdata(dev); \ + unsigned long flags; \ \ if (ch_done_mask & 1) \ flush_channel(dev, 0, 0, 0); \ @@ -427,8 +431,10 @@ static void talitos_done_##name(unsigned long data) \ out: \ /* At this point, all completed channels have been processed */ \ /* Unmask done interrupts for channels completed later on. */ \ + spin_lock_irqsave(&priv->reg_lock, flags); \ setbits32(priv->reg + TALITOS_IMR, ch_done_mask); \ setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT); \ + spin_unlock_irqrestore(&priv->reg_lock, flags); \ } DEF_TALITOS_DONE(4ch, TALITOS_ISR_4CHDONE) DEF_TALITOS_DONE(ch0_2, TALITOS_ISR_CH_0_2_DONE) @@ -619,22 +625,28 @@ static irqreturn_t talitos_interrupt_##name(int irq, void *data) \ struct device *dev = data; \ struct talitos_private *priv = dev_get_drvdata(dev); \ u32 isr, isr_lo; \ + unsigned long flags; \ \ + spin_lock_irqsave(&priv->reg_lock, flags); \ isr = in_be32(priv->reg + TALITOS_ISR); \ isr_lo = in_be32(priv->reg + TALITOS_ISR_LO); \ /* Acknowledge interrupt */ \ out_be32(priv->reg + TALITOS_ICR, isr & (ch_done_mask | ch_err_mask)); \ out_be32(priv->reg + TALITOS_ICR_LO, isr_lo); \ \ - if (unlikely((isr & ~TALITOS_ISR_4CHDONE) & ch_err_mask || isr_lo)) \ - talitos_error(dev, isr, isr_lo); \ - else \ + if (unlikely(isr & ch_err_mask || isr_lo)) { \ + spin_unlock_irqrestore(&priv->reg_lock, flags); \ + talitos_error(dev, isr & ch_err_mask, isr_lo); \ + } \ + else { \ if (likely(isr & ch_done_mask)) { \ /* mask further done interrupts. */ \ clrbits32(priv->reg + TALITOS_IMR, ch_done_mask); \ /* done_task will unmask done interrupts at exit */ \ tasklet_schedule(&priv->done_task[tlet]); \ } \ + spin_unlock_irqrestore(&priv->reg_lock, flags); \ + } \ \ return (isr & (ch_done_mask | ch_err_mask) || isr_lo) ? IRQ_HANDLED : \ IRQ_NONE; \ @@ -2719,6 +2731,8 @@ static int talitos_probe(struct platform_device *ofdev) priv->ofdev = ofdev; + spin_lock_init(&priv->reg_lock); + err = talitos_probe_irq(ofdev); if (err) goto err_out; diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index cf9da362d64f..ef378b5b17e4 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -91,11 +91,10 @@ config DW_DMAC config AT_HDMAC tristate "Atmel AHB DMA support" - depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 + depends on ARCH_AT91 select DMA_ENGINE help - Support the Atmel AHB DMA controller. This can be integrated in - chips such as the Atmel AT91SAM9RL. + Support the Atmel AHB DMA controller. config FSL_DMA tristate "Freescale Elo and Elo Plus DMA support" diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index c301a8ec31aa..3d704abd7912 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1429,6 +1429,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, * signal */ release_phy_channel(plchan); + plchan->phychan_hold = 0; } /* Dequeue jobs and free LLIs */ if (plchan->at) { diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 7aa58d204892..445fdf811695 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -221,10 +221,6 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first) vdbg_dump_regs(atchan); - /* clear any pending interrupt */ - while (dma_readl(atdma, EBCISR)) - cpu_relax(); - channel_writel(atchan, SADDR, 0); channel_writel(atchan, DADDR, 0); channel_writel(atchan, CTRLA, 0); diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index dc89455f5550..750925f9638b 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -104,13 +104,6 @@ static void coh901318_list_print(struct coh901318_chan *cohc, static struct coh901318_base *debugfs_dma_base; static struct dentry *dma_dentry; -static int coh901318_debugfs_open(struct inode *inode, struct file *file) -{ - - file->private_data = inode->i_private; - return 0; -} - static int coh901318_debugfs_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos) { @@ -158,7 +151,7 @@ static int coh901318_debugfs_read(struct file *file, char __user *buf, static const struct file_operations coh901318_debugfs_status_operations = { .owner = THIS_MODULE, - .open = coh901318_debugfs_open, + .open = simple_open, .read = coh901318_debugfs_read, .llseek = default_llseek, }; diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 767bcc31b365..2397f6f451b1 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -332,6 +332,20 @@ struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type) } EXPORT_SYMBOL(dma_find_channel); +/* + * net_dma_find_channel - find a channel for net_dma + * net_dma has alignment requirements + */ +struct dma_chan *net_dma_find_channel(void) +{ + struct dma_chan *chan = dma_find_channel(DMA_MEMCPY); + if (chan && !is_dma_copy_aligned(chan->device, 1, 1, 1)) + return NULL; + + return chan; +} +EXPORT_SYMBOL(net_dma_find_channel); + /** * dma_issue_pending_all - flush all pending operations across all channels */ diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index a45b5d2a5987..bb787d8e1529 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -571,11 +571,14 @@ static void imxdma_tasklet(unsigned long data) if (desc->desc.callback) desc->desc.callback(desc->desc.callback_param); - dma_cookie_complete(&desc->desc); - - /* If we are dealing with a cyclic descriptor keep it on ld_active */ + /* If we are dealing with a cyclic descriptor keep it on ld_active + * and dont mark the descripor as complete. + * Only in non-cyclic cases it would be marked as complete + */ if (imxdma_chan_is_doing_cyclic(imxdmac)) goto out; + else + dma_cookie_complete(&desc->desc); /* Free 2D slot if it was an interleaved transfer */ if (imxdmac->enabled_2d) { diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 31493d80e0e9..73b2b65cb1de 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -546,9 +546,9 @@ void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags, PCI_DMA_TODEVICE, flags, 0); } -unsigned long ioat_get_current_completion(struct ioat_chan_common *chan) +dma_addr_t ioat_get_current_completion(struct ioat_chan_common *chan) { - unsigned long phys_complete; + dma_addr_t phys_complete; u64 completion; completion = *chan->completion; @@ -569,7 +569,7 @@ unsigned long ioat_get_current_completion(struct ioat_chan_common *chan) } bool ioat_cleanup_preamble(struct ioat_chan_common *chan, - unsigned long *phys_complete) + dma_addr_t *phys_complete) { *phys_complete = ioat_get_current_completion(chan); if (*phys_complete == chan->last_completion) @@ -580,14 +580,14 @@ bool ioat_cleanup_preamble(struct ioat_chan_common *chan, return true; } -static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) +static void __cleanup(struct ioat_dma_chan *ioat, dma_addr_t phys_complete) { struct ioat_chan_common *chan = &ioat->base; struct list_head *_desc, *n; struct dma_async_tx_descriptor *tx; - dev_dbg(to_dev(chan), "%s: phys_complete: %lx\n", - __func__, phys_complete); + dev_dbg(to_dev(chan), "%s: phys_complete: %llx\n", + __func__, (unsigned long long) phys_complete); list_for_each_safe(_desc, n, &ioat->used_desc) { struct ioat_desc_sw *desc; @@ -652,7 +652,7 @@ static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) static void ioat1_cleanup(struct ioat_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; prefetch(chan->completion); @@ -698,7 +698,7 @@ static void ioat1_timer_event(unsigned long data) mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); spin_unlock_bh(&ioat->desc_lock); } else if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - unsigned long phys_complete; + dma_addr_t phys_complete; spin_lock_bh(&ioat->desc_lock); /* if we haven't made progress and we have already diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index c7888bccd974..5e8fe01ba69d 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -88,7 +88,7 @@ struct ioatdma_device { struct ioat_chan_common { struct dma_chan common; void __iomem *reg_base; - unsigned long last_completion; + dma_addr_t last_completion; spinlock_t cleanup_lock; unsigned long state; #define IOAT_COMPLETION_PENDING 0 @@ -310,7 +310,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device); void __devexit ioat_dma_remove(struct ioatdma_device *device); struct dca_provider * __devinit ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase); -unsigned long ioat_get_current_completion(struct ioat_chan_common *chan); +dma_addr_t ioat_get_current_completion(struct ioat_chan_common *chan); void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *chan, int idx); enum dma_status ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, @@ -318,7 +318,7 @@ enum dma_status ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags, size_t len, struct ioat_dma_descriptor *hw); bool ioat_cleanup_preamble(struct ioat_chan_common *chan, - unsigned long *phys_complete); + dma_addr_t *phys_complete); void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type); void ioat_kobject_del(struct ioatdma_device *device); extern const struct sysfs_ops ioat_sysfs_ops; diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index e8e110ff3d96..86895760b598 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -128,7 +128,7 @@ static void ioat2_start_null_desc(struct ioat2_dma_chan *ioat) spin_unlock_bh(&ioat->prep_lock); } -static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) +static void __cleanup(struct ioat2_dma_chan *ioat, dma_addr_t phys_complete) { struct ioat_chan_common *chan = &ioat->base; struct dma_async_tx_descriptor *tx; @@ -179,7 +179,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) static void ioat2_cleanup(struct ioat2_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; spin_lock_bh(&chan->cleanup_lock); if (ioat_cleanup_preamble(chan, &phys_complete)) @@ -260,7 +260,7 @@ int ioat2_reset_sync(struct ioat_chan_common *chan, unsigned long tmo) static void ioat2_restart_channel(struct ioat2_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; ioat2_quiesce(chan, 0); if (ioat_cleanup_preamble(chan, &phys_complete)) @@ -275,7 +275,7 @@ void ioat2_timer_event(unsigned long data) struct ioat_chan_common *chan = &ioat->base; if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - unsigned long phys_complete; + dma_addr_t phys_complete; u64 status; status = ioat_chansts(chan); @@ -572,9 +572,9 @@ bool reshape_ring(struct ioat2_dma_chan *ioat, int order) */ struct ioat_chan_common *chan = &ioat->base; struct dma_chan *c = &chan->common; - const u16 curr_size = ioat2_ring_size(ioat); + const u32 curr_size = ioat2_ring_size(ioat); const u16 active = ioat2_ring_active(ioat); - const u16 new_size = 1 << order; + const u32 new_size = 1 << order; struct ioat_ring_ent **ring; u16 i; diff --git a/drivers/dma/ioat/dma_v2.h b/drivers/dma/ioat/dma_v2.h index a2c413b2b8d8..be2a55b95c23 100644 --- a/drivers/dma/ioat/dma_v2.h +++ b/drivers/dma/ioat/dma_v2.h @@ -74,7 +74,7 @@ static inline struct ioat2_dma_chan *to_ioat2_chan(struct dma_chan *c) return container_of(chan, struct ioat2_dma_chan, base); } -static inline u16 ioat2_ring_size(struct ioat2_dma_chan *ioat) +static inline u32 ioat2_ring_size(struct ioat2_dma_chan *ioat) { return 1 << ioat->alloc_order; } @@ -91,7 +91,7 @@ static inline u16 ioat2_ring_pending(struct ioat2_dma_chan *ioat) return CIRC_CNT(ioat->head, ioat->issued, ioat2_ring_size(ioat)); } -static inline u16 ioat2_ring_space(struct ioat2_dma_chan *ioat) +static inline u32 ioat2_ring_space(struct ioat2_dma_chan *ioat) { return ioat2_ring_size(ioat) - ioat2_ring_active(ioat); } diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 2c4476c0e405..f7f1dc62c15c 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -257,7 +257,7 @@ static bool desc_has_ext(struct ioat_ring_ent *desc) * The difference from the dma_v2.c __cleanup() is that this routine * handles extended descriptors and dma-unmapping raid operations. */ -static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) +static void __cleanup(struct ioat2_dma_chan *ioat, dma_addr_t phys_complete) { struct ioat_chan_common *chan = &ioat->base; struct ioat_ring_ent *desc; @@ -314,7 +314,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) static void ioat3_cleanup(struct ioat2_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; spin_lock_bh(&chan->cleanup_lock); if (ioat_cleanup_preamble(chan, &phys_complete)) @@ -333,7 +333,7 @@ static void ioat3_cleanup_event(unsigned long data) static void ioat3_restart_channel(struct ioat2_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; ioat2_quiesce(chan, 0); if (ioat_cleanup_preamble(chan, &phys_complete)) @@ -348,7 +348,7 @@ static void ioat3_timer_event(unsigned long data) struct ioat_chan_common *chan = &ioat->base; if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - unsigned long phys_complete; + dma_addr_t phys_complete; u64 status; status = ioat_chansts(chan); @@ -1149,6 +1149,44 @@ static int ioat3_reset_hw(struct ioat_chan_common *chan) return ioat2_reset_sync(chan, msecs_to_jiffies(200)); } +static bool is_jf_ioat(struct pci_dev *pdev) +{ + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_IOAT_JSF0: + case PCI_DEVICE_ID_INTEL_IOAT_JSF1: + case PCI_DEVICE_ID_INTEL_IOAT_JSF2: + case PCI_DEVICE_ID_INTEL_IOAT_JSF3: + case PCI_DEVICE_ID_INTEL_IOAT_JSF4: + case PCI_DEVICE_ID_INTEL_IOAT_JSF5: + case PCI_DEVICE_ID_INTEL_IOAT_JSF6: + case PCI_DEVICE_ID_INTEL_IOAT_JSF7: + case PCI_DEVICE_ID_INTEL_IOAT_JSF8: + case PCI_DEVICE_ID_INTEL_IOAT_JSF9: + return true; + default: + return false; + } +} + +static bool is_snb_ioat(struct pci_dev *pdev) +{ + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_IOAT_SNB0: + case PCI_DEVICE_ID_INTEL_IOAT_SNB1: + case PCI_DEVICE_ID_INTEL_IOAT_SNB2: + case PCI_DEVICE_ID_INTEL_IOAT_SNB3: + case PCI_DEVICE_ID_INTEL_IOAT_SNB4: + case PCI_DEVICE_ID_INTEL_IOAT_SNB5: + case PCI_DEVICE_ID_INTEL_IOAT_SNB6: + case PCI_DEVICE_ID_INTEL_IOAT_SNB7: + case PCI_DEVICE_ID_INTEL_IOAT_SNB8: + case PCI_DEVICE_ID_INTEL_IOAT_SNB9: + return true; + default: + return false; + } +} + int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) { struct pci_dev *pdev = device->pdev; @@ -1169,6 +1207,9 @@ int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) dma->device_alloc_chan_resources = ioat2_alloc_chan_resources; dma->device_free_chan_resources = ioat2_free_chan_resources; + if (is_jf_ioat(pdev) || is_snb_ioat(pdev)) + dma->copy_align = 6; + dma_cap_set(DMA_INTERRUPT, dma->cap_mask); dma->device_prep_dma_interrupt = ioat3_prep_interrupt_lock; diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index da6c4c2c066a..79e3eba29702 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -1252,8 +1252,8 @@ iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device) struct page **pq_hw = &pq[IOP_ADMA_NUM_SRC_TEST+2]; /* address conversion buffers (dma_map / page_address) */ void *pq_sw[IOP_ADMA_NUM_SRC_TEST+2]; - dma_addr_t pq_src[IOP_ADMA_NUM_SRC_TEST]; - dma_addr_t pq_dest[2]; + dma_addr_t pq_src[IOP_ADMA_NUM_SRC_TEST+2]; + dma_addr_t *pq_dest = &pq_src[IOP_ADMA_NUM_SRC_TEST]; int i; struct dma_async_tx_descriptor *tx; diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index c81ef7e10e08..655d4ce6ed0d 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -201,10 +201,6 @@ static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx) { - struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(tx->chan); - - mxs_dma_enable_chan(mxs_chan); - return dma_cookie_assign(tx); } @@ -558,9 +554,9 @@ static enum dma_status mxs_dma_tx_status(struct dma_chan *chan, static void mxs_dma_issue_pending(struct dma_chan *chan) { - /* - * Nothing to do. We only have a single descriptor. - */ + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); + + mxs_dma_enable_chan(mxs_chan); } static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 282caf118be8..2ee6e23930ad 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2225,12 +2225,9 @@ static inline void free_desc_list(struct list_head *list) { struct dma_pl330_dmac *pdmac; struct dma_pl330_desc *desc; - struct dma_pl330_chan *pch; + struct dma_pl330_chan *pch = NULL; unsigned long flags; - if (list_empty(list)) - return; - /* Finish off the work list */ list_for_each_entry(desc, list, node) { dma_async_tx_callback callback; @@ -2247,6 +2244,10 @@ static inline void free_desc_list(struct list_head *list) desc->pchan = NULL; } + /* pch will be unset if list was empty */ + if (!pch) + return; + pdmac = pch->dmac; spin_lock_irqsave(&pdmac->pool_lock, flags); @@ -2257,12 +2258,9 @@ static inline void free_desc_list(struct list_head *list) static inline void handle_cyclic_desc_list(struct list_head *list) { struct dma_pl330_desc *desc; - struct dma_pl330_chan *pch; + struct dma_pl330_chan *pch = NULL; unsigned long flags; - if (list_empty(list)) - return; - list_for_each_entry(desc, list, node) { dma_async_tx_callback callback; @@ -2274,6 +2272,10 @@ static inline void handle_cyclic_desc_list(struct list_head *list) callback(desc->txd.callback_param); } + /* pch will be unset if list was empty */ + if (!pch) + return; + spin_lock_irqsave(&pch->lock, flags); list_splice_tail_init(list, &pch->work_list); spin_unlock_irqrestore(&pch->lock, flags); @@ -2926,8 +2928,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) INIT_LIST_HEAD(&pd->channels); /* Initialize channel parameters */ - num_chan = max(pdat ? pdat->nr_valid_peri : (u8)pi->pcfg.num_peri, - (u8)pi->pcfg.num_chan); + if (pdat) + num_chan = max_t(int, pdat->nr_valid_peri, pi->pcfg.num_chan); + else + num_chan = max_t(int, pi->pcfg.num_peri, pi->pcfg.num_chan); + pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL); for (i = 0; i < num_chan; i++) { diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index 16a6b48883cf..ec78ccef9132 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -585,7 +585,7 @@ static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx) static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sg, unsigned int sglen, - enum dma_transfer_direction dir, unsigned long flags) + enum dma_transfer_direction dir, unsigned long flags, void *context) { struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_desc *txd; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index bdd41d4bfa8d..2ed1ac3513f3 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -18,6 +18,7 @@ #include <linux/pm_runtime.h> #include <linux/err.h> #include <linux/amba/bus.h> +#include <linux/regulator/consumer.h> #include <plat/ste_dma40.h> @@ -69,6 +70,22 @@ enum d40_command { }; /* + * enum d40_events - The different Event Enables for the event lines. + * + * @D40_DEACTIVATE_EVENTLINE: De-activate Event line, stopping the logical chan. + * @D40_ACTIVATE_EVENTLINE: Activate the Event line, to start a logical chan. + * @D40_SUSPEND_REQ_EVENTLINE: Requesting for suspending a event line. + * @D40_ROUND_EVENTLINE: Status check for event line. + */ + +enum d40_events { + D40_DEACTIVATE_EVENTLINE = 0, + D40_ACTIVATE_EVENTLINE = 1, + D40_SUSPEND_REQ_EVENTLINE = 2, + D40_ROUND_EVENTLINE = 3 +}; + +/* * These are the registers that has to be saved and later restored * when the DMA hw is powered off. * TODO: Add save/restore of D40_DREG_GCC on dma40 v3 or later, if that works. @@ -870,8 +887,8 @@ static void d40_save_restore_registers(struct d40_base *base, bool save) } #endif -static int d40_channel_execute_command(struct d40_chan *d40c, - enum d40_command command) +static int __d40_execute_command_phy(struct d40_chan *d40c, + enum d40_command command) { u32 status; int i; @@ -880,6 +897,12 @@ static int d40_channel_execute_command(struct d40_chan *d40c, unsigned long flags; u32 wmask; + if (command == D40_DMA_STOP) { + ret = __d40_execute_command_phy(d40c, D40_DMA_SUSPEND_REQ); + if (ret) + return ret; + } + spin_lock_irqsave(&d40c->base->execmd_lock, flags); if (d40c->phy_chan->num % 2 == 0) @@ -973,67 +996,109 @@ static void d40_term_all(struct d40_chan *d40c) } d40c->pending_tx = 0; - d40c->busy = false; } -static void __d40_config_set_event(struct d40_chan *d40c, bool enable, - u32 event, int reg) +static void __d40_config_set_event(struct d40_chan *d40c, + enum d40_events event_type, u32 event, + int reg) { void __iomem *addr = chan_base(d40c) + reg; int tries; + u32 status; + + switch (event_type) { + + case D40_DEACTIVATE_EVENTLINE: - if (!enable) { writel((D40_DEACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event)) | ~D40_EVENTLINE_MASK(event), addr); - return; - } + break; + + case D40_SUSPEND_REQ_EVENTLINE: + status = (readl(addr) & D40_EVENTLINE_MASK(event)) >> + D40_EVENTLINE_POS(event); + + if (status == D40_DEACTIVATE_EVENTLINE || + status == D40_SUSPEND_REQ_EVENTLINE) + break; + writel((D40_SUSPEND_REQ_EVENTLINE << D40_EVENTLINE_POS(event)) + | ~D40_EVENTLINE_MASK(event), addr); + + for (tries = 0 ; tries < D40_SUSPEND_MAX_IT; tries++) { + + status = (readl(addr) & D40_EVENTLINE_MASK(event)) >> + D40_EVENTLINE_POS(event); + + cpu_relax(); + /* + * Reduce the number of bus accesses while + * waiting for the DMA to suspend. + */ + udelay(3); + + if (status == D40_DEACTIVATE_EVENTLINE) + break; + } + + if (tries == D40_SUSPEND_MAX_IT) { + chan_err(d40c, + "unable to stop the event_line chl %d (log: %d)" + "status %x\n", d40c->phy_chan->num, + d40c->log_num, status); + } + break; + + case D40_ACTIVATE_EVENTLINE: /* * The hardware sometimes doesn't register the enable when src and dst * event lines are active on the same logical channel. Retry to ensure * it does. Usually only one retry is sufficient. */ - tries = 100; - while (--tries) { - writel((D40_ACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event)) - | ~D40_EVENTLINE_MASK(event), addr); + tries = 100; + while (--tries) { + writel((D40_ACTIVATE_EVENTLINE << + D40_EVENTLINE_POS(event)) | + ~D40_EVENTLINE_MASK(event), addr); - if (readl(addr) & D40_EVENTLINE_MASK(event)) - break; - } + if (readl(addr) & D40_EVENTLINE_MASK(event)) + break; + } - if (tries != 99) - dev_dbg(chan2dev(d40c), - "[%s] workaround enable S%cLNK (%d tries)\n", - __func__, reg == D40_CHAN_REG_SSLNK ? 'S' : 'D', - 100 - tries); + if (tries != 99) + dev_dbg(chan2dev(d40c), + "[%s] workaround enable S%cLNK (%d tries)\n", + __func__, reg == D40_CHAN_REG_SSLNK ? 'S' : 'D', + 100 - tries); - WARN_ON(!tries); -} + WARN_ON(!tries); + break; -static void d40_config_set_event(struct d40_chan *d40c, bool do_enable) -{ - unsigned long flags; + case D40_ROUND_EVENTLINE: + BUG(); + break; - spin_lock_irqsave(&d40c->phy_chan->lock, flags); + } +} +static void d40_config_set_event(struct d40_chan *d40c, + enum d40_events event_type) +{ /* Enable event line connected to device (or memcpy) */ if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) || (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) { u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type); - __d40_config_set_event(d40c, do_enable, event, + __d40_config_set_event(d40c, event_type, event, D40_CHAN_REG_SSLNK); } if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) { u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type); - __d40_config_set_event(d40c, do_enable, event, + __d40_config_set_event(d40c, event_type, event, D40_CHAN_REG_SDLNK); } - - spin_unlock_irqrestore(&d40c->phy_chan->lock, flags); } static u32 d40_chan_has_events(struct d40_chan *d40c) @@ -1047,6 +1112,64 @@ static u32 d40_chan_has_events(struct d40_chan *d40c) return val; } +static int +__d40_execute_command_log(struct d40_chan *d40c, enum d40_command command) +{ + unsigned long flags; + int ret = 0; + u32 active_status; + void __iomem *active_reg; + + if (d40c->phy_chan->num % 2 == 0) + active_reg = d40c->base->virtbase + D40_DREG_ACTIVE; + else + active_reg = d40c->base->virtbase + D40_DREG_ACTIVO; + + + spin_lock_irqsave(&d40c->phy_chan->lock, flags); + + switch (command) { + case D40_DMA_STOP: + case D40_DMA_SUSPEND_REQ: + + active_status = (readl(active_reg) & + D40_CHAN_POS_MASK(d40c->phy_chan->num)) >> + D40_CHAN_POS(d40c->phy_chan->num); + + if (active_status == D40_DMA_RUN) + d40_config_set_event(d40c, D40_SUSPEND_REQ_EVENTLINE); + else + d40_config_set_event(d40c, D40_DEACTIVATE_EVENTLINE); + + if (!d40_chan_has_events(d40c) && (command == D40_DMA_STOP)) + ret = __d40_execute_command_phy(d40c, command); + + break; + + case D40_DMA_RUN: + + d40_config_set_event(d40c, D40_ACTIVATE_EVENTLINE); + ret = __d40_execute_command_phy(d40c, command); + break; + + case D40_DMA_SUSPENDED: + BUG(); + break; + } + + spin_unlock_irqrestore(&d40c->phy_chan->lock, flags); + return ret; +} + +static int d40_channel_execute_command(struct d40_chan *d40c, + enum d40_command command) +{ + if (chan_is_logical(d40c)) + return __d40_execute_command_log(d40c, command); + else + return __d40_execute_command_phy(d40c, command); +} + static u32 d40_get_prmo(struct d40_chan *d40c) { static const unsigned int phy_map[] = { @@ -1149,15 +1272,7 @@ static int d40_pause(struct d40_chan *d40c) spin_lock_irqsave(&d40c->lock, flags); res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ); - if (res == 0) { - if (chan_is_logical(d40c)) { - d40_config_set_event(d40c, false); - /* Resume the other logical channels if any */ - if (d40_chan_has_events(d40c)) - res = d40_channel_execute_command(d40c, - D40_DMA_RUN); - } - } + pm_runtime_mark_last_busy(d40c->base->dev); pm_runtime_put_autosuspend(d40c->base->dev); spin_unlock_irqrestore(&d40c->lock, flags); @@ -1174,45 +1289,17 @@ static int d40_resume(struct d40_chan *d40c) spin_lock_irqsave(&d40c->lock, flags); pm_runtime_get_sync(d40c->base->dev); - if (d40c->base->rev == 0) - if (chan_is_logical(d40c)) { - res = d40_channel_execute_command(d40c, - D40_DMA_SUSPEND_REQ); - goto no_suspend; - } /* If bytes left to transfer or linked tx resume job */ - if (d40_residue(d40c) || d40_tx_is_linked(d40c)) { - - if (chan_is_logical(d40c)) - d40_config_set_event(d40c, true); - + if (d40_residue(d40c) || d40_tx_is_linked(d40c)) res = d40_channel_execute_command(d40c, D40_DMA_RUN); - } -no_suspend: pm_runtime_mark_last_busy(d40c->base->dev); pm_runtime_put_autosuspend(d40c->base->dev); spin_unlock_irqrestore(&d40c->lock, flags); return res; } -static int d40_terminate_all(struct d40_chan *chan) -{ - unsigned long flags; - int ret = 0; - - ret = d40_pause(chan); - if (!ret && chan_is_physical(chan)) - ret = d40_channel_execute_command(chan, D40_DMA_STOP); - - spin_lock_irqsave(&chan->lock, flags); - d40_term_all(chan); - spin_unlock_irqrestore(&chan->lock, flags); - - return ret; -} - static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx) { struct d40_chan *d40c = container_of(tx->chan, @@ -1232,20 +1319,6 @@ static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx) static int d40_start(struct d40_chan *d40c) { - if (d40c->base->rev == 0) { - int err; - - if (chan_is_logical(d40c)) { - err = d40_channel_execute_command(d40c, - D40_DMA_SUSPEND_REQ); - if (err) - return err; - } - } - - if (chan_is_logical(d40c)) - d40_config_set_event(d40c, true); - return d40_channel_execute_command(d40c, D40_DMA_RUN); } @@ -1258,10 +1331,10 @@ static struct d40_desc *d40_queue_start(struct d40_chan *d40c) d40d = d40_first_queued(d40c); if (d40d != NULL) { - if (!d40c->busy) + if (!d40c->busy) { d40c->busy = true; - - pm_runtime_get_sync(d40c->base->dev); + pm_runtime_get_sync(d40c->base->dev); + } /* Remove from queue */ d40_desc_remove(d40d); @@ -1388,8 +1461,8 @@ static void dma_tasklet(unsigned long data) return; - err: - /* Rescue manoeuvre if receiving double interrupts */ +err: + /* Rescue manouver if receiving double interrupts */ if (d40c->pending_tx > 0) d40c->pending_tx--; spin_unlock_irqrestore(&d40c->lock, flags); @@ -1770,7 +1843,6 @@ static int d40_config_memcpy(struct d40_chan *d40c) return 0; } - static int d40_free_dma(struct d40_chan *d40c) { @@ -1806,43 +1878,18 @@ static int d40_free_dma(struct d40_chan *d40c) } pm_runtime_get_sync(d40c->base->dev); - res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ); + res = d40_channel_execute_command(d40c, D40_DMA_STOP); if (res) { - chan_err(d40c, "suspend failed\n"); + chan_err(d40c, "stop failed\n"); goto out; } - if (chan_is_logical(d40c)) { - /* Release logical channel, deactivate the event line */ + d40_alloc_mask_free(phy, is_src, chan_is_logical(d40c) ? event : 0); - d40_config_set_event(d40c, false); + if (chan_is_logical(d40c)) d40c->base->lookup_log_chans[d40c->log_num] = NULL; - - /* - * Check if there are more logical allocation - * on this phy channel. - */ - if (!d40_alloc_mask_free(phy, is_src, event)) { - /* Resume the other logical channels if any */ - if (d40_chan_has_events(d40c)) { - res = d40_channel_execute_command(d40c, - D40_DMA_RUN); - if (res) - chan_err(d40c, - "Executing RUN command\n"); - } - goto out; - } - } else { - (void) d40_alloc_mask_free(phy, is_src, 0); - } - - /* Release physical channel */ - res = d40_channel_execute_command(d40c, D40_DMA_STOP); - if (res) { - chan_err(d40c, "Failed to stop channel\n"); - goto out; - } + else + d40c->base->lookup_phy_chans[phy->num] = NULL; if (d40c->busy) { pm_runtime_mark_last_busy(d40c->base->dev); @@ -1852,7 +1899,6 @@ static int d40_free_dma(struct d40_chan *d40c) d40c->busy = false; d40c->phy_chan = NULL; d40c->configured = false; - d40c->base->lookup_phy_chans[phy->num] = NULL; out: pm_runtime_mark_last_busy(d40c->base->dev); @@ -2070,7 +2116,7 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, if (sg_next(&sg_src[sg_len - 1]) == sg_src) desc->cyclic = true; - if (direction != DMA_NONE) { + if (direction != DMA_TRANS_NONE) { dma_addr_t dev_addr = d40_get_dev_addr(chan, direction); if (direction == DMA_DEV_TO_MEM) @@ -2371,6 +2417,31 @@ static void d40_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&d40c->lock, flags); } +static void d40_terminate_all(struct dma_chan *chan) +{ + unsigned long flags; + struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); + int ret; + + spin_lock_irqsave(&d40c->lock, flags); + + pm_runtime_get_sync(d40c->base->dev); + ret = d40_channel_execute_command(d40c, D40_DMA_STOP); + if (ret) + chan_err(d40c, "Failed to stop channel\n"); + + d40_term_all(d40c); + pm_runtime_mark_last_busy(d40c->base->dev); + pm_runtime_put_autosuspend(d40c->base->dev); + if (d40c->busy) { + pm_runtime_mark_last_busy(d40c->base->dev); + pm_runtime_put_autosuspend(d40c->base->dev); + } + d40c->busy = false; + + spin_unlock_irqrestore(&d40c->lock, flags); +} + static int dma40_config_to_halfchannel(struct d40_chan *d40c, struct stedma40_half_channel_info *info, @@ -2551,7 +2622,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, switch (cmd) { case DMA_TERMINATE_ALL: - return d40_terminate_all(d40c); + d40_terminate_all(chan); + return 0; case DMA_PAUSE: return d40_pause(d40c); case DMA_RESUME: @@ -2908,6 +2980,12 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) dev_info(&pdev->dev, "hardware revision: %d @ 0x%x\n", rev, res->start); + if (rev < 2) { + d40_err(&pdev->dev, "hardware revision: %d is not supported", + rev); + goto failure; + } + plat_data = pdev->dev.platform_data; /* Count the number of logical channels in use */ @@ -2998,6 +3076,7 @@ failure: if (base) { kfree(base->lcla_pool.alloc_map); + kfree(base->reg_val_backup_chan); kfree(base->lookup_log_chans); kfree(base->lookup_phy_chans); kfree(base->phy_res); diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h index 8d3d490968a3..51e8e5396e9b 100644 --- a/drivers/dma/ste_dma40_ll.h +++ b/drivers/dma/ste_dma40_ll.h @@ -62,8 +62,6 @@ #define D40_SREG_ELEM_LOG_LIDX_MASK (0xFF << D40_SREG_ELEM_LOG_LIDX_POS) /* Link register */ -#define D40_DEACTIVATE_EVENTLINE 0x0 -#define D40_ACTIVATE_EVENTLINE 0x1 #define D40_EVENTLINE_POS(i) (2 * i) #define D40_EVENTLINE_MASK(i) (0x3 << D40_EVENTLINE_POS(i)) diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index 36e1486eb9aa..d0c372e30de4 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -754,9 +754,7 @@ static int __init mce_amd_init(void) if (c->x86_vendor != X86_VENDOR_AMD) return 0; - if ((c->x86 < 0xf || c->x86 > 0x12) && - (c->x86 != 0x14 || c->x86_model > 0xf) && - (c->x86 != 0x15 || c->x86_model > 0xf)) + if (c->x86 < 0xf || c->x86 > 0x15) return 0; fam_ops = kzalloc(sizeof(struct amd_decoder_ops), GFP_KERNEL); @@ -797,7 +795,7 @@ static int __init mce_amd_init(void) break; default: - printk(KERN_WARNING "Huh? What family is that: %d?!\n", c->x86); + printk(KERN_WARNING "Huh? What family is it: 0x%x?!\n", c->x86); kfree(fam_ops); return -EINVAL; } diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c index 1d5cf06f6c6b..e99d00976189 100644 --- a/drivers/edac/tile_edac.c +++ b/drivers/edac/tile_edac.c @@ -145,7 +145,11 @@ static int __devinit tile_edac_mc_probe(struct platform_device *pdev) mci->edac_ctl_cap = EDAC_FLAG_SECDED; mci->mod_name = DRV_NAME; +#ifdef __tilegx__ + mci->ctl_name = "TILEGx_Memory_Controller"; +#else mci->ctl_name = "TILEPro_Memory_Controller"; +#endif mci->dev_name = dev_name(&pdev->dev); mci->edac_check = tile_edac_check; diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index d25599f2a3f8..47408e802ab6 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -191,6 +191,190 @@ utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len) } } +static bool +validate_device_path(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) +{ + struct efi_generic_dev_path *node; + int offset = 0; + + node = (struct efi_generic_dev_path *)buffer; + + if (len < sizeof(*node)) + return false; + + while (offset <= len - sizeof(*node) && + node->length >= sizeof(*node) && + node->length <= len - offset) { + offset += node->length; + + if ((node->type == EFI_DEV_END_PATH || + node->type == EFI_DEV_END_PATH2) && + node->sub_type == EFI_DEV_END_ENTIRE) + return true; + + node = (struct efi_generic_dev_path *)(buffer + offset); + } + + /* + * If we're here then either node->length pointed past the end + * of the buffer or we reached the end of the buffer without + * finding a device path end node. + */ + return false; +} + +static bool +validate_boot_order(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) +{ + /* An array of 16-bit integers */ + if ((len % 2) != 0) + return false; + + return true; +} + +static bool +validate_load_option(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) +{ + u16 filepathlength; + int i, desclength = 0, namelen; + + namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName)); + + /* Either "Boot" or "Driver" followed by four digits of hex */ + for (i = match; i < match+4; i++) { + if (var->VariableName[i] > 127 || + hex_to_bin(var->VariableName[i] & 0xff) < 0) + return true; + } + + /* Reject it if there's 4 digits of hex and then further content */ + if (namelen > match + 4) + return false; + + /* A valid entry must be at least 8 bytes */ + if (len < 8) + return false; + + filepathlength = buffer[4] | buffer[5] << 8; + + /* + * There's no stored length for the description, so it has to be + * found by hand + */ + desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; + + /* Each boot entry must have a descriptor */ + if (!desclength) + return false; + + /* + * If the sum of the length of the description, the claimed filepath + * length and the original header are greater than the length of the + * variable, it's malformed + */ + if ((desclength + filepathlength + 6) > len) + return false; + + /* + * And, finally, check the filepath + */ + return validate_device_path(var, match, buffer + desclength + 6, + filepathlength); +} + +static bool +validate_uint16(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) +{ + /* A single 16-bit integer */ + if (len != 2) + return false; + + return true; +} + +static bool +validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) +{ + int i; + + for (i = 0; i < len; i++) { + if (buffer[i] > 127) + return false; + + if (buffer[i] == 0) + return true; + } + + return false; +} + +struct variable_validate { + char *name; + bool (*validate)(struct efi_variable *var, int match, u8 *data, + unsigned long len); +}; + +static const struct variable_validate variable_validate[] = { + { "BootNext", validate_uint16 }, + { "BootOrder", validate_boot_order }, + { "DriverOrder", validate_boot_order }, + { "Boot*", validate_load_option }, + { "Driver*", validate_load_option }, + { "ConIn", validate_device_path }, + { "ConInDev", validate_device_path }, + { "ConOut", validate_device_path }, + { "ConOutDev", validate_device_path }, + { "ErrOut", validate_device_path }, + { "ErrOutDev", validate_device_path }, + { "Timeout", validate_uint16 }, + { "Lang", validate_ascii_string }, + { "PlatformLang", validate_ascii_string }, + { "", NULL }, +}; + +static bool +validate_var(struct efi_variable *var, u8 *data, unsigned long len) +{ + int i; + u16 *unicode_name = var->VariableName; + + for (i = 0; variable_validate[i].validate != NULL; i++) { + const char *name = variable_validate[i].name; + int match; + + for (match = 0; ; match++) { + char c = name[match]; + u16 u = unicode_name[match]; + + /* All special variables are plain ascii */ + if (u > 127) + return true; + + /* Wildcard in the matching name means we've matched */ + if (c == '*') + return variable_validate[i].validate(var, + match, data, len); + + /* Case sensitive match */ + if (c != u) + break; + + /* Reached the end of the string while matching */ + if (!c) + return variable_validate[i].validate(var, + match, data, len); + } + } + + return true; +} + static efi_status_t get_var_data_locked(struct efivars *efivars, struct efi_variable *var) { @@ -324,6 +508,12 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) return -EINVAL; } + if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 || + validate_var(new_var, new_var->Data, new_var->DataSize) == false) { + printk(KERN_ERR "efivars: Malformed variable content\n"); + return -EINVAL; + } + spin_lock(&efivars->lock); status = efivars->ops->set_variable(new_var->VariableName, &new_var->VendorGuid, @@ -626,6 +816,12 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 || + validate_var(new_var, new_var->Data, new_var->DataSize) == false) { + printk(KERN_ERR "efivars: Malformed variable content\n"); + return -EINVAL; + } + spin_lock(&efivars->lock); /* diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index edadbdad31d0..e03653d69357 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -430,7 +430,7 @@ config GPIO_ML_IOH config GPIO_SODAVILLE bool "Intel Sodaville GPIO support" - depends on X86 && PCI && OF && BROKEN + depends on X86 && PCI && OF select GPIO_GENERIC select GENERIC_IRQ_CHIP help diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index 9ad1703d1408..ae5d7f12ce66 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -252,7 +252,7 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid) if (ret < 0) memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat)); - for (bank = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); + for (bank = 0, bit = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); bank++, bit = 0) { pending = dev->irq_stat[bank] & dev->irq_mask[bank]; diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 1adc2ec1e383..4461540653a8 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -965,18 +965,15 @@ static void omap_gpio_mod_init(struct gpio_bank *bank) } _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->irqenable_inv); - _gpio_rmw(base, bank->regs->irqstatus, l, - bank->regs->irqenable_inv == false); - _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->debounce_en != 0); - _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->ctrl != 0); + _gpio_rmw(base, bank->regs->irqstatus, l, !bank->regs->irqenable_inv); if (bank->regs->debounce_en) - _gpio_rmw(base, bank->regs->debounce_en, 0, 1); + __raw_writel(0, base + bank->regs->debounce_en); /* Save OE default value (0xffffffff) in the context */ bank->context.oe = __raw_readl(bank->base + bank->regs->direction); /* Initialize interface clk ungated, module enabled */ if (bank->regs->ctrl) - _gpio_rmw(base, bank->regs->ctrl, 0, 1); + __raw_writel(0, base + bank->regs->ctrl); } static __devinit void diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index e8729cc2ba2b..2cd958e0b822 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -230,16 +230,12 @@ static void pch_gpio_setup(struct pch_gpio *chip) static int pch_irq_type(struct irq_data *d, unsigned int type) { - u32 im; - u32 __iomem *im_reg; - u32 ien; - u32 im_pos; - int ch; - unsigned long flags; - u32 val; - int irq = d->irq; struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct pch_gpio *chip = gc->private; + u32 im, im_pos, val; + u32 __iomem *im_reg; + unsigned long flags; + int ch, irq = d->irq; ch = irq - chip->irq_base; if (irq <= chip->irq_base + 7) { @@ -270,30 +266,22 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) case IRQ_TYPE_LEVEL_LOW: val = PCH_LEVEL_L; break; - case IRQ_TYPE_PROBE: - goto end; default: - dev_warn(chip->dev, "%s: unknown type(%dd)", - __func__, type); - goto end; + goto unlock; } /* Set interrupt mode */ im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); iowrite32(im | (val << (im_pos * 4)), im_reg); - /* iclr */ - iowrite32(BIT(ch), &chip->reg->iclr); + /* And the handler */ + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + __irq_set_handler_locked(d->irq, handle_level_irq); + else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + __irq_set_handler_locked(d->irq, handle_edge_irq); - /* IMASKCLR */ - iowrite32(BIT(ch), &chip->reg->imaskclr); - - /* Enable interrupt */ - ien = ioread32(&chip->reg->ien); - iowrite32(ien | BIT(ch), &chip->reg->ien); -end: +unlock: spin_unlock_irqrestore(&chip->spinlock, flags); - return 0; } @@ -313,18 +301,24 @@ static void pch_irq_mask(struct irq_data *d) iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imask); } +static void pch_irq_ack(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct pch_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->iclr); +} + static irqreturn_t pch_gpio_handler(int irq, void *dev_id) { struct pch_gpio *chip = dev_id; u32 reg_val = ioread32(&chip->reg->istatus); - int i; - int ret = IRQ_NONE; + int i, ret = IRQ_NONE; for (i = 0; i < gpio_pins[chip->ioh]; i++) { if (reg_val & BIT(i)) { dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n", __func__, i, irq, reg_val); - iowrite32(BIT(i), &chip->reg->iclr); generic_handle_irq(chip->irq_base + i); ret = IRQ_HANDLED; } @@ -343,6 +337,7 @@ static __devinit void pch_gpio_alloc_generic_chip(struct pch_gpio *chip, gc->private = chip; ct = gc->chip_types; + ct->chip.irq_ack = pch_irq_ack; ct->chip.irq_mask = pch_irq_mask; ct->chip.irq_unmask = pch_irq_unmask; ct->chip.irq_set_type = pch_irq_type; @@ -357,6 +352,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, s32 ret; struct pch_gpio *chip; int irq_base; + u32 msk; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) @@ -408,8 +404,13 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, } chip->irq_base = irq_base; + /* Mask all interrupts, but enable them */ + msk = (1 << gpio_pins[chip->ioh]) - 1; + iowrite32(msk, &chip->reg->imask); + iowrite32(msk, &chip->reg->ien); + ret = request_irq(pdev->irq, pch_gpio_handler, - IRQF_SHARED, KBUILD_MODNAME, chip); + IRQF_SHARED, KBUILD_MODNAME, chip); if (ret != 0) { dev_err(&pdev->dev, "%s request_irq failed\n", __func__); @@ -418,8 +419,6 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); - /* Initialize interrupt ien register */ - iowrite32(0, &chip->reg->ien); end: return 0; diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 5689ce62fd81..fc3ace3fd4cb 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -64,6 +64,7 @@ struct pxa_gpio_chip { unsigned long irq_mask; unsigned long irq_edge_rise; unsigned long irq_edge_fall; + int (*set_wake)(unsigned int gpio, unsigned int on); #ifdef CONFIG_PM unsigned long saved_gplr; @@ -269,7 +270,8 @@ static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) (value ? GPSR_OFFSET : GPCR_OFFSET)); } -static int __devinit pxa_init_gpio_chip(int gpio_end) +static int __devinit pxa_init_gpio_chip(int gpio_end, + int (*set_wake)(unsigned int, unsigned int)) { int i, gpio, nbanks = gpio_to_bank(gpio_end) + 1; struct pxa_gpio_chip *chips; @@ -285,6 +287,7 @@ static int __devinit pxa_init_gpio_chip(int gpio_end) sprintf(chips[i].label, "gpio-%d", i); chips[i].regbase = gpio_reg_base + BANK_OFF(i); + chips[i].set_wake = set_wake; c->base = gpio; c->label = chips[i].label; @@ -412,6 +415,17 @@ static void pxa_mask_muxed_gpio(struct irq_data *d) writel_relaxed(gfer, c->regbase + GFER_OFFSET); } +static int pxa_gpio_set_wake(struct irq_data *d, unsigned int on) +{ + int gpio = pxa_irq_to_gpio(d->irq); + struct pxa_gpio_chip *c = gpio_to_pxachip(gpio); + + if (c->set_wake) + return c->set_wake(gpio, on); + else + return 0; +} + static void pxa_unmask_muxed_gpio(struct irq_data *d) { int gpio = pxa_irq_to_gpio(d->irq); @@ -427,6 +441,7 @@ static struct irq_chip pxa_muxed_gpio_chip = { .irq_mask = pxa_mask_muxed_gpio, .irq_unmask = pxa_unmask_muxed_gpio, .irq_set_type = pxa_gpio_irq_type, + .irq_set_wake = pxa_gpio_set_wake, }; static int pxa_gpio_nums(void) @@ -471,6 +486,7 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev) struct pxa_gpio_chip *c; struct resource *res; struct clk *clk; + struct pxa_gpio_platform_data *info; int gpio, irq, ret; int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0; @@ -516,7 +532,8 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev) } /* Initialize GPIO chips */ - pxa_init_gpio_chip(pxa_last_gpio); + info = dev_get_platdata(&pdev->dev); + pxa_init_gpio_chip(pxa_last_gpio, info ? info->gpio_set_wake : NULL); /* clear all GPIO edge detects */ for_each_gpio_chip(gpio, c) { diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index 46277877b7ec..e991d9171961 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -452,12 +452,14 @@ static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = { }; #endif +#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_EXYNOS5) static struct samsung_gpio_cfg exynos_gpio_cfg = { .set_pull = exynos_gpio_setpull, .get_pull = exynos_gpio_getpull, .set_config = samsung_gpio_setcfg_4bit, .get_config = samsung_gpio_getcfg_4bit, }; +#endif #if defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450) static struct samsung_gpio_cfg s5p64x0_gpio_cfg_rbank = { @@ -2123,8 +2125,8 @@ static struct samsung_gpio_chip s5pv210_gpios_4bit[] = { * uses the above macro and depends on the banks being listed in order here. */ -static struct samsung_gpio_chip exynos4_gpios_1[] = { #ifdef CONFIG_ARCH_EXYNOS4 +static struct samsung_gpio_chip exynos4_gpios_1[] = { { .chip = { .base = EXYNOS4_GPA0(0), @@ -2222,11 +2224,11 @@ static struct samsung_gpio_chip exynos4_gpios_1[] = { .label = "GPF3", }, }, -#endif }; +#endif -static struct samsung_gpio_chip exynos4_gpios_2[] = { #ifdef CONFIG_ARCH_EXYNOS4 +static struct samsung_gpio_chip exynos4_gpios_2[] = { { .chip = { .base = EXYNOS4_GPJ0(0), @@ -2367,11 +2369,11 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { .to_irq = samsung_gpiolib_to_irq, }, }, -#endif }; +#endif -static struct samsung_gpio_chip exynos4_gpios_3[] = { #ifdef CONFIG_ARCH_EXYNOS4 +static struct samsung_gpio_chip exynos4_gpios_3[] = { { .chip = { .base = EXYNOS4_GPZ(0), @@ -2379,11 +2381,11 @@ static struct samsung_gpio_chip exynos4_gpios_3[] = { .label = "GPZ", }, }, -#endif }; +#endif -static struct samsung_gpio_chip exynos5_gpios_1[] = { #ifdef CONFIG_ARCH_EXYNOS5 +static struct samsung_gpio_chip exynos5_gpios_1[] = { { .chip = { .base = EXYNOS5_GPA0(0), @@ -2541,11 +2543,11 @@ static struct samsung_gpio_chip exynos5_gpios_1[] = { .to_irq = samsung_gpiolib_to_irq, }, }, -#endif }; +#endif -static struct samsung_gpio_chip exynos5_gpios_2[] = { #ifdef CONFIG_ARCH_EXYNOS5 +static struct samsung_gpio_chip exynos5_gpios_2[] = { { .chip = { .base = EXYNOS5_GPE0(0), @@ -2602,11 +2604,11 @@ static struct samsung_gpio_chip exynos5_gpios_2[] = { }, }, -#endif }; +#endif -static struct samsung_gpio_chip exynos5_gpios_3[] = { #ifdef CONFIG_ARCH_EXYNOS5 +static struct samsung_gpio_chip exynos5_gpios_3[] = { { .chip = { .base = EXYNOS5_GPV0(0), @@ -2638,11 +2640,11 @@ static struct samsung_gpio_chip exynos5_gpios_3[] = { .label = "GPV4", }, }, -#endif }; +#endif -static struct samsung_gpio_chip exynos5_gpios_4[] = { #ifdef CONFIG_ARCH_EXYNOS5 +static struct samsung_gpio_chip exynos5_gpios_4[] = { { .chip = { .base = EXYNOS5_GPZ(0), @@ -2650,8 +2652,8 @@ static struct samsung_gpio_chip exynos5_gpios_4[] = { .label = "GPZ", }, }, -#endif }; +#endif #if defined(CONFIG_ARCH_EXYNOS) && defined(CONFIG_OF) @@ -2719,7 +2721,9 @@ static __init int samsung_gpiolib_init(void) { struct samsung_gpio_chip *chip; int i, nr_chips; +#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS5250) void __iomem *gpio_base1, *gpio_base2, *gpio_base3, *gpio_base4; +#endif int group = 0; samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs)); @@ -2971,6 +2975,7 @@ static __init int samsung_gpiolib_init(void) return 0; +#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS5250) err_ioremap4: iounmap(gpio_base3); err_ioremap3: @@ -2979,6 +2984,7 @@ err_ioremap2: iounmap(gpio_base1); err_ioremap1: return -ENOMEM; +#endif } core_initcall(samsung_gpiolib_init); diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c index 9ba15d31d242..031e5d24837d 100644 --- a/drivers/gpio/gpio-sodaville.c +++ b/drivers/gpio/gpio-sodaville.c @@ -41,7 +41,7 @@ struct sdv_gpio_chip_data { int irq_base; void __iomem *gpio_pub_base; - struct irq_domain id; + struct irq_domain *id; struct irq_chip_generic *gc; struct bgpio_chip bgpio; }; @@ -51,10 +51,9 @@ static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct sdv_gpio_chip_data *sd = gc->private; void __iomem *type_reg; - u32 irq_offs = d->irq - sd->irq_base; u32 reg; - if (irq_offs < 8) + if (d->hwirq < 8) type_reg = sd->gpio_pub_base + GPIT1R0; else type_reg = sd->gpio_pub_base + GPIT1R1; @@ -63,11 +62,11 @@ static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type) switch (type) { case IRQ_TYPE_LEVEL_HIGH: - reg &= ~BIT(4 * (irq_offs % 8)); + reg &= ~BIT(4 * (d->hwirq % 8)); break; case IRQ_TYPE_LEVEL_LOW: - reg |= BIT(4 * (irq_offs % 8)); + reg |= BIT(4 * (d->hwirq % 8)); break; default: @@ -91,7 +90,7 @@ static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data) u32 irq_bit = __fls(irq_stat); irq_stat &= ~BIT(irq_bit); - generic_handle_irq(sd->irq_base + irq_bit); + generic_handle_irq(irq_find_mapping(sd->id, irq_bit)); } return IRQ_HANDLED; @@ -127,7 +126,7 @@ static int sdv_xlate(struct irq_domain *h, struct device_node *node, } static struct irq_domain_ops irq_domain_sdv_ops = { - .dt_translate = sdv_xlate, + .xlate = sdv_xlate, }; static __devinit int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, @@ -149,10 +148,6 @@ static __devinit int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, if (ret) goto out_free_desc; - sd->id.irq_base = sd->irq_base; - sd->id.of_node = of_node_get(pdev->dev.of_node); - sd->id.ops = &irq_domain_sdv_ops; - /* * This gpio irq controller latches level irqs. Testing shows that if * we unmask & ACK the IRQ before the source of the interrupt is gone @@ -179,7 +174,10 @@ static __devinit int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); - irq_domain_add(&sd->id); + sd->id = irq_domain_add_legacy(pdev->dev.of_node, SDV_NUM_PUB_GPIOS, + sd->irq_base, 0, &irq_domain_sdv_ops, sd); + if (!sd->id) + goto out_free_irq; return 0; out_free_irq: free_irq(pdev->irq, sd); @@ -260,7 +258,6 @@ static void sdv_gpio_remove(struct pci_dev *pdev) { struct sdv_gpio_chip_data *sd = pci_get_drvdata(pdev); - irq_domain_del(&sd->id); free_irq(pdev->irq, sd); irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS); diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 32de6707e3c4..12f349b3830d 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -22,7 +22,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/gpio.h> -#include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/module.h> #include <linux/irqdomain.h> @@ -37,7 +37,8 @@ #define GPIO_PORT(x) (((x) >> 3) & 0x3) #define GPIO_BIT(x) ((x) & 0x7) -#define GPIO_REG(x) (GPIO_BANK(x) * 0x80 + GPIO_PORT(x) * 4) +#define GPIO_REG(x) (GPIO_BANK(x) * tegra_gpio_bank_stride + \ + GPIO_PORT(x) * 4) #define GPIO_CNF(x) (GPIO_REG(x) + 0x00) #define GPIO_OE(x) (GPIO_REG(x) + 0x10) @@ -48,12 +49,12 @@ #define GPIO_INT_LVL(x) (GPIO_REG(x) + 0x60) #define GPIO_INT_CLR(x) (GPIO_REG(x) + 0x70) -#define GPIO_MSK_CNF(x) (GPIO_REG(x) + 0x800) -#define GPIO_MSK_OE(x) (GPIO_REG(x) + 0x810) -#define GPIO_MSK_OUT(x) (GPIO_REG(x) + 0X820) -#define GPIO_MSK_INT_STA(x) (GPIO_REG(x) + 0x840) -#define GPIO_MSK_INT_ENB(x) (GPIO_REG(x) + 0x850) -#define GPIO_MSK_INT_LVL(x) (GPIO_REG(x) + 0x860) +#define GPIO_MSK_CNF(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x00) +#define GPIO_MSK_OE(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x10) +#define GPIO_MSK_OUT(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0X20) +#define GPIO_MSK_INT_STA(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x40) +#define GPIO_MSK_INT_ENB(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x50) +#define GPIO_MSK_INT_LVL(x) (GPIO_REG(x) + tegra_gpio_upper_offset + 0x60) #define GPIO_INT_LVL_MASK 0x010101 #define GPIO_INT_LVL_EDGE_RISING 0x000101 @@ -78,6 +79,8 @@ struct tegra_gpio_bank { static struct irq_domain *irq_domain; static void __iomem *regs; static u32 tegra_gpio_bank_count; +static u32 tegra_gpio_bank_stride; +static u32 tegra_gpio_upper_offset; static struct tegra_gpio_bank *tegra_gpio_banks; static inline void tegra_gpio_writel(u32 val, u32 reg) @@ -333,6 +336,26 @@ static struct irq_chip tegra_gpio_irq_chip = { #endif }; +struct tegra_gpio_soc_config { + u32 bank_stride; + u32 upper_offset; +}; + +static struct tegra_gpio_soc_config tegra20_gpio_config = { + .bank_stride = 0x80, + .upper_offset = 0x800, +}; + +static struct tegra_gpio_soc_config tegra30_gpio_config = { + .bank_stride = 0x100, + .upper_offset = 0x80, +}; + +static struct of_device_id tegra_gpio_of_match[] __devinitdata = { + { .compatible = "nvidia,tegra30-gpio", .data = &tegra30_gpio_config }, + { .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config }, + { }, +}; /* This lock class tells lockdep that GPIO irqs are in a different * category than their parents, so it won't report false recursion. @@ -341,6 +364,8 @@ static struct lock_class_key gpio_lock_class; static int __devinit tegra_gpio_probe(struct platform_device *pdev) { + const struct of_device_id *match; + struct tegra_gpio_soc_config *config; int irq_base; struct resource *res; struct tegra_gpio_bank *bank; @@ -348,6 +373,15 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) int i; int j; + match = of_match_device(tegra_gpio_of_match, &pdev->dev); + if (match) + config = (struct tegra_gpio_soc_config *)match->data; + else + config = &tegra20_gpio_config; + + tegra_gpio_bank_stride = config->bank_stride; + tegra_gpio_upper_offset = config->upper_offset; + for (;;) { res = platform_get_resource(pdev, IORESOURCE_IRQ, tegra_gpio_bank_count); if (!res) @@ -402,7 +436,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) return -ENODEV; } - for (i = 0; i < 7; i++) { + for (i = 0; i < tegra_gpio_bank_count; i++) { for (j = 0; j < 4; j++) { int gpio = tegra_gpio_compose(i, j, 0); tegra_gpio_writel(0x00, GPIO_INT_ENB(gpio)); @@ -441,11 +475,6 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) return 0; } -static struct of_device_id tegra_gpio_of_match[] __devinitdata = { - { .compatible = "nvidia,tegra20-gpio", }, - { }, -}; - static struct platform_driver tegra_gpio_driver = { .driver = { .name = "tegra-gpio", @@ -485,7 +514,7 @@ static int dbg_gpio_show(struct seq_file *s, void *unused) int i; int j; - for (i = 0; i < 7; i++) { + for (i = 0; i < tegra_gpio_bank_count; i++) { for (j = 0; j < 4; j++) { int gpio = tegra_gpio_compose(i, j, 0); seq_printf(s, diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index cc1148837e24..e354bc0b052a 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -9,6 +9,7 @@ menuconfig DRM depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU select I2C select I2C_ALGOBIT + select DMA_SHARED_BUFFER help Kernel-level support for the Direct Rendering Infrastructure (DRI) introduced in XFree86 4.0. If you say Y here, you need to select diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a858532806ae..c20da5bda355 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -12,7 +12,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ - drm_trace_points.o drm_global.o + drm_trace_points.o drm_global.o drm_prime.o drm-$(CONFIG_COMPAT) += drm_ioc32.o diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 30372f7b2d45..348b367debeb 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1510,8 +1510,8 @@ int drm_freebufs(struct drm_device *dev, void *data, * \param arg pointer to a drm_buf_map structure. * \return zero on success or a negative number on failure. * - * Maps the AGP, SG or PCI buffer region with do_mmap(), and copies information - * about each buffer into user space. For PCI buffers, it calls do_mmap() with + * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information + * about each buffer into user space. For PCI buffers, it calls vm_mmap() with * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls * drm_mmap_dma(). */ @@ -1553,18 +1553,14 @@ int drm_mapbufs(struct drm_device *dev, void *data, retcode = -EINVAL; goto done; } - down_write(¤t->mm->mmap_sem); - virtual = do_mmap(file_priv->filp, 0, map->size, + virtual = vm_mmap(file_priv->filp, 0, map->size, PROT_READ | PROT_WRITE, MAP_SHARED, token); - up_write(¤t->mm->mmap_sem); } else { - down_write(¤t->mm->mmap_sem); - virtual = do_mmap(file_priv->filp, 0, dma->byte_count, + virtual = vm_mmap(file_priv->filp, 0, dma->byte_count, PROT_READ | PROT_WRITE, MAP_SHARED, 0); - up_write(¤t->mm->mmap_sem); } if (virtual > -1024UL) { /* Real error */ diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d3aaeb6ae236..c79870a75c2f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3335,10 +3335,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, ret = crtc->funcs->page_flip(crtc, fb, e); if (ret) { - spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(e); + if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); + } } out: diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 0b65fbc8a630..6116e3b75393 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -136,6 +136,10 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + + DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 7740dd26f007..a0d6e894d97c 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -559,9 +559,13 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, return -EINVAL; /* Need to resize the fb object !!! */ - if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) { + if (var->bits_per_pixel > fb->bits_per_pixel || + var->xres > fb->width || var->yres > fb->height || + var->xres_virtual > fb->width || var->yres_virtual > fb->height) { DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb " - "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel, + "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", + var->xres, var->yres, var->bits_per_pixel, + var->xres_virtual, var->yres_virtual, fb->width, fb->height, fb->bits_per_pixel); return -EINVAL; } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 7348a3dab250..123de28f94ef 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -271,6 +271,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp, if (dev->driver->driver_features & DRIVER_GEM) drm_gem_open(dev, priv); + if (drm_core_check_feature(dev, DRIVER_PRIME)) + drm_prime_init_file_private(&priv->prime); + if (dev->driver->open) { ret = dev->driver->open(dev, priv); if (ret < 0) @@ -504,12 +507,12 @@ int drm_release(struct inode *inode, struct file *filp) drm_events_release(file_priv); - if (dev->driver->driver_features & DRIVER_GEM) - drm_gem_release(dev, file_priv); - if (dev->driver->driver_features & DRIVER_MODESET) drm_fb_release(file_priv); + if (dev->driver->driver_features & DRIVER_GEM) + drm_gem_release(dev, file_priv); + mutex_lock(&dev->ctxlist_mutex); if (!list_empty(&dev->ctxlist)) { struct drm_ctx_list *pos, *n; @@ -571,6 +574,10 @@ int drm_release(struct inode *inode, struct file *filp) if (dev->driver->postclose) dev->driver->postclose(dev, file_priv); + + if (drm_core_check_feature(dev, DRIVER_PRIME)) + drm_prime_destroy_file_private(&file_priv->prime); + kfree(file_priv); /* ======================================================== diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 0ef358e53245..83114b5e3cee 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -35,6 +35,7 @@ #include <linux/mman.h> #include <linux/pagemap.h> #include <linux/shmem_fs.h> +#include <linux/dma-buf.h> #include "drmP.h" /** @file drm_gem.c @@ -232,6 +233,10 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle) idr_remove(&filp->object_idr, handle); spin_unlock(&filp->table_lock); + if (obj->import_attach) + drm_prime_remove_imported_buf_handle(&filp->prime, + obj->import_attach->dmabuf); + if (dev->driver->gem_close_object) dev->driver->gem_close_object(obj, filp); drm_gem_object_handle_unreference_unlocked(obj); @@ -527,6 +532,10 @@ drm_gem_object_release_handle(int id, void *ptr, void *data) struct drm_gem_object *obj = ptr; struct drm_device *dev = obj->dev; + if (obj->import_attach) + drm_prime_remove_imported_buf_handle(&file_priv->prime, + obj->import_attach->dmabuf); + if (dev->driver->gem_close_object) dev->driver->gem_close_object(obj, file_priv); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c new file mode 100644 index 000000000000..1bdf2b54eaf6 --- /dev/null +++ b/drivers/gpu/drm/drm_prime.c @@ -0,0 +1,304 @@ +/* + * Copyright © 2012 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * Rob Clark <rob.clark@linaro.org> + * + */ + +#include <linux/export.h> +#include <linux/dma-buf.h> +#include "drmP.h" + +/* + * DMA-BUF/GEM Object references and lifetime overview: + * + * On the export the dma_buf holds a reference to the exporting GEM + * object. It takes this reference in handle_to_fd_ioctl, when it + * first calls .prime_export and stores the exporting GEM object in + * the dma_buf priv. This reference is released when the dma_buf + * object goes away in the driver .release function. + * + * On the import the importing GEM object holds a reference to the + * dma_buf (which in turn holds a ref to the exporting GEM object). + * It takes that reference in the fd_to_handle ioctl. + * It calls dma_buf_get, creates an attachment to it and stores the + * attachment in the GEM object. When this attachment is destroyed + * when the imported object is destroyed, we remove the attachment + * and drop the reference to the dma_buf. + * + * Thus the chain of references always flows in one direction + * (avoiding loops): importing_gem -> dmabuf -> exporting_gem + * + * Self-importing: if userspace is using PRIME as a replacement for flink + * then it will get a fd->handle request for a GEM object that it created. + * Drivers should detect this situation and return back the gem object + * from the dma-buf private. + */ + +struct drm_prime_member { + struct list_head entry; + struct dma_buf *dma_buf; + uint32_t handle; +}; + +int drm_gem_prime_handle_to_fd(struct drm_device *dev, + struct drm_file *file_priv, uint32_t handle, uint32_t flags, + int *prime_fd) +{ + struct drm_gem_object *obj; + void *buf; + + obj = drm_gem_object_lookup(dev, file_priv, handle); + if (!obj) + return -ENOENT; + + mutex_lock(&file_priv->prime.lock); + /* re-export the original imported object */ + if (obj->import_attach) { + get_dma_buf(obj->import_attach->dmabuf); + *prime_fd = dma_buf_fd(obj->import_attach->dmabuf, flags); + drm_gem_object_unreference_unlocked(obj); + mutex_unlock(&file_priv->prime.lock); + return 0; + } + + if (obj->export_dma_buf) { + get_dma_buf(obj->export_dma_buf); + *prime_fd = dma_buf_fd(obj->export_dma_buf, flags); + drm_gem_object_unreference_unlocked(obj); + } else { + buf = dev->driver->gem_prime_export(dev, obj, flags); + if (IS_ERR(buf)) { + /* normally the created dma-buf takes ownership of the ref, + * but if that fails then drop the ref + */ + drm_gem_object_unreference_unlocked(obj); + mutex_unlock(&file_priv->prime.lock); + return PTR_ERR(buf); + } + obj->export_dma_buf = buf; + *prime_fd = dma_buf_fd(buf, flags); + } + mutex_unlock(&file_priv->prime.lock); + return 0; +} +EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); + +int drm_gem_prime_fd_to_handle(struct drm_device *dev, + struct drm_file *file_priv, int prime_fd, uint32_t *handle) +{ + struct dma_buf *dma_buf; + struct drm_gem_object *obj; + int ret; + + dma_buf = dma_buf_get(prime_fd); + if (IS_ERR(dma_buf)) + return PTR_ERR(dma_buf); + + mutex_lock(&file_priv->prime.lock); + + ret = drm_prime_lookup_imported_buf_handle(&file_priv->prime, + dma_buf, handle); + if (!ret) { + ret = 0; + goto out_put; + } + + /* never seen this one, need to import */ + obj = dev->driver->gem_prime_import(dev, dma_buf); + if (IS_ERR(obj)) { + ret = PTR_ERR(obj); + goto out_put; + } + + ret = drm_gem_handle_create(file_priv, obj, handle); + drm_gem_object_unreference_unlocked(obj); + if (ret) + goto out_put; + + ret = drm_prime_add_imported_buf_handle(&file_priv->prime, + dma_buf, *handle); + if (ret) + goto fail; + + mutex_unlock(&file_priv->prime.lock); + return 0; + +fail: + /* hmm, if driver attached, we are relying on the free-object path + * to detach.. which seems ok.. + */ + drm_gem_object_handle_unreference_unlocked(obj); +out_put: + dma_buf_put(dma_buf); + mutex_unlock(&file_priv->prime.lock); + return ret; +} +EXPORT_SYMBOL(drm_gem_prime_fd_to_handle); + +int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_prime_handle *args = data; + uint32_t flags; + + if (!drm_core_check_feature(dev, DRIVER_PRIME)) + return -EINVAL; + + if (!dev->driver->prime_handle_to_fd) + return -ENOSYS; + + /* check flags are valid */ + if (args->flags & ~DRM_CLOEXEC) + return -EINVAL; + + /* we only want to pass DRM_CLOEXEC which is == O_CLOEXEC */ + flags = args->flags & DRM_CLOEXEC; + + return dev->driver->prime_handle_to_fd(dev, file_priv, + args->handle, flags, &args->fd); +} + +int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_prime_handle *args = data; + + if (!drm_core_check_feature(dev, DRIVER_PRIME)) + return -EINVAL; + + if (!dev->driver->prime_fd_to_handle) + return -ENOSYS; + + return dev->driver->prime_fd_to_handle(dev, file_priv, + args->fd, &args->handle); +} + +/* + * drm_prime_pages_to_sg + * + * this helper creates an sg table object from a set of pages + * the driver is responsible for mapping the pages into the + * importers address space + */ +struct sg_table *drm_prime_pages_to_sg(struct page **pages, int nr_pages) +{ + struct sg_table *sg = NULL; + struct scatterlist *iter; + int i; + int ret; + + sg = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!sg) + goto out; + + ret = sg_alloc_table(sg, nr_pages, GFP_KERNEL); + if (ret) + goto out; + + for_each_sg(sg->sgl, iter, nr_pages, i) + sg_set_page(iter, pages[i], PAGE_SIZE, 0); + + return sg; +out: + kfree(sg); + return NULL; +} +EXPORT_SYMBOL(drm_prime_pages_to_sg); + +/* helper function to cleanup a GEM/prime object */ +void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg) +{ + struct dma_buf_attachment *attach; + struct dma_buf *dma_buf; + attach = obj->import_attach; + if (sg) + dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); + dma_buf = attach->dmabuf; + dma_buf_detach(attach->dmabuf, attach); + /* remove the reference */ + dma_buf_put(dma_buf); +} +EXPORT_SYMBOL(drm_prime_gem_destroy); + +void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv) +{ + INIT_LIST_HEAD(&prime_fpriv->head); + mutex_init(&prime_fpriv->lock); +} +EXPORT_SYMBOL(drm_prime_init_file_private); + +void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv) +{ + struct drm_prime_member *member, *safe; + list_for_each_entry_safe(member, safe, &prime_fpriv->head, entry) { + list_del(&member->entry); + kfree(member); + } +} +EXPORT_SYMBOL(drm_prime_destroy_file_private); + +int drm_prime_add_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle) +{ + struct drm_prime_member *member; + + member = kmalloc(sizeof(*member), GFP_KERNEL); + if (!member) + return -ENOMEM; + + member->dma_buf = dma_buf; + member->handle = handle; + list_add(&member->entry, &prime_fpriv->head); + return 0; +} +EXPORT_SYMBOL(drm_prime_add_imported_buf_handle); + +int drm_prime_lookup_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle) +{ + struct drm_prime_member *member; + + list_for_each_entry(member, &prime_fpriv->head, entry) { + if (member->dma_buf == dma_buf) { + *handle = member->handle; + return 0; + } + } + return -ENOENT; +} +EXPORT_SYMBOL(drm_prime_lookup_imported_buf_handle); + +void drm_prime_remove_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) +{ + struct drm_prime_member *member, *safe; + + mutex_lock(&prime_fpriv->lock); + list_for_each_entry_safe(member, safe, &prime_fpriv->head, entry) { + if (member->dma_buf == dma_buf) { + list_del(&member->entry); + kfree(member); + } + } + mutex_unlock(&prime_fpriv->lock); +} +EXPORT_SYMBOL(drm_prime_remove_imported_buf_handle); diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index c8c83dad2ce1..37c9a523dd1c 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -1,6 +1,6 @@ #include "drmP.h" #include <linux/usb.h> -#include <linux/export.h> +#include <linux/module.h> int drm_get_usb_dev(struct usb_interface *interface, const struct usb_device_id *id, @@ -114,3 +114,7 @@ void drm_usb_exit(struct drm_driver *driver, usb_deregister(udriver); } EXPORT_SYMBOL(drm_usb_exit); + +MODULE_AUTHOR("David Airlie"); +MODULE_DESCRIPTION("USB DRM support"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 4a3a5f72ed4a..de8d2090bce3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -34,14 +34,14 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, unsigned int flags, struct exynos_drm_gem_buf *buf) { - dma_addr_t start_addr, end_addr; + dma_addr_t start_addr; unsigned int npages, page_size, i = 0; struct scatterlist *sgl; int ret = 0; DRM_DEBUG_KMS("%s\n", __FILE__); - if (flags & EXYNOS_BO_NONCONTIG) { + if (IS_NONCONTIG_BUFFER(flags)) { DRM_DEBUG_KMS("not support allocation type.\n"); return -EINVAL; } @@ -52,13 +52,13 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, } if (buf->size >= SZ_1M) { - npages = (buf->size >> SECTION_SHIFT) + 1; + npages = buf->size >> SECTION_SHIFT; page_size = SECTION_SIZE; } else if (buf->size >= SZ_64K) { - npages = (buf->size >> 16) + 1; + npages = buf->size >> 16; page_size = SZ_64K; } else { - npages = (buf->size >> PAGE_SHIFT) + 1; + npages = buf->size >> PAGE_SHIFT; page_size = PAGE_SIZE; } @@ -76,26 +76,13 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, return -ENOMEM; } - buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size, - &buf->dma_addr, GFP_KERNEL); - if (!buf->kvaddr) { - DRM_ERROR("failed to allocate buffer.\n"); - ret = -ENOMEM; - goto err1; - } - - start_addr = buf->dma_addr; - end_addr = buf->dma_addr + buf->size; - - buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL); - if (!buf->pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; - goto err2; - } - - start_addr = buf->dma_addr; - end_addr = buf->dma_addr + buf->size; + buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size, + &buf->dma_addr, GFP_KERNEL); + if (!buf->kvaddr) { + DRM_ERROR("failed to allocate buffer.\n"); + ret = -ENOMEM; + goto err1; + } buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL); if (!buf->pages) { @@ -105,23 +92,17 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, } sgl = buf->sgt->sgl; + start_addr = buf->dma_addr; while (i < npages) { buf->pages[i] = phys_to_page(start_addr); sg_set_page(sgl, buf->pages[i], page_size, 0); sg_dma_address(sgl) = start_addr; start_addr += page_size; - if (end_addr - start_addr < page_size) - break; sgl = sg_next(sgl); i++; } - buf->pages[i] = phys_to_page(start_addr); - - sgl = sg_next(sgl); - sg_set_page(sgl, buf->pages[i+1], end_addr - start_addr, 0); - DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", (unsigned long)buf->kvaddr, (unsigned long)buf->dma_addr, @@ -150,7 +131,7 @@ static void lowlevel_buffer_deallocate(struct drm_device *dev, * non-continuous memory would be released by exynos * gem framework. */ - if (flags & EXYNOS_BO_NONCONTIG) { + if (IS_NONCONTIG_BUFFER(flags)) { DRM_DEBUG_KMS("not support allocation type.\n"); return; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 411832e8e17a..eaf630dc5dba 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -54,16 +54,18 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev, * * P.S. note that this driver is considered for modularization. */ - ret = subdrv->probe(dev, subdrv->manager.dev); + ret = subdrv->probe(dev, subdrv->dev); if (ret) return ret; } - if (subdrv->is_local) + if (!subdrv->manager) return 0; + subdrv->manager->dev = subdrv->dev; + /* create and initialize a encoder for this sub driver. */ - encoder = exynos_drm_encoder_create(dev, &subdrv->manager, + encoder = exynos_drm_encoder_create(dev, subdrv->manager, (1 << MAX_CRTC) - 1); if (!encoder) { DRM_ERROR("failed to create encoder\n"); @@ -186,7 +188,7 @@ int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file) list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { if (subdrv->open) { - ret = subdrv->open(dev, subdrv->manager.dev, file); + ret = subdrv->open(dev, subdrv->dev, file); if (ret) goto err; } @@ -197,7 +199,7 @@ int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file) err: list_for_each_entry_reverse(subdrv, &subdrv->list, list) { if (subdrv->close) - subdrv->close(dev, subdrv->manager.dev, file); + subdrv->close(dev, subdrv->dev, file); } return ret; } @@ -209,7 +211,7 @@ void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file) list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { if (subdrv->close) - subdrv->close(dev, subdrv->manager.dev, file); + subdrv->close(dev, subdrv->dev, file); } } EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index fbd0a232c93d..1d814175cd49 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -225,24 +225,25 @@ struct exynos_drm_private { * Exynos drm sub driver structure. * * @list: sub driver has its own list object to register to exynos drm driver. + * @dev: pointer to device object for subdrv device driver. * @drm_dev: pointer to drm_device and this pointer would be set * when sub driver calls exynos_drm_subdrv_register(). - * @is_local: appear encoder and connector disrelated device. + * @manager: subdrv has its own manager to control a hardware appropriately + * and we can access a hardware drawing on this manager. * @probe: this callback would be called by exynos drm driver after * subdrv is registered to it. * @remove: this callback is used to release resources created * by probe callback. * @open: this would be called with drm device file open. * @close: this would be called with drm device file close. - * @manager: subdrv has its own manager to control a hardware appropriately - * and we can access a hardware drawing on this manager. * @encoder: encoder object owned by this sub driver. * @connector: connector object owned by this sub driver. */ struct exynos_drm_subdrv { struct list_head list; + struct device *dev; struct drm_device *drm_dev; - bool is_local; + struct exynos_drm_manager *manager; int (*probe)(struct drm_device *drm_dev, struct device *dev); void (*remove)(struct drm_device *dev); @@ -251,7 +252,6 @@ struct exynos_drm_subdrv { void (*close)(struct drm_device *drm_dev, struct device *dev, struct drm_file *file); - struct exynos_drm_manager manager; struct drm_encoder *encoder; struct drm_connector *connector; }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index ecb6db229700..29fdbfeb43cb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -172,7 +172,7 @@ static void fimd_dpms(struct device *subdrv_dev, int mode) static void fimd_apply(struct device *subdrv_dev) { struct fimd_context *ctx = get_fimd_context(subdrv_dev); - struct exynos_drm_manager *mgr = &ctx->subdrv.manager; + struct exynos_drm_manager *mgr = ctx->subdrv.manager; struct exynos_drm_manager_ops *mgr_ops = mgr->ops; struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops; struct fimd_win_data *win_data; @@ -577,6 +577,13 @@ static struct exynos_drm_overlay_ops fimd_overlay_ops = { .disable = fimd_win_disable, }; +static struct exynos_drm_manager fimd_manager = { + .pipe = -1, + .ops = &fimd_manager_ops, + .overlay_ops = &fimd_overlay_ops, + .display_ops = &fimd_display_ops, +}; + static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc) { struct exynos_drm_private *dev_priv = drm_dev->dev_private; @@ -628,7 +635,7 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) struct fimd_context *ctx = (struct fimd_context *)dev_id; struct exynos_drm_subdrv *subdrv = &ctx->subdrv; struct drm_device *drm_dev = subdrv->drm_dev; - struct exynos_drm_manager *manager = &subdrv->manager; + struct exynos_drm_manager *manager = subdrv->manager; u32 val; val = readl(ctx->regs + VIDINTCON1); @@ -744,7 +751,7 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) static int fimd_power_on(struct fimd_context *ctx, bool enable) { struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct device *dev = subdrv->manager.dev; + struct device *dev = subdrv->dev; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -867,13 +874,10 @@ static int __devinit fimd_probe(struct platform_device *pdev) subdrv = &ctx->subdrv; + subdrv->dev = dev; + subdrv->manager = &fimd_manager; subdrv->probe = fimd_subdrv_probe; subdrv->remove = fimd_subdrv_remove; - subdrv->manager.pipe = -1; - subdrv->manager.ops = &fimd_manager_ops; - subdrv->manager.overlay_ops = &fimd_overlay_ops; - subdrv->manager.display_ops = &fimd_display_ops; - subdrv->manager.dev = dev; mutex_init(&ctx->lock); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index fa1aa94a3d8e..1dffa8359f88 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -56,9 +56,28 @@ static unsigned int convert_to_vm_err_msg(int msg) return out_msg; } -static unsigned int mask_gem_flags(unsigned int flags) +static int check_gem_flags(unsigned int flags) { - return flags &= EXYNOS_BO_NONCONTIG; + if (flags & ~(EXYNOS_BO_MASK)) { + DRM_ERROR("invalid flags.\n"); + return -EINVAL; + } + + return 0; +} + +static unsigned long roundup_gem_size(unsigned long size, unsigned int flags) +{ + if (!IS_NONCONTIG_BUFFER(flags)) { + if (size >= SZ_1M) + return roundup(size, SECTION_SIZE); + else if (size >= SZ_64K) + return roundup(size, SZ_64K); + else + goto out; + } +out: + return roundup(size, PAGE_SIZE); } static struct page **exynos_gem_get_pages(struct drm_gem_object *obj, @@ -130,22 +149,12 @@ static int exynos_drm_gem_map_pages(struct drm_gem_object *obj, unsigned long pfn; if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - unsigned long usize = buf->size; - if (!buf->pages) return -EINTR; - while (usize > 0) { - pfn = page_to_pfn(buf->pages[page_offset++]); - vm_insert_mixed(vma, f_vaddr, pfn); - f_vaddr += PAGE_SIZE; - usize -= PAGE_SIZE; - } - - return 0; - } - - pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset; + pfn = page_to_pfn(buf->pages[page_offset++]); + } else + pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset; return vm_insert_mixed(vma, f_vaddr, pfn); } @@ -319,10 +328,17 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, struct exynos_drm_gem_buf *buf; int ret; - size = roundup(size, PAGE_SIZE); - DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size); + if (!size) { + DRM_ERROR("invalid size.\n"); + return ERR_PTR(-EINVAL); + } + + size = roundup_gem_size(size, flags); + DRM_DEBUG_KMS("%s\n", __FILE__); - flags = mask_gem_flags(flags); + ret = check_gem_flags(flags); + if (ret) + return ERR_PTR(ret); buf = exynos_drm_init_buf(dev, size); if (!buf) @@ -331,7 +347,7 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, exynos_gem_obj = exynos_drm_gem_init(dev, size); if (!exynos_gem_obj) { ret = -ENOMEM; - goto err; + goto err_fini_buf; } exynos_gem_obj->buffer = buf; @@ -347,18 +363,19 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, ret = exynos_drm_gem_get_pages(&exynos_gem_obj->base); if (ret < 0) { drm_gem_object_release(&exynos_gem_obj->base); - goto err; + goto err_fini_buf; } } else { ret = exynos_drm_alloc_buf(dev, buf, flags); if (ret < 0) { drm_gem_object_release(&exynos_gem_obj->base); - goto err; + goto err_fini_buf; } } return exynos_gem_obj; -err: + +err_fini_buf: exynos_drm_fini_buf(dev, buf); return ERR_PTR(ret); } @@ -497,6 +514,8 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, if (!buffer->pages) return -EINVAL; + vma->vm_flags |= VM_MIXEDMAP; + do { ret = vm_insert_page(vma, uaddr, buffer->pages[i++]); if (ret) { @@ -554,10 +573,8 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, obj->filp->f_op = &exynos_drm_gem_fops; obj->filp->private_data = obj; - down_write(¤t->mm->mmap_sem); - addr = do_mmap(obj->filp, 0, args->size, + addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE, MAP_SHARED, 0); - up_write(¤t->mm->mmap_sem); drm_gem_object_unreference_unlocked(obj); @@ -685,7 +702,6 @@ int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct drm_gem_object *obj = vma->vm_private_data; - struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); struct drm_device *dev = obj->dev; unsigned long f_vaddr; pgoff_t page_offset; @@ -697,21 +713,10 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) mutex_lock(&dev->struct_mutex); - /* - * allocate all pages as desired size if user wants to allocate - * physically non-continuous memory. - */ - if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { - ret = exynos_drm_gem_get_pages(obj); - if (ret < 0) - goto err; - } - ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset); if (ret < 0) DRM_ERROR("failed to map pages.\n"); -err: mutex_unlock(&dev->struct_mutex); return convert_to_vm_err_msg(ret); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index e40fbad8b705..4ed842039505 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -29,6 +29,8 @@ #define to_exynos_gem_obj(x) container_of(x,\ struct exynos_drm_gem_obj, base) +#define IS_NONCONTIG_BUFFER(f) (f & EXYNOS_BO_NONCONTIG) + /* * exynos drm gem buffer structure. * diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index 14eb26b0ba1c..3424463676e0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -30,9 +30,8 @@ struct drm_hdmi_context, subdrv); /* these callback points shoud be set by specific drivers. */ -static struct exynos_hdmi_display_ops *hdmi_display_ops; -static struct exynos_hdmi_manager_ops *hdmi_manager_ops; -static struct exynos_hdmi_overlay_ops *hdmi_overlay_ops; +static struct exynos_hdmi_ops *hdmi_ops; +static struct exynos_mixer_ops *mixer_ops; struct drm_hdmi_context { struct exynos_drm_subdrv subdrv; @@ -40,31 +39,20 @@ struct drm_hdmi_context { struct exynos_drm_hdmi_context *mixer_ctx; }; -void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops - *display_ops) +void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops) { DRM_DEBUG_KMS("%s\n", __FILE__); - if (display_ops) - hdmi_display_ops = display_ops; + if (ops) + hdmi_ops = ops; } -void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops - *manager_ops) +void exynos_mixer_ops_register(struct exynos_mixer_ops *ops) { DRM_DEBUG_KMS("%s\n", __FILE__); - if (manager_ops) - hdmi_manager_ops = manager_ops; -} - -void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops - *overlay_ops) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (overlay_ops) - hdmi_overlay_ops = overlay_ops; + if (ops) + mixer_ops = ops; } static bool drm_hdmi_is_connected(struct device *dev) @@ -73,8 +61,8 @@ static bool drm_hdmi_is_connected(struct device *dev) DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_display_ops && hdmi_display_ops->is_connected) - return hdmi_display_ops->is_connected(ctx->hdmi_ctx->ctx); + if (hdmi_ops && hdmi_ops->is_connected) + return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx); return false; } @@ -86,9 +74,9 @@ static int drm_hdmi_get_edid(struct device *dev, DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_display_ops && hdmi_display_ops->get_edid) - return hdmi_display_ops->get_edid(ctx->hdmi_ctx->ctx, - connector, edid, len); + if (hdmi_ops && hdmi_ops->get_edid) + return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector, edid, + len); return 0; } @@ -99,9 +87,8 @@ static int drm_hdmi_check_timing(struct device *dev, void *timing) DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_display_ops && hdmi_display_ops->check_timing) - return hdmi_display_ops->check_timing(ctx->hdmi_ctx->ctx, - timing); + if (hdmi_ops && hdmi_ops->check_timing) + return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing); return 0; } @@ -112,8 +99,8 @@ static int drm_hdmi_power_on(struct device *dev, int mode) DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_display_ops && hdmi_display_ops->power_on) - return hdmi_display_ops->power_on(ctx->hdmi_ctx->ctx, mode); + if (hdmi_ops && hdmi_ops->power_on) + return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode); return 0; } @@ -130,13 +117,13 @@ static int drm_hdmi_enable_vblank(struct device *subdrv_dev) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct exynos_drm_manager *manager = &subdrv->manager; + struct exynos_drm_manager *manager = subdrv->manager; DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_overlay_ops && hdmi_overlay_ops->enable_vblank) - return hdmi_overlay_ops->enable_vblank(ctx->mixer_ctx->ctx, - manager->pipe); + if (mixer_ops && mixer_ops->enable_vblank) + return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx, + manager->pipe); return 0; } @@ -147,8 +134,8 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev) DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_overlay_ops && hdmi_overlay_ops->disable_vblank) - return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx); + if (mixer_ops && mixer_ops->disable_vblank) + return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); } static void drm_hdmi_mode_fixup(struct device *subdrv_dev, @@ -160,9 +147,9 @@ static void drm_hdmi_mode_fixup(struct device *subdrv_dev, DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_manager_ops && hdmi_manager_ops->mode_fixup) - hdmi_manager_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector, - mode, adjusted_mode); + if (hdmi_ops && hdmi_ops->mode_fixup) + hdmi_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector, mode, + adjusted_mode); } static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) @@ -171,8 +158,8 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_manager_ops && hdmi_manager_ops->mode_set) - hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode); + if (hdmi_ops && hdmi_ops->mode_set) + hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode); } static void drm_hdmi_get_max_resol(struct device *subdrv_dev, @@ -182,9 +169,8 @@ static void drm_hdmi_get_max_resol(struct device *subdrv_dev, DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_manager_ops && hdmi_manager_ops->get_max_resol) - hdmi_manager_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, - height); + if (hdmi_ops && hdmi_ops->get_max_resol) + hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height); } static void drm_hdmi_commit(struct device *subdrv_dev) @@ -193,8 +179,8 @@ static void drm_hdmi_commit(struct device *subdrv_dev) DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_manager_ops && hdmi_manager_ops->commit) - hdmi_manager_ops->commit(ctx->hdmi_ctx->ctx); + if (hdmi_ops && hdmi_ops->commit) + hdmi_ops->commit(ctx->hdmi_ctx->ctx); } static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) @@ -209,8 +195,8 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - if (hdmi_manager_ops && hdmi_manager_ops->disable) - hdmi_manager_ops->disable(ctx->hdmi_ctx->ctx); + if (hdmi_ops && hdmi_ops->disable) + hdmi_ops->disable(ctx->hdmi_ctx->ctx); break; default: DRM_DEBUG_KMS("unkown dps mode: %d\n", mode); @@ -235,8 +221,8 @@ static void drm_mixer_mode_set(struct device *subdrv_dev, DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_overlay_ops && hdmi_overlay_ops->win_mode_set) - hdmi_overlay_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay); + if (mixer_ops && mixer_ops->win_mode_set) + mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay); } static void drm_mixer_commit(struct device *subdrv_dev, int zpos) @@ -245,8 +231,8 @@ static void drm_mixer_commit(struct device *subdrv_dev, int zpos) DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_overlay_ops && hdmi_overlay_ops->win_commit) - hdmi_overlay_ops->win_commit(ctx->mixer_ctx->ctx, zpos); + if (mixer_ops && mixer_ops->win_commit) + mixer_ops->win_commit(ctx->mixer_ctx->ctx, zpos); } static void drm_mixer_disable(struct device *subdrv_dev, int zpos) @@ -255,8 +241,8 @@ static void drm_mixer_disable(struct device *subdrv_dev, int zpos) DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_overlay_ops && hdmi_overlay_ops->win_disable) - hdmi_overlay_ops->win_disable(ctx->mixer_ctx->ctx, zpos); + if (mixer_ops && mixer_ops->win_disable) + mixer_ops->win_disable(ctx->mixer_ctx->ctx, zpos); } static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { @@ -265,6 +251,12 @@ static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { .disable = drm_mixer_disable, }; +static struct exynos_drm_manager hdmi_manager = { + .pipe = -1, + .ops = &drm_hdmi_manager_ops, + .overlay_ops = &drm_hdmi_overlay_ops, + .display_ops = &drm_hdmi_display_ops, +}; static int hdmi_subdrv_probe(struct drm_device *drm_dev, struct device *dev) @@ -332,12 +324,9 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) subdrv = &ctx->subdrv; + subdrv->dev = dev; + subdrv->manager = &hdmi_manager; subdrv->probe = hdmi_subdrv_probe; - subdrv->manager.pipe = -1; - subdrv->manager.ops = &drm_hdmi_manager_ops; - subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops; - subdrv->manager.display_ops = &drm_hdmi_display_ops; - subdrv->manager.dev = dev; platform_set_drvdata(pdev, subdrv); diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h index 44497cfb6c74..f3ae192c8dcf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h @@ -38,15 +38,15 @@ struct exynos_drm_hdmi_context { void *ctx; }; -struct exynos_hdmi_display_ops { +struct exynos_hdmi_ops { + /* display */ bool (*is_connected)(void *ctx); int (*get_edid)(void *ctx, struct drm_connector *connector, u8 *edid, int len); int (*check_timing)(void *ctx, void *timing); int (*power_on)(void *ctx, int mode); -}; -struct exynos_hdmi_manager_ops { + /* manager */ void (*mode_fixup)(void *ctx, struct drm_connector *connector, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); @@ -57,22 +57,17 @@ struct exynos_hdmi_manager_ops { void (*disable)(void *ctx); }; -struct exynos_hdmi_overlay_ops { +struct exynos_mixer_ops { + /* manager */ int (*enable_vblank)(void *ctx, int pipe); void (*disable_vblank)(void *ctx); + + /* overlay */ void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay); void (*win_commit)(void *ctx, int zpos); void (*win_disable)(void *ctx, int zpos); }; -extern struct platform_driver hdmi_driver; -extern struct platform_driver mixer_driver; - -void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops - *display_ops); -void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops - *manager_ops); -void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops - *overlay_ops); - +void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops); +void exynos_mixer_ops_register(struct exynos_mixer_ops *ops); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index c277a3a445f5..f92fe4c6174a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -24,6 +24,10 @@ struct exynos_plane { static const uint32_t formats[] = { DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_NV12, + DRM_FORMAT_NV12M, + DRM_FORMAT_NV12MT, }; static int diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 8e1339f9fe1f..7b9c153dceb6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -199,7 +199,7 @@ static void vidi_dpms(struct device *subdrv_dev, int mode) static void vidi_apply(struct device *subdrv_dev) { struct vidi_context *ctx = get_vidi_context(subdrv_dev); - struct exynos_drm_manager *mgr = &ctx->subdrv.manager; + struct exynos_drm_manager *mgr = ctx->subdrv.manager; struct exynos_drm_manager_ops *mgr_ops = mgr->ops; struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops; struct vidi_win_data *win_data; @@ -374,6 +374,13 @@ static struct exynos_drm_overlay_ops vidi_overlay_ops = { .disable = vidi_win_disable, }; +static struct exynos_drm_manager vidi_manager = { + .pipe = -1, + .ops = &vidi_manager_ops, + .overlay_ops = &vidi_overlay_ops, + .display_ops = &vidi_display_ops, +}; + static void vidi_finish_pageflip(struct drm_device *drm_dev, int crtc) { struct exynos_drm_private *dev_priv = drm_dev->dev_private; @@ -425,7 +432,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work) struct vidi_context *ctx = container_of(work, struct vidi_context, work); struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct exynos_drm_manager *manager = &subdrv->manager; + struct exynos_drm_manager *manager = subdrv->manager; if (manager->pipe < 0) return; @@ -471,7 +478,7 @@ static void vidi_subdrv_remove(struct drm_device *drm_dev) static int vidi_power_on(struct vidi_context *ctx, bool enable) { struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct device *dev = subdrv->manager.dev; + struct device *dev = subdrv->dev; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -611,13 +618,10 @@ static int __devinit vidi_probe(struct platform_device *pdev) ctx->raw_edid = (struct edid *)fake_edid_info; subdrv = &ctx->subdrv; + subdrv->dev = dev; + subdrv->manager = &vidi_manager; subdrv->probe = vidi_subdrv_probe; subdrv->remove = vidi_subdrv_remove; - subdrv->manager.pipe = -1; - subdrv->manager.ops = &vidi_manager_ops; - subdrv->manager.overlay_ops = &vidi_overlay_ops; - subdrv->manager.display_ops = &vidi_display_ops; - subdrv->manager.dev = dev; mutex_init(&ctx->lock); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 575a8cbd3533..b00353876458 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -40,7 +40,6 @@ #include "exynos_hdmi.h" -#define HDMI_OVERLAY_NUMBER 3 #define MAX_WIDTH 1920 #define MAX_HEIGHT 1080 #define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev)) @@ -1194,7 +1193,7 @@ static int hdmi_conf_index(struct hdmi_context *hdata, static bool hdmi_is_connected(void *ctx) { - struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct hdmi_context *hdata = ctx; u32 val = hdmi_reg_read(hdata, HDMI_HPD_STATUS); if (val) @@ -1207,7 +1206,7 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector, u8 *edid, int len) { struct edid *raw_edid; - struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct hdmi_context *hdata = ctx; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -1275,7 +1274,7 @@ static int hdmi_v14_check_timing(struct fb_videomode *check_timing) static int hdmi_check_timing(void *ctx, void *timing) { - struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct hdmi_context *hdata = ctx; struct fb_videomode *check_timing = timing; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -1312,13 +1311,6 @@ static int hdmi_display_power_on(void *ctx, int mode) return 0; } -static struct exynos_hdmi_display_ops display_ops = { - .is_connected = hdmi_is_connected, - .get_edid = hdmi_get_edid, - .check_timing = hdmi_check_timing, - .power_on = hdmi_display_power_on, -}; - static void hdmi_set_acr(u32 freq, u8 *acr) { u32 n, cts; @@ -1914,7 +1906,7 @@ static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector, struct drm_display_mode *adjusted_mode) { struct drm_display_mode *m; - struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct hdmi_context *hdata = ctx; int index; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -1951,7 +1943,7 @@ static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector, static void hdmi_mode_set(void *ctx, void *mode) { - struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct hdmi_context *hdata = ctx; int conf_idx; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -1974,7 +1966,7 @@ static void hdmi_get_max_resol(void *ctx, unsigned int *width, static void hdmi_commit(void *ctx) { - struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct hdmi_context *hdata = ctx; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -1985,7 +1977,7 @@ static void hdmi_commit(void *ctx) static void hdmi_disable(void *ctx) { - struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct hdmi_context *hdata = ctx; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -1996,7 +1988,14 @@ static void hdmi_disable(void *ctx) } } -static struct exynos_hdmi_manager_ops manager_ops = { +static struct exynos_hdmi_ops hdmi_ops = { + /* display */ + .is_connected = hdmi_is_connected, + .get_edid = hdmi_get_edid, + .check_timing = hdmi_check_timing, + .power_on = hdmi_display_power_on, + + /* manager */ .mode_fixup = hdmi_mode_fixup, .mode_set = hdmi_mode_set, .get_max_resol = hdmi_get_max_resol, @@ -2020,7 +2019,7 @@ static void hdmi_hotplug_func(struct work_struct *work) static irqreturn_t hdmi_irq_handler(int irq, void *arg) { struct exynos_drm_hdmi_context *ctx = arg; - struct hdmi_context *hdata = (struct hdmi_context *)ctx->ctx; + struct hdmi_context *hdata = ctx->ctx; u32 intc_flag; intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG); @@ -2173,7 +2172,7 @@ static int hdmi_runtime_suspend(struct device *dev) DRM_DEBUG_KMS("%s\n", __func__); - hdmi_resource_poweroff((struct hdmi_context *)ctx->ctx); + hdmi_resource_poweroff(ctx->ctx); return 0; } @@ -2184,7 +2183,7 @@ static int hdmi_runtime_resume(struct device *dev) DRM_DEBUG_KMS("%s\n", __func__); - hdmi_resource_poweron((struct hdmi_context *)ctx->ctx); + hdmi_resource_poweron(ctx->ctx); return 0; } @@ -2322,8 +2321,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) hdata->irq = res->start; /* register specific callbacks to common hdmi. */ - exynos_drm_display_ops_register(&display_ops); - exynos_drm_manager_ops_register(&manager_ops); + exynos_hdmi_ops_register(&hdmi_ops); hdmi_resource_poweron(hdata); @@ -2351,7 +2349,7 @@ err_data: static int __devexit hdmi_remove(struct platform_device *pdev) { struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev); - struct hdmi_context *hdata = (struct hdmi_context *)ctx->ctx; + struct hdmi_context *hdata = ctx->ctx; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 4d5f41e19527..e15438c01129 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -37,7 +37,8 @@ #include "exynos_drm_drv.h" #include "exynos_drm_hdmi.h" -#define HDMI_OVERLAY_NUMBER 3 +#define MIXER_WIN_NR 3 +#define MIXER_DEFAULT_WIN 0 #define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev)) @@ -75,16 +76,12 @@ struct mixer_resources { }; struct mixer_context { - struct fb_videomode *default_timing; - unsigned int default_win; - unsigned int default_bpp; unsigned int irq; int pipe; bool interlace; - bool vp_enabled; struct mixer_resources mixer_res; - struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER]; + struct hdmi_win_data win_data[MIXER_WIN_NR]; }; static const u8 filter_y_horiz_tap8[] = { @@ -643,9 +640,9 @@ static void mixer_win_mode_set(void *ctx, win = overlay->zpos; if (win == DEFAULT_ZPOS) - win = mixer_ctx->default_win; + win = MIXER_DEFAULT_WIN; - if (win < 0 || win > HDMI_OVERLAY_NUMBER) { + if (win < 0 || win > MIXER_WIN_NR) { DRM_ERROR("overlay plane[%d] is wrong\n", win); return; } @@ -683,9 +680,9 @@ static void mixer_win_commit(void *ctx, int zpos) DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); if (win == DEFAULT_ZPOS) - win = mixer_ctx->default_win; + win = MIXER_DEFAULT_WIN; - if (win < 0 || win > HDMI_OVERLAY_NUMBER) { + if (win < 0 || win > MIXER_WIN_NR) { DRM_ERROR("overlay plane[%d] is wrong\n", win); return; } @@ -706,9 +703,9 @@ static void mixer_win_disable(void *ctx, int zpos) DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); if (win == DEFAULT_ZPOS) - win = mixer_ctx->default_win; + win = MIXER_DEFAULT_WIN; - if (win < 0 || win > HDMI_OVERLAY_NUMBER) { + if (win < 0 || win > MIXER_WIN_NR) { DRM_ERROR("overlay plane[%d] is wrong\n", win); return; } @@ -722,9 +719,12 @@ static void mixer_win_disable(void *ctx, int zpos) spin_unlock_irqrestore(&res->reg_slock, flags); } -static struct exynos_hdmi_overlay_ops overlay_ops = { +static struct exynos_mixer_ops mixer_ops = { + /* manager */ .enable_vblank = mixer_enable_vblank, .disable_vblank = mixer_disable_vblank, + + /* overlay */ .win_mode_set = mixer_win_mode_set, .win_commit = mixer_win_commit, .win_disable = mixer_win_disable, @@ -771,8 +771,7 @@ static void mixer_finish_pageflip(struct drm_device *drm_dev, int crtc) static irqreturn_t mixer_irq_handler(int irq, void *arg) { struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg; - struct mixer_context *ctx = - (struct mixer_context *)drm_hdmi_ctx->ctx; + struct mixer_context *ctx = drm_hdmi_ctx->ctx; struct mixer_resources *res = &ctx->mixer_res; u32 val, val_base; @@ -902,7 +901,7 @@ static int mixer_runtime_resume(struct device *dev) DRM_DEBUG_KMS("resume - start\n"); - mixer_resource_poweron((struct mixer_context *)ctx->ctx); + mixer_resource_poweron(ctx->ctx); return 0; } @@ -913,7 +912,7 @@ static int mixer_runtime_suspend(struct device *dev) DRM_DEBUG_KMS("suspend - start\n"); - mixer_resource_poweroff((struct mixer_context *)ctx->ctx); + mixer_resource_poweroff(ctx->ctx); return 0; } @@ -926,8 +925,7 @@ static const struct dev_pm_ops mixer_pm_ops = { static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, struct platform_device *pdev) { - struct mixer_context *mixer_ctx = - (struct mixer_context *)ctx->ctx; + struct mixer_context *mixer_ctx = ctx->ctx; struct device *dev = &pdev->dev; struct mixer_resources *mixer_res = &mixer_ctx->mixer_res; struct resource *res; @@ -1076,7 +1074,7 @@ static int __devinit mixer_probe(struct platform_device *pdev) goto fail; /* register specific callback point to common hdmi. */ - exynos_drm_overlay_ops_register(&overlay_ops); + exynos_mixer_ops_register(&mixer_ops); mixer_resource_poweron(ctx); @@ -1093,7 +1091,7 @@ static int mixer_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; struct exynos_drm_hdmi_context *drm_hdmi_ctx = platform_get_drvdata(pdev); - struct mixer_context *ctx = (struct mixer_context *)drm_hdmi_ctx->ctx; + struct mixer_context *ctx = drm_hdmi_ctx->ctx; dev_info(dev, "remove successful\n"); diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.h b/drivers/gpu/drm/gma500/mdfld_dsi_output.h index 21071cef92a4..36eb0744841c 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.h +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.h @@ -29,7 +29,6 @@ #define __MDFLD_DSI_OUTPUT_H__ #include <linux/backlight.h> -#include <linux/version.h> #include <drm/drmP.h> #include <drm/drm.h> #include <drm/drm_crtc.h> diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index 2c8a60c3b98e..f920fb5e42b6 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -129,6 +129,7 @@ static int i810_map_buffer(struct drm_buf *buf, struct drm_file *file_priv) if (buf_priv->currently_mapped == I810_BUF_MAPPED) return -EINVAL; + /* This is all entirely broken */ down_write(¤t->mm->mmap_sem); old_fops = file_priv->filp->f_op; file_priv->filp->f_op = &i810_buffer_fops; @@ -157,11 +158,8 @@ static int i810_unmap_buffer(struct drm_buf *buf) if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EINVAL; - down_write(¤t->mm->mmap_sem); - retcode = do_munmap(current->mm, - (unsigned long)buf_priv->virtual, + retcode = vm_munmap((unsigned long)buf_priv->virtual, (size_t) buf->total); - up_write(¤t->mm->mmap_sem); buf_priv->currently_mapped = I810_BUF_UNMAPPED; buf_priv->virtual = NULL; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index fdb7ccefffbd..e6162a1681f0 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1224,6 +1224,9 @@ static int i915_emon_status(struct seq_file *m, void *unused) unsigned long temp, chipset, gfx; int ret; + if (!IS_GEN5(dev)) + return -ENODEV; + ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; @@ -1502,14 +1505,6 @@ static int i915_ppgtt_info(struct seq_file *m, void *data) return 0; } -static int -i915_debugfs_common_open(struct inode *inode, - struct file *filp) -{ - filp->private_data = inode->i_private; - return 0; -} - static ssize_t i915_wedged_read(struct file *filp, char __user *ubuf, @@ -1560,7 +1555,7 @@ i915_wedged_write(struct file *filp, static const struct file_operations i915_wedged_fops = { .owner = THIS_MODULE, - .open = i915_debugfs_common_open, + .open = simple_open, .read = i915_wedged_read, .write = i915_wedged_write, .llseek = default_llseek, @@ -1622,7 +1617,7 @@ i915_max_freq_write(struct file *filp, static const struct file_operations i915_max_freq_fops = { .owner = THIS_MODULE, - .open = i915_debugfs_common_open, + .open = simple_open, .read = i915_max_freq_read, .write = i915_max_freq_write, .llseek = default_llseek, @@ -1693,7 +1688,7 @@ i915_cache_sharing_write(struct file *filp, static const struct file_operations i915_cache_sharing_fops = { .owner = THIS_MODULE, - .open = i915_debugfs_common_open, + .open = simple_open, .read = i915_cache_sharing_read, .write = i915_cache_sharing_write, .llseek = default_llseek, diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9341eb8ce93b..ba60f3c8f911 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1183,6 +1183,21 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) return can_switch; } +static bool +intel_enable_ppgtt(struct drm_device *dev) +{ + if (i915_enable_ppgtt >= 0) + return i915_enable_ppgtt; + +#ifdef CONFIG_INTEL_IOMMU + /* Disable ppgtt on SNB if VT-d is on. */ + if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif + + return true; +} + static int i915_load_gem_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1197,7 +1212,7 @@ static int i915_load_gem_init(struct drm_device *dev) drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); mutex_lock(&dev->struct_mutex); - if (i915_enable_ppgtt && HAS_ALIASING_PPGTT(dev)) { + if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { /* PPGTT pdes are stolen from global gtt ptes, so shrink the * aperture accordingly when using aliasing ppgtt. */ gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; @@ -1207,8 +1222,10 @@ static int i915_load_gem_init(struct drm_device *dev) i915_gem_do_init(dev, 0, mappable_size, gtt_size); ret = i915_gem_init_aliasing_ppgtt(dev); - if (ret) + if (ret) { + mutex_unlock(&dev->struct_mutex); return ret; + } } else { /* Let GEM Manage all of the aperture. * @@ -1684,6 +1701,9 @@ void i915_update_gfx_val(struct drm_i915_private *dev_priv) unsigned long diffms; u32 count; + if (dev_priv->info->gen != 5) + return; + getrawmonotonic(&now); diff1 = timespec_sub(now, dev_priv->last_time2); @@ -2104,12 +2124,14 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); - spin_lock(&mchdev_lock); - i915_mch_dev = dev_priv; - dev_priv->mchdev_lock = &mchdev_lock; - spin_unlock(&mchdev_lock); + if (IS_GEN5(dev)) { + spin_lock(&mchdev_lock); + i915_mch_dev = dev_priv; + dev_priv->mchdev_lock = &mchdev_lock; + spin_unlock(&mchdev_lock); - ips_ping_for_i915_load(); + ips_ping_for_i915_load(); + } return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 1a7559b59997..ae8a64f9f845 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -64,9 +64,13 @@ MODULE_PARM_DESC(semaphores, "Use semaphores for inter-ring sync (default: -1 (use per-chip defaults))"); int i915_enable_rc6 __read_mostly = -1; -module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600); +module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0400); MODULE_PARM_DESC(i915_enable_rc6, - "Enable power-saving render C-state 6 (default: -1 (use per-chip default)"); + "Enable power-saving render C-state 6. " + "Different stages can be selected via bitmask values " + "(0 = disable; 1 = enable rc6; 2 = enable deep rc6; 4 = enable deepest rc6). " + "For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. " + "default: -1 (use per-chip default)"); int i915_enable_fbc __read_mostly = -1; module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600); @@ -103,8 +107,8 @@ MODULE_PARM_DESC(enable_hangcheck, "WARNING: Disabling this can cause system wide hangs. " "(default: true)"); -bool i915_enable_ppgtt __read_mostly = 1; -module_param_named(i915_enable_ppgtt, i915_enable_ppgtt, bool, 0600); +int i915_enable_ppgtt __read_mostly = -1; +module_param_named(i915_enable_ppgtt, i915_enable_ppgtt, int, 0600); MODULE_PARM_DESC(i915_enable_ppgtt, "Enable PPGTT (default: true)"); @@ -292,6 +296,7 @@ static const struct pci_device_id pciidlist[] = { /* aka */ INTEL_VGA_DEVICE(0x0152, &intel_ivybridge_d_info), /* GT1 desktop */ INTEL_VGA_DEVICE(0x0162, &intel_ivybridge_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x015a, &intel_ivybridge_d_info), /* GT1 server */ + INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */ {0, 0, 0} }; @@ -533,7 +538,9 @@ static int i915_drm_thaw(struct drm_device *dev) drm_irq_install(dev); /* Resume the modeset for every activated CRTC */ + mutex_lock(&dev->mode_config.mutex); drm_helper_resume_force_mode(dev); + mutex_unlock(&dev->mode_config.mutex); if (IS_IRONLAKE_M(dev)) ironlake_enable_rc6(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c0f19f572004..5fabc6c31fec 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1053,6 +1053,27 @@ struct drm_i915_file_private { #include "i915_trace.h" +/** + * RC6 is a special power stage which allows the GPU to enter an very + * low-voltage mode when idle, using down to 0V while at this stage. This + * stage is entered automatically when the GPU is idle when RC6 support is + * enabled, and as soon as new workload arises GPU wakes up automatically as well. + * + * There are different RC6 modes available in Intel GPU, which differentiate + * among each other with the latency required to enter and leave RC6 and + * voltage consumed by the GPU in different states. + * + * The combination of the following flags define which states GPU is allowed + * to enter, while RC6 is the normal RC6 state, RC6p is the deep RC6, and + * RC6pp is deepest RC6. Their support by hardware varies according to the + * GPU, BIOS, chipset and platform. RC6 is usually the safest one and the one + * which brings the most power savings; deeper states save more power, but + * require higher latency to switch to and wake up. + */ +#define INTEL_RC6_ENABLE (1<<0) +#define INTEL_RC6p_ENABLE (1<<1) +#define INTEL_RC6pp_ENABLE (1<<2) + extern struct drm_ioctl_desc i915_ioctls[]; extern int i915_max_ioctl; extern unsigned int i915_fbpercrtc __always_unused; @@ -1065,7 +1086,7 @@ extern int i915_vbt_sdvo_panel_type __read_mostly; extern int i915_enable_rc6 __read_mostly; extern int i915_enable_fbc __read_mostly; extern bool i915_enable_hangcheck __read_mostly; -extern bool i915_enable_ppgtt __read_mostly; +extern int i915_enable_ppgtt __read_mostly; extern int i915_suspend(struct drm_device *dev, pm_message_t state); extern int i915_resume(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1f441f5c2405..0d1e4b7b4b99 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1087,11 +1087,9 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, if (obj == NULL) return -ENOENT; - down_write(¤t->mm->mmap_sem); - addr = do_mmap(obj->filp, 0, args->size, + addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE, MAP_SHARED, args->offset); - up_write(¤t->mm->mmap_sem); drm_gem_object_unreference_unlocked(obj); if (IS_ERR((void *)addr)) return addr; @@ -1472,16 +1470,19 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, list_move_tail(&obj->ring_list, &ring->active_list); obj->last_rendering_seqno = seqno; - if (obj->fenced_gpu_access) { - struct drm_i915_fence_reg *reg; - - BUG_ON(obj->fence_reg == I915_FENCE_REG_NONE); + if (obj->fenced_gpu_access) { obj->last_fenced_seqno = seqno; obj->last_fenced_ring = ring; - reg = &dev_priv->fence_regs[obj->fence_reg]; - list_move_tail(®->lru_list, &dev_priv->mm.fence_list); + /* Bump MRU to take account of the delayed flush */ + if (obj->fence_reg != I915_FENCE_REG_NONE) { + struct drm_i915_fence_reg *reg; + + reg = &dev_priv->fence_regs[obj->fence_reg]; + list_move_tail(®->lru_list, + &dev_priv->mm.fence_list); + } } } @@ -1490,6 +1491,7 @@ i915_gem_object_move_off_active(struct drm_i915_gem_object *obj) { list_del_init(&obj->ring_list); obj->last_rendering_seqno = 0; + obj->last_fenced_seqno = 0; } static void @@ -1518,6 +1520,7 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) BUG_ON(!list_empty(&obj->gpu_write_list)); BUG_ON(!obj->active); obj->ring = NULL; + obj->last_fenced_ring = NULL; i915_gem_object_move_off_active(obj); obj->fenced_gpu_access = false; @@ -3754,12 +3757,32 @@ void i915_gem_init_ppgtt(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; uint32_t pd_offset; struct intel_ring_buffer *ring; + struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; + uint32_t __iomem *pd_addr; + uint32_t pd_entry; int i; if (!dev_priv->mm.aliasing_ppgtt) return; - pd_offset = dev_priv->mm.aliasing_ppgtt->pd_offset; + + pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(uint32_t); + for (i = 0; i < ppgtt->num_pd_entries; i++) { + dma_addr_t pt_addr; + + if (dev_priv->mm.gtt->needs_dmar) + pt_addr = ppgtt->pt_dma_addr[i]; + else + pt_addr = page_to_phys(ppgtt->pt_pages[i]); + + pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr); + pd_entry |= GEN6_PDE_VALID; + + writel(pd_entry, pd_addr + i); + } + readl(pd_addr); + + pd_offset = ppgtt->pd_offset; pd_offset /= 64; /* in cachelines, */ pd_offset <<= 16; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 81687af00893..de431942ded4 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -498,8 +498,8 @@ pin_and_fence_object(struct drm_i915_gem_object *obj, if (ret) goto err_unpin; } + obj->pending_fenced_gpu_access = true; } - obj->pending_fenced_gpu_access = need_fence; } entry->offset = obj->gtt_offset; @@ -1133,6 +1133,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, return -EINVAL; } + if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) { + DRM_DEBUG("execbuf with %u cliprects\n", + args->num_cliprects); + return -EINVAL; + } cliprects = kmalloc(args->num_cliprects * sizeof(*cliprects), GFP_KERNEL); if (cliprects == NULL) { @@ -1404,7 +1409,8 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, struct drm_i915_gem_exec_object2 *exec2_list = NULL; int ret; - if (args->buffer_count < 1) { + if (args->buffer_count < 1 || + args->buffer_count > UINT_MAX / sizeof(*exec2_list)) { DRM_DEBUG("execbuf2 with %d buffers\n", args->buffer_count); return -EINVAL; } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 2eacd78bb93b..a135c61f4119 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -65,9 +65,7 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct i915_hw_ppgtt *ppgtt; - uint32_t pd_entry; unsigned first_pd_entry_in_global_pt; - uint32_t __iomem *pd_addr; int i; int ret = -ENOMEM; @@ -100,7 +98,6 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) goto err_pt_alloc; } - pd_addr = dev_priv->mm.gtt->gtt + first_pd_entry_in_global_pt; for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; if (dev_priv->mm.gtt->needs_dmar) { @@ -117,13 +114,7 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) ppgtt->pt_dma_addr[i] = pt_addr; } else pt_addr = page_to_phys(ppgtt->pt_pages[i]); - - pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr); - pd_entry |= GEN6_PDE_VALID; - - writel(pd_entry, pd_addr + i); } - readl(pd_addr); ppgtt->scratch_page_dma_addr = dev_priv->mm.gtt->scratch_page_dma; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 3886cf051bac..9d24d65f0c3e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -568,6 +568,7 @@ #define CM0_MASK_SHIFT 16 #define CM0_IZ_OPT_DISABLE (1<<6) #define CM0_ZR_OPT_DISABLE (1<<5) +#define CM0_STC_EVICT_DISABLE_LRA_SNB (1<<5) #define CM0_DEPTH_EVICT_DISABLE (1<<4) #define CM0_COLOR_EVICT_DISABLE (1<<3) #define CM0_DEPTH_WRITE_DISABLE (1<<1) @@ -2385,6 +2386,7 @@ #define PIPECONF_DISABLE 0 #define PIPECONF_DOUBLE_WIDE (1<<30) #define I965_PIPECONF_ACTIVE (1<<30) +#define PIPECONF_FRAME_START_DELAY_MASK (3<<27) #define PIPECONF_SINGLE_WIDE 0 #define PIPECONF_PIPE_UNLOCKED 0 #define PIPECONF_PIPE_LOCKED (1<<25) @@ -3727,6 +3729,9 @@ #define GT_FIFO_FREE_ENTRIES 0x120008 #define GT_FIFO_NUM_RESERVED_ENTRIES 20 +#define GEN6_UCGCTL1 0x9400 +# define GEN6_BLBUNIT_CLOCK_GATE_DISABLE (1 << 5) + #define GEN6_UCGCTL2 0x9404 # define GEN6_RCZUNIT_CLOCK_GATE_DISABLE (1 << 13) # define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE (1 << 12) diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 8168d8f8a634..b48fc2a8410c 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -24,6 +24,7 @@ * Eric Anholt <eric@anholt.net> * */ +#include <linux/dmi.h> #include <drm/drm_dp_helper.h> #include "drmP.h" #include "drm.h" @@ -621,6 +622,26 @@ init_vbt_defaults(struct drm_i915_private *dev_priv) dev_priv->edp.bpp = 18; } +static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id) +{ + DRM_DEBUG_KMS("Falling back to manually reading VBT from " + "VBIOS ROM for %s\n", + id->ident); + return 1; +} + +static const struct dmi_system_id intel_no_opregion_vbt[] = { + { + .callback = intel_no_opregion_vbt_callback, + .ident = "ThinkCentre A57", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "97027RG"), + }, + }, + { } +}; + /** * intel_parse_bios - find VBT and initialize settings from the BIOS * @dev: DRM device @@ -641,7 +662,7 @@ intel_parse_bios(struct drm_device *dev) init_vbt_defaults(dev_priv); /* XXX Should this validation be moved to intel_opregion.c? */ - if (dev_priv->opregion.vbt) { + if (!dmi_check_system(intel_no_opregion_vbt) && dev_priv->opregion.vbt) { struct vbt_header *vbt = dev_priv->opregion.vbt; if (memcmp(vbt->signature, "$VBT", 4) == 0) { DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n", diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 4d3d736a4f56..90b9793fd5da 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -430,8 +430,8 @@ intel_crt_detect(struct drm_connector *connector, bool force) { struct drm_device *dev = connector->dev; struct intel_crt *crt = intel_attached_crt(connector); - struct drm_crtc *crtc; enum drm_connector_status status; + struct intel_load_detect_pipe tmp; if (I915_HAS_HOTPLUG(dev)) { if (intel_crt_detect_hotplug(connector)) { @@ -450,23 +450,16 @@ intel_crt_detect(struct drm_connector *connector, bool force) return connector->status; /* for pre-945g platforms use load detect */ - crtc = crt->base.base.crtc; - if (crtc && crtc->enabled) { - status = intel_crt_load_detect(crt); - } else { - struct intel_load_detect_pipe tmp; - - if (intel_get_load_detect_pipe(&crt->base, connector, NULL, - &tmp)) { - if (intel_crt_detect_ddc(connector)) - status = connector_status_connected; - else - status = intel_crt_load_detect(crt); - intel_release_load_detect_pipe(&crt->base, connector, - &tmp); - } else - status = connector_status_unknown; - } + if (intel_get_load_detect_pipe(&crt->base, connector, NULL, + &tmp)) { + if (intel_crt_detect_ddc(connector)) + status = connector_status_connected; + else + status = intel_crt_load_detect(crt); + intel_release_load_detect_pipe(&crt->base, connector, + &tmp); + } else + status = connector_status_unknown; return status; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d514719f65e2..1b1cf3b3ff51 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2245,6 +2245,33 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, } static int +intel_finish_fb(struct drm_framebuffer *old_fb) +{ + struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj; + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + bool was_interruptible = dev_priv->mm.interruptible; + int ret; + + wait_event(dev_priv->pending_flip_queue, + atomic_read(&dev_priv->mm.wedged) || + atomic_read(&obj->pending_flip) == 0); + + /* Big Hammer, we also need to ensure that any pending + * MI_WAIT_FOR_EVENT inside a user batch buffer on the + * current scanout is retired before unpinning the old + * framebuffer. + * + * This should only fail upon a hung GPU, in which case we + * can safely continue. + */ + dev_priv->mm.interruptible = false; + ret = i915_gem_object_finish_gpu(obj); + dev_priv->mm.interruptible = was_interruptible; + + return ret; +} + +static int intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { @@ -2282,25 +2309,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return ret; } - if (old_fb) { - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj; - - wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->mm.wedged) || - atomic_read(&obj->pending_flip) == 0); - - /* Big Hammer, we also need to ensure that any pending - * MI_WAIT_FOR_EVENT inside a user batch buffer on the - * current scanout is retired before unpinning the old - * framebuffer. - * - * This should only fail upon a hung GPU, in which case we - * can safely continue. - */ - ret = i915_gem_object_finish_gpu(obj); - (void) ret; - } + if (old_fb) + intel_finish_fb(old_fb); ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y, LEAVE_ATOMIC_MODE_SET); @@ -3371,6 +3381,23 @@ static void intel_crtc_disable(struct drm_crtc *crtc) struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct drm_device *dev = crtc->dev; + /* Flush any pending WAITs before we disable the pipe. Note that + * we need to drop the struct_mutex in order to acquire it again + * during the lowlevel dpms routines around a couple of the + * operations. It does not look trivial nor desirable to move + * that locking higher. So instead we leave a window for the + * submission of further commands on the fb before we can actually + * disable it. This race with userspace exists anyway, and we can + * only rely on the pipe being disabled by userspace after it + * receives the hotplug notification and has flushed any pending + * batches. + */ + if (crtc->fb) { + mutex_lock(&dev->struct_mutex); + intel_finish_fb(crtc->fb); + mutex_unlock(&dev->struct_mutex); + } + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe); @@ -3451,8 +3478,11 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, return false; } - /* All interlaced capable intel hw wants timings in frames. */ - drm_mode_set_crtcinfo(adjusted_mode, 0); + /* All interlaced capable intel hw wants timings in frames. Note though + * that intel_lvds_mode_fixup does some funny tricks with the crtc + * timings, so we need to be careful not to clobber these.*/ + if (!(adjusted_mode->private_flags & INTEL_MODE_CRTC_TIMINGS_SET)) + drm_mode_set_crtcinfo(adjusted_mode, 0); return true; } @@ -5539,7 +5569,8 @@ void ironlake_init_pch_refclk(struct drm_device *dev) if (intel_panel_use_ssc(dev_priv) && can_ssc) { DRM_DEBUG_KMS("Using SSC on panel\n"); temp |= DREF_SSC1_ENABLE; - } + } else + temp &= ~DREF_SSC1_ENABLE; /* Get SSC going before enabling the outputs */ I915_WRITE(PCH_DREF_CONTROL, temp); @@ -7041,9 +7072,6 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - int dpll_reg = DPLL(pipe); - int dpll = I915_READ(dpll_reg); if (HAS_PCH_SPLIT(dev)) return; @@ -7056,10 +7084,15 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) * the manual case. */ if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) { + int pipe = intel_crtc->pipe; + int dpll_reg = DPLL(pipe); + u32 dpll; + DRM_DEBUG_DRIVER("downclocking LVDS\n"); assert_panel_unlocked(dev_priv, pipe); + dpll = I915_READ(dpll_reg); dpll |= DISPLAY_RATE_SELECT_FPA1; I915_WRITE(dpll_reg, dpll); intel_wait_for_vblank(dev, pipe); @@ -7067,7 +7100,6 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) if (!(dpll & DISPLAY_RATE_SELECT_FPA1)) DRM_DEBUG_DRIVER("failed to downclock LVDS!\n"); } - } /** @@ -7437,7 +7469,13 @@ static int intel_gen6_queue_flip(struct drm_device *dev, OUT_RING(fb->pitches[0] | obj->tiling_mode); OUT_RING(obj->gtt_offset); - pf = I915_READ(PF_CTL(intel_crtc->pipe)) & PF_ENABLE; + /* Contrary to the suggestions in the documentation, + * "Enable Panel Fitter" does not seem to be required when page + * flipping with a non-native mode, and worse causes a normal + * modeset to fail. + * pf = I915_READ(PF_CTL(intel_crtc->pipe)) & PF_ENABLE; + */ + pf = 0; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; OUT_RING(pf | pipesrc); ADVANCE_LP_RING(); @@ -7580,6 +7618,12 @@ static void intel_sanitize_modesetting(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; u32 reg, val; + /* Clear any frame start delays used for debugging left by the BIOS */ + for_each_pipe(pipe) { + reg = PIPECONF(pipe); + I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); + } + if (HAS_PCH_SPLIT(dev)) return; @@ -8215,7 +8259,7 @@ void intel_init_emon(struct drm_device *dev) dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK); } -static bool intel_enable_rc6(struct drm_device *dev) +static int intel_enable_rc6(struct drm_device *dev) { /* * Respect the kernel parameter if it is set @@ -8233,11 +8277,11 @@ static bool intel_enable_rc6(struct drm_device *dev) * Disable rc6 on Sandybridge */ if (INTEL_INFO(dev)->gen == 6) { - DRM_DEBUG_DRIVER("Sandybridge: RC6 disabled\n"); - return 0; + DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n"); + return INTEL_RC6_ENABLE; } - DRM_DEBUG_DRIVER("RC6 enabled\n"); - return 1; + DRM_DEBUG_DRIVER("RC6 and deep RC6 enabled\n"); + return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE); } void gen6_enable_rps(struct drm_i915_private *dev_priv) @@ -8247,6 +8291,7 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) u32 pcu_mbox, rc6_mask = 0; u32 gtfifodbg; int cur_freq, min_freq, max_freq; + int rc6_mode; int i; /* Here begins a magic sequence of register writes to enable @@ -8284,9 +8329,20 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RC6p_THRESHOLD, 100000); I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */ - if (intel_enable_rc6(dev_priv->dev)) - rc6_mask = GEN6_RC_CTL_RC6_ENABLE | - ((IS_GEN7(dev_priv->dev)) ? GEN6_RC_CTL_RC6p_ENABLE : 0); + rc6_mode = intel_enable_rc6(dev_priv->dev); + if (rc6_mode & INTEL_RC6_ENABLE) + rc6_mask |= GEN6_RC_CTL_RC6_ENABLE; + + if (rc6_mode & INTEL_RC6p_ENABLE) + rc6_mask |= GEN6_RC_CTL_RC6p_ENABLE; + + if (rc6_mode & INTEL_RC6pp_ENABLE) + rc6_mask |= GEN6_RC_CTL_RC6pp_ENABLE; + + DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n", + (rc6_mode & INTEL_RC6_ENABLE) ? "on" : "off", + (rc6_mode & INTEL_RC6p_ENABLE) ? "on" : "off", + (rc6_mode & INTEL_RC6pp_ENABLE) ? "on" : "off"); I915_WRITE(GEN6_RC_CONTROL, rc6_mask | @@ -8510,6 +8566,10 @@ static void gen6_init_clock_gating(struct drm_device *dev) I915_WRITE(WM2_LP_ILK, 0); I915_WRITE(WM1_LP_ILK, 0); + I915_WRITE(GEN6_UCGCTL1, + I915_READ(GEN6_UCGCTL1) | + GEN6_BLBUNIT_CLOCK_GATE_DISABLE); + /* According to the BSpec vol1g, bit 12 (RCPBUNIT) clock * gating disable must be set. Failure to set it results in * flickering pixels due to Z write ordering failures after diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 110552ff302c..4b637919f74f 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -219,14 +219,38 @@ intel_dp_max_data_rate(int max_link_clock, int max_lanes) return (max_link_clock * max_lanes * 8) / 10; } +static bool +intel_dp_adjust_dithering(struct intel_dp *intel_dp, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); + int max_lanes = intel_dp_max_lane_count(intel_dp); + int max_rate, mode_rate; + + mode_rate = intel_dp_link_required(mode->clock, 24); + max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); + + if (mode_rate > max_rate) { + mode_rate = intel_dp_link_required(mode->clock, 18); + if (mode_rate > max_rate) + return false; + + if (adjusted_mode) + adjusted_mode->private_flags + |= INTEL_MODE_DP_FORCE_6BPC; + + return true; + } + + return true; +} + static int intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct intel_dp *intel_dp = intel_attached_dp(connector); - int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); - int max_lanes = intel_dp_max_lane_count(intel_dp); - int max_rate, mode_rate; if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) @@ -236,16 +260,8 @@ intel_dp_mode_valid(struct drm_connector *connector, return MODE_PANEL; } - mode_rate = intel_dp_link_required(mode->clock, 24); - max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); - - if (mode_rate > max_rate) { - mode_rate = intel_dp_link_required(mode->clock, 18); - if (mode_rate > max_rate) - return MODE_CLOCK_HIGH; - else - mode->private_flags |= INTEL_MODE_DP_FORCE_6BPC; - } + if (!intel_dp_adjust_dithering(intel_dp, mode, NULL)) + return MODE_CLOCK_HIGH; if (mode->clock < 10000) return MODE_CLOCK_LOW; @@ -672,7 +688,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, int lane_count, clock; int max_lane_count = intel_dp_max_lane_count(intel_dp); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; - int bpp = mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + int bpp; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { @@ -686,6 +702,11 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, mode->clock = intel_dp->panel_fixed_mode->clock; } + if (!intel_dp_adjust_dithering(intel_dp, mode, adjusted_mode)) + return false; + + bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { for (clock = 0; clock <= max_clock; clock++) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5a14149b3794..715afa153025 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -105,6 +105,10 @@ #define INTEL_MODE_PIXEL_MULTIPLIER_SHIFT (0x0) #define INTEL_MODE_PIXEL_MULTIPLIER_MASK (0xf << INTEL_MODE_PIXEL_MULTIPLIER_SHIFT) #define INTEL_MODE_DP_FORCE_6BPC (0x10) +/* This flag must be set by the encoder's mode_fixup if it changes the crtc + * timings in the mode to prevent the crtc fixup from overwriting them. + * Currently only lvds needs that. */ +#define INTEL_MODE_CRTC_TIMINGS_SET (0x20) static inline void intel_mode_set_pixel_multiplier(struct drm_display_mode *mode, diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 19ecd78b8a2c..6e9ee33fd412 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -279,6 +279,8 @@ void intel_fb_restore_mode(struct drm_device *dev) struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; + mutex_lock(&dev->mode_config.mutex); + ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); @@ -286,4 +288,6 @@ void intel_fb_restore_mode(struct drm_device *dev) /* Be sure to shut off any planes that may be active */ list_for_each_entry(plane, &config->plane_list, head) plane->funcs->disable_plane(plane); + + mutex_unlock(&dev->mode_config.mutex); } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index cae3e5f17a49..2d7f47b56b6a 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -136,7 +136,7 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, val &= ~VIDEO_DIP_SELECT_MASK; - I915_WRITE(VIDEO_DIP_CTL, val | port | flags); + I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); for (i = 0; i < len; i += 4) { I915_WRITE(VIDEO_DIP_DATA, *data); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 601c86e664af..8fdc95700218 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -390,7 +390,7 @@ int intel_setup_gmbus(struct drm_device *dev) bus->has_gpio = intel_gpio_setup(bus, i); /* XXX force bit banging until GMBUS is fully debugged */ - if (bus->has_gpio && IS_GEN2(dev)) + if (bus->has_gpio) bus->force_bit = true; } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index c5c0973af8a1..9c71183629c2 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -187,6 +187,8 @@ centre_horizontally(struct drm_display_mode *mode, mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; + + mode->private_flags |= INTEL_MODE_CRTC_TIMINGS_SET; } static void @@ -208,6 +210,8 @@ centre_vertically(struct drm_display_mode *mode, mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; + + mode->private_flags |= INTEL_MODE_CRTC_TIMINGS_SET; } static inline u32 panel_fitter_scaling(u32 source, u32 target) @@ -283,6 +287,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, for_each_pipe(pipe) I915_WRITE(BCLRPAT(pipe), 0); + drm_mode_set_crtcinfo(adjusted_mode, 0); + switch (intel_lvds->fitting_mode) { case DRM_MODE_SCALE_CENTER: /* @@ -744,7 +750,7 @@ static const struct dmi_system_id intel_no_lvds[] = { .ident = "Hewlett-Packard t5745", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_BOARD_NAME, "hp t5745"), + DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"), }, }, { @@ -752,7 +758,15 @@ static const struct dmi_system_id intel_no_lvds[] = { .ident = "Hewlett-Packard st5747", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_BOARD_NAME, "hp st5747"), + DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "MSI Wind Box DC500", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_BOARD_NAME, "MS-7469"), }, }, diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 230a141dbea3..48177ec4720e 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -47,8 +47,6 @@ intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, adjusted_mode->vtotal = fixed_mode->vtotal; adjusted_mode->clock = fixed_mode->clock; - - drm_mode_set_crtcinfo(adjusted_mode, 0); } /* adjusted_mode has been preset to be the panel's fixed mode */ diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index fc66af6a9448..62892a826ede 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -398,6 +398,17 @@ static int init_render_ring(struct intel_ring_buffer *ring) return ret; } + + if (IS_GEN6(dev)) { + /* From the Sandybridge PRM, volume 1 part 3, page 24: + * "If this bit is set, STCunit will have LRA as replacement + * policy. [...] This bit must be reset. LRA replacement + * policy is not supported." + */ + I915_WRITE(CACHE_MODE_0, + CM0_STC_EVICT_DISABLE_LRA_SNB << CM0_MASK_SHIFT); + } + if (INTEL_INFO(dev)->gen >= 6) { I915_WRITE(INSTPM, INSTPM_FORCE_ORDERING << 16 | INSTPM_FORCE_ORDERING); @@ -626,7 +637,7 @@ gen6_ring_get_seqno(struct intel_ring_buffer *ring) /* Workaround to force correct ordering between irq and seqno writes on * ivb (and maybe also on snb) by reading from a CS register (like * ACTHD) before reading the status page. */ - if (IS_GEN7(dev)) + if (IS_GEN6(dev) || IS_GEN7(dev)) intel_ring_get_active_head(ring); return intel_read_status_page(ring, I915_GEM_HWS_INDEX); } @@ -1038,7 +1049,7 @@ int intel_init_ring_buffer(struct drm_device *dev, * of the buffer. */ ring->effective_size = ring->size; - if (IS_I830(ring->dev)) + if (IS_I830(ring->dev) || IS_845G(ring->dev)) ring->effective_size -= 128; return 0; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index e36b171c1e7d..ae5e748f39bb 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -731,6 +731,7 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd, uint16_t width, height; uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len; uint16_t h_sync_offset, v_sync_offset; + int mode_clock; width = mode->crtc_hdisplay; height = mode->crtc_vdisplay; @@ -745,7 +746,11 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd, h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; - dtd->part1.clock = mode->clock / 10; + mode_clock = mode->clock; + mode_clock /= intel_mode_get_pixel_multiplier(mode) ?: 1; + mode_clock /= 10; + dtd->part1.clock = mode_clock; + dtd->part1.h_active = width & 0xff; dtd->part1.h_blank = h_blank_len & 0xff; dtd->part1.h_high = (((width >> 8) & 0xf) << 4) | @@ -996,7 +1001,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); u32 sdvox; struct intel_sdvo_in_out_map in_out; - struct intel_sdvo_dtd input_dtd; + struct intel_sdvo_dtd input_dtd, output_dtd; int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); int rate; @@ -1021,20 +1026,13 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, intel_sdvo->attached_output)) return; - /* We have tried to get input timing in mode_fixup, and filled into - * adjusted_mode. - */ - if (intel_sdvo->is_tv || intel_sdvo->is_lvds) { - input_dtd = intel_sdvo->input_dtd; - } else { - /* Set the output timing to the screen */ - if (!intel_sdvo_set_target_output(intel_sdvo, - intel_sdvo->attached_output)) - return; - - intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); - (void) intel_sdvo_set_output_timing(intel_sdvo, &input_dtd); - } + /* lvds has a special fixed output timing. */ + if (intel_sdvo->is_lvds) + intel_sdvo_get_dtd_from_mode(&output_dtd, + intel_sdvo->sdvo_lvds_fixed_mode); + else + intel_sdvo_get_dtd_from_mode(&output_dtd, mode); + (void) intel_sdvo_set_output_timing(intel_sdvo, &output_dtd); /* Set the input timing to the screen. Assume always input 0. */ if (!intel_sdvo_set_target_input(intel_sdvo)) @@ -1052,6 +1050,10 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, !intel_sdvo_set_tv_format(intel_sdvo)) return; + /* We have tried to get input timing in mode_fixup, and filled into + * adjusted_mode. + */ + intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); (void) intel_sdvo_set_input_timing(intel_sdvo, &input_dtd); switch (pixel_multiplier) { @@ -1218,8 +1220,14 @@ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct in static int intel_sdvo_supports_hotplug(struct intel_sdvo *intel_sdvo) { + struct drm_device *dev = intel_sdvo->base.base.dev; u8 response[2]; + /* HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise + * on the line. */ + if (IS_I945G(dev) || IS_I945GM(dev)) + return false; + return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, &response, 2) && response[0]; } diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 7aa0450399a1..e90dfb625c42 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -95,7 +95,6 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, /* must disable */ sprctl |= SPRITE_TRICKLE_FEED_DISABLE; sprctl |= SPRITE_ENABLE; - sprctl |= SPRITE_DEST_KEY; /* Sizes are 0 based */ src_w--; @@ -411,6 +410,9 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, old_obj = intel_plane->obj; + src_w = src_w >> 16; + src_h = src_h >> 16; + /* Pipe must be running... */ if (!(I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE)) return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index ca1639918f57..97a81260485a 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -13,6 +13,7 @@ config DRM_NOUVEAU select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT select ACPI_WMI if ACPI select MXM_WMI if ACPI + select POWER_SUPPLY help Choose this option for open-source nVidia support. diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 7814a760c164..284bd25d5d21 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -270,7 +270,7 @@ static bool nouveau_dsm_detect(void) struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; struct pci_dev *pdev = NULL; int has_dsm = 0; - int has_optimus; + int has_optimus = 0; int vga_count = 0; bool guid_valid; int retval; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 637afe71de56..0be4a815e706 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -177,14 +177,15 @@ bios_shadow_pci(struct nvbios *bios) if (!pci_enable_rom(pdev)) { void __iomem *rom = pci_map_rom(pdev, &length); - if (rom) { + if (rom && length) { bios->data = kmalloc(length, GFP_KERNEL); if (bios->data) { memcpy_fromio(bios->data, rom, length); bios->length = length; } - pci_unmap_rom(pdev, rom); } + if (rom) + pci_unmap_rom(pdev, rom); pci_disable_rom(pdev); } @@ -6155,10 +6156,14 @@ dcb_fake_connectors(struct nvbios *bios) /* heuristic: if we ever get a non-zero connector field, assume * that all the indices are valid and we don't need fake them. + * + * and, as usual, a blacklist of boards with bad bios data.. */ - for (i = 0; i < dcbt->entries; i++) { - if (dcbt->entry[i].connector) - return; + if (!nv_match_device(bios->dev, 0x0392, 0x107d, 0x20a2)) { + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector) + return; + } } /* no useful connector info available, we need to make it up diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index 44e6416d4a33..846afb0bfef4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c @@ -436,11 +436,11 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, } if (dev_priv->card_type < NV_C0) { - init->subchan[0].handle = NvSw; - init->subchan[0].grclass = NV_SW; - init->nr_subchan = 1; - } else { - init->nr_subchan = 0; + init->subchan[0].handle = 0x00000000; + init->subchan[0].grclass = 0x0000; + init->subchan[1].handle = NvSw; + init->subchan[1].grclass = NV_SW; + init->nr_subchan = 2; } /* Named memory object area */ diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h index bcf0fd9e313e..23d4edf992b7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.h +++ b/drivers/gpu/drm/nouveau/nouveau_dma.h @@ -48,8 +48,8 @@ void nv50_dma_push(struct nouveau_channel *, struct nouveau_bo *, /* Hardcoded object assignments to subchannels (subchannel id). */ enum { - NvSubSw = 0, - NvSubM2MF = 1, + NvSubM2MF = 0, + NvSubSw = 1, NvSub2D = 2, NvSubCtxSurf2D = 2, NvSubGdiRect = 3, diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c index 59ea1c14eca0..c3de36384522 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hdmi.c +++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c @@ -32,7 +32,9 @@ static bool hdmi_sor(struct drm_encoder *encoder) { struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; - if (dev_priv->chipset < 0xa3) + if (dev_priv->chipset < 0xa3 || + dev_priv->chipset == 0xaa || + dev_priv->chipset == 0xac) return false; return true; } diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index e2be95af2e52..77e564667b5c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -29,10 +29,6 @@ #include "nouveau_i2c.h" #include "nouveau_hw.h" -#define T_TIMEOUT 2200000 -#define T_RISEFALL 1000 -#define T_HOLD 5000 - static void i2c_drive_scl(void *data, int state) { @@ -113,175 +109,6 @@ i2c_sense_sda(void *data) return 0; } -static void -i2c_delay(struct nouveau_i2c_chan *port, u32 nsec) -{ - udelay((nsec + 500) / 1000); -} - -static bool -i2c_raise_scl(struct nouveau_i2c_chan *port) -{ - u32 timeout = T_TIMEOUT / T_RISEFALL; - - i2c_drive_scl(port, 1); - do { - i2c_delay(port, T_RISEFALL); - } while (!i2c_sense_scl(port) && --timeout); - - return timeout != 0; -} - -static int -i2c_start(struct nouveau_i2c_chan *port) -{ - int ret = 0; - - port->state = i2c_sense_scl(port); - port->state |= i2c_sense_sda(port) << 1; - if (port->state != 3) { - i2c_drive_scl(port, 0); - i2c_drive_sda(port, 1); - if (!i2c_raise_scl(port)) - ret = -EBUSY; - } - - i2c_drive_sda(port, 0); - i2c_delay(port, T_HOLD); - i2c_drive_scl(port, 0); - i2c_delay(port, T_HOLD); - return ret; -} - -static void -i2c_stop(struct nouveau_i2c_chan *port) -{ - i2c_drive_scl(port, 0); - i2c_drive_sda(port, 0); - i2c_delay(port, T_RISEFALL); - - i2c_drive_scl(port, 1); - i2c_delay(port, T_HOLD); - i2c_drive_sda(port, 1); - i2c_delay(port, T_HOLD); -} - -static int -i2c_bitw(struct nouveau_i2c_chan *port, int sda) -{ - i2c_drive_sda(port, sda); - i2c_delay(port, T_RISEFALL); - - if (!i2c_raise_scl(port)) - return -ETIMEDOUT; - i2c_delay(port, T_HOLD); - - i2c_drive_scl(port, 0); - i2c_delay(port, T_HOLD); - return 0; -} - -static int -i2c_bitr(struct nouveau_i2c_chan *port) -{ - int sda; - - i2c_drive_sda(port, 1); - i2c_delay(port, T_RISEFALL); - - if (!i2c_raise_scl(port)) - return -ETIMEDOUT; - i2c_delay(port, T_HOLD); - - sda = i2c_sense_sda(port); - - i2c_drive_scl(port, 0); - i2c_delay(port, T_HOLD); - return sda; -} - -static int -i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last) -{ - int i, bit; - - *byte = 0; - for (i = 7; i >= 0; i--) { - bit = i2c_bitr(port); - if (bit < 0) - return bit; - *byte |= bit << i; - } - - return i2c_bitw(port, last ? 1 : 0); -} - -static int -i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte) -{ - int i, ret; - for (i = 7; i >= 0; i--) { - ret = i2c_bitw(port, !!(byte & (1 << i))); - if (ret < 0) - return ret; - } - - ret = i2c_bitr(port); - if (ret == 1) /* nack */ - ret = -EIO; - return ret; -} - -static int -i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg) -{ - u32 addr = msg->addr << 1; - if (msg->flags & I2C_M_RD) - addr |= 1; - return i2c_put_byte(port, addr); -} - -static int -i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) -{ - struct nouveau_i2c_chan *port = (struct nouveau_i2c_chan *)adap; - struct i2c_msg *msg = msgs; - int ret = 0, mcnt = num; - - while (!ret && mcnt--) { - u8 remaining = msg->len; - u8 *ptr = msg->buf; - - ret = i2c_start(port); - if (ret == 0) - ret = i2c_addr(port, msg); - - if (msg->flags & I2C_M_RD) { - while (!ret && remaining--) - ret = i2c_get_byte(port, ptr++, !remaining); - } else { - while (!ret && remaining--) - ret = i2c_put_byte(port, *ptr++); - } - - msg++; - } - - i2c_stop(port); - return (ret < 0) ? ret : num; -} - -static u32 -i2c_bit_func(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - -const struct i2c_algorithm nouveau_i2c_bit_algo = { - .master_xfer = i2c_bit_xfer, - .functionality = i2c_bit_func -}; - static const uint32_t nv50_i2c_port[] = { 0x00e138, 0x00e150, 0x00e168, 0x00e180, 0x00e254, 0x00e274, 0x00e764, 0x00e780, @@ -384,12 +211,10 @@ nouveau_i2c_init(struct drm_device *dev) case 0: /* NV04:NV50 */ port->drive = entry[0]; port->sense = entry[1]; - port->adapter.algo = &nouveau_i2c_bit_algo; break; case 4: /* NV4E */ port->drive = 0x600800 + entry[1]; port->sense = port->drive; - port->adapter.algo = &nouveau_i2c_bit_algo; break; case 5: /* NV50- */ port->drive = entry[0] & 0x0f; @@ -402,7 +227,6 @@ nouveau_i2c_init(struct drm_device *dev) port->drive = 0x00d014 + (port->drive * 0x20); port->sense = port->drive; } - port->adapter.algo = &nouveau_i2c_bit_algo; break; case 6: /* NV50- DP AUX */ port->drive = entry[0]; @@ -413,7 +237,7 @@ nouveau_i2c_init(struct drm_device *dev) break; } - if (!port->adapter.algo) { + if (!port->adapter.algo && !port->drive) { NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", i, port->type, port->drive, port->sense); kfree(port); @@ -429,7 +253,26 @@ nouveau_i2c_init(struct drm_device *dev) port->dcb = ROM32(entry[0]); i2c_set_adapdata(&port->adapter, i2c); - ret = i2c_add_adapter(&port->adapter); + if (port->adapter.algo != &nouveau_dp_i2c_algo) { + port->adapter.algo_data = &port->bit; + port->bit.udelay = 10; + port->bit.timeout = usecs_to_jiffies(2200); + port->bit.data = port; + port->bit.setsda = i2c_drive_sda; + port->bit.setscl = i2c_drive_scl; + port->bit.getsda = i2c_sense_sda; + port->bit.getscl = i2c_sense_scl; + + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 1); + i2c_drive_scl(port, 1); + + ret = i2c_bit_add_bus(&port->adapter); + } else { + port->adapter.algo = &nouveau_dp_i2c_algo; + ret = i2c_add_adapter(&port->adapter); + } + if (ret) { NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); kfree(port); diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index 4d2e4e9031be..1d083893a4d7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -34,6 +34,7 @@ struct nouveau_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; + struct i2c_algo_bit_data bit; struct list_head head; u8 index; u8 type; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 34d591b7d4ef..da3e7c3abab7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -235,6 +235,7 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) return -EPERM; strncpy(string, profile, sizeof(string)); + string[sizeof(string) - 1] = 0; if ((ptr = strchr(string, '\n'))) *ptr = '\0'; diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index a4886b36d0fa..c2a8511e855a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -642,7 +642,7 @@ nouveau_card_channel_init(struct drm_device *dev) OUT_RING (chan, chan->vram_handle); OUT_RING (chan, chan->gart_handle); } else - if (dev_priv->card_type <= NV_C0) { + if (dev_priv->card_type <= NV_D0) { ret = nouveau_gpuobj_gr_new(chan, 0x9039, 0x9039); if (ret) goto error; diff --git a/drivers/gpu/drm/nouveau/nv10_gpio.c b/drivers/gpu/drm/nouveau/nv10_gpio.c index 550ad3fcf0af..9d79180069df 100644 --- a/drivers/gpu/drm/nouveau/nv10_gpio.c +++ b/drivers/gpu/drm/nouveau/nv10_gpio.c @@ -65,7 +65,7 @@ nv10_gpio_drive(struct drm_device *dev, int line, int dir, int out) if (line < 10) { line = (line - 2) * 4; reg = NV_PCRTC_GPIO_EXT; - mask = 0x00000003 << ((line - 2) * 4); + mask = 0x00000003; data = (dir << 1) | out; } else if (line < 14) { diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index a7844ab6a50c..274640212475 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -42,7 +42,7 @@ nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane) struct drm_nouveau_private *dev_priv = dev->dev_private; static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ static const u8 nv50[] = { 16, 8, 0, 24 }; - if (dev_priv->card_type == 0xaf) + if (dev_priv->chipset == 0xaf) return nvaf[lane]; return nv50[lane]; } diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c index 5bf55038fd92..f704e942372e 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fb.c +++ b/drivers/gpu/drm/nouveau/nvc0_fb.c @@ -54,6 +54,11 @@ nvc0_mfb_isr(struct drm_device *dev) nvc0_mfb_subp_isr(dev, unit, subp); units &= ~(1 << unit); } + + /* we do something horribly wrong and upset PMFB a lot, so mask off + * interrupts from it after the first one until it's fixed + */ + nv_mask(dev, 0x000640, 0x02000000, 0x00000000); } static void diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index d1bd239cd9e9..5ce9bf51a8de 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -1306,8 +1306,11 @@ struct atom_context *atom_parse(struct card_info *card, void *bios) int atom_asic_init(struct atom_context *ctx) { + struct radeon_device *rdev = ctx->card->dev->dev_private; int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR); uint32_t ps[16]; + int ret; + memset(ps, 0, 64); ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR)); @@ -1317,7 +1320,17 @@ int atom_asic_init(struct atom_context *ctx) if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) return 1; - return atom_execute_table(ctx, ATOM_CMD_INIT, ps); + ret = atom_execute_table(ctx, ATOM_CMD_INIT, ps); + if (ret) + return ret; + + memset(ps, 0, 64); + + if (rdev->family < CHIP_R600) { + if (CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_SPDFANCNTL)) + atom_execute_table(ctx, ATOM_CMD_SPDFANCNTL, ps); + } + return ret; } void atom_destroy(struct atom_context *ctx) diff --git a/drivers/gpu/drm/radeon/atom.h b/drivers/gpu/drm/radeon/atom.h index 93cfe2086ba0..25fea631dad2 100644 --- a/drivers/gpu/drm/radeon/atom.h +++ b/drivers/gpu/drm/radeon/atom.h @@ -44,6 +44,7 @@ #define ATOM_CMD_SETSCLK 0x0A #define ATOM_CMD_SETMCLK 0x0B #define ATOM_CMD_SETPCLK 0x0C +#define ATOM_CMD_SPDFANCNTL 0x39 #define ATOM_DATA_FWI_PTR 0xC #define ATOM_DATA_IIO_PTR 0x32 diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index b5ff1f7b6f7e..af1054f8202a 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -575,6 +575,9 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (rdev->family < CHIP_RV770) pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; + /* use frac fb div on APUs */ + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) + pll->flags |= RADEON_PLL_USE_FRAC_FB_DIV; } else { pll->flags |= RADEON_PLL_LEGACY; @@ -955,8 +958,8 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode break; } - if (radeon_encoder->active_device & - (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) { + if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || + (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index e607c4d7dd98..2d39f9977e00 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -230,6 +230,10 @@ atombios_dvo_setup(struct drm_encoder *encoder, int action) if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) return; + /* some R4xx chips have the wrong frev */ + if (rdev->family <= CHIP_RV410) + frev = 1; + switch (frev) { case 1: switch (crev) { diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 81801c176aa5..fe33d35dae8c 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -2553,7 +2553,7 @@ static void r100_pll_errata_after_data(struct radeon_device *rdev) * or the chip could hang on a subsequent access */ if (rdev->pll_errata & CHIP_ERRATA_PLL_DELAY) { - udelay(5000); + mdelay(5); } /* This function is required to workaround a hardware bug in some (all?) diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 391bd2636a80..c8187c4b6ae8 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1135,7 +1135,7 @@ static void r600_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc } if (rdev->flags & RADEON_IS_AGP) { size_bf = mc->gtt_start; - size_af = 0xFFFFFFFF - mc->gtt_end + 1; + size_af = 0xFFFFFFFF - mc->gtt_end; if (size_bf > size_af) { if (mc->mc_vram_size > size_bf) { dev_warn(rdev->dev, "limiting VRAM\n"); @@ -1149,7 +1149,7 @@ static void r600_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc mc->real_vram_size = size_af; mc->mc_vram_size = size_af; } - mc->vram_start = mc->gtt_end; + mc->vram_start = mc->gtt_end + 1; } mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; dev_info(rdev->dev, "VRAM: %lluM 0x%08llX - 0x%08llX (%lluM used)\n", @@ -2839,7 +2839,7 @@ void r600_rlc_stop(struct radeon_device *rdev) /* r7xx asics need to soft reset RLC before halting */ WREG32(SRBM_SOFT_RESET, SOFT_RESET_RLC); RREG32(SRBM_SOFT_RESET); - udelay(15000); + mdelay(15); WREG32(SRBM_SOFT_RESET, 0); RREG32(SRBM_SOFT_RESET); } diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c index 84c546250955..75ed17c96115 100644 --- a/drivers/gpu/drm/radeon/r600_cp.c +++ b/drivers/gpu/drm/radeon/r600_cp.c @@ -407,7 +407,7 @@ static void r600_cp_load_microcode(drm_radeon_private_t *dev_priv) RADEON_WRITE(R600_GRBM_SOFT_RESET, R600_SOFT_RESET_CP); RADEON_READ(R600_GRBM_SOFT_RESET); - DRM_UDELAY(15000); + mdelay(15); RADEON_WRITE(R600_GRBM_SOFT_RESET, 0); fw_data = (const __be32 *)dev_priv->me_fw->data; @@ -500,7 +500,7 @@ static void r700_cp_load_microcode(drm_radeon_private_t *dev_priv) RADEON_WRITE(R600_GRBM_SOFT_RESET, R600_SOFT_RESET_CP); RADEON_READ(R600_GRBM_SOFT_RESET); - DRM_UDELAY(15000); + mdelay(15); RADEON_WRITE(R600_GRBM_SOFT_RESET, 0); fw_data = (const __be32 *)dev_priv->pfp_fw->data; @@ -1797,7 +1797,7 @@ static void r600_cp_init_ring_buffer(struct drm_device *dev, RADEON_WRITE(R600_GRBM_SOFT_RESET, R600_SOFT_RESET_CP); RADEON_READ(R600_GRBM_SOFT_RESET); - DRM_UDELAY(15000); + mdelay(15); RADEON_WRITE(R600_GRBM_SOFT_RESET, 0); diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c index 6ae0c75f016a..9c6b29a41927 100644 --- a/drivers/gpu/drm/radeon/radeon_clocks.c +++ b/drivers/gpu/drm/radeon/radeon_clocks.c @@ -633,7 +633,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) tmp &= ~(R300_SCLK_FORCE_VAP); tmp |= RADEON_SCLK_FORCE_CP; WREG32_PLL(RADEON_SCLK_CNTL, tmp); - udelay(15000); + mdelay(15); tmp = RREG32_PLL(R300_SCLK_CNTL2); tmp &= ~(R300_SCLK_FORCE_TCL | @@ -651,12 +651,12 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) tmp |= (RADEON_ENGIN_DYNCLK_MODE | (0x01 << RADEON_ACTIVE_HILO_LAT_SHIFT)); WREG32_PLL(RADEON_CLK_PWRMGT_CNTL, tmp); - udelay(15000); + mdelay(15); tmp = RREG32_PLL(RADEON_CLK_PIN_CNTL); tmp |= RADEON_SCLK_DYN_START_CNTL; WREG32_PLL(RADEON_CLK_PIN_CNTL, tmp); - udelay(15000); + mdelay(15); /* When DRI is enabled, setting DYN_STOP_LAT to zero can cause some R200 to lockup randomly, leave them as set by BIOS. @@ -696,7 +696,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) tmp |= RADEON_SCLK_MORE_FORCEON; } WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); - udelay(15000); + mdelay(15); } /* RV200::A11 A12, RV250::A11 A12 */ @@ -709,7 +709,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) tmp |= RADEON_TCL_BYPASS_DISABLE; WREG32_PLL(RADEON_PLL_PWRMGT_CNTL, tmp); } - udelay(15000); + mdelay(15); /*enable dynamic mode for display clocks (PIXCLK and PIX2CLK) */ tmp = RREG32_PLL(RADEON_PIXCLKS_CNTL); @@ -722,14 +722,14 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) RADEON_PIXCLK_TMDS_ALWAYS_ONb); WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); - udelay(15000); + mdelay(15); tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); tmp |= (RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); - udelay(15000); + mdelay(15); } } else { /* Turn everything OFF (ForceON to everything) */ @@ -861,7 +861,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) } WREG32_PLL(RADEON_SCLK_CNTL, tmp); - udelay(16000); + mdelay(16); if ((rdev->family == CHIP_R300) || (rdev->family == CHIP_R350)) { @@ -870,7 +870,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) R300_SCLK_FORCE_GA | R300_SCLK_FORCE_CBA); WREG32_PLL(R300_SCLK_CNTL2, tmp); - udelay(16000); + mdelay(16); } if (rdev->flags & RADEON_IS_IGP) { @@ -878,7 +878,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) tmp &= ~(RADEON_FORCEON_MCLKA | RADEON_FORCEON_YCLKA); WREG32_PLL(RADEON_MCLK_CNTL, tmp); - udelay(16000); + mdelay(16); } if ((rdev->family == CHIP_RV200) || @@ -887,7 +887,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); tmp |= RADEON_SCLK_MORE_FORCEON; WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); - udelay(16000); + mdelay(16); } tmp = RREG32_PLL(RADEON_PIXCLKS_CNTL); @@ -900,7 +900,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) RADEON_PIXCLK_TMDS_ALWAYS_ONb); WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); - udelay(16000); + mdelay(16); tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 81fc100be7e1..2cad9fde92fc 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -2845,7 +2845,7 @@ bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder) case 4: val = RBIOS16(index); index += 2; - udelay(val * 1000); + mdelay(val); break; case 6: slave_addr = id & 0xff; @@ -3044,7 +3044,7 @@ static void combios_parse_pll_table(struct drm_device *dev, uint16_t offset) udelay(150); break; case 2: - udelay(1000); + mdelay(1); break; case 3: while (tmp--) { @@ -3075,13 +3075,13 @@ static void combios_parse_pll_table(struct drm_device *dev, uint16_t offset) /*mclk_cntl |= 0x00001111;*//* ??? */ WREG32_PLL(RADEON_MCLK_CNTL, mclk_cntl); - udelay(10000); + mdelay(10); #endif WREG32_PLL (RADEON_CLK_PWRMGT_CNTL, tmp & ~RADEON_CG_NO1_DEBUG_0); - udelay(10000); + mdelay(10); } break; default: diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index bd05156edbdb..3c2e7a000a2a 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -970,7 +970,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) encoder = obj_to_encoder(obj); - if (encoder->encoder_type != DRM_MODE_ENCODER_DAC || + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) continue; @@ -1000,6 +1000,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) * cases the DVI port is actually a virtual KVM port connected to the service * processor. */ +out: if ((!rdev->is_atom_bios) && (ret == connector_status_disconnected) && rdev->mode_info.bios_hardcoded_edid_size) { @@ -1007,7 +1008,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) ret = connector_status_connected; } -out: /* updated in get modes as well since we need to know if it's analog or digital */ radeon_connector_update_scratch_regs(connector, ret); return ret; diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index ea7df16e2f84..5992502a3448 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -241,8 +241,8 @@ int radeon_wb_init(struct radeon_device *rdev) rdev->wb.use_event = true; } } - /* always use writeback/events on NI */ - if (ASIC_IS_DCE5(rdev)) { + /* always use writeback/events on NI, APUs */ + if (rdev->family >= CHIP_PALM) { rdev->wb.enabled = true; rdev->wb.use_event = true; } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 8086c96e0b06..0a1d4bd65edc 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -533,7 +533,7 @@ static void radeon_crtc_init(struct drm_device *dev, int index) radeon_legacy_init_crtc(dev, radeon_crtc); } -static const char *encoder_names[36] = { +static const char *encoder_names[37] = { "NONE", "INTERNAL_LVDS", "INTERNAL_TMDS1", @@ -570,6 +570,7 @@ static const char *encoder_names[36] = { "INTERNAL_UNIPHY2", "NUTMEG", "TRAVIS", + "INTERNAL_VCE" }; static const char *connector_names[15] = { diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index 85bcfc8923a7..3edec1c198e3 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -900,6 +900,10 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, struct radeon_i2c_chan *i2c; int ret; + /* don't add the mm_i2c bus unless hw_i2c is enabled */ + if (rec->mm_i2c && (radeon_hw_i2c == 0)) + return NULL; + i2c = kzalloc(sizeof(struct radeon_i2c_chan), GFP_KERNEL); if (i2c == NULL) return NULL; diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 66d5fe1c8174..65060b77c805 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -147,6 +147,12 @@ static bool radeon_msi_ok(struct radeon_device *rdev) (rdev->pdev->subsystem_device == 0x01fd)) return true; + /* RV515 seems to have MSI issues where it loses + * MSI rearms occasionally. This leads to lockups and freezes. + * disable it by default. + */ + if (rdev->family == CHIP_RV515) + return false; if (rdev->flags & RADEON_IS_IGP) { /* APUs work fine with MSIs */ if (rdev->family >= CHIP_PALM) diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index 2f46e0c8df53..42db254f6bb0 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -88,7 +88,7 @@ static void radeon_legacy_lvds_update(struct drm_encoder *encoder, int mode) lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); lvds_pll_cntl |= RADEON_LVDS_PLL_EN; WREG32(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); - udelay(1000); + mdelay(1); lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET; @@ -101,7 +101,7 @@ static void radeon_legacy_lvds_update(struct drm_encoder *encoder, int mode) (backlight_level << RADEON_LVDS_BL_MOD_LEVEL_SHIFT)); if (is_mac) lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN; - udelay(panel_pwr_delay * 1000); + mdelay(panel_pwr_delay); WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); break; case DRM_MODE_DPMS_STANDBY: @@ -118,10 +118,10 @@ static void radeon_legacy_lvds_update(struct drm_encoder *encoder, int mode) WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_BLON | RADEON_LVDS_EN | RADEON_LVDS_DIGON); } - udelay(panel_pwr_delay * 1000); + mdelay(panel_pwr_delay); WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); - udelay(panel_pwr_delay * 1000); + mdelay(panel_pwr_delay); break; } @@ -656,7 +656,7 @@ static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_enc WREG32(RADEON_DAC_MACRO_CNTL, tmp); - udelay(2000); + mdelay(2); if (RREG32(RADEON_DAC_CNTL) & RADEON_DAC_CMP_OUTPUT) found = connector_status_connected; @@ -1499,7 +1499,7 @@ static enum drm_connector_status radeon_legacy_tv_dac_detect(struct drm_encoder tmp = dac_cntl2 | RADEON_DAC2_DAC2_CLK_SEL | RADEON_DAC2_CMP_EN; WREG32(RADEON_DAC_CNTL2, tmp); - udelay(10000); + mdelay(10); if (ASIC_IS_R300(rdev)) { if (RREG32(RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUT_B) diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 6f70158d34e4..df6a4dbd93f8 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -241,7 +241,8 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset, domain_start = bo->rdev->mc.vram_start; else domain_start = bo->rdev->mc.gtt_start; - WARN_ON_ONCE((*gpu_addr - domain_start) > max_offset); + WARN_ON_ONCE(max_offset < + (radeon_bo_gpu_offset(bo) - domain_start)); } return 0; diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index c62ae4be3845..cdab1aeaed6e 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -969,7 +969,7 @@ void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) } if (rdev->flags & RADEON_IS_AGP) { size_bf = mc->gtt_start; - size_af = 0xFFFFFFFF - mc->gtt_end + 1; + size_af = 0xFFFFFFFF - mc->gtt_end; if (size_bf > size_af) { if (mc->mc_vram_size > size_bf) { dev_warn(rdev->dev, "limiting VRAM\n"); @@ -983,7 +983,7 @@ void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) mc->real_vram_size = size_af; mc->mc_vram_size = size_af; } - mc->vram_start = mc->gtt_end; + mc->vram_start = mc->gtt_end + 1; } mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; dev_info(rdev->dev, "VRAM: %lluM 0x%08llX - 0x%08llX (%lluM used)\n", diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index ac7a199ffece..27bda986fc2b 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -2999,8 +2999,8 @@ int si_rlc_init(struct radeon_device *rdev) } r = radeon_bo_pin(rdev->rlc.save_restore_obj, RADEON_GEM_DOMAIN_VRAM, &rdev->rlc.save_restore_gpu_addr); + radeon_bo_unreserve(rdev->rlc.save_restore_obj); if (r) { - radeon_bo_unreserve(rdev->rlc.save_restore_obj); dev_warn(rdev->dev, "(%d) pin RLC sr bo failed\n", r); si_rlc_fini(rdev); return r; @@ -3023,9 +3023,8 @@ int si_rlc_init(struct radeon_device *rdev) } r = radeon_bo_pin(rdev->rlc.clear_state_obj, RADEON_GEM_DOMAIN_VRAM, &rdev->rlc.clear_state_gpu_addr); + radeon_bo_unreserve(rdev->rlc.clear_state_obj); if (r) { - - radeon_bo_unreserve(rdev->rlc.clear_state_obj); dev_warn(rdev->dev, "(%d) pin RLC c bo failed\n", r); si_rlc_fini(rdev); return r; diff --git a/drivers/gpu/drm/savage/savage_state.c b/drivers/gpu/drm/savage/savage_state.c index 031aaaf79ac2..b6d8608375cd 100644 --- a/drivers/gpu/drm/savage/savage_state.c +++ b/drivers/gpu/drm/savage/savage_state.c @@ -988,7 +988,7 @@ int savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_ * for locking on FreeBSD. */ if (cmdbuf->size) { - kcmd_addr = kmalloc(cmdbuf->size * 8, GFP_KERNEL); + kcmd_addr = kmalloc_array(cmdbuf->size, 8, GFP_KERNEL); if (kcmd_addr == NULL) return -ENOMEM; @@ -1015,8 +1015,8 @@ int savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_ cmdbuf->vb_addr = kvb_addr; } if (cmdbuf->nbox) { - kbox_addr = kmalloc(cmdbuf->nbox * sizeof(struct drm_clip_rect), - GFP_KERNEL); + kbox_addr = kmalloc_array(cmdbuf->nbox, sizeof(struct drm_clip_rect), + GFP_KERNEL); if (kbox_addr == NULL) { ret = -ENOMEM; goto done; diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 5340c5f3987b..53673907a6a0 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -47,7 +47,7 @@ static struct vm_operations_struct udl_gem_vm_ops = { static const struct file_operations udl_driver_fops = { .owner = THIS_MODULE, .open = drm_open, - .mmap = drm_gem_mmap, + .mmap = udl_drm_gem_mmap, .poll = drm_poll, .read = drm_read, .unlocked_ioctl = drm_ioctl, diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 1612954a5bc4..96820d03a303 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -121,6 +121,7 @@ struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev, int udl_gem_vmap(struct udl_gem_object *obj); void udl_gem_vunmap(struct udl_gem_object *obj); +int udl_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 852642dc1187..92f19ef329b0 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -71,6 +71,20 @@ int udl_dumb_destroy(struct drm_file *file, struct drm_device *dev, return drm_gem_handle_delete(file, handle); } +int udl_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret; + + ret = drm_gem_mmap(filp, vma); + if (ret) + return ret; + + vma->vm_flags &= ~VM_PFNMAP; + vma->vm_flags |= VM_MIXEDMAP; + + return ret; +} + int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct udl_gem_object *obj = to_udl_bo(vma->vm_private_data); diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a3d033252995..ffddcba32af6 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -34,7 +34,7 @@ config HID config HID_BATTERY_STRENGTH bool depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY - default y + default n config HIDRAW bool "/dev/hidraw raw HID device support" diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c index 12f9777c385d..45c3433f7986 100644 --- a/drivers/hid/hid-picolcd.c +++ b/drivers/hid/hid-picolcd.c @@ -1525,12 +1525,6 @@ static const struct file_operations picolcd_debug_reset_fops = { /* * The "eeprom" file */ -static int picolcd_debug_eeprom_open(struct inode *i, struct file *f) -{ - f->private_data = i->i_private; - return 0; -} - static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, size_t s, loff_t *off) { @@ -1618,7 +1612,7 @@ static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u, */ static const struct file_operations picolcd_debug_eeprom_fops = { .owner = THIS_MODULE, - .open = picolcd_debug_eeprom_open, + .open = simple_open, .read = picolcd_debug_eeprom_read, .write = picolcd_debug_eeprom_write, .llseek = generic_file_llseek, @@ -1627,12 +1621,6 @@ static const struct file_operations picolcd_debug_eeprom_fops = { /* * The "flash" file */ -static int picolcd_debug_flash_open(struct inode *i, struct file *f) -{ - f->private_data = i->i_private; - return 0; -} - /* record a flash address to buf (bounds check to be done by caller) */ static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off) { @@ -1817,7 +1805,7 @@ static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u, */ static const struct file_operations picolcd_debug_flash_fops = { .owner = THIS_MODULE, - .open = picolcd_debug_flash_open, + .open = simple_open, .read = picolcd_debug_flash_read, .write = picolcd_debug_flash_write, .llseek = generic_file_llseek, diff --git a/drivers/hid/hid-tivo.c b/drivers/hid/hid-tivo.c index de47039c708c..9f85f827607f 100644 --- a/drivers/hid/hid-tivo.c +++ b/drivers/hid/hid-tivo.c @@ -62,7 +62,7 @@ static int tivo_input_mapping(struct hid_device *hdev, struct hid_input *hi, static const struct hid_device_id tivo_devices[] = { /* TiVo Slide Bluetooth remote, pairs with a Broadcom dongle */ - { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, { } }; diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c index 17dabc1f339e..eec329197c16 100644 --- a/drivers/hid/hid-wiimote-debug.c +++ b/drivers/hid/hid-wiimote-debug.c @@ -23,12 +23,6 @@ struct wiimote_debug { struct dentry *drm; }; -static int wiidebug_eeprom_open(struct inode *i, struct file *f) -{ - f->private_data = i->i_private; - return 0; -} - static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, loff_t *off) { @@ -83,7 +77,7 @@ static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, static const struct file_operations wiidebug_eeprom_fops = { .owner = THIS_MODULE, - .open = wiidebug_eeprom_open, + .open = simple_open, .read = wiidebug_eeprom_read, .llseek = generic_file_llseek, }; diff --git a/drivers/hsi/Kconfig b/drivers/hsi/Kconfig new file mode 100644 index 000000000000..d94e38dd80c7 --- /dev/null +++ b/drivers/hsi/Kconfig @@ -0,0 +1,19 @@ +# +# HSI driver configuration +# +menuconfig HSI + tristate "HSI support" + ---help--- + The "High speed synchronous Serial Interface" is + synchronous serial interface used mainly to connect + application engines and cellular modems. + +if HSI + +config HSI_BOARDINFO + bool + default y + +source "drivers/hsi/clients/Kconfig" + +endif # HSI diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile new file mode 100644 index 000000000000..9d5d33f90de2 --- /dev/null +++ b/drivers/hsi/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for HSI +# +obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o +obj-$(CONFIG_HSI) += hsi.o +obj-y += clients/ diff --git a/drivers/hsi/clients/Kconfig b/drivers/hsi/clients/Kconfig new file mode 100644 index 000000000000..3bacd275f479 --- /dev/null +++ b/drivers/hsi/clients/Kconfig @@ -0,0 +1,13 @@ +# +# HSI clients configuration +# + +comment "HSI clients" + +config HSI_CHAR + tristate "HSI/SSI character driver" + depends on HSI + ---help--- + If you say Y here, you will enable the HSI/SSI character driver. + This driver provides a simple character device interface for + serial communication with the cellular modem over HSI/SSI bus. diff --git a/drivers/hsi/clients/Makefile b/drivers/hsi/clients/Makefile new file mode 100644 index 000000000000..327c0e27c8b0 --- /dev/null +++ b/drivers/hsi/clients/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for HSI clients +# + +obj-$(CONFIG_HSI_CHAR) += hsi_char.o diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c new file mode 100644 index 000000000000..3ad91f6447d8 --- /dev/null +++ b/drivers/hsi/clients/hsi_char.c @@ -0,0 +1,802 @@ +/* + * HSI character device driver, implements the character device + * interface. + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * + * Contact: Andras Domokos <andras.domokos@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/atomic.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/kmemleak.h> +#include <linux/ioctl.h> +#include <linux/wait.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/device.h> +#include <linux/cdev.h> +#include <linux/uaccess.h> +#include <linux/scatterlist.h> +#include <linux/stat.h> +#include <linux/hsi/hsi.h> +#include <linux/hsi/hsi_char.h> + +#define HSC_DEVS 16 /* Num of channels */ +#define HSC_MSGS 4 + +#define HSC_RXBREAK 0 + +#define HSC_ID_BITS 6 +#define HSC_PORT_ID_BITS 4 +#define HSC_ID_MASK 3 +#define HSC_PORT_ID_MASK 3 +#define HSC_CH_MASK 0xf + +/* + * We support up to 4 controllers that can have up to 4 + * ports, which should currently be more than enough. + */ +#define HSC_BASEMINOR(id, port_id) \ + ((((id) & HSC_ID_MASK) << HSC_ID_BITS) | \ + (((port_id) & HSC_PORT_ID_MASK) << HSC_PORT_ID_BITS)) + +enum { + HSC_CH_OPEN, + HSC_CH_READ, + HSC_CH_WRITE, + HSC_CH_WLINE, +}; + +enum { + HSC_RX, + HSC_TX, +}; + +struct hsc_client_data; +/** + * struct hsc_channel - hsi_char internal channel data + * @ch: channel number + * @flags: Keeps state of the channel (open/close, reading, writing) + * @free_msgs_list: List of free HSI messages/requests + * @rx_msgs_queue: List of pending RX requests + * @tx_msgs_queue: List of pending TX requests + * @lock: Serialize access to the lists + * @cl: reference to the associated hsi_client + * @cl_data: reference to the client data that this channels belongs to + * @rx_wait: RX requests wait queue + * @tx_wait: TX requests wait queue + */ +struct hsc_channel { + unsigned int ch; + unsigned long flags; + struct list_head free_msgs_list; + struct list_head rx_msgs_queue; + struct list_head tx_msgs_queue; + spinlock_t lock; + struct hsi_client *cl; + struct hsc_client_data *cl_data; + wait_queue_head_t rx_wait; + wait_queue_head_t tx_wait; +}; + +/** + * struct hsc_client_data - hsi_char internal client data + * @cdev: Characther device associated to the hsi_client + * @lock: Lock to serialize open/close access + * @flags: Keeps track of port state (rx hwbreak armed) + * @usecnt: Use count for claiming the HSI port (mutex protected) + * @cl: Referece to the HSI client + * @channels: Array of channels accessible by the client + */ +struct hsc_client_data { + struct cdev cdev; + struct mutex lock; + unsigned long flags; + unsigned int usecnt; + struct hsi_client *cl; + struct hsc_channel channels[HSC_DEVS]; +}; + +/* Stores the major number dynamically allocated for hsi_char */ +static unsigned int hsc_major; +/* Maximum buffer size that hsi_char will accept from userspace */ +static unsigned int max_data_size = 0x1000; +module_param(max_data_size, uint, 0); +MODULE_PARM_DESC(max_data_size, "max read/write data size [4,8..65536] (^2)"); + +static void hsc_add_tail(struct hsc_channel *channel, struct hsi_msg *msg, + struct list_head *queue) +{ + unsigned long flags; + + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(&msg->link, queue); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static struct hsi_msg *hsc_get_first_msg(struct hsc_channel *channel, + struct list_head *queue) +{ + struct hsi_msg *msg = NULL; + unsigned long flags; + + spin_lock_irqsave(&channel->lock, flags); + + if (list_empty(queue)) + goto out; + + msg = list_first_entry(queue, struct hsi_msg, link); + list_del(&msg->link); +out: + spin_unlock_irqrestore(&channel->lock, flags); + + return msg; +} + +static inline void hsc_msg_free(struct hsi_msg *msg) +{ + kfree(sg_virt(msg->sgt.sgl)); + hsi_free_msg(msg); +} + +static void hsc_free_list(struct list_head *list) +{ + struct hsi_msg *msg, *tmp; + + list_for_each_entry_safe(msg, tmp, list, link) { + list_del(&msg->link); + hsc_msg_free(msg); + } +} + +static void hsc_reset_list(struct hsc_channel *channel, struct list_head *l) +{ + unsigned long flags; + LIST_HEAD(list); + + spin_lock_irqsave(&channel->lock, flags); + list_splice_init(l, &list); + spin_unlock_irqrestore(&channel->lock, flags); + + hsc_free_list(&list); +} + +static inline struct hsi_msg *hsc_msg_alloc(unsigned int alloc_size) +{ + struct hsi_msg *msg; + void *buf; + + msg = hsi_alloc_msg(1, GFP_KERNEL); + if (!msg) + goto out; + buf = kmalloc(alloc_size, GFP_KERNEL); + if (!buf) { + hsi_free_msg(msg); + goto out; + } + sg_init_one(msg->sgt.sgl, buf, alloc_size); + /* Ignore false positive, due to sg pointer handling */ + kmemleak_ignore(buf); + + return msg; +out: + return NULL; +} + +static inline int hsc_msgs_alloc(struct hsc_channel *channel) +{ + struct hsi_msg *msg; + int i; + + for (i = 0; i < HSC_MSGS; i++) { + msg = hsc_msg_alloc(max_data_size); + if (!msg) + goto out; + msg->channel = channel->ch; + list_add_tail(&msg->link, &channel->free_msgs_list); + } + + return 0; +out: + hsc_free_list(&channel->free_msgs_list); + + return -ENOMEM; +} + +static inline unsigned int hsc_msg_len_get(struct hsi_msg *msg) +{ + return msg->sgt.sgl->length; +} + +static inline void hsc_msg_len_set(struct hsi_msg *msg, unsigned int len) +{ + msg->sgt.sgl->length = len; +} + +static void hsc_rx_completed(struct hsi_msg *msg) +{ + struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl); + struct hsc_channel *channel = cl_data->channels + msg->channel; + + if (test_bit(HSC_CH_READ, &channel->flags)) { + hsc_add_tail(channel, msg, &channel->rx_msgs_queue); + wake_up(&channel->rx_wait); + } else { + hsc_add_tail(channel, msg, &channel->free_msgs_list); + } +} + +static void hsc_rx_msg_destructor(struct hsi_msg *msg) +{ + msg->status = HSI_STATUS_ERROR; + hsc_msg_len_set(msg, 0); + hsc_rx_completed(msg); +} + +static void hsc_tx_completed(struct hsi_msg *msg) +{ + struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl); + struct hsc_channel *channel = cl_data->channels + msg->channel; + + if (test_bit(HSC_CH_WRITE, &channel->flags)) { + hsc_add_tail(channel, msg, &channel->tx_msgs_queue); + wake_up(&channel->tx_wait); + } else { + hsc_add_tail(channel, msg, &channel->free_msgs_list); + } +} + +static void hsc_tx_msg_destructor(struct hsi_msg *msg) +{ + msg->status = HSI_STATUS_ERROR; + hsc_msg_len_set(msg, 0); + hsc_tx_completed(msg); +} + +static void hsc_break_req_destructor(struct hsi_msg *msg) +{ + struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl); + + hsi_free_msg(msg); + clear_bit(HSC_RXBREAK, &cl_data->flags); +} + +static void hsc_break_received(struct hsi_msg *msg) +{ + struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl); + struct hsc_channel *channel = cl_data->channels; + int i, ret; + + /* Broadcast HWBREAK on all channels */ + for (i = 0; i < HSC_DEVS; i++, channel++) { + struct hsi_msg *msg2; + + if (!test_bit(HSC_CH_READ, &channel->flags)) + continue; + msg2 = hsc_get_first_msg(channel, &channel->free_msgs_list); + if (!msg2) + continue; + clear_bit(HSC_CH_READ, &channel->flags); + hsc_msg_len_set(msg2, 0); + msg2->status = HSI_STATUS_COMPLETED; + hsc_add_tail(channel, msg2, &channel->rx_msgs_queue); + wake_up(&channel->rx_wait); + } + hsi_flush(msg->cl); + ret = hsi_async_read(msg->cl, msg); + if (ret < 0) + hsc_break_req_destructor(msg); +} + +static int hsc_break_request(struct hsi_client *cl) +{ + struct hsc_client_data *cl_data = hsi_client_drvdata(cl); + struct hsi_msg *msg; + int ret; + + if (test_and_set_bit(HSC_RXBREAK, &cl_data->flags)) + return -EBUSY; + + msg = hsi_alloc_msg(0, GFP_KERNEL); + if (!msg) { + clear_bit(HSC_RXBREAK, &cl_data->flags); + return -ENOMEM; + } + msg->break_frame = 1; + msg->complete = hsc_break_received; + msg->destructor = hsc_break_req_destructor; + ret = hsi_async_read(cl, msg); + if (ret < 0) + hsc_break_req_destructor(msg); + + return ret; +} + +static int hsc_break_send(struct hsi_client *cl) +{ + struct hsi_msg *msg; + int ret; + + msg = hsi_alloc_msg(0, GFP_ATOMIC); + if (!msg) + return -ENOMEM; + msg->break_frame = 1; + msg->complete = hsi_free_msg; + msg->destructor = hsi_free_msg; + ret = hsi_async_write(cl, msg); + if (ret < 0) + hsi_free_msg(msg); + + return ret; +} + +static int hsc_rx_set(struct hsi_client *cl, struct hsc_rx_config *rxc) +{ + struct hsi_config tmp; + int ret; + + if ((rxc->mode != HSI_MODE_STREAM) && (rxc->mode != HSI_MODE_FRAME)) + return -EINVAL; + if ((rxc->channels == 0) || (rxc->channels > HSC_DEVS)) + return -EINVAL; + if (rxc->channels & (rxc->channels - 1)) + return -EINVAL; + if ((rxc->flow != HSI_FLOW_SYNC) && (rxc->flow != HSI_FLOW_PIPE)) + return -EINVAL; + tmp = cl->rx_cfg; + cl->rx_cfg.mode = rxc->mode; + cl->rx_cfg.channels = rxc->channels; + cl->rx_cfg.flow = rxc->flow; + ret = hsi_setup(cl); + if (ret < 0) { + cl->rx_cfg = tmp; + return ret; + } + if (rxc->mode == HSI_MODE_FRAME) + hsc_break_request(cl); + + return ret; +} + +static inline void hsc_rx_get(struct hsi_client *cl, struct hsc_rx_config *rxc) +{ + rxc->mode = cl->rx_cfg.mode; + rxc->channels = cl->rx_cfg.channels; + rxc->flow = cl->rx_cfg.flow; +} + +static int hsc_tx_set(struct hsi_client *cl, struct hsc_tx_config *txc) +{ + struct hsi_config tmp; + int ret; + + if ((txc->mode != HSI_MODE_STREAM) && (txc->mode != HSI_MODE_FRAME)) + return -EINVAL; + if ((txc->channels == 0) || (txc->channels > HSC_DEVS)) + return -EINVAL; + if (txc->channels & (txc->channels - 1)) + return -EINVAL; + if ((txc->arb_mode != HSI_ARB_RR) && (txc->arb_mode != HSI_ARB_PRIO)) + return -EINVAL; + tmp = cl->tx_cfg; + cl->tx_cfg.mode = txc->mode; + cl->tx_cfg.channels = txc->channels; + cl->tx_cfg.speed = txc->speed; + cl->tx_cfg.arb_mode = txc->arb_mode; + ret = hsi_setup(cl); + if (ret < 0) { + cl->tx_cfg = tmp; + return ret; + } + + return ret; +} + +static inline void hsc_tx_get(struct hsi_client *cl, struct hsc_tx_config *txc) +{ + txc->mode = cl->tx_cfg.mode; + txc->channels = cl->tx_cfg.channels; + txc->speed = cl->tx_cfg.speed; + txc->arb_mode = cl->tx_cfg.arb_mode; +} + +static ssize_t hsc_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos __maybe_unused) +{ + struct hsc_channel *channel = file->private_data; + struct hsi_msg *msg; + ssize_t ret; + + if (len == 0) + return 0; + if (!IS_ALIGNED(len, sizeof(u32))) + return -EINVAL; + if (len > max_data_size) + len = max_data_size; + if (channel->ch >= channel->cl->rx_cfg.channels) + return -ECHRNG; + if (test_and_set_bit(HSC_CH_READ, &channel->flags)) + return -EBUSY; + msg = hsc_get_first_msg(channel, &channel->free_msgs_list); + if (!msg) { + ret = -ENOSPC; + goto out; + } + hsc_msg_len_set(msg, len); + msg->complete = hsc_rx_completed; + msg->destructor = hsc_rx_msg_destructor; + ret = hsi_async_read(channel->cl, msg); + if (ret < 0) { + hsc_add_tail(channel, msg, &channel->free_msgs_list); + goto out; + } + + ret = wait_event_interruptible(channel->rx_wait, + !list_empty(&channel->rx_msgs_queue)); + if (ret < 0) { + clear_bit(HSC_CH_READ, &channel->flags); + hsi_flush(channel->cl); + return -EINTR; + } + + msg = hsc_get_first_msg(channel, &channel->rx_msgs_queue); + if (msg) { + if (msg->status != HSI_STATUS_ERROR) { + ret = copy_to_user((void __user *)buf, + sg_virt(msg->sgt.sgl), hsc_msg_len_get(msg)); + if (ret) + ret = -EFAULT; + else + ret = hsc_msg_len_get(msg); + } else { + ret = -EIO; + } + hsc_add_tail(channel, msg, &channel->free_msgs_list); + } +out: + clear_bit(HSC_CH_READ, &channel->flags); + + return ret; +} + +static ssize_t hsc_write(struct file *file, const char __user *buf, size_t len, + loff_t *ppos __maybe_unused) +{ + struct hsc_channel *channel = file->private_data; + struct hsi_msg *msg; + ssize_t ret; + + if ((len == 0) || !IS_ALIGNED(len, sizeof(u32))) + return -EINVAL; + if (len > max_data_size) + len = max_data_size; + if (channel->ch >= channel->cl->tx_cfg.channels) + return -ECHRNG; + if (test_and_set_bit(HSC_CH_WRITE, &channel->flags)) + return -EBUSY; + msg = hsc_get_first_msg(channel, &channel->free_msgs_list); + if (!msg) { + clear_bit(HSC_CH_WRITE, &channel->flags); + return -ENOSPC; + } + if (copy_from_user(sg_virt(msg->sgt.sgl), (void __user *)buf, len)) { + ret = -EFAULT; + goto out; + } + hsc_msg_len_set(msg, len); + msg->complete = hsc_tx_completed; + msg->destructor = hsc_tx_msg_destructor; + ret = hsi_async_write(channel->cl, msg); + if (ret < 0) + goto out; + + ret = wait_event_interruptible(channel->tx_wait, + !list_empty(&channel->tx_msgs_queue)); + if (ret < 0) { + clear_bit(HSC_CH_WRITE, &channel->flags); + hsi_flush(channel->cl); + return -EINTR; + } + + msg = hsc_get_first_msg(channel, &channel->tx_msgs_queue); + if (msg) { + if (msg->status == HSI_STATUS_ERROR) + ret = -EIO; + else + ret = hsc_msg_len_get(msg); + + hsc_add_tail(channel, msg, &channel->free_msgs_list); + } +out: + clear_bit(HSC_CH_WRITE, &channel->flags); + + return ret; +} + +static long hsc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct hsc_channel *channel = file->private_data; + unsigned int state; + struct hsc_rx_config rxc; + struct hsc_tx_config txc; + long ret = 0; + + switch (cmd) { + case HSC_RESET: + hsi_flush(channel->cl); + break; + case HSC_SET_PM: + if (copy_from_user(&state, (void __user *)arg, sizeof(state))) + return -EFAULT; + if (state == HSC_PM_DISABLE) { + if (test_and_set_bit(HSC_CH_WLINE, &channel->flags)) + return -EINVAL; + ret = hsi_start_tx(channel->cl); + } else if (state == HSC_PM_ENABLE) { + if (!test_and_clear_bit(HSC_CH_WLINE, &channel->flags)) + return -EINVAL; + ret = hsi_stop_tx(channel->cl); + } else { + ret = -EINVAL; + } + break; + case HSC_SEND_BREAK: + return hsc_break_send(channel->cl); + case HSC_SET_RX: + if (copy_from_user(&rxc, (void __user *)arg, sizeof(rxc))) + return -EFAULT; + return hsc_rx_set(channel->cl, &rxc); + case HSC_GET_RX: + hsc_rx_get(channel->cl, &rxc); + if (copy_to_user((void __user *)arg, &rxc, sizeof(rxc))) + return -EFAULT; + break; + case HSC_SET_TX: + if (copy_from_user(&txc, (void __user *)arg, sizeof(txc))) + return -EFAULT; + return hsc_tx_set(channel->cl, &txc); + case HSC_GET_TX: + hsc_tx_get(channel->cl, &txc); + if (copy_to_user((void __user *)arg, &txc, sizeof(txc))) + return -EFAULT; + break; + default: + return -ENOIOCTLCMD; + } + + return ret; +} + +static inline void __hsc_port_release(struct hsc_client_data *cl_data) +{ + BUG_ON(cl_data->usecnt == 0); + + if (--cl_data->usecnt == 0) { + hsi_flush(cl_data->cl); + hsi_release_port(cl_data->cl); + } +} + +static int hsc_open(struct inode *inode, struct file *file) +{ + struct hsc_client_data *cl_data; + struct hsc_channel *channel; + int ret = 0; + + pr_debug("open, minor = %d\n", iminor(inode)); + + cl_data = container_of(inode->i_cdev, struct hsc_client_data, cdev); + mutex_lock(&cl_data->lock); + channel = cl_data->channels + (iminor(inode) & HSC_CH_MASK); + + if (test_and_set_bit(HSC_CH_OPEN, &channel->flags)) { + ret = -EBUSY; + goto out; + } + /* + * Check if we have already claimed the port associated to the HSI + * client. If not then try to claim it, else increase its refcount + */ + if (cl_data->usecnt == 0) { + ret = hsi_claim_port(cl_data->cl, 0); + if (ret < 0) + goto out; + hsi_setup(cl_data->cl); + } + cl_data->usecnt++; + + ret = hsc_msgs_alloc(channel); + if (ret < 0) { + __hsc_port_release(cl_data); + goto out; + } + + file->private_data = channel; + mutex_unlock(&cl_data->lock); + + return ret; +out: + mutex_unlock(&cl_data->lock); + + return ret; +} + +static int hsc_release(struct inode *inode __maybe_unused, struct file *file) +{ + struct hsc_channel *channel = file->private_data; + struct hsc_client_data *cl_data = channel->cl_data; + + mutex_lock(&cl_data->lock); + file->private_data = NULL; + if (test_and_clear_bit(HSC_CH_WLINE, &channel->flags)) + hsi_stop_tx(channel->cl); + __hsc_port_release(cl_data); + hsc_reset_list(channel, &channel->rx_msgs_queue); + hsc_reset_list(channel, &channel->tx_msgs_queue); + hsc_reset_list(channel, &channel->free_msgs_list); + clear_bit(HSC_CH_READ, &channel->flags); + clear_bit(HSC_CH_WRITE, &channel->flags); + clear_bit(HSC_CH_OPEN, &channel->flags); + wake_up(&channel->rx_wait); + wake_up(&channel->tx_wait); + mutex_unlock(&cl_data->lock); + + return 0; +} + +static const struct file_operations hsc_fops = { + .owner = THIS_MODULE, + .read = hsc_read, + .write = hsc_write, + .unlocked_ioctl = hsc_ioctl, + .open = hsc_open, + .release = hsc_release, +}; + +static void __devinit hsc_channel_init(struct hsc_channel *channel) +{ + init_waitqueue_head(&channel->rx_wait); + init_waitqueue_head(&channel->tx_wait); + spin_lock_init(&channel->lock); + INIT_LIST_HEAD(&channel->free_msgs_list); + INIT_LIST_HEAD(&channel->rx_msgs_queue); + INIT_LIST_HEAD(&channel->tx_msgs_queue); +} + +static int __devinit hsc_probe(struct device *dev) +{ + const char devname[] = "hsi_char"; + struct hsc_client_data *cl_data; + struct hsc_channel *channel; + struct hsi_client *cl = to_hsi_client(dev); + unsigned int hsc_baseminor; + dev_t hsc_dev; + int ret; + int i; + + cl_data = kzalloc(sizeof(*cl_data), GFP_KERNEL); + if (!cl_data) { + dev_err(dev, "Could not allocate hsc_client_data\n"); + return -ENOMEM; + } + hsc_baseminor = HSC_BASEMINOR(hsi_id(cl), hsi_port_id(cl)); + if (!hsc_major) { + ret = alloc_chrdev_region(&hsc_dev, hsc_baseminor, + HSC_DEVS, devname); + if (ret > 0) + hsc_major = MAJOR(hsc_dev); + } else { + hsc_dev = MKDEV(hsc_major, hsc_baseminor); + ret = register_chrdev_region(hsc_dev, HSC_DEVS, devname); + } + if (ret < 0) { + dev_err(dev, "Device %s allocation failed %d\n", + hsc_major ? "minor" : "major", ret); + goto out1; + } + mutex_init(&cl_data->lock); + hsi_client_set_drvdata(cl, cl_data); + cdev_init(&cl_data->cdev, &hsc_fops); + cl_data->cdev.owner = THIS_MODULE; + cl_data->cl = cl; + for (i = 0, channel = cl_data->channels; i < HSC_DEVS; i++, channel++) { + hsc_channel_init(channel); + channel->ch = i; + channel->cl = cl; + channel->cl_data = cl_data; + } + + /* 1 hsi client -> N char devices (one for each channel) */ + ret = cdev_add(&cl_data->cdev, hsc_dev, HSC_DEVS); + if (ret) { + dev_err(dev, "Could not add char device %d\n", ret); + goto out2; + } + + return 0; +out2: + unregister_chrdev_region(hsc_dev, HSC_DEVS); +out1: + kfree(cl_data); + + return ret; +} + +static int __devexit hsc_remove(struct device *dev) +{ + struct hsi_client *cl = to_hsi_client(dev); + struct hsc_client_data *cl_data = hsi_client_drvdata(cl); + dev_t hsc_dev = cl_data->cdev.dev; + + cdev_del(&cl_data->cdev); + unregister_chrdev_region(hsc_dev, HSC_DEVS); + hsi_client_set_drvdata(cl, NULL); + kfree(cl_data); + + return 0; +} + +static struct hsi_client_driver hsc_driver = { + .driver = { + .name = "hsi_char", + .owner = THIS_MODULE, + .probe = hsc_probe, + .remove = __devexit_p(hsc_remove), + }, +}; + +static int __init hsc_init(void) +{ + int ret; + + if ((max_data_size < 4) || (max_data_size > 0x10000) || + (max_data_size & (max_data_size - 1))) { + pr_err("Invalid max read/write data size"); + return -EINVAL; + } + + ret = hsi_register_client_driver(&hsc_driver); + if (ret) { + pr_err("Error while registering HSI/SSI driver %d", ret); + return ret; + } + + pr_info("HSI/SSI char device loaded\n"); + + return 0; +} +module_init(hsc_init); + +static void __exit hsc_exit(void) +{ + hsi_unregister_client_driver(&hsc_driver); + pr_info("HSI char device removed\n"); +} +module_exit(hsc_exit); + +MODULE_AUTHOR("Andras Domokos <andras.domokos@nokia.com>"); +MODULE_ALIAS("hsi:hsi_char"); +MODULE_DESCRIPTION("HSI character device"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c new file mode 100644 index 000000000000..2d58f939d27f --- /dev/null +++ b/drivers/hsi/hsi.c @@ -0,0 +1,507 @@ +/* + * HSI core. + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * + * Contact: Carlos Chinea <carlos.chinea@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include <linux/hsi/hsi.h> +#include <linux/compiler.h> +#include <linux/list.h> +#include <linux/kobject.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/notifier.h> +#include "hsi_core.h" + +static ssize_t modalias_show(struct device *dev, + struct device_attribute *a __maybe_unused, char *buf) +{ + return sprintf(buf, "hsi:%s\n", dev_name(dev)); +} + +static struct device_attribute hsi_bus_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL, +}; + +static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + add_uevent_var(env, "MODALIAS=hsi:%s", dev_name(dev)); + + return 0; +} + +static int hsi_bus_match(struct device *dev, struct device_driver *driver) +{ + return strcmp(dev_name(dev), driver->name) == 0; +} + +static struct bus_type hsi_bus_type = { + .name = "hsi", + .dev_attrs = hsi_bus_dev_attrs, + .match = hsi_bus_match, + .uevent = hsi_bus_uevent, +}; + +static void hsi_client_release(struct device *dev) +{ + kfree(to_hsi_client(dev)); +} + +static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) +{ + struct hsi_client *cl; + + cl = kzalloc(sizeof(*cl), GFP_KERNEL); + if (!cl) + return; + cl->tx_cfg = info->tx_cfg; + cl->rx_cfg = info->rx_cfg; + cl->device.bus = &hsi_bus_type; + cl->device.parent = &port->device; + cl->device.release = hsi_client_release; + dev_set_name(&cl->device, info->name); + cl->device.platform_data = info->platform_data; + if (info->archdata) + cl->device.archdata = *info->archdata; + if (device_register(&cl->device) < 0) { + pr_err("hsi: failed to register client: %s\n", info->name); + put_device(&cl->device); + } +} + +static void hsi_scan_board_info(struct hsi_controller *hsi) +{ + struct hsi_cl_info *cl_info; + struct hsi_port *p; + + list_for_each_entry(cl_info, &hsi_board_list, list) + if (cl_info->info.hsi_id == hsi->id) { + p = hsi_find_port_num(hsi, cl_info->info.port); + if (!p) + continue; + hsi_new_client(p, &cl_info->info); + } +} + +static int hsi_remove_client(struct device *dev, void *data __maybe_unused) +{ + device_unregister(dev); + + return 0; +} + +static int hsi_remove_port(struct device *dev, void *data __maybe_unused) +{ + device_for_each_child(dev, NULL, hsi_remove_client); + device_unregister(dev); + + return 0; +} + +static void hsi_controller_release(struct device *dev) +{ + struct hsi_controller *hsi = to_hsi_controller(dev); + + kfree(hsi->port); + kfree(hsi); +} + +static void hsi_port_release(struct device *dev) +{ + kfree(to_hsi_port(dev)); +} + +/** + * hsi_unregister_controller - Unregister an HSI controller + * @hsi: The HSI controller to register + */ +void hsi_unregister_controller(struct hsi_controller *hsi) +{ + device_for_each_child(&hsi->device, NULL, hsi_remove_port); + device_unregister(&hsi->device); +} +EXPORT_SYMBOL_GPL(hsi_unregister_controller); + +/** + * hsi_register_controller - Register an HSI controller and its ports + * @hsi: The HSI controller to register + * + * Returns -errno on failure, 0 on success. + */ +int hsi_register_controller(struct hsi_controller *hsi) +{ + unsigned int i; + int err; + + err = device_add(&hsi->device); + if (err < 0) + return err; + for (i = 0; i < hsi->num_ports; i++) { + hsi->port[i]->device.parent = &hsi->device; + err = device_add(&hsi->port[i]->device); + if (err < 0) + goto out; + } + /* Populate HSI bus with HSI clients */ + hsi_scan_board_info(hsi); + + return 0; +out: + while (i-- > 0) + device_del(&hsi->port[i]->device); + device_del(&hsi->device); + + return err; +} +EXPORT_SYMBOL_GPL(hsi_register_controller); + +/** + * hsi_register_client_driver - Register an HSI client to the HSI bus + * @drv: HSI client driver to register + * + * Returns -errno on failure, 0 on success. + */ +int hsi_register_client_driver(struct hsi_client_driver *drv) +{ + drv->driver.bus = &hsi_bus_type; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(hsi_register_client_driver); + +static inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused) +{ + return 0; +} + +static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) +{ + return 0; +} + +/** + * hsi_put_controller - Free an HSI controller + * + * @hsi: Pointer to the HSI controller to freed + * + * HSI controller drivers should only use this function if they need + * to free their allocated hsi_controller structures before a successful + * call to hsi_register_controller. Other use is not allowed. + */ +void hsi_put_controller(struct hsi_controller *hsi) +{ + unsigned int i; + + if (!hsi) + return; + + for (i = 0; i < hsi->num_ports; i++) + if (hsi->port && hsi->port[i]) + put_device(&hsi->port[i]->device); + put_device(&hsi->device); +} +EXPORT_SYMBOL_GPL(hsi_put_controller); + +/** + * hsi_alloc_controller - Allocate an HSI controller and its ports + * @n_ports: Number of ports on the HSI controller + * @flags: Kernel allocation flags + * + * Return NULL on failure or a pointer to an hsi_controller on success. + */ +struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags) +{ + struct hsi_controller *hsi; + struct hsi_port **port; + unsigned int i; + + if (!n_ports) + return NULL; + + hsi = kzalloc(sizeof(*hsi), flags); + if (!hsi) + return NULL; + port = kzalloc(sizeof(*port)*n_ports, flags); + if (!port) { + kfree(hsi); + return NULL; + } + hsi->num_ports = n_ports; + hsi->port = port; + hsi->device.release = hsi_controller_release; + device_initialize(&hsi->device); + + for (i = 0; i < n_ports; i++) { + port[i] = kzalloc(sizeof(**port), flags); + if (port[i] == NULL) + goto out; + port[i]->num = i; + port[i]->async = hsi_dummy_msg; + port[i]->setup = hsi_dummy_cl; + port[i]->flush = hsi_dummy_cl; + port[i]->start_tx = hsi_dummy_cl; + port[i]->stop_tx = hsi_dummy_cl; + port[i]->release = hsi_dummy_cl; + mutex_init(&port[i]->lock); + ATOMIC_INIT_NOTIFIER_HEAD(&port[i]->n_head); + dev_set_name(&port[i]->device, "port%d", i); + hsi->port[i]->device.release = hsi_port_release; + device_initialize(&hsi->port[i]->device); + } + + return hsi; +out: + hsi_put_controller(hsi); + + return NULL; +} +EXPORT_SYMBOL_GPL(hsi_alloc_controller); + +/** + * hsi_free_msg - Free an HSI message + * @msg: Pointer to the HSI message + * + * Client is responsible to free the buffers pointed by the scatterlists. + */ +void hsi_free_msg(struct hsi_msg *msg) +{ + if (!msg) + return; + sg_free_table(&msg->sgt); + kfree(msg); +} +EXPORT_SYMBOL_GPL(hsi_free_msg); + +/** + * hsi_alloc_msg - Allocate an HSI message + * @nents: Number of memory entries + * @flags: Kernel allocation flags + * + * nents can be 0. This mainly makes sense for read transfer. + * In that case, HSI drivers will call the complete callback when + * there is data to be read without consuming it. + * + * Return NULL on failure or a pointer to an hsi_msg on success. + */ +struct hsi_msg *hsi_alloc_msg(unsigned int nents, gfp_t flags) +{ + struct hsi_msg *msg; + int err; + + msg = kzalloc(sizeof(*msg), flags); + if (!msg) + return NULL; + + if (!nents) + return msg; + + err = sg_alloc_table(&msg->sgt, nents, flags); + if (unlikely(err)) { + kfree(msg); + msg = NULL; + } + + return msg; +} +EXPORT_SYMBOL_GPL(hsi_alloc_msg); + +/** + * hsi_async - Submit an HSI transfer to the controller + * @cl: HSI client sending the transfer + * @msg: The HSI transfer passed to controller + * + * The HSI message must have the channel, ttype, complete and destructor + * fields set beforehand. If nents > 0 then the client has to initialize + * also the scatterlists to point to the buffers to write to or read from. + * + * HSI controllers relay on pre-allocated buffers from their clients and they + * do not allocate buffers on their own. + * + * Once the HSI message transfer finishes, the HSI controller calls the + * complete callback with the status and actual_len fields of the HSI message + * updated. The complete callback can be called before returning from + * hsi_async. + * + * Returns -errno on failure or 0 on success + */ +int hsi_async(struct hsi_client *cl, struct hsi_msg *msg) +{ + struct hsi_port *port = hsi_get_port(cl); + + if (!hsi_port_claimed(cl)) + return -EACCES; + + WARN_ON_ONCE(!msg->destructor || !msg->complete); + msg->cl = cl; + + return port->async(msg); +} +EXPORT_SYMBOL_GPL(hsi_async); + +/** + * hsi_claim_port - Claim the HSI client's port + * @cl: HSI client that wants to claim its port + * @share: Flag to indicate if the client wants to share the port or not. + * + * Returns -errno on failure, 0 on success. + */ +int hsi_claim_port(struct hsi_client *cl, unsigned int share) +{ + struct hsi_port *port = hsi_get_port(cl); + int err = 0; + + mutex_lock(&port->lock); + if ((port->claimed) && (!port->shared || !share)) { + err = -EBUSY; + goto out; + } + if (!try_module_get(to_hsi_controller(port->device.parent)->owner)) { + err = -ENODEV; + goto out; + } + port->claimed++; + port->shared = !!share; + cl->pclaimed = 1; +out: + mutex_unlock(&port->lock); + + return err; +} +EXPORT_SYMBOL_GPL(hsi_claim_port); + +/** + * hsi_release_port - Release the HSI client's port + * @cl: HSI client which previously claimed its port + */ +void hsi_release_port(struct hsi_client *cl) +{ + struct hsi_port *port = hsi_get_port(cl); + + mutex_lock(&port->lock); + /* Allow HW driver to do some cleanup */ + port->release(cl); + if (cl->pclaimed) + port->claimed--; + BUG_ON(port->claimed < 0); + cl->pclaimed = 0; + if (!port->claimed) + port->shared = 0; + module_put(to_hsi_controller(port->device.parent)->owner); + mutex_unlock(&port->lock); +} +EXPORT_SYMBOL_GPL(hsi_release_port); + +static int hsi_event_notifier_call(struct notifier_block *nb, + unsigned long event, void *data __maybe_unused) +{ + struct hsi_client *cl = container_of(nb, struct hsi_client, nb); + + (*cl->ehandler)(cl, event); + + return 0; +} + +/** + * hsi_register_port_event - Register a client to receive port events + * @cl: HSI client that wants to receive port events + * @cb: Event handler callback + * + * Clients should register a callback to be able to receive + * events from the ports. Registration should happen after + * claiming the port. + * The handler can be called in interrupt context. + * + * Returns -errno on error, or 0 on success. + */ +int hsi_register_port_event(struct hsi_client *cl, + void (*handler)(struct hsi_client *, unsigned long)) +{ + struct hsi_port *port = hsi_get_port(cl); + + if (!handler || cl->ehandler) + return -EINVAL; + if (!hsi_port_claimed(cl)) + return -EACCES; + cl->ehandler = handler; + cl->nb.notifier_call = hsi_event_notifier_call; + + return atomic_notifier_chain_register(&port->n_head, &cl->nb); +} +EXPORT_SYMBOL_GPL(hsi_register_port_event); + +/** + * hsi_unregister_port_event - Stop receiving port events for a client + * @cl: HSI client that wants to stop receiving port events + * + * Clients should call this function before releasing their associated + * port. + * + * Returns -errno on error, or 0 on success. + */ +int hsi_unregister_port_event(struct hsi_client *cl) +{ + struct hsi_port *port = hsi_get_port(cl); + int err; + + WARN_ON(!hsi_port_claimed(cl)); + + err = atomic_notifier_chain_unregister(&port->n_head, &cl->nb); + if (!err) + cl->ehandler = NULL; + + return err; +} +EXPORT_SYMBOL_GPL(hsi_unregister_port_event); + +/** + * hsi_event -Notifies clients about port events + * @port: Port where the event occurred + * @event: The event type + * + * Clients should not be concerned about wake line behavior. However, due + * to a race condition in HSI HW protocol, clients need to be notified + * about wake line changes, so they can implement a workaround for it. + * + * Events: + * HSI_EVENT_START_RX - Incoming wake line high + * HSI_EVENT_STOP_RX - Incoming wake line down + * + * Returns -errno on error, or 0 on success. + */ +int hsi_event(struct hsi_port *port, unsigned long event) +{ + return atomic_notifier_call_chain(&port->n_head, event, NULL); +} +EXPORT_SYMBOL_GPL(hsi_event); + +static int __init hsi_init(void) +{ + return bus_register(&hsi_bus_type); +} +postcore_initcall(hsi_init); + +static void __exit hsi_exit(void) +{ + bus_unregister(&hsi_bus_type); +} +module_exit(hsi_exit); + +MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>"); +MODULE_DESCRIPTION("High-speed Synchronous Serial Interface (HSI) framework"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hsi/hsi_boardinfo.c b/drivers/hsi/hsi_boardinfo.c new file mode 100644 index 000000000000..e56bc6da5f98 --- /dev/null +++ b/drivers/hsi/hsi_boardinfo.c @@ -0,0 +1,62 @@ +/* + * HSI clients registration interface + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * + * Contact: Carlos Chinea <carlos.chinea@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include <linux/hsi/hsi.h> +#include <linux/list.h> +#include <linux/slab.h> +#include "hsi_core.h" + +/* + * hsi_board_list is only used internally by the HSI framework. + * No one else is allowed to make use of it. + */ +LIST_HEAD(hsi_board_list); +EXPORT_SYMBOL_GPL(hsi_board_list); + +/** + * hsi_register_board_info - Register HSI clients information + * @info: Array of HSI clients on the board + * @len: Length of the array + * + * HSI clients are statically declared and registered on board files. + * + * HSI clients will be automatically registered to the HSI bus once the + * controller and the port where the clients wishes to attach are registered + * to it. + * + * Return -errno on failure, 0 on success. + */ +int __init hsi_register_board_info(struct hsi_board_info const *info, + unsigned int len) +{ + struct hsi_cl_info *cl_info; + + cl_info = kzalloc(sizeof(*cl_info) * len, GFP_KERNEL); + if (!cl_info) + return -ENOMEM; + + for (; len; len--, info++, cl_info++) { + cl_info->info = *info; + list_add_tail(&cl_info->list, &hsi_board_list); + } + + return 0; +} diff --git a/drivers/hsi/hsi_core.h b/drivers/hsi/hsi_core.h new file mode 100644 index 000000000000..ab5c2fb175fd --- /dev/null +++ b/drivers/hsi/hsi_core.h @@ -0,0 +1,35 @@ +/* + * HSI framework internal interfaces, + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * + * Contact: Carlos Chinea <carlos.chinea@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __LINUX_HSI_CORE_H__ +#define __LINUX_HSI_CORE_H__ + +#include <linux/hsi/hsi.h> + +struct hsi_cl_info { + struct list_head list; + struct hsi_board_info info; +}; + +extern struct list_head hsi_board_list; + +#endif /* __LINUX_HSI_CORE_H__ */ diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5b32d56dbb4d..8deedc1b9840 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -253,7 +253,8 @@ config SENSORS_K10TEMP If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported are later revisions of the AMD Family 10h and all revisions of the AMD Family 11h, - 12h (Llano), 14h (Brazos) and 15h (Bulldozer) microarchitectures. + 12h (Llano), 14h (Brazos) and 15h (Bulldozer/Trinity) + microarchitectures. This driver can also be built as a module. If so, the module will be called k10temp. @@ -425,7 +426,7 @@ config SENSORS_GL520SM config SENSORS_GPIO_FAN tristate "GPIO fan" - depends on GENERIC_GPIO + depends on GPIOLIB help If you say yes here you get support for fans connected to GPIO lines. @@ -883,7 +884,7 @@ source drivers/hwmon/pmbus/Kconfig config SENSORS_SHT15 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." - depends on GENERIC_GPIO + depends on GPIOLIB help If you say yes here you get support for the Sensiron SHT10, SHT11, SHT15, SHT71, SHT75 humidity and temperature sensors. diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 554f046bcf20..9140236a0182 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -391,6 +391,7 @@ static ssize_t show_str(struct device *dev, break; default: BUG(); + val = ""; } return sprintf(buf, "%s\n", val); @@ -632,6 +633,7 @@ static int register_ro_attrs(struct acpi_power_meter_resource *resource, sensors->dev_attr.show = ro->show; sensors->index = ro->index; + sysfs_attr_init(&sensors->dev_attr.attr); res = device_create_file(dev, &sensors->dev_attr); if (res) { sensors->dev_attr.attr.name = NULL; @@ -661,6 +663,7 @@ static int register_rw_attrs(struct acpi_power_meter_resource *resource, sensors->dev_attr.store = rw->set; sensors->index = rw->index; + sysfs_attr_init(&sensors->dev_attr.attr); res = device_create_file(dev, &sensors->dev_attr); if (res) { sensors->dev_attr.attr.name = NULL; diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c index 0e0cfcc36f8d..f85ce70d9677 100644 --- a/drivers/hwmon/ad7314.c +++ b/drivers/hwmon/ad7314.c @@ -47,7 +47,7 @@ struct ad7314_data { u16 rx ____cacheline_aligned; }; -static int ad7314_spi_read(struct ad7314_data *chip, s16 *data) +static int ad7314_spi_read(struct ad7314_data *chip) { int ret; @@ -57,9 +57,7 @@ static int ad7314_spi_read(struct ad7314_data *chip, s16 *data) return ret; } - *data = be16_to_cpu(chip->rx); - - return ret; + return be16_to_cpu(chip->rx); } static ssize_t ad7314_show_temperature(struct device *dev, @@ -70,12 +68,12 @@ static ssize_t ad7314_show_temperature(struct device *dev, s16 data; int ret; - ret = ad7314_spi_read(chip, &data); + ret = ad7314_spi_read(chip); if (ret < 0) return ret; switch (spi_get_device_id(chip->spi_dev)->driver_data) { case ad7314: - data = (data & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET; + data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET; data = (data << 6) >> 6; return sprintf(buf, "%d\n", 250 * data); @@ -86,7 +84,7 @@ static ssize_t ad7314_show_temperature(struct device *dev, * with a sign bit - which is a 14 bit 2's complement * register. 1lsb - 31.25 milli degrees centigrade */ - data &= ADT7301_TEMP_MASK; + data = ret & ADT7301_TEMP_MASK; data = (data << 2) >> 2; return sprintf(buf, "%d\n", @@ -128,6 +126,7 @@ static int __devinit ad7314_probe(struct spi_device *spi_dev) ret = PTR_ERR(chip->hwmon_dev); goto error_remove_group; } + chip->spi_dev = spi_dev; return 0; error_remove_group: diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index ff37363ea5bc..44e1fd7f3d81 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -233,18 +233,15 @@ static const auto_chan_table_t auto_channel_select_table_adm1030 = { * nearest match if no exact match where found. */ static int -get_fan_auto_nearest(struct adm1031_data *data, - int chan, u8 val, u8 reg, u8 *new_reg) +get_fan_auto_nearest(struct adm1031_data *data, int chan, u8 val, u8 reg) { int i; int first_match = -1, exact_match = -1; u8 other_reg_val = (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1]; - if (val == 0) { - *new_reg = 0; + if (val == 0) return 0; - } for (i = 0; i < 8; i++) { if ((val == (*data->chan_select_table)[i][chan]) && @@ -264,13 +261,11 @@ get_fan_auto_nearest(struct adm1031_data *data, } if (exact_match >= 0) - *new_reg = exact_match; + return exact_match; else if (first_match >= 0) - *new_reg = first_match; - else - return -EINVAL; + return first_match; - return 0; + return -EINVAL; } static ssize_t show_fan_auto_channel(struct device *dev, @@ -301,11 +296,12 @@ set_fan_auto_channel(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); - ret = get_fan_auto_nearest(data, nr, val, data->conf1, ®); - if (ret) { + ret = get_fan_auto_nearest(data, nr, val, data->conf1); + if (ret < 0) { mutex_unlock(&data->update_lock); return ret; } + reg = ret; data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1); if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) ^ (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) { diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c index 7765e4f74ec5..1958f03efd7a 100644 --- a/drivers/hwmon/ads1015.c +++ b/drivers/hwmon/ads1015.c @@ -59,14 +59,11 @@ struct ads1015_data { struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; }; -static int ads1015_read_value(struct i2c_client *client, unsigned int channel, - int *value) +static int ads1015_read_adc(struct i2c_client *client, unsigned int channel) { u16 config; - s16 conversion; struct ads1015_data *data = i2c_get_clientdata(client); unsigned int pga = data->channel_data[channel].pga; - int fullscale; unsigned int data_rate = data->channel_data[channel].data_rate; unsigned int conversion_time_ms; int res; @@ -78,7 +75,6 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel, if (res < 0) goto err_unlock; config = res; - fullscale = fullscale_table[pga]; conversion_time_ms = DIV_ROUND_UP(1000, data_rate_table[data_rate]); /* setup and start single conversion */ @@ -105,33 +101,36 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel, } res = i2c_smbus_read_word_swapped(client, ADS1015_CONVERSION); - if (res < 0) - goto err_unlock; - conversion = res; - - mutex_unlock(&data->update_lock); - - *value = DIV_ROUND_CLOSEST(conversion * fullscale, 0x7ff0); - - return 0; err_unlock: mutex_unlock(&data->update_lock); return res; } +static int ads1015_reg_to_mv(struct i2c_client *client, unsigned int channel, + s16 reg) +{ + struct ads1015_data *data = i2c_get_clientdata(client); + unsigned int pga = data->channel_data[channel].pga; + int fullscale = fullscale_table[pga]; + + return DIV_ROUND_CLOSEST(reg * fullscale, 0x7ff0); +} + /* sysfs callback function */ static ssize_t show_in(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); - int in; int res; + int index = attr->index; - res = ads1015_read_value(client, attr->index, &in); + res = ads1015_read_adc(client, index); + if (res < 0) + return res; - return (res < 0) ? res : sprintf(buf, "%d\n", in); + return sprintf(buf, "%d\n", ads1015_reg_to_mv(client, index, res)); } static const struct sensor_device_attribute ads1015_in[] = { diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 0d3141fbbc20..b9d512331ed4 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -52,7 +52,7 @@ module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ -#define NUM_REAL_CORES 16 /* Number of Real cores per cpu */ +#define NUM_REAL_CORES 32 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */ #define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) @@ -709,6 +709,10 @@ static void __cpuinit put_core_offline(unsigned int cpu) indx = TO_ATTR_NO(cpu); + /* The core id is too big, just return */ + if (indx > MAX_CORE_DATA - 1) + return; + if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu) coretemp_remove_core(pdata, &pdev->dev, indx); diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 729499e75210..ece4159bd453 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -276,6 +276,7 @@ static bool duty_mode_enabled(u8 pwm_enable) return false; default: BUG(); + return true; } } @@ -291,6 +292,7 @@ static bool auto_mode_enabled(u8 pwm_enable) return true; default: BUG(); + return false; } } diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index b7494af1e4a9..e8e18cab1fb8 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -122,6 +122,41 @@ static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4) return true; } +/* + * Newer BKDG versions have an updated recommendation on how to properly + * initialize the running average range (was: 0xE, now: 0x9). This avoids + * counter saturations resulting in bogus power readings. + * We correct this value ourselves to cope with older BIOSes. + */ +static DEFINE_PCI_DEVICE_TABLE(affected_device) = { + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, + { 0 } +}; + +static void __devinit tweak_runavg_range(struct pci_dev *pdev) +{ + u32 val; + + /* + * let this quirk apply only to the current version of the + * northbridge, since future versions may change the behavior + */ + if (!pci_match_id(affected_device, pdev)) + return; + + pci_bus_read_config_dword(pdev->bus, + PCI_DEVFN(PCI_SLOT(pdev->devfn), 5), + REG_TDP_RUNNING_AVERAGE, &val); + if ((val & 0xf) != 0xe) + return; + + val &= ~0xf; + val |= 0x9; + pci_bus_write_config_dword(pdev->bus, + PCI_DEVFN(PCI_SLOT(pdev->devfn), 5), + REG_TDP_RUNNING_AVERAGE, val); +} + static void __devinit fam15h_power_init_data(struct pci_dev *f4, struct fam15h_power_data *data) { @@ -155,6 +190,13 @@ static int __devinit fam15h_power_probe(struct pci_dev *pdev, struct device *dev; int err; + /* + * though we ignore every other northbridge, we still have to + * do the tweaking on _each_ node in MCM processors as the counters + * are working hand-in-hand + */ + tweak_runavg_range(pdev); + if (!fam15h_power_is_internal_node0(pdev)) { err = -ENODEV; goto exit; diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index aba29d63f195..307bb325dde9 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -33,6 +33,9 @@ static bool force; module_param(force, bool, 0444); MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); +/* PCI-IDs for Northbridge devices not used anywhere else */ +#define PCI_DEVICE_ID_AMD_15H_M10H_NB_F3 0x1403 + /* CPUID function 0x80000001, ebx */ #define CPUID_PKGTYPE_MASK 0xf0000000 #define CPUID_PKGTYPE_F 0x00000000 @@ -210,6 +213,7 @@ static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_NB_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 193067e27b6f..de8f7adaccbd 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -596,8 +596,10 @@ static int max6639_remove(struct i2c_client *client) return 0; } -static int max6639_suspend(struct i2c_client *client, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int max6639_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); int data = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG); if (data < 0) return data; @@ -606,8 +608,9 @@ static int max6639_suspend(struct i2c_client *client, pm_message_t mesg) MAX6639_REG_GCONFIG, data | MAX6639_GCONFIG_STANDBY); } -static int max6639_resume(struct i2c_client *client) +static int max6639_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); int data = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG); if (data < 0) return data; @@ -615,6 +618,7 @@ static int max6639_resume(struct i2c_client *client) return i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG, data & ~MAX6639_GCONFIG_STANDBY); } +#endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id max6639_id[] = { {"max6639", 0}, @@ -623,15 +627,18 @@ static const struct i2c_device_id max6639_id[] = { MODULE_DEVICE_TABLE(i2c, max6639_id); +static const struct dev_pm_ops max6639_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(max6639_suspend, max6639_resume) +}; + static struct i2c_driver max6639_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "max6639", + .pm = &max6639_pm_ops, }, .probe = max6639_probe, .remove = max6639_remove, - .suspend = max6639_suspend, - .resume = max6639_resume, .id_table = max6639_id, .detect = max6639_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index be51037363c8..29b319db573e 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -710,13 +710,13 @@ static u16 pmbus_data2reg(struct pmbus_data *data, * If a negative value is stored in any of the referenced registers, this value * reflects an error code which will be returned. */ -static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val) +static int pmbus_get_boolean(struct pmbus_data *data, int index) { u8 s1 = (index >> 24) & 0xff; u8 s2 = (index >> 16) & 0xff; u8 reg = (index >> 8) & 0xff; u8 mask = index & 0xff; - int status; + int ret, status; u8 regval; status = data->status[reg]; @@ -725,7 +725,7 @@ static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val) regval = status & mask; if (!s1 && !s2) - *val = !!regval; + ret = !!regval; else { long v1, v2; struct pmbus_sensor *sensor1, *sensor2; @@ -739,9 +739,9 @@ static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val) v1 = pmbus_reg2data(data, sensor1); v2 = pmbus_reg2data(data, sensor2); - *val = !!(regval && v1 >= v2); + ret = !!(regval && v1 >= v2); } - return 0; + return ret; } static ssize_t pmbus_show_boolean(struct device *dev, @@ -750,11 +750,10 @@ static ssize_t pmbus_show_boolean(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct pmbus_data *data = pmbus_update_device(dev); int val; - int err; - err = pmbus_get_boolean(data, attr->index, &val); - if (err) - return err; + val = pmbus_get_boolean(data, attr->index); + if (val < 0) + return val; return snprintf(buf, PAGE_SIZE, "%d\n", val); } diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index d3b778da3f86..c5f6be478bad 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c @@ -343,10 +343,11 @@ exit: return err; } -static int __init smsc47b397_find(unsigned short *addr) +static int __init smsc47b397_find(void) { u8 id, rev; char *name; + unsigned short addr; superio_enter(); id = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID); @@ -370,14 +371,14 @@ static int __init smsc47b397_find(unsigned short *addr) rev = superio_inb(SUPERIO_REG_DEVREV); superio_select(SUPERIO_REG_LD8); - *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) + addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) | superio_inb(SUPERIO_REG_BASE_LSB); pr_info("found SMSC %s (base address 0x%04x, revision %u)\n", - name, *addr, rev); + name, addr, rev); superio_exit(); - return 0; + return addr; } static int __init smsc47b397_init(void) @@ -385,9 +386,10 @@ static int __init smsc47b397_init(void) unsigned short address; int ret; - ret = smsc47b397_find(&address); - if (ret) + ret = smsc47b397_find(); + if (ret < 0) return ret; + address = ret; ret = platform_driver_register(&smsc47b397_driver); if (ret) diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index c590c1469793..b5aa38dd7ab9 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -491,10 +491,10 @@ static const struct attribute_group smsc47m1_group = { .attrs = smsc47m1_attributes, }; -static int __init smsc47m1_find(unsigned short *addr, - struct smsc47m1_sio_data *sio_data) +static int __init smsc47m1_find(struct smsc47m1_sio_data *sio_data) { u8 val; + unsigned short addr; superio_enter(); val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID); @@ -546,9 +546,9 @@ static int __init smsc47m1_find(unsigned short *addr, } superio_select(); - *addr = (superio_inb(SUPERIO_REG_BASE) << 8) + addr = (superio_inb(SUPERIO_REG_BASE) << 8) | superio_inb(SUPERIO_REG_BASE + 1); - if (*addr == 0) { + if (addr == 0) { pr_info("Device address not set, will not use\n"); superio_exit(); return -ENODEV; @@ -565,7 +565,7 @@ static int __init smsc47m1_find(unsigned short *addr, } superio_exit(); - return 0; + return addr; } /* Restore device to its initial state */ @@ -938,13 +938,15 @@ static int __init sm_smsc47m1_init(void) unsigned short address; struct smsc47m1_sio_data sio_data; - if (smsc47m1_find(&address, &sio_data)) - return -ENODEV; + err = smsc47m1_find(&sio_data); + if (err < 0) + return err; + address = err; /* Sets global pdev as a side effect */ err = smsc47m1_device_add(address, &sio_data); if (err) - goto exit; + return err; err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe); if (err) @@ -955,7 +957,6 @@ static int __init sm_smsc47m1_init(void) exit_device: platform_device_unregister(pdev); smsc47m1_restore(&sio_data); -exit: return err; } diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index a25350cf9554..54922ed12978 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -2619,15 +2619,15 @@ static struct platform_driver w83627ehf_driver = { static int __init w83627ehf_find(int sioaddr, unsigned short *addr, struct w83627ehf_sio_data *sio_data) { - static const char __initdata sio_name_W83627EHF[] = "W83627EHF"; - static const char __initdata sio_name_W83627EHG[] = "W83627EHG"; - static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; - static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P"; - static const char __initdata sio_name_W83627UHG[] = "W83627UHG"; - static const char __initdata sio_name_W83667HG[] = "W83667HG"; - static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B"; - static const char __initdata sio_name_NCT6775[] = "NCT6775F"; - static const char __initdata sio_name_NCT6776[] = "NCT6776F"; + static const char sio_name_W83627EHF[] __initconst = "W83627EHF"; + static const char sio_name_W83627EHG[] __initconst = "W83627EHG"; + static const char sio_name_W83627DHG[] __initconst = "W83627DHG"; + static const char sio_name_W83627DHG_P[] __initconst = "W83627DHG-P"; + static const char sio_name_W83627UHG[] __initconst = "W83627UHG"; + static const char sio_name_W83667HG[] __initconst = "W83667HG"; + static const char sio_name_W83667HG_B[] __initconst = "W83667HG-B"; + static const char sio_name_NCT6775[] __initconst = "NCT6775F"; + static const char sio_name_NCT6776[] __initconst = "NCT6776F"; u16 val; const char *sio_name; diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 37f42113af31..00e8f213f56e 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -182,7 +182,6 @@ static int i2c_dw_pci_resume(struct device *dev) pci_restore_state(pdev); i2c_dw_init(i2c); - i2c_dw_enable(i2c); return 0; } diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index f086131cb1c7..c811289b61e2 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -324,7 +324,7 @@ static s32 pch_i2c_wait_for_xfer_complete(struct i2c_algo_pch_data *adap) { long ret; ret = wait_event_timeout(pch_event, - (adap->pch_event_flag != 0), msecs_to_jiffies(50)); + (adap->pch_event_flag != 0), msecs_to_jiffies(1000)); if (ret == 0) { pch_err(adap, "timeout: %x\n", adap->pch_event_flag); @@ -1063,6 +1063,6 @@ module_exit(pch_pci_exit); MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semico ML7213/ML7223/ML7831 IOH I2C"); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Tomoya MORINAGA. <tomoya-linux@dsn.lapis-semi.com>"); +MODULE_AUTHOR("Tomoya MORINAGA. <tomoya.rohm@gmail.com>"); module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); module_param(pch_clk, int, (S_IRUSR | S_IWUSR)); diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 3d471d56bf15..76b8af44f634 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -227,6 +227,7 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, return -EINVAL; init_completion(&i2c->cmd_complete); + i2c->cmd_err = 0; flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0; @@ -252,6 +253,9 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, if (i2c->cmd_err == -ENXIO) mxs_i2c_reset(i2c); + else + writel(MXS_I2C_QUEUECTRL_QUEUE_RUN, + i2c->regs + MXS_I2C_QUEUECTRL_CLR); dev_dbg(i2c->dev, "Done with err=%d\n", i2c->cmd_err); @@ -299,8 +303,6 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id) MXS_I2C_CTRL1_SLAVE_STOP_IRQ | MXS_I2C_CTRL1_SLAVE_IRQ)) /* MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ is only for slaves */ i2c->cmd_err = -EIO; - else - i2c->cmd_err = 0; is_last_cmd = (readl(i2c->regs + MXS_I2C_QUEUESTAT) & MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK) == 0; @@ -384,8 +386,6 @@ static int __devexit mxs_i2c_remove(struct platform_device *pdev) if (ret) return -EBUSY; - writel(MXS_I2C_QUEUECTRL_QUEUE_RUN, - i2c->regs + MXS_I2C_QUEUECTRL_CLR); writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET); platform_set_drvdata(pdev, NULL); diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index 04be9f82e14b..eb8ad538c79f 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -546,8 +546,7 @@ static int i2c_pnx_controller_suspend(struct platform_device *pdev, { struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); - /* FIXME: shouldn't this be clk_disable? */ - clk_enable(alg_data->clk); + clk_disable(alg_data->clk); return 0; } diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index e978635e60f0..55e5ea62ccee 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -516,6 +516,14 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) return 0; + /* + * NACK interrupt is generated before the I2C controller generates the + * STOP condition on the bus. So wait for 2 clock periods before resetting + * the controller so that STOP condition has been delivered properly. + */ + if (i2c_dev->msg_err == I2C_ERR_NO_ACK) + udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->bus_clk_rate)); + tegra_i2c_init(i2c_dev); if (i2c_dev->msg_err == I2C_ERR_NO_ACK) { if (msg->flags & I2C_M_IGNORE_NAK) diff --git a/drivers/idle/i7300_idle.c b/drivers/idle/i7300_idle.c index c976285d313e..fa080ebd568f 100644 --- a/drivers/idle/i7300_idle.c +++ b/drivers/idle/i7300_idle.c @@ -516,12 +516,6 @@ static struct notifier_block i7300_idle_nb = { MODULE_DEVICE_TABLE(pci, pci_tbl); -int stats_open_generic(struct inode *inode, struct file *fp) -{ - fp->private_data = inode->i_private; - return 0; -} - static ssize_t stats_read_ul(struct file *fp, char __user *ubuf, size_t count, loff_t *off) { @@ -534,7 +528,7 @@ static ssize_t stats_read_ul(struct file *fp, char __user *ubuf, size_t count, } static const struct file_operations idle_fops = { - .open = stats_open_generic, + .open = simple_open, .read = stats_read_ul, .llseek = default_llseek, }; diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 426bb7617ec6..b0d0bc8a6fb6 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -1854,6 +1854,8 @@ static bool generate_unmatched_resp(struct ib_mad_private *recv, response->mad.mad.mad_hdr.method = IB_MGMT_METHOD_GET_RESP; response->mad.mad.mad_hdr.status = cpu_to_be16(IB_MGMT_MAD_STATUS_UNSUPPORTED_METHOD_ATTRIB); + if (recv->mad.mad.mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) + response->mad.mad.mad_hdr.status |= IB_SMP_DIRECTION; return true; } else { @@ -1869,6 +1871,7 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv, struct ib_mad_list_head *mad_list; struct ib_mad_agent_private *mad_agent; int port_num; + int ret = IB_MAD_RESULT_SUCCESS; mad_list = (struct ib_mad_list_head *)(unsigned long)wc->wr_id; qp_info = mad_list->mad_queue->qp_info; @@ -1952,8 +1955,6 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv, local: /* Give driver "right of first refusal" on incoming MAD */ if (port_priv->device->process_mad) { - int ret; - ret = port_priv->device->process_mad(port_priv->device, 0, port_priv->port_num, wc, &recv->grh, @@ -1981,7 +1982,8 @@ local: * or via recv_handler in ib_mad_complete_recv() */ recv = NULL; - } else if (generate_unmatched_resp(recv, response)) { + } else if ((ret & IB_MAD_RESULT_SUCCESS) && + generate_unmatched_resp(recv, response)) { agent_send_response(&response->mad.mad, &recv->grh, wc, port_priv->device, port_num, qp_info->qp->qp_num); } diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 83b720ef6c34..246fdc151652 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -179,7 +179,7 @@ static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused, { struct ib_port_attr attr; char *speed = ""; - int rate = -1; /* in deci-Gb/sec */ + int rate; /* in deci-Gb/sec */ ssize_t ret; ret = ib_query_port(p->ibdev, p->port_num, &attr); @@ -187,9 +187,6 @@ static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused, return ret; switch (attr.active_speed) { - case IB_SPEED_SDR: - rate = 25; - break; case IB_SPEED_DDR: speed = " DDR"; rate = 50; @@ -210,6 +207,10 @@ static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused, speed = " EDR"; rate = 250; break; + case IB_SPEED_SDR: + default: /* default to SDR for invalid rates */ + rate = 25; + break; } rate *= ib_width_enum_to_int(attr.active_width); diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 75d305629300..b948b6dd5d55 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -247,12 +247,17 @@ static int ib_link_query_port(struct ib_device *ibdev, u8 port, err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad); if (err) - return err; + goto out; /* Checking LinkSpeedActive for FDR-10 */ if (out_mad->data[15] & 0x1) props->active_speed = IB_SPEED_FDR10; } + + /* Avoid wrong speed value returned by FW if the IB link is down. */ + if (props->state == IB_PORT_DOWN) + props->active_speed = IB_SPEED_SDR; + out: kfree(in_mad); kfree(out_mad); diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 69e2ad06e515..daf21b899999 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -3232,6 +3232,7 @@ static void srpt_add_one(struct ib_device *device) srq_attr.attr.max_wr = sdev->srq_size; srq_attr.attr.max_sge = 1; srq_attr.attr.srq_limit = 0; + srq_attr.srq_type = IB_SRQT_BASIC; sdev->srq = ib_create_srq(sdev->pd, &srq_attr); if (IS_ERR(sdev->srq)) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 2d787796bf50..7faf4a7fcaa9 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -380,8 +380,7 @@ config INPUT_TWL4030_VIBRA config INPUT_TWL6040_VIBRA tristate "Support for TWL6040 Vibrator" - depends on TWL4030_CORE - select TWL6040_CORE + depends on TWL6040_CORE select INPUT_FF_MEMLESS help This option enables support for TWL6040 Vibrator Driver. diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c index 34aebb8cd080..3c843cd725fa 100644 --- a/drivers/input/misc/da9052_onkey.c +++ b/drivers/input/misc/da9052_onkey.c @@ -95,7 +95,8 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev) input_dev = input_allocate_device(); if (!onkey || !input_dev) { dev_err(&pdev->dev, "Failed to allocate memory\n"); - return -ENOMEM; + error = -ENOMEM; + goto err_free_mem; } onkey->input = input_dev; diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 45874fed523a..14e94f56cb7d 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -28,7 +28,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/workqueue.h> -#include <linux/i2c/twl.h> +#include <linux/input.h> #include <linux/mfd/twl6040.h> #include <linux/slab.h> #include <linux/delay.h> @@ -257,7 +257,7 @@ static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL); static int __devinit twl6040_vibra_probe(struct platform_device *pdev) { - struct twl4030_vibra_data *pdata = pdev->dev.platform_data; + struct twl6040_vibra_data *pdata = pdev->dev.platform_data; struct vibra_info *info; int ret; diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index d2c0db159b18..479011004a11 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -486,7 +486,6 @@ static void elantech_input_sync_v4(struct psmouse *psmouse) unsigned char *packet = psmouse->packet; input_report_key(dev, BTN_LEFT, packet[0] & 0x01); - input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); input_mt_report_pointer_emulation(dev, true); input_sync(dev); } @@ -967,6 +966,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) return -1; + __set_bit(INPUT_PROP_POINTER, dev->propbit); __set_bit(EV_KEY, dev->evbit); __set_bit(EV_ABS, dev->evbit); __clear_bit(EV_REL, dev->evbit); @@ -1017,7 +1017,9 @@ static int elantech_set_input_params(struct psmouse *psmouse) */ psmouse_warn(psmouse, "couldn't query resolution data.\n"); } - + /* v4 is clickpad, with only one button. */ + __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); + __clear_bit(BTN_RIGHT, dev->keybit); __set_bit(BTN_TOOL_QUADTAP, dev->keybit); /* For X to recognize me as touchpad. */ input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); @@ -1245,6 +1247,8 @@ static void elantech_disconnect(struct psmouse *psmouse) */ static int elantech_reconnect(struct psmouse *psmouse) { + psmouse_reset(psmouse); + if (elantech_detect(psmouse, 0)) return -1; @@ -1324,6 +1328,8 @@ int elantech_init(struct psmouse *psmouse) if (!etd) return -ENOMEM; + psmouse_reset(psmouse); + etd->parity[0] = 1; for (i = 1; i < 256; i++) etd->parity[i] = etd->parity[i & (i - 1)] ^ 1; diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index a9ad8e1402be..39fe9b737cae 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -12,9 +12,9 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/input-polldev.h> +#include <linux/gpio.h> #include <linux/gpio_mouse.h> -#include <asm/gpio.h> /* * Timer function which is run every scan_ms ms when the device is opened. diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index a977bfaa6821..661a0ca3b3d6 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -741,6 +741,14 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) } } else { /* SFAC packet */ + if ((packet[0] & (FSP_PB0_LBTN|FSP_PB0_PHY_BTN)) == + FSP_PB0_LBTN) { + /* On-pad click in SFAC mode should be handled + * by userspace. On-pad clicks in MFMC mode + * are real clickpad clicks, and not ignored. + */ + packet[0] &= ~FSP_PB0_LBTN; + } /* no multi-finger information */ ad->last_mt_fgr = 0; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 8081a0a5d602..a4b14a41cbf4 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -274,7 +274,8 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) static unsigned char param = 0xc8; struct synaptics_data *priv = psmouse->private; - if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) + if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || + SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c))) return 0; if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL)) diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 22b218018137..f3102494237d 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -304,7 +304,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) return 0; if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) { - printk(KERN_WARNING "trackpoint.c: failed to get extended button data\n"); + psmouse_warn(psmouse, "failed to get extended button data\n"); button_info = 0; } @@ -326,16 +326,18 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group); if (error) { - printk(KERN_ERR - "trackpoint.c: failed to create sysfs attributes, error: %d\n", - error); + psmouse_err(psmouse, + "failed to create sysfs attributes, error: %d\n", + error); kfree(psmouse->private); psmouse->private = NULL; return -1; } - printk(KERN_INFO "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n", - firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f); + psmouse_info(psmouse, + "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n", + firmware_id, + (button_info & 0xf0) >> 4, button_info & 0x0f); return 0; } diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index 6c6f6d8ea9b4..f7eda3d00fad 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -1,6 +1,4 @@ /* - * drivers/input/touchscreen/tps6507x_ts.c - * * Touchscreen driver for the tps6507x chip. * * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com) @@ -376,4 +374,4 @@ module_platform_driver(tps6507x_ts_driver); MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>"); MODULE_DESCRIPTION("TPS6507x - TouchScreen driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tps6507x-tsc"); +MODULE_ALIAS("platform:tps6507x-ts"); diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index ae2ec929e52f..a5bee8e2dfce 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2707,7 +2707,8 @@ static void unmap_sg(struct device *dev, struct scatterlist *sglist, * The exported alloc_coherent function for dma_ops. */ static void *alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_addr, gfp_t flag) + dma_addr_t *dma_addr, gfp_t flag, + struct dma_attrs *attrs) { unsigned long flags; void *virt_addr; @@ -2765,7 +2766,8 @@ out_free: * The exported free_coherent function for dma_ops. */ static void free_coherent(struct device *dev, size_t size, - void *virt_addr, dma_addr_t dma_addr) + void *virt_addr, dma_addr_t dma_addr, + struct dma_attrs *attrs) { unsigned long flags; struct protection_domain *domain; @@ -2846,8 +2848,8 @@ static void __init prealloc_protection_domains(void) } static struct dma_map_ops amd_iommu_dma_ops = { - .alloc_coherent = alloc_coherent, - .free_coherent = free_coherent, + .alloc = alloc_coherent, + .free = free_coherent, .map_page = map_page, .unmap_page = unmap_page, .map_sg = map_sg, diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 132f93b05154..f93d5ac8f81c 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2949,7 +2949,8 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr, } static void *intel_alloc_coherent(struct device *hwdev, size_t size, - dma_addr_t *dma_handle, gfp_t flags) + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs) { void *vaddr; int order; @@ -2981,7 +2982,7 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size, } static void intel_free_coherent(struct device *hwdev, size_t size, void *vaddr, - dma_addr_t dma_handle) + dma_addr_t dma_handle, struct dma_attrs *attrs) { int order; @@ -3126,8 +3127,8 @@ static int intel_mapping_error(struct device *dev, dma_addr_t dma_addr) } struct dma_map_ops intel_dma_ops = { - .alloc_coherent = intel_alloc_coherent, - .free_coherent = intel_free_coherent, + .alloc = intel_alloc_coherent, + .free = intel_free_coherent, .map_sg = intel_map_sg, .unmap_sg = intel_unmap_sg, .map_page = intel_map_page, diff --git a/drivers/iommu/omap-iommu-debug.c b/drivers/iommu/omap-iommu-debug.c index 103dbd92e256..f55fc5dfbadc 100644 --- a/drivers/iommu/omap-iommu-debug.c +++ b/drivers/iommu/omap-iommu-debug.c @@ -323,15 +323,9 @@ err_out: return count; } -static int debug_open_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - #define DEBUG_FOPS(name) \ static const struct file_operations debug_##name##_fops = { \ - .open = debug_open_generic, \ + .open = simple_open, \ .read = debug_read_##name, \ .write = debug_write_##name, \ .llseek = generic_file_llseek, \ @@ -339,7 +333,7 @@ static int debug_open_generic(struct inode *inode, struct file *file) #define DEBUG_FOPS_RO(name) \ static const struct file_operations debug_##name##_fops = { \ - .open = debug_open_generic, \ + .open = simple_open, \ .read = debug_read_##name, \ .llseek = generic_file_llseek, \ }; diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index b3d6ac17272d..a6d9fd2858f7 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -176,7 +176,7 @@ static void if_close(struct tty_struct *tty, struct file *filp) struct cardstate *cs = tty->driver_data; if (!cs) { /* happens if we didn't find cs in open */ - printk(KERN_DEBUG "%s: no cardstate\n", __func__); + gig_dbg(DEBUG_IF, "%s: no cardstate", __func__); return; } diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c index 05ed4d0cb18b..c0b8c960ee3f 100644 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -891,7 +891,7 @@ open_bchannel(struct fritzcard *fc, struct channel_req *rq) { struct bchannel *bch; - if (rq->adr.channel > 2) + if (rq->adr.channel == 0 || rq->adr.channel > 2) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index d055ae7fa040..e2c83a2d7691 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -1962,7 +1962,7 @@ open_bchannel(struct hfc_pci *hc, struct channel_req *rq) { struct bchannel *bch; - if (rq->adr.channel > 2) + if (rq->adr.channel == 0 || rq->adr.channel > 2) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c index 602338734634..8cde2a0538ab 100644 --- a/drivers/isdn/hardware/mISDN/hfcsusb.c +++ b/drivers/isdn/hardware/mISDN/hfcsusb.c @@ -486,7 +486,7 @@ open_bchannel(struct hfcsusb *hw, struct channel_req *rq) { struct bchannel *bch; - if (rq->adr.channel > 2) + if (rq->adr.channel == 0 || rq->adr.channel > 2) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c index b47e9bed2185..884369f09cad 100644 --- a/drivers/isdn/hardware/mISDN/mISDNipac.c +++ b/drivers/isdn/hardware/mISDN/mISDNipac.c @@ -1506,7 +1506,7 @@ open_bchannel(struct ipac_hw *ipac, struct channel_req *rq) { struct bchannel *bch; - if (rq->adr.channel > 2) + if (rq->adr.channel == 0 || rq->adr.channel > 2) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c index 10446ab404b5..9a6da6edcfa8 100644 --- a/drivers/isdn/hardware/mISDN/mISDNisar.c +++ b/drivers/isdn/hardware/mISDN/mISDNisar.c @@ -1670,7 +1670,7 @@ isar_open(struct isar_hw *isar, struct channel_req *rq) { struct bchannel *bch; - if (rq->adr.channel > 2) + if (rq->adr.channel == 0 || rq->adr.channel > 2) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c index dd6de9f7a8a3..c726e09d0981 100644 --- a/drivers/isdn/hardware/mISDN/netjet.c +++ b/drivers/isdn/hardware/mISDN/netjet.c @@ -860,7 +860,7 @@ open_bchannel(struct tiger_hw *card, struct channel_req *rq) { struct bchannel *bch; - if (rq->adr.channel > 2) + if (rq->adr.channel == 0 || rq->adr.channel > 2) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c index 7f1e7ba75cd1..2183357f0799 100644 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -1015,7 +1015,7 @@ open_bchannel(struct w6692_hw *card, struct channel_req *rq) { struct bchannel *bch; - if (rq->adr.channel > 2) + if (rq->adr.channel == 0 || rq->adr.channel > 2) return -EINVAL; if (rq->protocol == ISDN_P_NONE) return -EINVAL; diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c index 800243b6037e..64ad702a2ecc 100644 --- a/drivers/leds/leds-atmel-pwm.c +++ b/drivers/leds/leds-atmel-pwm.c @@ -35,7 +35,7 @@ static void pwmled_brightness(struct led_classdev *cdev, enum led_brightness b) * NOTE: we reuse the platform_data structure of GPIO leds, * but repurpose its "gpio" number as a PWM channel number. */ -static int __init pwmled_probe(struct platform_device *pdev) +static int __devinit pwmled_probe(struct platform_device *pdev) { const struct gpio_led_platform_data *pdata; struct pwmled *leds; diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index d8433f2d53bc..73973fdbd8be 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -112,7 +112,7 @@ err_free_addr: return err; } -static void __devexit gpio_ext_free(struct netxbig_gpio_ext *gpio_ext) +static void gpio_ext_free(struct netxbig_gpio_ext *gpio_ext) { int i; @@ -294,7 +294,7 @@ static ssize_t netxbig_led_sata_show(struct device *dev, static DEVICE_ATTR(sata, 0644, netxbig_led_sata_show, netxbig_led_sata_store); -static void __devexit delete_netxbig_led(struct netxbig_led_data *led_dat) +static void delete_netxbig_led(struct netxbig_led_data *led_dat) { if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) device_remove_file(led_dat->cdev.dev, &dev_attr_sata); diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index 2f0a14421a73..01cf89ec6944 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -255,7 +255,7 @@ err_free_cmd: return ret; } -static void __devexit delete_ns2_led(struct ns2_led_data *led_dat) +static void delete_ns2_led(struct ns2_led_data *led_dat) { device_remove_file(led_dat->cdev.dev, &dev_attr_sata); led_classdev_unregister(&led_dat->cdev); diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 3d0dfa7a89a2..17e2b472e16d 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -539,9 +539,6 @@ static int bitmap_new_disk_sb(struct bitmap *bitmap) bitmap->events_cleared = bitmap->mddev->events; sb->events_cleared = cpu_to_le64(bitmap->mddev->events); - bitmap->flags |= BITMAP_HOSTENDIAN; - sb->version = cpu_to_le32(BITMAP_MAJOR_HOSTENDIAN); - kunmap_atomic(sb); return 0; @@ -1730,8 +1727,7 @@ int bitmap_create(struct mddev *mddev) bitmap->chunkshift = (ffz(~mddev->bitmap_info.chunksize) - BITMAP_BLOCK_SHIFT); - /* now that chunksize and chunkshift are set, we can use these macros */ - chunks = (blocks + bitmap->chunkshift - 1) >> + chunks = (blocks + (1 << bitmap->chunkshift) - 1) >> bitmap->chunkshift; pages = (chunks + PAGE_COUNTER_RATIO - 1) / PAGE_COUNTER_RATIO; @@ -1788,7 +1784,9 @@ int bitmap_load(struct mddev *mddev) * re-add of a missing device */ start = mddev->recovery_cp; + mutex_lock(&mddev->bitmap_info.mutex); err = bitmap_init_from_disk(bitmap, start); + mutex_unlock(&mddev->bitmap_info.mutex); if (err) goto out; diff --git a/drivers/md/bitmap.h b/drivers/md/bitmap.h index 55ca5aec84e4..b44b0aba2d47 100644 --- a/drivers/md/bitmap.h +++ b/drivers/md/bitmap.h @@ -101,9 +101,6 @@ typedef __u16 bitmap_counter_t; #define BITMAP_BLOCK_SHIFT 9 -/* how many blocks per chunk? (this is variable) */ -#define CHUNK_BLOCK_RATIO(bitmap) ((bitmap)->mddev->bitmap_info.chunksize >> BITMAP_BLOCK_SHIFT) - #endif /* diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index 1f23e048f077..08d9a207259a 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -134,7 +134,7 @@ static void cn_ulog_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct dm_ulog_request *tfr = (struct dm_ulog_request *)(msg + 1); - if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN)) return; spin_lock(&receiving_list_lock); diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 922a3385eead..754f38f8a692 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -718,8 +718,8 @@ static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m) return 0; m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL); - request_module("scsi_dh_%s", m->hw_handler_name); - if (scsi_dh_handler_exist(m->hw_handler_name) == 0) { + if (!try_then_request_module(scsi_dh_handler_exist(m->hw_handler_name), + "scsi_dh_%s", m->hw_handler_name)) { ti->error = "unknown hardware handler type"; ret = -EINVAL; goto fail; diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index b0ba52459ed7..68965e663248 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -859,7 +859,7 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs) int ret; unsigned redundancy = 0; struct raid_dev *dev; - struct md_rdev *rdev, *freshest; + struct md_rdev *rdev, *tmp, *freshest; struct mddev *mddev = &rs->md; switch (rs->raid_type->level) { @@ -877,7 +877,7 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs) } freshest = NULL; - rdev_for_each(rdev, mddev) { + rdev_for_each_safe(rdev, tmp, mddev) { if (!rdev->meta_bdev) continue; diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 213ae32a0fc4..2fd87b544a93 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -279,8 +279,10 @@ static void __cell_release(struct cell *cell, struct bio_list *inmates) hlist_del(&cell->list); - bio_list_add(inmates, cell->holder); - bio_list_merge(inmates, &cell->bios); + if (inmates) { + bio_list_add(inmates, cell->holder); + bio_list_merge(inmates, &cell->bios); + } mempool_free(cell, prison->cell_pool); } @@ -303,9 +305,10 @@ static void cell_release(struct cell *cell, struct bio_list *bios) */ static void __cell_release_singleton(struct cell *cell, struct bio *bio) { - hlist_del(&cell->list); BUG_ON(cell->holder != bio); BUG_ON(!bio_list_empty(&cell->bios)); + + __cell_release(cell, NULL); } static void cell_release_singleton(struct cell *cell, struct bio *bio) @@ -1177,6 +1180,7 @@ static void no_space(struct cell *cell) static void process_discard(struct thin_c *tc, struct bio *bio) { int r; + unsigned long flags; struct pool *pool = tc->pool; struct cell *cell, *cell2; struct cell_key key, key2; @@ -1218,7 +1222,9 @@ static void process_discard(struct thin_c *tc, struct bio *bio) m->bio = bio; if (!ds_add_work(&pool->all_io_ds, &m->list)) { + spin_lock_irqsave(&pool->lock, flags); list_add(&m->list, &pool->prepared_discards); + spin_unlock_irqrestore(&pool->lock, flags); wake_worker(pool); } } else { @@ -2626,8 +2632,10 @@ static int thin_endio(struct dm_target *ti, if (h->all_io_entry) { INIT_LIST_HEAD(&work); ds_dec(h->all_io_entry, &work); + spin_lock_irqsave(&pool->lock, flags); list_for_each_entry_safe(m, tmp, &work, list) list_add(&m->list, &pool->prepared_discards); + spin_unlock_irqrestore(&pool->lock, flags); } mempool_free(h, pool->endio_hook_pool); @@ -2759,6 +2767,6 @@ static void dm_thin_exit(void) module_init(dm_thin_init); module_exit(dm_thin_exit); -MODULE_DESCRIPTION(DM_NAME "device-mapper thin provisioning target"); +MODULE_DESCRIPTION(DM_NAME " thin provisioning target"); MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index b0fcc7d02adb..fa211d80fc0a 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -198,6 +198,7 @@ out: static int linear_run (struct mddev *mddev) { struct linear_conf *conf; + int ret; if (md_check_no_bitmap(mddev)) return -EINVAL; @@ -211,7 +212,13 @@ static int linear_run (struct mddev *mddev) blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->backing_dev_info.congested_fn = linear_congested; mddev->queue->backing_dev_info.congested_data = mddev; - return md_integrity_register(mddev); + + ret = md_integrity_register(mddev); + if (ret) { + kfree(conf); + mddev->private = NULL; + } + return ret; } static int linear_add(struct mddev *mddev, struct md_rdev *rdev) diff --git a/drivers/md/md.c b/drivers/md/md.c index b572e1e386ce..477eb2e180c0 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -7560,14 +7560,14 @@ void md_check_recovery(struct mddev *mddev) * any transients in the value of "sync_action". */ set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); - clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); /* Clear some bits that don't mean anything, but * might be left set */ clear_bit(MD_RECOVERY_INTR, &mddev->recovery); clear_bit(MD_RECOVERY_DONE, &mddev->recovery); - if (test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) + if (!test_and_clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery) || + test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) goto unlock; /* no recovery is running. * remove any failed drives, then @@ -8140,7 +8140,8 @@ static int md_notify_reboot(struct notifier_block *this, for_each_mddev(mddev, tmp) { if (mddev_trylock(mddev)) { - __md_stop_writes(mddev); + if (mddev->pers) + __md_stop_writes(mddev); mddev->safemode = 2; mddev_unlock(mddev); } diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 6f31f5596e01..de63a1fc3737 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -407,6 +407,8 @@ static sector_t raid0_size(struct mddev *mddev, sector_t sectors, int raid_disks return array_sectors; } +static int raid0_stop(struct mddev *mddev); + static int raid0_run(struct mddev *mddev) { struct r0conf *conf; @@ -454,7 +456,12 @@ static int raid0_run(struct mddev *mddev) blk_queue_merge_bvec(mddev->queue, raid0_mergeable_bvec); dump_zones(mddev); - return md_integrity_register(mddev); + + ret = md_integrity_register(mddev); + if (ret) + raid0_stop(mddev); + + return ret; } static int raid0_stop(struct mddev *mddev) @@ -625,6 +632,7 @@ static void *raid0_takeover_raid10(struct mddev *mddev) static void *raid0_takeover_raid1(struct mddev *mddev) { struct r0conf *priv_conf; + int chunksect; /* Check layout: * - (N - 1) mirror drives must be already faulty @@ -635,10 +643,25 @@ static void *raid0_takeover_raid1(struct mddev *mddev) return ERR_PTR(-EINVAL); } + /* + * a raid1 doesn't have the notion of chunk size, so + * figure out the largest suitable size we can use. + */ + chunksect = 64 * 2; /* 64K by default */ + + /* The array must be an exact multiple of chunksize */ + while (chunksect && (mddev->array_sectors & (chunksect - 1))) + chunksect >>= 1; + + if ((chunksect << 9) < PAGE_SIZE) + /* array size does not allow a suitable chunk size */ + return ERR_PTR(-EINVAL); + /* Set new parameters */ mddev->new_level = 0; mddev->new_layout = 0; - mddev->new_chunk_sectors = 128; /* by default set chunk size to 64k */ + mddev->new_chunk_sectors = chunksect; + mddev->chunk_sectors = chunksect; mddev->delta_disks = 1 - mddev->raid_disks; mddev->raid_disks = 1; /* make sure it will be not marked as dirty */ diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 4a40a200d769..15dd59b84e94 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1712,6 +1712,7 @@ static int process_checks(struct r1bio *r1_bio) struct r1conf *conf = mddev->private; int primary; int i; + int vcnt; for (primary = 0; primary < conf->raid_disks * 2; primary++) if (r1_bio->bios[primary]->bi_end_io == end_sync_read && @@ -1721,9 +1722,9 @@ static int process_checks(struct r1bio *r1_bio) break; } r1_bio->read_disk = primary; + vcnt = (r1_bio->sectors + PAGE_SIZE / 512 - 1) >> (PAGE_SHIFT - 9); for (i = 0; i < conf->raid_disks * 2; i++) { int j; - int vcnt = r1_bio->sectors >> (PAGE_SHIFT- 9); struct bio *pbio = r1_bio->bios[primary]; struct bio *sbio = r1_bio->bios[i]; int size; @@ -1738,7 +1739,7 @@ static int process_checks(struct r1bio *r1_bio) s = sbio->bi_io_vec[j].bv_page; if (memcmp(page_address(p), page_address(s), - PAGE_SIZE)) + sbio->bi_io_vec[j].bv_len)) break; } } else @@ -2386,8 +2387,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp int ok = 1; for (i = 0 ; i < conf->raid_disks * 2 ; i++) if (r1_bio->bios[i]->bi_end_io == end_sync_write) { - struct md_rdev *rdev = - rcu_dereference(conf->mirrors[i].rdev); + struct md_rdev *rdev = conf->mirrors[i].rdev; ok = rdev_set_badblocks(rdev, sector_nr, min_bad, 0 ) && ok; @@ -2636,11 +2636,13 @@ static struct r1conf *setup_conf(struct mddev *mddev) return ERR_PTR(err); } +static int stop(struct mddev *mddev); static int run(struct mddev *mddev) { struct r1conf *conf; int i; struct md_rdev *rdev; + int ret; if (mddev->level != 1) { printk(KERN_ERR "md/raid1:%s: raid level not set to mirroring (%d)\n", @@ -2705,7 +2707,11 @@ static int run(struct mddev *mddev) mddev->queue->backing_dev_info.congested_data = mddev; blk_queue_merge_bvec(mddev->queue, raid1_mergeable_bvec); } - return md_integrity_register(mddev); + + ret = md_integrity_register(mddev); + if (ret) + stop(mddev); + return ret; } static int stop(struct mddev *mddev) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 3540316886f2..c8dbb84d5357 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1788,6 +1788,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio) struct r10conf *conf = mddev->private; int i, first; struct bio *tbio, *fbio; + int vcnt; atomic_set(&r10_bio->remaining, 1); @@ -1802,10 +1803,10 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio) first = i; fbio = r10_bio->devs[i].bio; + vcnt = (r10_bio->sectors + (PAGE_SIZE >> 9) - 1) >> (PAGE_SHIFT - 9); /* now find blocks with errors */ for (i=0 ; i < conf->copies ; i++) { int j, d; - int vcnt = r10_bio->sectors >> (PAGE_SHIFT-9); tbio = r10_bio->devs[i].bio; @@ -1821,7 +1822,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio) for (j = 0; j < vcnt; j++) if (memcmp(page_address(fbio->bi_io_vec[j].bv_page), page_address(tbio->bi_io_vec[j].bv_page), - PAGE_SIZE)) + fbio->bi_io_vec[j].bv_len)) break; if (j == vcnt) continue; @@ -1871,7 +1872,6 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio) */ for (i = 0; i < conf->copies; i++) { int j, d; - int vcnt = r10_bio->sectors >> (PAGE_SHIFT-9); tbio = r10_bio->devs[i].repl_bio; if (!tbio || !tbio->bi_end_io) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 23ac880bba9a..f351422938e0 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2471,39 +2471,41 @@ handle_failed_sync(struct r5conf *conf, struct stripe_head *sh, int abort = 0; int i; - md_done_sync(conf->mddev, STRIPE_SECTORS, 0); clear_bit(STRIPE_SYNCING, &sh->state); s->syncing = 0; s->replacing = 0; /* There is nothing more to do for sync/check/repair. + * Don't even need to abort as that is handled elsewhere + * if needed, and not always wanted e.g. if there is a known + * bad block here. * For recover/replace we need to record a bad block on all * non-sync devices, or abort the recovery */ - if (!test_bit(MD_RECOVERY_RECOVER, &conf->mddev->recovery)) - return; - /* During recovery devices cannot be removed, so locking and - * refcounting of rdevs is not needed - */ - for (i = 0; i < conf->raid_disks; i++) { - struct md_rdev *rdev = conf->disks[i].rdev; - if (rdev - && !test_bit(Faulty, &rdev->flags) - && !test_bit(In_sync, &rdev->flags) - && !rdev_set_badblocks(rdev, sh->sector, - STRIPE_SECTORS, 0)) - abort = 1; - rdev = conf->disks[i].replacement; - if (rdev - && !test_bit(Faulty, &rdev->flags) - && !test_bit(In_sync, &rdev->flags) - && !rdev_set_badblocks(rdev, sh->sector, - STRIPE_SECTORS, 0)) - abort = 1; - } - if (abort) { - conf->recovery_disabled = conf->mddev->recovery_disabled; - set_bit(MD_RECOVERY_INTR, &conf->mddev->recovery); + if (test_bit(MD_RECOVERY_RECOVER, &conf->mddev->recovery)) { + /* During recovery devices cannot be removed, so + * locking and refcounting of rdevs is not needed + */ + for (i = 0; i < conf->raid_disks; i++) { + struct md_rdev *rdev = conf->disks[i].rdev; + if (rdev + && !test_bit(Faulty, &rdev->flags) + && !test_bit(In_sync, &rdev->flags) + && !rdev_set_badblocks(rdev, sh->sector, + STRIPE_SECTORS, 0)) + abort = 1; + rdev = conf->disks[i].replacement; + if (rdev + && !test_bit(Faulty, &rdev->flags) + && !test_bit(In_sync, &rdev->flags) + && !rdev_set_badblocks(rdev, sh->sector, + STRIPE_SECTORS, 0)) + abort = 1; + } + if (abort) + conf->recovery_disabled = + conf->mddev->recovery_disabled; } + md_done_sync(conf->mddev, STRIPE_SECTORS, !abort); } static int want_replace(struct stripe_head *sh, int disk_idx) @@ -3203,7 +3205,8 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s) /* Not in-sync */; else if (is_bad) { /* also not in-sync */ - if (!test_bit(WriteErrorSeen, &rdev->flags)) { + if (!test_bit(WriteErrorSeen, &rdev->flags) && + test_bit(R5_UPTODATE, &dev->flags)) { /* treat as in-sync, but with a read error * which we can now try to correct */ @@ -3276,12 +3279,14 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s) /* If there is a failed device being replaced, * we must be recovering. * else if we are after recovery_cp, we must be syncing + * else if MD_RECOVERY_REQUESTED is set, we also are syncing. * else we can only be replacing * sync and recovery both need to read all devices, and so * use the same flag. */ if (do_recovery || - sh->sector >= conf->mddev->recovery_cp) + sh->sector >= conf->mddev->recovery_cp || + test_bit(MD_RECOVERY_REQUESTED, &(conf->mddev->recovery))) s->syncing = 1; else s->replacing = 1; diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index 7f98984e4fad..eab2ea424200 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -54,6 +54,7 @@ struct xc5000_priv { struct list_head hybrid_tuner_instance_list; u32 if_khz; + u32 xtal_khz; u32 freq_hz; u32 bandwidth; u8 video_standard; @@ -214,9 +215,9 @@ static const struct xc5000_fw_cfg xc5000a_1_6_114 = { .size = 12401, }; -static const struct xc5000_fw_cfg xc5000c_41_024_5_31875 = { - .name = "dvb-fe-xc5000c-41.024.5-31875.fw", - .size = 16503, +static const struct xc5000_fw_cfg xc5000c_41_024_5 = { + .name = "dvb-fe-xc5000c-41.024.5.fw", + .size = 16497, }; static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id) @@ -226,7 +227,7 @@ static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id) case XC5000A: return &xc5000a_1_6_114; case XC5000C: - return &xc5000c_41_024_5_31875; + return &xc5000c_41_024_5; } } @@ -572,6 +573,31 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode) return found; } +static int xc_set_xtal(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = XC_RESULT_SUCCESS; + + switch (priv->chip_id) { + default: + case XC5000A: + /* 32.000 MHz xtal is default */ + break; + case XC5000C: + switch (priv->xtal_khz) { + default: + case 32000: + /* 32.000 MHz xtal is default */ + break; + case 31875: + /* 31.875 MHz xtal configuration */ + ret = xc_write_reg(priv, 0x000f, 0x8081); + break; + } + break; + } + return ret; +} static int xc5000_fwupload(struct dvb_frontend *fe) { @@ -603,6 +629,8 @@ static int xc5000_fwupload(struct dvb_frontend *fe) } else { printk(KERN_INFO "xc5000: firmware uploading...\n"); ret = xc_load_i2c_sequence(fe, fw->data); + if (XC_RESULT_SUCCESS == ret) + ret = xc_set_xtal(fe); printk(KERN_INFO "xc5000: firmware upload complete...\n"); } @@ -1164,6 +1192,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, priv->if_khz = cfg->if_khz; } + if (priv->xtal_khz == 0) + priv->xtal_khz = cfg->xtal_khz; + if (priv->radio_input == 0) priv->radio_input = cfg->radio_input; diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index 3396f8e02b40..39a73bf01406 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -34,6 +34,7 @@ struct xc5000_config { u8 i2c_address; u32 if_khz; u8 radio_input; + u32 xtal_khz; int chip_id; }; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 4555baa383b2..0f64d7182657 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -143,10 +143,12 @@ struct dvb_frontend_private { static void dvb_frontend_wakeup(struct dvb_frontend *fe); static int dtv_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p_out); +static int dtv_property_legacy_params_sync(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p); static bool has_get_frontend(struct dvb_frontend *fe) { - return fe->ops.get_frontend; + return fe->ops.get_frontend != NULL; } /* @@ -697,6 +699,7 @@ restart: fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; fepriv->delay = HZ / 2; } + dtv_property_legacy_params_sync(fe, &fepriv->parameters_out); fe->ops.read_status(fe, &s); if (s != fepriv->status) { dvb_frontend_add_event(fe, s); /* update event list */ @@ -1443,6 +1446,28 @@ static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system) __func__); return -EINVAL; } + /* + * Get a delivery system that is compatible with DVBv3 + * NOTE: in order for this to work with softwares like Kaffeine that + * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to + * DVB-S, drivers that support both should put the SYS_DVBS entry + * before the SYS_DVBS2, otherwise it won't switch back to DVB-S. + * The real fix is that userspace applications should not use DVBv3 + * and not trust on calling FE_SET_FRONTEND to switch the delivery + * system. + */ + ncaps = 0; + while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { + if (fe->ops.delsys[ncaps] == desired_system) { + delsys = desired_system; + break; + } + ncaps++; + } + if (delsys == SYS_UNDEFINED) { + dprintk("%s() Couldn't find a delivery system that matches %d\n", + __func__, desired_system); + } } else { /* * This is a DVBv5 call. So, it likely knows the supported @@ -1491,9 +1516,10 @@ static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system) __func__); return -EINVAL; } - c->delivery_system = delsys; } + c->delivery_system = delsys; + /* * The DVBv3 or DVBv5 call is requesting a different system. So, * emulation is needed. @@ -1833,6 +1859,13 @@ static int dtv_set_frontend(struct dvb_frontend *fe) return -EINVAL; /* + * Initialize output parameters to match the values given by + * the user. FE_SET_FRONTEND triggers an initial frontend event + * with status = 0, which copies output parameters to userspace. + */ + dtv_property_legacy_params_sync(fe, &fepriv->parameters_out); + + /* * Be sure that the bandwidth will be filled for all * non-satellite systems, as tuners need to know what * low pass/Nyquist half filter should be applied, in diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c index 3b7b102f20ae..482d249ca7f3 100644 --- a/drivers/media/dvb/dvb-usb/it913x.c +++ b/drivers/media/dvb/dvb-usb/it913x.c @@ -238,12 +238,27 @@ static int it913x_read_reg(struct usb_device *udev, u32 reg) static u32 it913x_query(struct usb_device *udev, u8 pro) { - int ret; + int ret, i; u8 data[4]; - ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ, - 0x1222, 0, &data[0], 3); + u8 ver; + + for (i = 0; i < 5; i++) { + ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ, + 0x1222, 0, &data[0], 3); + ver = data[0]; + if (ver > 0 && ver < 3) + break; + msleep(100); + } - it913x_config.chip_ver = data[0]; + if (ver < 1 || ver > 2) { + info("Failed to identify chip version applying 1"); + it913x_config.chip_ver = 0x1; + it913x_config.chip_type = 0x9135; + return 0; + } + + it913x_config.chip_ver = ver; it913x_config.chip_type = (u16)(data[2] << 8) + data[1]; info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver, @@ -660,30 +675,41 @@ static int it913x_download_firmware(struct usb_device *udev, if ((packet_size > min_pkt) || (i == fw->size)) { fw_data = (u8 *)(fw->data + pos); pos += packet_size; - if (packet_size > 0) - ret |= it913x_io(udev, WRITE_DATA, + if (packet_size > 0) { + ret = it913x_io(udev, WRITE_DATA, DEV_0, CMD_SCATTER_WRITE, 0, 0, fw_data, packet_size); + if (ret < 0) + break; + } udelay(1000); } } i++; } - ret |= it913x_io(udev, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0); - - msleep(100); - if (ret < 0) - info("FRM Firmware Download Failed (%04x)" , ret); + info("FRM Firmware Download Failed (%d)" , ret); else info("FRM Firmware Download Completed - Resetting Device"); - ret |= it913x_return_status(udev); + msleep(30); + + ret = it913x_io(udev, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0); + if (ret < 0) + info("FRM Device not responding to reboot"); + + ret = it913x_return_status(udev); + if (ret == 0) { + info("FRM Failed to reboot device"); + return -ENODEV; + } msleep(30); - ret |= it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400); + ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400); + + msleep(30); /* Tuner function */ if (it913x_config.dual_mode) @@ -901,5 +927,5 @@ module_usb_driver(it913x_driver); MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); MODULE_DESCRIPTION("it913x USB 2 Driver"); -MODULE_VERSION("1.27"); +MODULE_VERSION("1.28"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c index 36d11756492f..a414b1f2b6a5 100644 --- a/drivers/media/dvb/frontends/drxk_hard.c +++ b/drivers/media/dvb/frontends/drxk_hard.c @@ -1520,8 +1520,10 @@ static int scu_command(struct drxk_state *state, dprintk(1, "\n"); if ((cmd == 0) || ((parameterLen > 0) && (parameter == NULL)) || - ((resultLen > 0) && (result == NULL))) - goto error; + ((resultLen > 0) && (result == NULL))) { + printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); + return status; + } mutex_lock(&state->mutex); diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index b09c5fae489b..af526586fa26 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1046,6 +1046,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) goto exit_unregister_led; } + data->dev->driver_type = RC_DRIVER_IR_RAW; data->dev->driver_name = WBCIR_NAME; data->dev->input_name = WBCIR_NAME; data->dev->input_phys = "wbcir/cir0"; diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f2479c5c0eb2..ce1e7ba940f6 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -492,7 +492,7 @@ config VIDEO_VS6624 config VIDEO_MT9M032 tristate "MT9M032 camera sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select VIDEO_APTINA_PLL ---help--- This driver supports MT9M032 camera sensors from Aptina, monochrome diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 5452beef8e11..989e556913ed 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1763,13 +1763,13 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); if (iarg > AUDIO_STEREO_SWAPPED) return -EINVAL; - return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg); + return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg + 1); case AUDIO_BILINGUAL_CHANNEL_SELECT: IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); if (iarg > AUDIO_STEREO_SWAPPED) return -EINVAL; - return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg); + return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg + 1); default: return -EINVAL; diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c index 7636672c3548..645973c5feb0 100644 --- a/drivers/media/video/mt9m032.c +++ b/drivers/media/video/mt9m032.c @@ -392,10 +392,11 @@ static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, } /* Scaling is not supported, the format is thus fixed. */ - ret = mt9m032_get_pad_format(subdev, fh, fmt); + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + ret = 0; done: - mutex_lock(&sensor->lock); + mutex_unlock(&sensor->lock); return ret; } diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 4a44f9a1bae0..b76b0ac0958f 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -468,22 +468,30 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, spin_unlock_irqrestore(&stream->clock.lock, flags); } -static int uvc_video_clock_init(struct uvc_streaming *stream) +static void uvc_video_clock_reset(struct uvc_streaming *stream) { struct uvc_clock *clock = &stream->clock; - spin_lock_init(&clock->lock); clock->head = 0; clock->count = 0; - clock->size = 32; clock->last_sof = -1; clock->sof_offset = -1; +} + +static int uvc_video_clock_init(struct uvc_streaming *stream) +{ + struct uvc_clock *clock = &stream->clock; + + spin_lock_init(&clock->lock); + clock->size = 32; clock->samples = kmalloc(clock->size * sizeof(*clock->samples), GFP_KERNEL); if (clock->samples == NULL) return -ENOMEM; + uvc_video_clock_reset(stream); + return 0; } @@ -1424,8 +1432,6 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) if (free_buffers) uvc_free_urb_buffers(stream); - - uvc_video_clock_cleanup(stream); } /* @@ -1555,10 +1561,6 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) uvc_video_stats_start(stream); - ret = uvc_video_clock_init(stream); - if (ret < 0) - return ret; - if (intf->num_altsetting > 1) { struct usb_host_endpoint *best_ep = NULL; unsigned int best_psize = 3 * 1024; @@ -1683,6 +1685,8 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset) stream->frozen = 0; + uvc_video_clock_reset(stream); + ret = uvc_commit_video(stream, &stream->ctrl); if (ret < 0) { uvc_queue_enable(&stream->queue, 0); @@ -1819,25 +1823,35 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) uvc_uninit_video(stream, 1); usb_set_interface(stream->dev->udev, stream->intfnum, 0); uvc_queue_enable(&stream->queue, 0); + uvc_video_clock_cleanup(stream); return 0; } - ret = uvc_queue_enable(&stream->queue, 1); + ret = uvc_video_clock_init(stream); if (ret < 0) return ret; + ret = uvc_queue_enable(&stream->queue, 1); + if (ret < 0) + goto error_queue; + /* Commit the streaming parameters. */ ret = uvc_commit_video(stream, &stream->ctrl); - if (ret < 0) { - uvc_queue_enable(&stream->queue, 0); - return ret; - } + if (ret < 0) + goto error_commit; ret = uvc_init_video(stream, GFP_KERNEL); - if (ret < 0) { - usb_set_interface(stream->dev->udev, stream->intfnum, 0); - uvc_queue_enable(&stream->queue, 0); - } + if (ret < 0) + goto error_video; + + return 0; + +error_video: + usb_set_interface(stream->dev->udev, stream->intfnum, 0); +error_commit: + uvc_queue_enable(&stream->queue, 0); +error_queue: + uvc_video_clock_cleanup(stream); return ret; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 29f463cc09cb..11e44386fa9b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -268,10 +268,17 @@ config TWL6030_PWM This is used to control charging LED brightness. config TWL6040_CORE - bool - depends on TWL4030_CORE && GENERIC_HARDIRQS + bool "Support for TWL6040 audio codec" + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE + select REGMAP_I2C default n + help + Say yes here if you want support for Texas Instruments TWL6040 audio + codec. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device (audio, vibra). config MFD_STMPE bool "Support STMicroelectronics STMPE" diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c index 3aa36eb5c79b..44a3fdbadef4 100644 --- a/drivers/mfd/aat2870-core.c +++ b/drivers/mfd/aat2870-core.c @@ -262,13 +262,6 @@ static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf) return count; } -static int aat2870_reg_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - - return 0; -} - static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -330,7 +323,7 @@ static ssize_t aat2870_reg_write_file(struct file *file, } static const struct file_operations aat2870_reg_fops = { - .open = aat2870_reg_open_file, + .open = simple_open, .read = aat2870_reg_read_file, .write = aat2870_reg_write_file, }; diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 60107ee166fc..1efad20fb175 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -483,12 +483,6 @@ struct ab3100_get_set_reg_priv { bool mode; }; -static int ab3100_get_set_reg_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t ab3100_get_set_reg(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -583,7 +577,7 @@ static ssize_t ab3100_get_set_reg(struct file *file, } static const struct file_operations ab3100_get_set_reg_fops = { - .open = ab3100_get_set_reg_open_file, + .open = simple_open, .write = ab3100_get_set_reg, .llseek = noop_llseek, }; diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 1895cf9fab8c..1582c3d95257 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -527,7 +527,9 @@ static void asic3_gpio_set(struct gpio_chip *chip, static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - return (offset < ASIC3_NUM_GPIOS) ? IRQ_BOARD_START + offset : -ENXIO; + struct asic3 *asic = container_of(chip, struct asic3, gpio); + + return (offset < ASIC3_NUM_GPIOS) ? asic->irq_base + offset : -ENXIO; } static __init int asic3_gpio_probe(struct platform_device *pdev, diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index ebc1e8658226..5be32489714f 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2788,6 +2788,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .constraints = { .name = "db8500-vape", .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .always_on = true, }, .consumer_supplies = db8500_vape_consumers, .num_consumer_supplies = ARRAY_SIZE(db8500_vape_consumers), diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 9fd4f63c45cc..738722cdecaa 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -813,7 +813,8 @@ err_revision: mc13xxx_add_subdevice(mc13xxx, "%s-adc"); if (mc13xxx->flags & MC13XXX_USE_CODEC) - mc13xxx_add_subdevice(mc13xxx, "%s-codec"); + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec", + pdata->codec, sizeof(*pdata->codec)); if (mc13xxx->flags & MC13XXX_USE_RTC) mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 95a2e546a489..7e96bb229724 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -25,7 +25,7 @@ #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/spinlock.h> -#include <linux/gpio.h> +#include <plat/cpu.h> #include <plat/usb.h> #include <linux/pm_runtime.h> @@ -502,19 +502,6 @@ static void omap_usbhs_init(struct device *dev) pm_runtime_get_sync(dev); spin_lock_irqsave(&omap->lock, flags); - if (pdata->ehci_data->phy_reset) { - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) - gpio_request_one(pdata->ehci_data->reset_gpio_port[0], - GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); - - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) - gpio_request_one(pdata->ehci_data->reset_gpio_port[1], - GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); - - /* Hold the PHY in RESET for enough time till DIR is high */ - udelay(10); - } - omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); @@ -593,39 +580,10 @@ static void omap_usbhs_init(struct device *dev) usbhs_omap_tll_init(dev, OMAP_TLL_CHANNEL_COUNT); } - if (pdata->ehci_data->phy_reset) { - /* Hold the PHY in RESET for enough time till - * PHY is settled and ready - */ - udelay(10); - - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) - gpio_set_value - (pdata->ehci_data->reset_gpio_port[0], 1); - - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) - gpio_set_value - (pdata->ehci_data->reset_gpio_port[1], 1); - } - spin_unlock_irqrestore(&omap->lock, flags); pm_runtime_put_sync(dev); } -static void omap_usbhs_deinit(struct device *dev) -{ - struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; - - if (pdata->ehci_data->phy_reset) { - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) - gpio_free(pdata->ehci_data->reset_gpio_port[0]); - - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) - gpio_free(pdata->ehci_data->reset_gpio_port[1]); - } -} - /** * usbhs_omap_probe - initialize TI-based HCDs @@ -860,7 +818,6 @@ static int __devexit usbhs_omap_remove(struct platform_device *pdev) { struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); - omap_usbhs_deinit(&pdev->dev); iounmap(omap->tll_base); iounmap(omap->uhh_base); clk_put(omap->init_60m_fclk); diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index 99ef944c621d..44afae0a69ce 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -80,44 +80,6 @@ static struct mfd_cell rc5t583_subdevs[] = { {.name = "rc5t583-key", } }; -int rc5t583_write(struct device *dev, uint8_t reg, uint8_t val) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_write(rc5t583->regmap, reg, val); -} - -int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - unsigned int ival; - int ret; - ret = regmap_read(rc5t583->regmap, reg, &ival); - if (!ret) - *val = (uint8_t)ival; - return ret; -} - -int rc5t583_set_bits(struct device *dev, unsigned int reg, - unsigned int bit_mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, bit_mask, bit_mask); -} - -int rc5t583_clear_bits(struct device *dev, unsigned int reg, - unsigned int bit_mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, bit_mask, 0); -} - -int rc5t583_update(struct device *dev, unsigned int reg, - unsigned int val, unsigned int mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, mask, val); -} - static int __rc5t583_set_ext_pwrreq1_control(struct device *dev, int id, int ext_pwr, int slots) { @@ -197,6 +159,7 @@ int rc5t583_ext_power_req_config(struct device *dev, int ds_id, ds_id, ext_pwr_req); return 0; } +EXPORT_SYMBOL(rc5t583_ext_power_req_config); static int rc5t583_clear_ext_power_req(struct rc5t583 *rc5t583, struct rc5t583_platform_data *pdata) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index b2d8e512d3cb..2d6bedadca09 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -30,7 +30,9 @@ #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/delay.h> -#include <linux/i2c/twl.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/err.h> #include <linux/mfd/core.h> #include <linux/mfd/twl6040.h> @@ -39,7 +41,7 @@ int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) { int ret; - u8 val = 0; + unsigned int val; mutex_lock(&twl6040->io_mutex); /* Vibra control registers from cache */ @@ -47,7 +49,7 @@ int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) reg == TWL6040_REG_VIBCTLR)) { val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)]; } else { - ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); + ret = regmap_read(twl6040->regmap, reg, &val); if (ret < 0) { mutex_unlock(&twl6040->io_mutex); return ret; @@ -64,7 +66,7 @@ int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val) int ret; mutex_lock(&twl6040->io_mutex); - ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); + ret = regmap_write(twl6040->regmap, reg, val); /* Cache the vibra control registers */ if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR) twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val; @@ -77,16 +79,9 @@ EXPORT_SYMBOL(twl6040_reg_write); int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) { int ret; - u8 val; mutex_lock(&twl6040->io_mutex); - ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); - if (ret) - goto out; - - val |= mask; - ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); -out: + ret = regmap_update_bits(twl6040->regmap, reg, mask, mask); mutex_unlock(&twl6040->io_mutex); return ret; } @@ -95,16 +90,9 @@ EXPORT_SYMBOL(twl6040_set_bits); int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) { int ret; - u8 val; mutex_lock(&twl6040->io_mutex); - ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); - if (ret) - goto out; - - val &= ~mask; - ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); -out: + ret = regmap_update_bits(twl6040->regmap, reg, mask, 0); mutex_unlock(&twl6040->io_mutex); return ret; } @@ -494,32 +482,58 @@ static struct resource twl6040_codec_rsrc[] = { }, }; -static int __devinit twl6040_probe(struct platform_device *pdev) +static bool twl6040_readable_reg(struct device *dev, unsigned int reg) { - struct twl4030_audio_data *pdata = pdev->dev.platform_data; + /* Register 0 is not readable */ + if (!reg) + return false; + return true; +} + +static struct regmap_config twl6040_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TWL6040_REG_STATUS, /* 0x2e */ + + .readable_reg = twl6040_readable_reg, +}; + +static int __devinit twl6040_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct twl6040_platform_data *pdata = client->dev.platform_data; struct twl6040 *twl6040; struct mfd_cell *cell = NULL; int ret, children = 0; if (!pdata) { - dev_err(&pdev->dev, "Platform data is missing\n"); + dev_err(&client->dev, "Platform data is missing\n"); return -EINVAL; } /* In order to operate correctly we need valid interrupt config */ - if (!pdata->naudint_irq || !pdata->irq_base) { - dev_err(&pdev->dev, "Invalid IRQ configuration\n"); + if (!client->irq || !pdata->irq_base) { + dev_err(&client->dev, "Invalid IRQ configuration\n"); return -EINVAL; } - twl6040 = kzalloc(sizeof(struct twl6040), GFP_KERNEL); - if (!twl6040) - return -ENOMEM; + twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040), + GFP_KERNEL); + if (!twl6040) { + ret = -ENOMEM; + goto err; + } + + twl6040->regmap = regmap_init_i2c(client, &twl6040_regmap_config); + if (IS_ERR(twl6040->regmap)) { + ret = PTR_ERR(twl6040->regmap); + goto err; + } - platform_set_drvdata(pdev, twl6040); + i2c_set_clientdata(client, twl6040); - twl6040->dev = &pdev->dev; - twl6040->irq = pdata->naudint_irq; + twl6040->dev = &client->dev; + twl6040->irq = client->irq; twl6040->irq_base = pdata->irq_base; mutex_init(&twl6040->mutex); @@ -588,12 +602,12 @@ static int __devinit twl6040_probe(struct platform_device *pdev) } if (children) { - ret = mfd_add_devices(&pdev->dev, pdev->id, twl6040->cells, + ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, NULL, 0); if (ret) goto mfd_err; } else { - dev_err(&pdev->dev, "No platform data found for children\n"); + dev_err(&client->dev, "No platform data found for children\n"); ret = -ENODEV; goto mfd_err; } @@ -608,14 +622,15 @@ gpio2_err: if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); gpio1_err: - platform_set_drvdata(pdev, NULL); - kfree(twl6040); + i2c_set_clientdata(client, NULL); + regmap_exit(twl6040->regmap); +err: return ret; } -static int __devexit twl6040_remove(struct platform_device *pdev) +static int __devexit twl6040_remove(struct i2c_client *client) { - struct twl6040 *twl6040 = platform_get_drvdata(pdev); + struct twl6040 *twl6040 = i2c_get_clientdata(client); if (twl6040->power_count) twl6040_power(twl6040, 0); @@ -626,23 +641,30 @@ static int __devexit twl6040_remove(struct platform_device *pdev) free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); twl6040_irq_exit(twl6040); - mfd_remove_devices(&pdev->dev); - platform_set_drvdata(pdev, NULL); - kfree(twl6040); + mfd_remove_devices(&client->dev); + i2c_set_clientdata(client, NULL); + regmap_exit(twl6040->regmap); return 0; } -static struct platform_driver twl6040_driver = { +static const struct i2c_device_id twl6040_i2c_id[] = { + { "twl6040", 0, }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id); + +static struct i2c_driver twl6040_driver = { + .driver = { + .name = "twl6040", + .owner = THIS_MODULE, + }, .probe = twl6040_probe, .remove = __devexit_p(twl6040_remove), - .driver = { - .owner = THIS_MODULE, - .name = "twl6040", - }, + .id_table = twl6040_i2c_id, }; -module_platform_driver(twl6040_driver); +module_i2c_driver(twl6040_driver); MODULE_DESCRIPTION("TWL6040 MFD"); MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c index 1c034b80d408..6673e578b3e9 100644 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ b/drivers/misc/ibmasm/ibmasmfs.c @@ -500,12 +500,6 @@ static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, return 1; } -static int remote_settings_file_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static int remote_settings_file_close(struct inode *inode, struct file *file) { return 0; @@ -600,7 +594,7 @@ static const struct file_operations r_heartbeat_fops = { }; static const struct file_operations remote_settings_fops = { - .open = remote_settings_file_open, + .open = simple_open, .release = remote_settings_file_close, .read = remote_settings_file_read, .write = remote_settings_file_write, diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 3f7ad83ed740..3aa9a969b373 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -134,12 +134,17 @@ static int force_hwbrks; static int hwbreaks_ok; static int hw_break_val; static int hw_break_val2; +static int cont_instead_of_sstep; +static unsigned long cont_thread_id; +static unsigned long sstep_thread_id; #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC) static int arch_needs_sstep_emulation = 1; #else static int arch_needs_sstep_emulation; #endif +static unsigned long cont_addr; static unsigned long sstep_addr; +static int restart_from_top_after_write; static int sstep_state; /* Storage for the registers, in GDB format. */ @@ -187,7 +192,8 @@ static int kgdbts_unreg_thread(void *ptr) */ while (!final_ack) msleep_interruptible(1500); - + /* Pause for any other threads to exit after final ack. */ + msleep_interruptible(1000); if (configured) kgdb_unregister_io_module(&kgdbts_io_ops); configured = 0; @@ -211,7 +217,7 @@ static unsigned long lookup_addr(char *arg) if (!strcmp(arg, "kgdbts_break_test")) addr = (unsigned long)kgdbts_break_test; else if (!strcmp(arg, "sys_open")) - addr = (unsigned long)sys_open; + addr = (unsigned long)do_sys_open; else if (!strcmp(arg, "do_fork")) addr = (unsigned long)do_fork; else if (!strcmp(arg, "hw_break_val")) @@ -283,6 +289,16 @@ static void hw_break_val_write(void) hw_break_val++; } +static int get_thread_id_continue(char *put_str, char *arg) +{ + char *ptr = &put_str[11]; + + if (put_str[1] != 'T' || put_str[2] != '0') + return 1; + kgdb_hex2long(&ptr, &cont_thread_id); + return 0; +} + static int check_and_rewind_pc(char *put_str, char *arg) { unsigned long addr = lookup_addr(arg); @@ -299,13 +315,21 @@ static int check_and_rewind_pc(char *put_str, char *arg) if (addr + BREAK_INSTR_SIZE == ip) offset = -BREAK_INSTR_SIZE; #endif - if (strcmp(arg, "silent") && ip + offset != addr) { + + if (arch_needs_sstep_emulation && sstep_addr && + ip + offset == sstep_addr && + ((!strcmp(arg, "sys_open") || !strcmp(arg, "do_fork")))) { + /* This is special case for emulated single step */ + v2printk("Emul: rewind hit single step bp\n"); + restart_from_top_after_write = 1; + } else if (strcmp(arg, "silent") && ip + offset != addr) { eprintk("kgdbts: BP mismatch %lx expected %lx\n", ip + offset, addr); return 1; } /* Readjust the instruction pointer if needed */ ip += offset; + cont_addr = ip; #ifdef GDB_ADJUSTS_BREAK_OFFSET instruction_pointer_set(&kgdbts_regs, ip); #endif @@ -315,6 +339,8 @@ static int check_and_rewind_pc(char *put_str, char *arg) static int check_single_step(char *put_str, char *arg) { unsigned long addr = lookup_addr(arg); + static int matched_id; + /* * From an arch indepent point of view the instruction pointer * should be on a different instruction @@ -324,6 +350,29 @@ static int check_single_step(char *put_str, char *arg) gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); v2printk("Singlestep stopped at IP: %lx\n", instruction_pointer(&kgdbts_regs)); + + if (sstep_thread_id != cont_thread_id) { + /* + * Ensure we stopped in the same thread id as before, else the + * debugger should continue until the original thread that was + * single stepped is scheduled again, emulating gdb's behavior. + */ + v2printk("ThrID does not match: %lx\n", cont_thread_id); + if (arch_needs_sstep_emulation) { + if (matched_id && + instruction_pointer(&kgdbts_regs) != addr) + goto continue_test; + matched_id++; + ts.idx -= 2; + sstep_state = 0; + return 0; + } + cont_instead_of_sstep = 1; + ts.idx -= 4; + return 0; + } +continue_test: + matched_id = 0; if (instruction_pointer(&kgdbts_regs) == addr) { eprintk("kgdbts: SingleStep failed at %lx\n", instruction_pointer(&kgdbts_regs)); @@ -365,10 +414,40 @@ static int got_break(char *put_str, char *arg) return 1; } +static void get_cont_catch(char *arg) +{ + /* Always send detach because the test is completed at this point */ + fill_get_buf("D"); +} + +static int put_cont_catch(char *put_str, char *arg) +{ + /* This is at the end of the test and we catch any and all input */ + v2printk("kgdbts: cleanup task: %lx\n", sstep_thread_id); + ts.idx--; + return 0; +} + +static int emul_reset(char *put_str, char *arg) +{ + if (strncmp(put_str, "$OK", 3)) + return 1; + if (restart_from_top_after_write) { + restart_from_top_after_write = 0; + ts.idx = -1; + } + return 0; +} + static void emul_sstep_get(char *arg) { if (!arch_needs_sstep_emulation) { - fill_get_buf(arg); + if (cont_instead_of_sstep) { + cont_instead_of_sstep = 0; + fill_get_buf("c"); + } else { + fill_get_buf(arg); + } return; } switch (sstep_state) { @@ -398,9 +477,11 @@ static void emul_sstep_get(char *arg) static int emul_sstep_put(char *put_str, char *arg) { if (!arch_needs_sstep_emulation) { - if (!strncmp(put_str+1, arg, 2)) - return 0; - return 1; + char *ptr = &put_str[11]; + if (put_str[1] != 'T' || put_str[2] != '0') + return 1; + kgdb_hex2long(&ptr, &sstep_thread_id); + return 0; } switch (sstep_state) { case 1: @@ -411,8 +492,7 @@ static int emul_sstep_put(char *put_str, char *arg) v2printk("Stopped at IP: %lx\n", instruction_pointer(&kgdbts_regs)); /* Want to stop at IP + break instruction size by default */ - sstep_addr = instruction_pointer(&kgdbts_regs) + - BREAK_INSTR_SIZE; + sstep_addr = cont_addr + BREAK_INSTR_SIZE; break; case 2: if (strncmp(put_str, "$OK", 3)) { @@ -424,6 +504,9 @@ static int emul_sstep_put(char *put_str, char *arg) if (strncmp(put_str, "$T0", 3)) { eprintk("kgdbts: failed continue sstep\n"); return 1; + } else { + char *ptr = &put_str[11]; + kgdb_hex2long(&ptr, &sstep_thread_id); } break; case 4: @@ -502,10 +585,10 @@ static struct test_struct bad_read_test[] = { static struct test_struct singlestep_break_test[] = { { "?", "S0*" }, /* Clear break points */ { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ - { "c", "T0*", }, /* Continue */ + { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ + { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */ { "g", "kgdbts_break_test", NULL, check_and_rewind_pc }, { "write", "OK", write_regs }, /* Write registers */ - { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */ { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ { "g", "kgdbts_break_test", NULL, check_single_step }, { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ @@ -523,16 +606,16 @@ static struct test_struct singlestep_break_test[] = { static struct test_struct do_fork_test[] = { { "?", "S0*" }, /* Clear break points */ { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ - { "c", "T0*", }, /* Continue */ - { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ - { "write", "OK", write_regs }, /* Write registers */ + { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */ + { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ + { "write", "OK", write_regs, emul_reset }, /* Write registers */ { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ { "g", "do_fork", NULL, check_single_step }, { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ - { "", "" }, + { "", "", get_cont_catch, put_cont_catch }, }; /* Test for hitting a breakpoint at sys_open for what ever the number @@ -541,16 +624,16 @@ static struct test_struct do_fork_test[] = { static struct test_struct sys_open_test[] = { { "?", "S0*" }, /* Clear break points */ { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ - { "c", "T0*", }, /* Continue */ - { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ - { "write", "OK", write_regs }, /* Write registers */ + { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */ + { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ + { "write", "OK", write_regs, emul_reset }, /* Write registers */ { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ { "g", "sys_open", NULL, check_single_step }, { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ - { "", "" }, + { "", "", get_cont_catch, put_cont_catch }, }; /* @@ -693,8 +776,8 @@ static int run_simple_test(int is_get_char, int chr) /* This callback is a put char which is when kgdb sends data to * this I/O module. */ - if (ts.tst[ts.idx].get[0] == '\0' && - ts.tst[ts.idx].put[0] == '\0') { + if (ts.tst[ts.idx].get[0] == '\0' && ts.tst[ts.idx].put[0] == '\0' && + !ts.tst[ts.idx].get_handler) { eprintk("kgdbts: ERROR: beyond end of test on" " '%s' line %i\n", ts.name, ts.idx); return 0; @@ -907,6 +990,17 @@ static void kgdbts_run_tests(void) if (ptr) sstep_test = simple_strtol(ptr+1, NULL, 10); + /* All HW break point tests */ + if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) { + hwbreaks_ok = 1; + v1printk("kgdbts:RUN hw breakpoint test\n"); + run_breakpoint_test(1); + v1printk("kgdbts:RUN hw write breakpoint test\n"); + run_hw_break_test(1); + v1printk("kgdbts:RUN access write breakpoint test\n"); + run_hw_break_test(0); + } + /* required internal KGDB tests */ v1printk("kgdbts:RUN plant and detach test\n"); run_plant_and_detach_test(0); @@ -924,35 +1018,11 @@ static void kgdbts_run_tests(void) /* ===Optional tests=== */ - /* All HW break point tests */ - if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) { - hwbreaks_ok = 1; - v1printk("kgdbts:RUN hw breakpoint test\n"); - run_breakpoint_test(1); - v1printk("kgdbts:RUN hw write breakpoint test\n"); - run_hw_break_test(1); - v1printk("kgdbts:RUN access write breakpoint test\n"); - run_hw_break_test(0); - } - if (nmi_sleep) { v1printk("kgdbts:RUN NMI sleep %i seconds test\n", nmi_sleep); run_nmi_sleep_test(nmi_sleep); } -#ifdef CONFIG_DEBUG_RODATA - /* Until there is an api to write to read-only text segments, use - * HW breakpoints for the remainder of any tests, else print a - * failure message if hw breakpoints do not work. - */ - if (!(arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT && hwbreaks_ok)) { - eprintk("kgdbts: HW breakpoints do not work," - "skipping remaining tests\n"); - return; - } - force_hwbrks = 1; -#endif /* CONFIG_DEBUG_RODATA */ - /* If the do_fork test is run it will be the last test that is * executed because a kernel thread will be spawned at the very * end to unregister the debug hooks. diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index eed213a5c8cb..dabec556ebb8 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -873,7 +873,7 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; - unsigned int from, nr, arg; + unsigned int from, nr, arg, trim_arg, erase_arg; int err = 0, type = MMC_BLK_SECDISCARD; if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) { @@ -881,20 +881,26 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, goto out; } + from = blk_rq_pos(req); + nr = blk_rq_sectors(req); + /* The sanitize operation is supported at v4.5 only */ if (mmc_can_sanitize(card)) { - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_SANITIZE_START, 1, 0); - goto out; + erase_arg = MMC_ERASE_ARG; + trim_arg = MMC_TRIM_ARG; + } else { + erase_arg = MMC_SECURE_ERASE_ARG; + trim_arg = MMC_SECURE_TRIM1_ARG; } - from = blk_rq_pos(req); - nr = blk_rq_sectors(req); - - if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr)) - arg = MMC_SECURE_TRIM1_ARG; - else - arg = MMC_SECURE_ERASE_ARG; + if (mmc_erase_group_aligned(card, from, nr)) + arg = erase_arg; + else if (mmc_can_trim(card)) + arg = trim_arg; + else { + err = -EINVAL; + goto out; + } retry: if (card->quirks & MMC_QUIRK_INAND_CMD38) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @@ -904,25 +910,41 @@ retry: INAND_CMD38_ARG_SECERASE, 0); if (err) - goto out; + goto out_retry; } + err = mmc_erase(card, from, nr, arg); - if (!err && arg == MMC_SECURE_TRIM1_ARG) { + if (err == -EIO) + goto out_retry; + if (err) + goto out; + + if (arg == MMC_SECURE_TRIM1_ARG) { if (card->quirks & MMC_QUIRK_INAND_CMD38) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, INAND_CMD38_ARG_EXT_CSD, INAND_CMD38_ARG_SECTRIM2, 0); if (err) - goto out; + goto out_retry; } + err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG); + if (err == -EIO) + goto out_retry; + if (err) + goto out; } -out: - if (err == -EIO && !mmc_blk_reset(md, card->host, type)) + + if (mmc_can_sanitize(card)) + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_SANITIZE_START, 1, 0); +out_retry: + if (err && !mmc_blk_reset(md, card->host, type)) goto retry; if (!err) mmc_blk_reset_success(md, type); +out: spin_lock_irq(&md->lock); __blk_end_request(req, err, blk_rq_bytes(req)); spin_unlock_irq(&md->lock); @@ -1623,24 +1645,6 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) return ret; } -static int -mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) -{ - int err; - - mmc_claim_host(card->host); - err = mmc_set_blocklen(card, 512); - mmc_release_host(card->host); - - if (err) { - pr_err("%s: unable to set block size to 512: %d\n", - md->disk->disk_name, err); - return -EINVAL; - } - - return 0; -} - static void mmc_blk_remove_req(struct mmc_blk_data *md) { struct mmc_card *card; @@ -1768,7 +1772,6 @@ static const struct mmc_fixup blk_fixups[] = static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md, *part_md; - int err; char cap_str[10]; /* @@ -1781,10 +1784,6 @@ static int mmc_blk_probe(struct mmc_card *card) if (IS_ERR(md)) return PTR_ERR(md); - err = mmc_blk_set_blksize(md, card); - if (err) - goto out; - string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2, cap_str, sizeof(cap_str)); pr_info("%s: %s %s %s %s\n", @@ -1809,7 +1808,7 @@ static int mmc_blk_probe(struct mmc_card *card) out: mmc_blk_remove_parts(card, md); mmc_blk_remove_req(md); - return err; + return 0; } static void mmc_blk_remove(struct mmc_card *card) @@ -1825,7 +1824,7 @@ static void mmc_blk_remove(struct mmc_card *card) } #ifdef CONFIG_PM -static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state) +static int mmc_blk_suspend(struct mmc_card *card) { struct mmc_blk_data *part_md; struct mmc_blk_data *md = mmc_get_drvdata(card); @@ -1845,8 +1844,6 @@ static int mmc_blk_resume(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { - mmc_blk_set_blksize(md, card); - /* * Resume involves the card going into idle state, * so current partition is always the main one. diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 2517547b4366..996f8e36e23d 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -139,7 +139,7 @@ static void mmc_queue_setup_discard(struct request_queue *q, queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); q->limits.max_discard_sectors = max_discard; - if (card->erased_byte == 0) + if (card->erased_byte == 0 && !mmc_can_discard(card)) q->limits.discard_zeroes_data = 1; q->limits.discard_granularity = card->pref_erase << 9; /* granularity must not be greater than max. discard */ diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 5d011a39dfff..c60cee92a2b2 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -122,14 +122,14 @@ static int mmc_bus_remove(struct device *dev) return 0; } -static int mmc_bus_suspend(struct device *dev, pm_message_t state) +static int mmc_bus_suspend(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); int ret = 0; if (dev->driver && drv->suspend) - ret = drv->suspend(card, state); + ret = drv->suspend(card); return ret; } @@ -165,20 +165,14 @@ static int mmc_runtime_idle(struct device *dev) return pm_runtime_suspend(dev); } +#endif /* !CONFIG_PM_RUNTIME */ + static const struct dev_pm_ops mmc_bus_pm_ops = { - .runtime_suspend = mmc_runtime_suspend, - .runtime_resume = mmc_runtime_resume, - .runtime_idle = mmc_runtime_idle, + SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, + mmc_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume) }; -#define MMC_PM_OPS_PTR (&mmc_bus_pm_ops) - -#else /* !CONFIG_PM_RUNTIME */ - -#define MMC_PM_OPS_PTR NULL - -#endif /* !CONFIG_PM_RUNTIME */ - static struct bus_type mmc_bus_type = { .name = "mmc", .dev_attrs = mmc_dev_attrs, @@ -186,9 +180,7 @@ static struct bus_type mmc_bus_type = { .uevent = mmc_bus_uevent, .probe = mmc_bus_probe, .remove = mmc_bus_remove, - .suspend = mmc_bus_suspend, - .resume = mmc_bus_resume, - .pm = MMC_PM_OPS_PTR, + .pm = &mmc_bus_pm_ops, }; int mmc_register_bus(void) @@ -267,6 +259,15 @@ int mmc_add_card(struct mmc_card *card) { int ret; const char *type; + const char *uhs_bus_speed_mode = ""; + static const char *const uhs_speeds[] = { + [UHS_SDR12_BUS_SPEED] = "SDR12 ", + [UHS_SDR25_BUS_SPEED] = "SDR25 ", + [UHS_SDR50_BUS_SPEED] = "SDR50 ", + [UHS_SDR104_BUS_SPEED] = "SDR104 ", + [UHS_DDR50_BUS_SPEED] = "DDR50 ", + }; + dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); @@ -296,6 +297,10 @@ int mmc_add_card(struct mmc_card *card) break; } + if (mmc_sd_card_uhs(card) && + (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds))) + uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed]; + if (mmc_host_is_spi(card->host)) { pr_info("%s: new %s%s%s card on SPI\n", mmc_hostname(card->host), @@ -303,13 +308,13 @@ int mmc_add_card(struct mmc_card *card) mmc_card_ddr_mode(card) ? "DDR " : "", type); } else { - pr_info("%s: new %s%s%s%s card at address %04x\n", + pr_info("%s: new %s%s%s%s%s card at address %04x\n", mmc_hostname(card->host), mmc_card_uhs(card) ? "ultra high speed " : (mmc_card_highspeed(card) ? "high speed " : ""), (mmc_card_hs200(card) ? "HS200 " : ""), mmc_card_ddr_mode(card) ? "DDR " : "", - type, card->rca); + uhs_bus_speed_mode, type, card->rca); } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/cd-gpio.c index 29de31e260dd..2c14be73254c 100644 --- a/drivers/mmc/core/cd-gpio.c +++ b/drivers/mmc/core/cd-gpio.c @@ -12,6 +12,7 @@ #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/jiffies.h> +#include <linux/mmc/cd-gpio.h> #include <linux/mmc/host.h> #include <linux/module.h> #include <linux/slab.h> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 14f262e9246d..ba821fe70bca 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -527,10 +527,14 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) if (data->flags & MMC_DATA_WRITE) /* - * The limit is really 250 ms, but that is - * insufficient for some crappy cards. + * The MMC spec "It is strongly recommended + * for hosts to implement more than 500ms + * timeout value even if the card indicates + * the 250ms maximum busy length." Even the + * previous value of 300ms is known to be + * insufficient for some cards. */ - limit_us = 300000; + limit_us = 3000000; else limit_us = 100000; @@ -1405,7 +1409,10 @@ static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card, { unsigned int erase_timeout; - if (card->ext_csd.erase_group_def & 1) { + if (arg == MMC_DISCARD_ARG || + (arg == MMC_TRIM_ARG && card->ext_csd.rev >= 6)) { + erase_timeout = card->ext_csd.trim_timeout; + } else if (card->ext_csd.erase_group_def & 1) { /* High Capacity Erase Group Size uses HC timeouts */ if (arg == MMC_TRIM_ARG) erase_timeout = card->ext_csd.trim_timeout; @@ -1677,8 +1684,6 @@ int mmc_can_trim(struct mmc_card *card) { if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) return 1; - if (mmc_can_discard(card)) - return 1; return 0; } EXPORT_SYMBOL(mmc_can_trim); @@ -1697,6 +1702,8 @@ EXPORT_SYMBOL(mmc_can_discard); int mmc_can_sanitize(struct mmc_card *card) { + if (!mmc_can_trim(card) && !mmc_can_erase(card)) + return 0; if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_SANITIZE) return 1; return 0; @@ -2231,6 +2238,7 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable) mmc_card_is_removable(host)) return err; + mmc_claim_host(host); if (card && mmc_card_mmc(card) && (card->ext_csd.cache_size > 0)) { enable = !!enable; @@ -2248,6 +2256,7 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable) card->ext_csd.cache_ctrl = enable; } } + mmc_release_host(host); return err; } @@ -2265,49 +2274,32 @@ int mmc_suspend_host(struct mmc_host *host) cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); - if (mmc_try_claim_host(host)) { - err = mmc_cache_ctrl(host, 0); - mmc_release_host(host); - } else { - err = -EBUSY; - } + err = mmc_cache_ctrl(host, 0); if (err) goto out; mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { - /* - * A long response time is not acceptable for device drivers - * when doing suspend. Prevent mmc_claim_host in the suspend - * sequence, to potentially wait "forever" by trying to - * pre-claim the host. - */ - if (mmc_try_claim_host(host)) { - if (host->bus_ops->suspend) { - err = host->bus_ops->suspend(host); - } - mmc_release_host(host); + if (host->bus_ops->suspend) + err = host->bus_ops->suspend(host); - if (err == -ENOSYS || !host->bus_ops->resume) { - /* - * We simply "remove" the card in this case. - * It will be redetected on resume. (Calling - * bus_ops->remove() with a claimed host can - * deadlock.) - */ - if (host->bus_ops->remove) - host->bus_ops->remove(host); - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_power_off(host); - mmc_release_host(host); - host->pm_flags = 0; - err = 0; - } - } else { - err = -EBUSY; + if (err == -ENOSYS || !host->bus_ops->resume) { + /* + * We simply "remove" the card in this case. + * It will be redetected on resume. (Calling + * bus_ops->remove() with a claimed host can + * deadlock.) + */ + if (host->bus_ops->remove) + host->bus_ops->remove(host); + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_power_off(host); + mmc_release_host(host); + host->pm_flags = 0; + err = 0; } } mmc_bus_put(host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 02914d609a91..54df5adc0413 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -695,6 +695,11 @@ static int mmc_select_powerclass(struct mmc_card *card, else if (host->ios.clock <= 200000000) index = EXT_CSD_PWR_CL_200_195; break; + case MMC_VDD_27_28: + case MMC_VDD_28_29: + case MMC_VDD_29_30: + case MMC_VDD_30_31: + case MMC_VDD_31_32: case MMC_VDD_32_33: case MMC_VDD_33_34: case MMC_VDD_34_35: @@ -1111,11 +1116,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; err = mmc_select_powerclass(card, ext_csd_bits, ext_csd); - if (err) { - pr_err("%s: power class selection to bus width %d failed\n", - mmc_hostname(card->host), 1 << bus_width); - goto err; - } + if (err) + pr_warning("%s: power class selection to bus width %d" + " failed\n", mmc_hostname(card->host), + 1 << bus_width); } /* @@ -1147,10 +1151,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_select_powerclass(card, ext_csd_bits[idx][0], ext_csd); if (err) - pr_err("%s: power class selection to " - "bus width %d failed\n", - mmc_hostname(card->host), - 1 << bus_width); + pr_warning("%s: power class selection to " + "bus width %d failed\n", + mmc_hostname(card->host), + 1 << bus_width); err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, @@ -1178,10 +1182,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_select_powerclass(card, ext_csd_bits[idx][1], ext_csd); if (err) - pr_err("%s: power class selection to " - "bus width %d ddr %d failed\n", - mmc_hostname(card->host), - 1 << bus_width, ddr); + pr_warning("%s: power class selection to " + "bus width %d ddr %d failed\n", + mmc_hostname(card->host), + 1 << bus_width, ddr); err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 40989e6bb53a..236842ec955a 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -192,9 +192,15 @@ static int sdio_bus_remove(struct device *dev) return ret; } -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM + +static int pm_no_operation(struct device *dev) +{ + return 0; +} static const struct dev_pm_ops sdio_bus_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_no_operation, pm_no_operation) SET_RUNTIME_PM_OPS( pm_generic_runtime_suspend, pm_generic_runtime_resume, @@ -204,11 +210,11 @@ static const struct dev_pm_ops sdio_bus_pm_ops = { #define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops) -#else /* !CONFIG_PM_RUNTIME */ +#else /* !CONFIG_PM */ #define SDIO_PM_OPS_PTR NULL -#endif /* !CONFIG_PM_RUNTIME */ +#endif /* !CONFIG_PM */ static struct bus_type sdio_bus_type = { .name = "sdio", diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h index 000b3ad0f5ca..787aba1682bb 100644 --- a/drivers/mmc/host/atmel-mci-regs.h +++ b/drivers/mmc/host/atmel-mci-regs.h @@ -31,6 +31,7 @@ # define ATMCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */ # define ATMCI_MR_PDCPADV ( 1 << 14) /* Padding Value */ # define ATMCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */ +# define ATMCI_MR_CLKODD(x) ((x) << 16) /* LSB of Clock Divider */ #define ATMCI_DTOR 0x0008 /* Data Timeout */ # define ATMCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */ # define ATMCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */ diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 9819dc09ce08..e94476beca18 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -77,6 +77,7 @@ struct atmel_mci_caps { bool has_cstor_reg; bool has_highspeed; bool has_rwproof; + bool has_odd_clk_div; }; struct atmel_mci_dma { @@ -482,7 +483,14 @@ err: static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host, unsigned int ns) { - return (ns * (host->bus_hz / 1000000) + 999) / 1000; + /* + * It is easier here to use us instead of ns for the timeout, + * it prevents from overflows during calculation. + */ + unsigned int us = DIV_ROUND_UP(ns, 1000); + + /* Maximum clock frequency is host->bus_hz/2 */ + return us * (DIV_ROUND_UP(host->bus_hz, 2000000)); } static void atmci_set_timeout(struct atmel_mci *host, @@ -1127,16 +1135,27 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } /* Calculate clock divider */ - clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1; - if (clkdiv > 255) { - dev_warn(&mmc->class_dev, - "clock %u too slow; using %lu\n", - clock_min, host->bus_hz / (2 * 256)); - clkdiv = 255; + if (host->caps.has_odd_clk_div) { + clkdiv = DIV_ROUND_UP(host->bus_hz, clock_min) - 2; + if (clkdiv > 511) { + dev_warn(&mmc->class_dev, + "clock %u too slow; using %lu\n", + clock_min, host->bus_hz / (511 + 2)); + clkdiv = 511; + } + host->mode_reg = ATMCI_MR_CLKDIV(clkdiv >> 1) + | ATMCI_MR_CLKODD(clkdiv & 1); + } else { + clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1; + if (clkdiv > 255) { + dev_warn(&mmc->class_dev, + "clock %u too slow; using %lu\n", + clock_min, host->bus_hz / (2 * 256)); + clkdiv = 255; + } + host->mode_reg = ATMCI_MR_CLKDIV(clkdiv); } - host->mode_reg = ATMCI_MR_CLKDIV(clkdiv); - /* * WRPROOF and RDPROOF prevent overruns/underruns by * stopping the clock when the FIFO is full/empty. @@ -2007,35 +2026,35 @@ static void __init atmci_get_cap(struct atmel_mci *host) "version: 0x%x\n", version); host->caps.has_dma = 0; - host->caps.has_pdc = 0; + host->caps.has_pdc = 1; host->caps.has_cfg_reg = 0; host->caps.has_cstor_reg = 0; host->caps.has_highspeed = 0; host->caps.has_rwproof = 0; + host->caps.has_odd_clk_div = 0; /* keep only major version number */ switch (version & 0xf00) { - case 0x100: - case 0x200: - host->caps.has_pdc = 1; - host->caps.has_rwproof = 1; - break; - case 0x300: - case 0x400: case 0x500: + host->caps.has_odd_clk_div = 1; + case 0x400: + case 0x300: #ifdef CONFIG_AT_HDMAC host->caps.has_dma = 1; #else - host->caps.has_dma = 0; dev_info(&host->pdev->dev, "has dma capability but dma engine is not selected, then use pio\n"); #endif + host->caps.has_pdc = 0; host->caps.has_cfg_reg = 1; host->caps.has_cstor_reg = 1; host->caps.has_highspeed = 1; + case 0x200: host->caps.has_rwproof = 1; + case 0x100: break; default: + host->caps.has_pdc = 0; dev_warn(&host->pdev->dev, "Unmanaged mci version, set minimum capabilities\n"); break; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bf3c9b456aaf..ab3fc4617107 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -526,8 +526,10 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) return -ENODEV; sg_len = dw_mci_pre_dma_transfer(host, data, 0); - if (sg_len < 0) + if (sg_len < 0) { + host->dma_ops->stop(host); return sg_len; + } host->using_dma = 1; @@ -1879,7 +1881,8 @@ static void dw_mci_init_dma(struct dw_mci *host) if (!host->dma_ops) goto no_dma; - if (host->dma_ops->init) { + if (host->dma_ops->init && host->dma_ops->start && + host->dma_ops->stop && host->dma_ops->cleanup) { if (host->dma_ops->init(host)) { dev_err(&host->dev, "%s: Unable to initialize " "DMA Controller.\n", __func__); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index b0f2ef988188..e3f5af96ab87 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -363,6 +363,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host) goto out; dmaengine_submit(desc); + dma_async_issue_pending(host->dmach); return; out: @@ -403,6 +404,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host) goto out; dmaengine_submit(desc); + dma_async_issue_pending(host->dmach); return; out: @@ -531,6 +533,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) goto out; dmaengine_submit(desc); + dma_async_issue_pending(host->dmach); return; out: dev_warn(mmc_dev(host->mmc), diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 47adb161d3ad..56d4499d4388 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -249,7 +249,7 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, * the pbias cell programming support is still missing when * booting with Device tree */ - if (of_have_populated_dt() && !vdd) + if (dev->of_node && !vdd) return 0; if (mmc_slot(host).before_set_reg) @@ -1549,7 +1549,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * can't be allowed when booting with device * tree. */ - (!of_have_populated_dt())) { + !host->dev->of_node) { /* * The mmc_select_voltage fn of the core does * not seem to set the power_mode to @@ -1741,7 +1741,7 @@ static const struct of_device_id omap_mmc_of_match[] = { .data = &omap4_reg_offset, }, {}, -} +}; MODULE_DEVICE_TABLE(of, omap_mmc_of_match); static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) @@ -1785,7 +1785,7 @@ static inline struct omap_mmc_platform_data } #endif -static int __init omap_hsmmc_probe(struct platform_device *pdev) +static int __devinit omap_hsmmc_probe(struct platform_device *pdev) { struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_host *mmc; @@ -1818,8 +1818,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) if (res == NULL || irq < 0) return -ENXIO; - res->start += pdata->reg_offset; - res->end += pdata->reg_offset; res = request_mem_region(res->start, resource_size(res), pdev->name); if (res == NULL) return -EBUSY; @@ -1843,7 +1841,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) host->dma_ch = -1; host->irq = irq; host->slot_id = 0; - host->mapbase = res->start; + host->mapbase = res->start + pdata->reg_offset; host->base = ioremap(host->mapbase, SZ_4K); host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; @@ -1875,8 +1873,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) goto err1; } - omap_hsmmc_context_save(host); - if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n"); mmc->caps2 |= MMC_CAP2_NO_MULTI_READ; @@ -1887,6 +1883,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(host->dev); + omap_hsmmc_context_save(host); + if (cpu_is_omap2430()) { host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); /* @@ -2018,8 +2016,7 @@ err_reg: err_irq_cd_init: free_irq(host->irq, host); err_irq: - pm_runtime_mark_last_busy(host->dev); - pm_runtime_put_autosuspend(host->dev); + pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); clk_put(host->fclk); if (host->got_dbclk) { @@ -2037,35 +2034,33 @@ err: return ret; } -static int omap_hsmmc_remove(struct platform_device *pdev) +static int __devexit omap_hsmmc_remove(struct platform_device *pdev) { struct omap_hsmmc_host *host = platform_get_drvdata(pdev); struct resource *res; - if (host) { - pm_runtime_get_sync(host->dev); - mmc_remove_host(host->mmc); - if (host->use_reg) - omap_hsmmc_reg_put(host); - if (host->pdata->cleanup) - host->pdata->cleanup(&pdev->dev); - free_irq(host->irq, host); - if (mmc_slot(host).card_detect_irq) - free_irq(mmc_slot(host).card_detect_irq, host); - - pm_runtime_put_sync(host->dev); - pm_runtime_disable(host->dev); - clk_put(host->fclk); - if (host->got_dbclk) { - clk_disable(host->dbclk); - clk_put(host->dbclk); - } + pm_runtime_get_sync(host->dev); + mmc_remove_host(host->mmc); + if (host->use_reg) + omap_hsmmc_reg_put(host); + if (host->pdata->cleanup) + host->pdata->cleanup(&pdev->dev); + free_irq(host->irq, host); + if (mmc_slot(host).card_detect_irq) + free_irq(mmc_slot(host).card_detect_irq, host); - mmc_free_host(host->mmc); - iounmap(host->base); - omap_hsmmc_gpio_free(pdev->dev.platform_data); + pm_runtime_put_sync(host->dev); + pm_runtime_disable(host->dev); + clk_put(host->fclk); + if (host->got_dbclk) { + clk_disable(host->dbclk); + clk_put(host->dbclk); } + mmc_free_host(host->mmc); + iounmap(host->base); + omap_hsmmc_gpio_free(pdev->dev.platform_data); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) release_mem_region(res->start, resource_size(res)); @@ -2078,49 +2073,45 @@ static int omap_hsmmc_remove(struct platform_device *pdev) static int omap_hsmmc_suspend(struct device *dev) { int ret = 0; - struct platform_device *pdev = to_platform_device(dev); - struct omap_hsmmc_host *host = platform_get_drvdata(pdev); + struct omap_hsmmc_host *host = dev_get_drvdata(dev); - if (host && host->suspended) + if (!host) return 0; - if (host) { - pm_runtime_get_sync(host->dev); - host->suspended = 1; - if (host->pdata->suspend) { - ret = host->pdata->suspend(&pdev->dev, - host->slot_id); - if (ret) { - dev_dbg(mmc_dev(host->mmc), - "Unable to handle MMC board" - " level suspend\n"); - host->suspended = 0; - return ret; - } - } - ret = mmc_suspend_host(host->mmc); + if (host && host->suspended) + return 0; + pm_runtime_get_sync(host->dev); + host->suspended = 1; + if (host->pdata->suspend) { + ret = host->pdata->suspend(dev, host->slot_id); if (ret) { + dev_dbg(dev, "Unable to handle MMC board" + " level suspend\n"); host->suspended = 0; - if (host->pdata->resume) { - ret = host->pdata->resume(&pdev->dev, - host->slot_id); - if (ret) - dev_dbg(mmc_dev(host->mmc), - "Unmask interrupt failed\n"); - } - goto err; + return ret; } + } + ret = mmc_suspend_host(host->mmc); - if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { - omap_hsmmc_disable_irq(host); - OMAP_HSMMC_WRITE(host->base, HCTL, - OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); + if (ret) { + host->suspended = 0; + if (host->pdata->resume) { + ret = host->pdata->resume(dev, host->slot_id); + if (ret) + dev_dbg(dev, "Unmask interrupt failed\n"); } - if (host->got_dbclk) - clk_disable(host->dbclk); + goto err; + } + if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { + omap_hsmmc_disable_irq(host); + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); } + + if (host->got_dbclk) + clk_disable(host->dbclk); err: pm_runtime_put_sync(host->dev); return ret; @@ -2130,38 +2121,37 @@ err: static int omap_hsmmc_resume(struct device *dev) { int ret = 0; - struct platform_device *pdev = to_platform_device(dev); - struct omap_hsmmc_host *host = platform_get_drvdata(pdev); + struct omap_hsmmc_host *host = dev_get_drvdata(dev); + + if (!host) + return 0; if (host && !host->suspended) return 0; - if (host) { - pm_runtime_get_sync(host->dev); + pm_runtime_get_sync(host->dev); - if (host->got_dbclk) - clk_enable(host->dbclk); + if (host->got_dbclk) + clk_enable(host->dbclk); - if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) - omap_hsmmc_conf_bus_power(host); + if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) + omap_hsmmc_conf_bus_power(host); - if (host->pdata->resume) { - ret = host->pdata->resume(&pdev->dev, host->slot_id); - if (ret) - dev_dbg(mmc_dev(host->mmc), - "Unmask interrupt failed\n"); - } + if (host->pdata->resume) { + ret = host->pdata->resume(dev, host->slot_id); + if (ret) + dev_dbg(dev, "Unmask interrupt failed\n"); + } - omap_hsmmc_protect_card(host); + omap_hsmmc_protect_card(host); - /* Notify the core to resume the host */ - ret = mmc_resume_host(host->mmc); - if (ret == 0) - host->suspended = 0; + /* Notify the core to resume the host */ + ret = mmc_resume_host(host->mmc); + if (ret == 0) + host->suspended = 0; - pm_runtime_mark_last_busy(host->dev); - pm_runtime_put_autosuspend(host->dev); - } + pm_runtime_mark_last_busy(host->dev); + pm_runtime_put_autosuspend(host->dev); return ret; @@ -2178,7 +2168,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev) host = platform_get_drvdata(to_platform_device(dev)); omap_hsmmc_context_save(host); - dev_dbg(mmc_dev(host->mmc), "disabled\n"); + dev_dbg(dev, "disabled\n"); return 0; } @@ -2189,7 +2179,7 @@ static int omap_hsmmc_runtime_resume(struct device *dev) host = platform_get_drvdata(to_platform_device(dev)); omap_hsmmc_context_restore(host); - dev_dbg(mmc_dev(host->mmc), "enabled\n"); + dev_dbg(dev, "enabled\n"); return 0; } @@ -2202,7 +2192,8 @@ static struct dev_pm_ops omap_hsmmc_dev_pm_ops = { }; static struct platform_driver omap_hsmmc_driver = { - .remove = omap_hsmmc_remove, + .probe = omap_hsmmc_probe, + .remove = __devexit_p(omap_hsmmc_remove), .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, @@ -2211,21 +2202,7 @@ static struct platform_driver omap_hsmmc_driver = { }, }; -static int __init omap_hsmmc_init(void) -{ - /* Register the MMC driver */ - return platform_driver_probe(&omap_hsmmc_driver, omap_hsmmc_probe); -} - -static void __exit omap_hsmmc_cleanup(void) -{ - /* Unregister MMC driver */ - platform_driver_unregister(&omap_hsmmc_driver); -} - -module_init(omap_hsmmc_init); -module_exit(omap_hsmmc_cleanup); - +module_platform_driver(omap_hsmmc_driver); MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 46fd1fd1b605..177f697b5835 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -20,6 +20,7 @@ */ #include <linux/io.h> +#include <linux/module.h> #include <linux/mmc/host.h> #include "sdhci-pltfm.h" diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 6193a0d7bde5..8abdaf6697a8 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -467,8 +467,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) clk_prepare_enable(clk); pltfm_host->clk = clk; - if (!is_imx25_esdhc(imx_data)) - host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index fbbebe251e01..69ef0beae104 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1418,8 +1418,6 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, slots = chip->num_slots; /* Quirk may have changed this */ - pci_enable_msi(pdev); - for (i = 0; i < slots; i++) { slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i); if (IS_ERR(slot)) { @@ -1438,8 +1436,6 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, return 0; free: - pci_disable_msi(pdev); - pci_set_drvdata(pdev, NULL); kfree(chip); @@ -1462,8 +1458,6 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev) for (i = 0; i < chip->num_slots; i++) sdhci_pci_remove_slot(chip->slots[i]); - pci_disable_msi(pdev); - pci_set_drvdata(pdev, NULL); kfree(chip); } diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index b19e7d435f8d..55a164fcaa15 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -20,6 +20,10 @@ #include <linux/io.h> #include <linux/gpio.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/mmc/host.h> @@ -53,6 +57,18 @@ struct sdhci_s3c { struct clk *clk_bus[MAX_BUS_CLK]; }; +/** + * struct sdhci_s3c_driver_data - S3C SDHCI platform specific driver data + * @sdhci_quirks: sdhci host specific quirks. + * + * Specifies platform specific configuration of sdhci controller. + * Note: A structure for driver specific platform data is used for future + * expansion of its usage. + */ +struct sdhci_s3c_drv_data { + unsigned int sdhci_quirks; +}; + static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) { return sdhci_priv(host); @@ -132,10 +148,10 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, return UINT_MAX; /* - * Clock divider's step is different as 1 from that of host controller - * when 'clk_type' is S3C_SDHCI_CLK_DIV_EXTERNAL. + * If controller uses a non-standard clock division, find the best clock + * speed possible with selected clock source and skip the division. */ - if (ourhost->pdata->clk_type) { + if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { rate = clk_round_rate(clksrc, wanted); return wanted - rate; } @@ -272,6 +288,8 @@ static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host) static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_s3c *ourhost = to_s3c(host); + unsigned long timeout; + u16 clk = 0; /* don't bother if the clock is going off */ if (clock == 0) @@ -282,6 +300,25 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); host->clock = clock; + + clk = SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + /* Wait max 20 ms */ + timeout = 20; + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { + printk(KERN_ERR "%s: Internal clock never " + "stabilised.\n", mmc_hostname(host->mmc)); + return; + } + timeout--; + mdelay(1); + } + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } /** @@ -382,16 +419,24 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) } } +static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data( + struct platform_device *pdev) +{ + return (struct sdhci_s3c_drv_data *) + platform_get_device_id(pdev)->driver_data; +} + static int __devinit sdhci_s3c_probe(struct platform_device *pdev) { - struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; + struct s3c_sdhci_platdata *pdata; + struct sdhci_s3c_drv_data *drv_data; struct device *dev = &pdev->dev; struct sdhci_host *host; struct sdhci_s3c *sc; struct resource *res; int ret, irq, ptr, clks; - if (!pdata) { + if (!pdev->dev.platform_data) { dev_err(dev, "no device data specified\n"); return -ENOENT; } @@ -402,18 +447,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) return irq; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "no memory specified\n"); - return -ENOENT; - } - host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); if (IS_ERR(host)) { dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); } + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto err_io_clk; + } + memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); + + drv_data = sdhci_s3c_get_driver_data(pdev); sc = sdhci_priv(host); sc->host = host; @@ -464,15 +511,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) goto err_no_busclks; } - sc->ioarea = request_mem_region(res->start, resource_size(res), - mmc_hostname(host->mmc)); - if (!sc->ioarea) { - dev_err(dev, "failed to reserve register area\n"); - ret = -ENXIO; - goto err_req_regs; - } - - host->ioaddr = ioremap_nocache(res->start, resource_size(res)); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->ioaddr = devm_request_and_ioremap(&pdev->dev, res); if (!host->ioaddr) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; @@ -491,6 +531,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; + if (drv_data) + host->quirks |= drv_data->sdhci_quirks; #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -518,6 +560,14 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->mmc->caps = MMC_CAP_NONREMOVABLE; + switch (pdata->max_width) { + case 8: + host->mmc->caps |= MMC_CAP_8_BIT_DATA; + case 4: + host->mmc->caps |= MMC_CAP_4_BIT_DATA; + break; + } + if (pdata->pm_caps) host->mmc->pm_caps |= pdata->pm_caps; @@ -531,7 +581,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ - if (pdata->clk_type) { + if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; @@ -544,10 +594,17 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (pdata->host_caps2) host->mmc->caps2 |= pdata->host_caps2; + pm_runtime_enable(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_suspend_ignore_children(&pdev->dev, 1); + ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); - goto err_add_host; + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + goto err_req_regs; } /* The following two methods of card detection might call @@ -561,10 +618,6 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) return 0; - err_add_host: - release_resource(sc->ioarea); - kfree(sc->ioarea); - err_req_regs: for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { if (sc->clk_bus[ptr]) { @@ -601,6 +654,8 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev) sdhci_remove_host(host, 1); + pm_runtime_disable(&pdev->dev); + for (ptr = 0; ptr < 3; ptr++) { if (sc->clk_bus[ptr]) { clk_disable(sc->clk_bus[ptr]); @@ -610,18 +665,13 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev) clk_disable(sc->clk_io); clk_put(sc->clk_io); - iounmap(host->ioaddr); - release_resource(sc->ioarea); - kfree(sc->ioarea); - sdhci_free_host(host); platform_set_drvdata(pdev, NULL); return 0; } -#ifdef CONFIG_PM - +#ifdef CONFIG_PM_SLEEP static int sdhci_s3c_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -635,10 +685,29 @@ static int sdhci_s3c_resume(struct device *dev) return sdhci_resume_host(host); } +#endif + +#ifdef CONFIG_PM_RUNTIME +static int sdhci_s3c_runtime_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + + return sdhci_runtime_suspend_host(host); +} +static int sdhci_s3c_runtime_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + + return sdhci_runtime_resume_host(host); +} +#endif + +#ifdef CONFIG_PM static const struct dev_pm_ops sdhci_s3c_pmops = { - .suspend = sdhci_s3c_suspend, - .resume = sdhci_s3c_resume, + SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume) + SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume, + NULL) }; #define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops) @@ -647,9 +716,31 @@ static const struct dev_pm_ops sdhci_s3c_pmops = { #define SDHCI_S3C_PMOPS NULL #endif +#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) +static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { + .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK, +}; +#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data) +#else +#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL) +#endif + +static struct platform_device_id sdhci_s3c_driver_ids[] = { + { + .name = "s3c-sdhci", + .driver_data = (kernel_ulong_t)NULL, + }, { + .name = "exynos4-sdhci", + .driver_data = EXYNOS4_SDHCI_DRV_DATA, + }, + { } +}; +MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids); + static struct platform_driver sdhci_s3c_driver = { .probe = sdhci_s3c_probe, .remove = __devexit_p(sdhci_s3c_remove), + .id_table = sdhci_s3c_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-sdhci", diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8262cadfdab7..ccefdebeff14 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -147,7 +147,7 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) u32 present, irqs; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || - !mmc_card_is_removable(host->mmc)) + (host->mmc->caps & MMC_CAP_NONREMOVABLE)) return; present = sdhci_readl(host, SDHCI_PRESENT_STATE) & @@ -2782,8 +2782,9 @@ int sdhci_add_host(struct sdhci_host *host) mmc_card_is_removable(mmc)) mmc->caps |= MMC_CAP_NEEDS_POLL; - /* UHS-I mode(s) supported by the host controller. */ - if (host->version >= SDHCI_SPEC_300) + /* Any UHS-I mode in caps implies SDR12 and SDR25 support. */ + if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | + SDHCI_SUPPORT_DDR50)) mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; /* SDR104 supports also implies SDR50 support */ diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index aafaf0b6eb1c..724b35e85a26 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -454,7 +454,8 @@ static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); else sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & - ((fls(host->clk / clk) - 1) << 16)); + ((fls(DIV_ROUND_UP(host->clk, + clk) - 1) - 1) << 16)); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); } @@ -1297,14 +1298,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) spin_lock_init(&host->lock); mmc->ops = &sh_mmcif_ops; - mmc->f_max = host->clk; - /* close to 400KHz */ - if (mmc->f_max < 51200000) - mmc->f_min = mmc->f_max / 128; - else if (mmc->f_max < 102400000) - mmc->f_min = mmc->f_max / 256; - else - mmc->f_min = mmc->f_max / 512; + mmc->f_max = host->clk / 2; + mmc->f_min = host->clk / 512; if (pd->ocr) mmc->ocr_avail = pd->ocr; mmc->caps = MMC_CAP_MMC_HIGHSPEED; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 94eb05b1afdf..58fc65f5c817 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -106,16 +106,14 @@ static int mtdchar_open(struct inode *inode, struct file *file) } if (mtd->type == MTD_ABSENT) { - put_mtd_device(mtd); ret = -ENODEV; - goto out; + goto out1; } mtd_ino = iget_locked(mnt->mnt_sb, devnum); if (!mtd_ino) { - put_mtd_device(mtd); ret = -ENOMEM; - goto out; + goto out1; } if (mtd_ino->i_state & I_NEW) { mtd_ino->i_private = mtd; @@ -127,23 +125,25 @@ static int mtdchar_open(struct inode *inode, struct file *file) /* You can't open it RW if it's not a writeable device */ if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { - iput(mtd_ino); - put_mtd_device(mtd); ret = -EACCES; - goto out; + goto out2; } mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); if (!mfi) { - iput(mtd_ino); - put_mtd_device(mtd); ret = -ENOMEM; - goto out; + goto out2; } mfi->ino = mtd_ino; mfi->mtd = mtd; file->private_data = mfi; + mutex_unlock(&mtd_mutex); + return 0; +out2: + iput(mtd_ino); +out1: + put_mtd_device(mtd); out: mutex_unlock(&mtd_mutex); simple_release_fs(&mnt, &count); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 75b1dde16358..9ec51cec2e14 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -266,6 +266,7 @@ int start_dma_without_bch_irq(struct gpmi_nand_data *this, desc->callback = dma_irq_callback; desc->callback_param = this; dmaengine_submit(desc); + dma_async_issue_pending(get_dma_chan(this)); /* Wait for the interrupt from the DMA block. */ err = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index e2cdebf40840..61af9bb560ab 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -386,19 +386,11 @@ out: return count; } -static int default_open(struct inode *inode, struct file *file) -{ - if (inode->i_private) - file->private_data = inode->i_private; - - return 0; -} - /* File operations for all UBI debugfs files */ static const struct file_operations dfs_fops = { .read = dfs_file_read, .write = dfs_file_write, - .open = default_open, + .open = simple_open, .llseek = no_llseek, .owner = THIS_MODULE, }; diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c index 25197b698dd6..b8b4c7ba884f 100644 --- a/drivers/net/arcnet/arc-rimi.c +++ b/drivers/net/arcnet/arc-rimi.c @@ -89,16 +89,16 @@ static int __init arcrimi_probe(struct net_device *dev) BUGLVL(D_NORMAL) printk(VERSION); BUGLVL(D_NORMAL) printk("E-mail me if you actually test the RIM I driver, please!\n"); - BUGMSG(D_NORMAL, "Given: node %02Xh, shmem %lXh, irq %d\n", + BUGLVL(D_NORMAL) printk("Given: node %02Xh, shmem %lXh, irq %d\n", dev->dev_addr[0], dev->mem_start, dev->irq); if (dev->mem_start <= 0 || dev->irq <= 0) { - BUGMSG(D_NORMAL, "No autoprobe for RIM I; you " + BUGLVL(D_NORMAL) printk("No autoprobe for RIM I; you " "must specify the shmem and irq!\n"); return -ENODEV; } if (dev->dev_addr[0] == 0) { - BUGMSG(D_NORMAL, "You need to specify your card's station " + BUGLVL(D_NORMAL) printk("You need to specify your card's station " "ID!\n"); return -ENODEV; } @@ -109,7 +109,7 @@ static int __init arcrimi_probe(struct net_device *dev) * will be taken. */ if (!request_mem_region(dev->mem_start, MIRROR_SIZE, "arcnet (90xx)")) { - BUGMSG(D_NORMAL, "Card memory already allocated\n"); + BUGLVL(D_NORMAL) printk("Card memory already allocated\n"); return -ENODEV; } return arcrimi_found(dev); diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 793b00138275..3463b469e657 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2173,9 +2173,10 @@ re_arm: * received frames (loopback). Since only the payload is given to this * function, it check for loopback. */ -static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u16 length) +static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u16 length) { struct port *port; + int ret = RX_HANDLER_ANOTHER; if (length >= sizeof(struct lacpdu)) { @@ -2184,11 +2185,12 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u if (!port->slave) { pr_warning("%s: Warning: port of slave %s is uninitialized\n", slave->dev->name, slave->dev->master->name); - return; + return ret; } switch (lacpdu->subtype) { case AD_TYPE_LACPDU: + ret = RX_HANDLER_CONSUMED; pr_debug("Received LACPDU on port %d\n", port->actor_port_number); /* Protect against concurrent state machines */ @@ -2198,6 +2200,7 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u break; case AD_TYPE_MARKER: + ret = RX_HANDLER_CONSUMED; // No need to convert fields to Little Endian since we don't use the marker's fields. switch (((struct bond_marker *)lacpdu)->tlv_type) { @@ -2219,6 +2222,7 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u } } } + return ret; } /** @@ -2456,18 +2460,20 @@ out: return NETDEV_TX_OK; } -void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, +int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, struct slave *slave) { + int ret = RX_HANDLER_ANOTHER; if (skb->protocol != PKT_TYPE_LACPDU) - return; + return ret; if (!pskb_may_pull(skb, sizeof(struct lacpdu))) - return; + return ret; read_lock(&bond->lock); - bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len); + ret = bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len); read_unlock(&bond->lock); + return ret; } /* diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h index 235b2cc58b28..5ee7e3c45db7 100644 --- a/drivers/net/bonding/bond_3ad.h +++ b/drivers/net/bonding/bond_3ad.h @@ -274,7 +274,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave); void bond_3ad_handle_link_change(struct slave *slave, char link); int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info); int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev); -void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, +int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, struct slave *slave); int bond_3ad_set_carrier(struct bonding *bond); void bond_3ad_update_lacp_rate(struct bonding *bond); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 0c76186bb9e7..bc13b3d77432 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -891,9 +891,15 @@ static void bond_do_fail_over_mac(struct bonding *bond, switch (bond->params.fail_over_mac) { case BOND_FOM_ACTIVE: - if (new_active) + if (new_active) { memcpy(bond->dev->dev_addr, new_active->dev->dev_addr, new_active->dev->addr_len); + write_unlock_bh(&bond->curr_slave_lock); + read_unlock(&bond->lock); + call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev); + read_lock(&bond->lock); + write_lock_bh(&bond->curr_slave_lock); + } break; case BOND_FOM_FOLLOW: /* @@ -1438,8 +1444,9 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) struct sk_buff *skb = *pskb; struct slave *slave; struct bonding *bond; - void (*recv_probe)(struct sk_buff *, struct bonding *, + int (*recv_probe)(struct sk_buff *, struct bonding *, struct slave *); + int ret = RX_HANDLER_ANOTHER; skb = skb_share_check(skb, GFP_ATOMIC); if (unlikely(!skb)) @@ -1458,8 +1465,12 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC); if (likely(nskb)) { - recv_probe(nskb, bond, slave); + ret = recv_probe(nskb, bond, slave); dev_kfree_skb(nskb); + if (ret == RX_HANDLER_CONSUMED) { + consume_skb(skb); + return ret; + } } } @@ -1481,7 +1492,7 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) memcpy(eth_hdr(skb)->h_dest, bond->dev->dev_addr, ETH_ALEN); } - return RX_HANDLER_ANOTHER; + return ret; } /* enslave device <slave> to bond device <master> */ @@ -2028,6 +2039,9 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) write_unlock_bh(&bond->lock); unblock_netpoll_tx(); + if (bond->slave_cnt == 0) + call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev); + bond_compute_features(bond); if (!(bond_dev->features & NETIF_F_VLAN_CHALLENGED) && (old_features & NETIF_F_VLAN_CHALLENGED)) @@ -2714,7 +2728,7 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 } } -static void bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, +static int bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, struct slave *slave) { struct arphdr *arp; @@ -2722,7 +2736,7 @@ static void bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, __be32 sip, tip; if (skb->protocol != __cpu_to_be16(ETH_P_ARP)) - return; + return RX_HANDLER_ANOTHER; read_lock(&bond->lock); @@ -2767,6 +2781,7 @@ static void bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, out_unlock: read_unlock(&bond->lock); + return RX_HANDLER_ANOTHER; } /* @@ -3001,7 +3016,11 @@ static void bond_ab_arp_commit(struct bonding *bond, int delta_in_ticks) trans_start + delta_in_ticks)) || bond->curr_active_slave != slave) { slave->link = BOND_LINK_UP; - bond->current_arp_slave = NULL; + if (bond->current_arp_slave) { + bond_set_slave_inactive_flags( + bond->current_arp_slave); + bond->current_arp_slave = NULL; + } pr_info("%s: link status definitely up for interface %s.\n", bond->dev->name, slave->dev->name); @@ -3695,17 +3714,52 @@ static void bond_set_multicast_list(struct net_device *bond_dev) read_unlock(&bond->lock); } -static int bond_neigh_setup(struct net_device *dev, struct neigh_parms *parms) +static int bond_neigh_init(struct neighbour *n) { - struct bonding *bond = netdev_priv(dev); + struct bonding *bond = netdev_priv(n->dev); struct slave *slave = bond->first_slave; + const struct net_device_ops *slave_ops; + struct neigh_parms parms; + int ret; + + if (!slave) + return 0; + + slave_ops = slave->dev->netdev_ops; + + if (!slave_ops->ndo_neigh_setup) + return 0; + + parms.neigh_setup = NULL; + parms.neigh_cleanup = NULL; + ret = slave_ops->ndo_neigh_setup(slave->dev, &parms); + if (ret) + return ret; + + /* + * Assign slave's neigh_cleanup to neighbour in case cleanup is called + * after the last slave has been detached. Assumes that all slaves + * utilize the same neigh_cleanup (true at this writing as only user + * is ipoib). + */ + n->parms->neigh_cleanup = parms.neigh_cleanup; + + if (!parms.neigh_setup) + return 0; + + return parms.neigh_setup(n); +} + +/* + * The bonding ndo_neigh_setup is called at init time beofre any + * slave exists. So we must declare proxy setup function which will + * be used at run time to resolve the actual slave neigh param setup. + */ +static int bond_neigh_setup(struct net_device *dev, + struct neigh_parms *parms) +{ + parms->neigh_setup = bond_neigh_init; - if (slave) { - const struct net_device_ops *slave_ops - = slave->dev->netdev_ops; - if (slave_ops->ndo_neigh_setup) - return slave_ops->ndo_neigh_setup(slave->dev, parms); - } return 0; } diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 9a66e2a910ae..9c1c8cd5223f 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -744,14 +744,14 @@ static void cfhsi_wake_up(struct work_struct *work) size_t fifo_occupancy = 0; /* Wakeup timeout */ - dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", + dev_dbg(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__); /* Check FIFO to check if modem has sent something. */ WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, &fifo_occupancy)); - dev_err(&cfhsi->ndev->dev, "%s: Bytes in FIFO: %u.\n", + dev_dbg(&cfhsi->ndev->dev, "%s: Bytes in FIFO: %u.\n", __func__, (unsigned) fifo_occupancy); /* Check if we misssed the interrupt. */ @@ -1210,7 +1210,7 @@ int cfhsi_probe(struct platform_device *pdev) static void cfhsi_shutdown(struct cfhsi *cfhsi) { - u8 *tx_buf, *rx_buf; + u8 *tx_buf, *rx_buf, *flip_buf; /* Stop TXing */ netif_tx_stop_all_queues(cfhsi->ndev); @@ -1234,7 +1234,7 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi) /* Store bufferes: will be freed later. */ tx_buf = cfhsi->tx_buf; rx_buf = cfhsi->rx_buf; - + flip_buf = cfhsi->rx_flip_buf; /* Flush transmit queues. */ cfhsi_abort_tx(cfhsi); @@ -1247,6 +1247,7 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi) /* Free buffers. */ kfree(tx_buf); kfree(rx_buf); + kfree(flip_buf); } int cfhsi_remove(struct platform_device *pdev) diff --git a/drivers/net/caif/caif_spi.c b/drivers/net/caif/caif_spi.c index 96391c36fa74..b71ce9bf0afb 100644 --- a/drivers/net/caif/caif_spi.c +++ b/drivers/net/caif/caif_spi.c @@ -127,12 +127,6 @@ static inline void dev_debugfs_rem(struct cfspi *cfspi) debugfs_remove(cfspi->dbgfs_dir); } -static int dbgfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t dbgfs_state(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -243,13 +237,13 @@ static ssize_t dbgfs_frame(struct file *file, char __user *user_buf, } static const struct file_operations dbgfs_state_fops = { - .open = dbgfs_open, + .open = simple_open, .read = dbgfs_state, .owner = THIS_MODULE }; static const struct file_operations dbgfs_frame_fops = { - .open = dbgfs_open, + .open = simple_open, .read = dbgfs_frame, .owner = THIS_MODULE }; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index 5234586dff15..629c4ba5d49d 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -875,6 +875,7 @@ static int pcan_usb_pro_init(struct peak_usb_device *dev) PCAN_USBPRO_INFO_FW, &fi, sizeof(fi)); if (err) { + kfree(usb_if); dev_err(dev->netdev->dev.parent, "unable to read %s firmware info (err %d)\n", pcan_usb_pro.name, err); @@ -885,6 +886,7 @@ static int pcan_usb_pro_init(struct peak_usb_device *dev) PCAN_USBPRO_INFO_BL, &bi, sizeof(bi)); if (err) { + kfree(usb_if); dev_err(dev->netdev->dev.parent, "unable to read %s bootloader info (err %d)\n", pcan_usb_pro.name, err); diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index d5c6d92f1ee7..442d91a2747b 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -107,14 +107,14 @@ static int dummy_dev_init(struct net_device *dev) return 0; } -static void dummy_dev_free(struct net_device *dev) +static void dummy_dev_uninit(struct net_device *dev) { free_percpu(dev->dstats); - free_netdev(dev); } static const struct net_device_ops dummy_netdev_ops = { .ndo_init = dummy_dev_init, + .ndo_uninit = dummy_dev_uninit, .ndo_start_xmit = dummy_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = set_multicast_list, @@ -128,7 +128,7 @@ static void dummy_setup(struct net_device *dev) /* Initialize the device structure. */ dev->netdev_ops = &dummy_netdev_ops; - dev->destructor = dummy_dev_free; + dev->destructor = free_netdev; /* Fill in device structure with ethernet-generic values. */ dev->tx_queue_len = 0; diff --git a/drivers/net/eql.c b/drivers/net/eql.c index a59cf961a436..f219d38acf58 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -125,6 +125,7 @@ #include <linux/if.h> #include <linux/if_arp.h> #include <linux/if_eql.h> +#include <linux/pkt_sched.h> #include <asm/uaccess.h> @@ -143,7 +144,7 @@ static void eql_timer(unsigned long param) equalizer_t *eql = (equalizer_t *) param; struct list_head *this, *tmp, *head; - spin_lock_bh(&eql->queue.lock); + spin_lock(&eql->queue.lock); head = &eql->queue.all_slaves; list_for_each_safe(this, tmp, head) { slave_t *slave = list_entry(this, slave_t, list); @@ -157,7 +158,7 @@ static void eql_timer(unsigned long param) } } - spin_unlock_bh(&eql->queue.lock); + spin_unlock(&eql->queue.lock); eql->timer.expires = jiffies + EQL_DEFAULT_RESCHED_IVAL; add_timer(&eql->timer); @@ -341,7 +342,7 @@ static netdev_tx_t eql_slave_xmit(struct sk_buff *skb, struct net_device *dev) struct net_device *slave_dev = slave->dev; skb->dev = slave_dev; - skb->priority = 1; + skb->priority = TC_PRIO_FILLER; slave->bytes_queued += skb->len; dev_queue_xmit(skb); dev->stats.tx_packets++; diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index 40ac41436549..c926857e8205 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -2476,7 +2476,7 @@ static irqreturn_t atl1_intr(int irq, void *data) "pcie phy link down %x\n", status); if (netif_running(adapter->netdev)) { /* reset MAC */ iowrite32(0, adapter->hw.hw_addr + REG_IMR); - schedule_work(&adapter->pcie_dma_to_rst_task); + schedule_work(&adapter->reset_dev_task); return IRQ_HANDLED; } } @@ -2488,7 +2488,7 @@ static irqreturn_t atl1_intr(int irq, void *data) "pcie DMA r/w error (status = 0x%x)\n", status); iowrite32(0, adapter->hw.hw_addr + REG_IMR); - schedule_work(&adapter->pcie_dma_to_rst_task); + schedule_work(&adapter->reset_dev_task); return IRQ_HANDLED; } @@ -2633,10 +2633,10 @@ static void atl1_down(struct atl1_adapter *adapter) atl1_clean_rx_ring(adapter); } -static void atl1_tx_timeout_task(struct work_struct *work) +static void atl1_reset_dev_task(struct work_struct *work) { struct atl1_adapter *adapter = - container_of(work, struct atl1_adapter, tx_timeout_task); + container_of(work, struct atl1_adapter, reset_dev_task); struct net_device *netdev = adapter->netdev; netif_device_detach(netdev); @@ -3038,12 +3038,10 @@ static int __devinit atl1_probe(struct pci_dev *pdev, (unsigned long)adapter); adapter->phy_timer_pending = false; - INIT_WORK(&adapter->tx_timeout_task, atl1_tx_timeout_task); + INIT_WORK(&adapter->reset_dev_task, atl1_reset_dev_task); INIT_WORK(&adapter->link_chg_task, atlx_link_chg_task); - INIT_WORK(&adapter->pcie_dma_to_rst_task, atl1_tx_timeout_task); - err = register_netdev(netdev); if (err) goto err_common; diff --git a/drivers/net/ethernet/atheros/atlx/atl1.h b/drivers/net/ethernet/atheros/atlx/atl1.h index 109d6da8be97..e04bf4d71e46 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.h +++ b/drivers/net/ethernet/atheros/atlx/atl1.h @@ -758,9 +758,8 @@ struct atl1_adapter { u16 link_speed; u16 link_duplex; spinlock_t lock; - struct work_struct tx_timeout_task; + struct work_struct reset_dev_task; struct work_struct link_chg_task; - struct work_struct pcie_dma_to_rst_task; struct timer_list phy_config_timer; bool phy_timer_pending; diff --git a/drivers/net/ethernet/atheros/atlx/atlx.c b/drivers/net/ethernet/atheros/atlx/atlx.c index 3cd8837236dc..c9e9dc57986c 100644 --- a/drivers/net/ethernet/atheros/atlx/atlx.c +++ b/drivers/net/ethernet/atheros/atlx/atlx.c @@ -194,7 +194,7 @@ static void atlx_tx_timeout(struct net_device *netdev) { struct atlx_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ - schedule_work(&adapter->tx_timeout_task); + schedule_work(&adapter->reset_dev_task); } /* diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index e37161f19250..2c9ee552dffc 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1173,6 +1173,13 @@ enum { }; +struct bnx2x_prev_path_list { + u8 bus; + u8 slot; + u8 path; + struct list_head list; +}; + struct bnx2x { /* Fields used in the tx and intr/napi performance paths * are grouped together in the beginning of the structure diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index f1f3ca65667a..4b054812713a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1721,6 +1721,29 @@ static void bnx2x_squeeze_objects(struct bnx2x *bp) } while (0) #endif +bool bnx2x_test_firmware_version(struct bnx2x *bp, bool is_err) +{ + /* build FW version dword */ + u32 my_fw = (BCM_5710_FW_MAJOR_VERSION) + + (BCM_5710_FW_MINOR_VERSION << 8) + + (BCM_5710_FW_REVISION_VERSION << 16) + + (BCM_5710_FW_ENGINEERING_VERSION << 24); + + /* read loaded FW from chip */ + u32 loaded_fw = REG_RD(bp, XSEM_REG_PRAM); + + DP(NETIF_MSG_IFUP, "loaded fw %x, my fw %x\n", loaded_fw, my_fw); + + if (loaded_fw != my_fw) { + if (is_err) + BNX2X_ERR("bnx2x with FW %x was already loaded, which mismatches my %x FW. aborting\n", + loaded_fw, my_fw); + return false; + } + + return true; +} + /* must be called with rtnl_lock */ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) { @@ -1815,23 +1838,8 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) } if (load_code != FW_MSG_CODE_DRV_LOAD_COMMON_CHIP && load_code != FW_MSG_CODE_DRV_LOAD_COMMON) { - /* build FW version dword */ - u32 my_fw = (BCM_5710_FW_MAJOR_VERSION) + - (BCM_5710_FW_MINOR_VERSION << 8) + - (BCM_5710_FW_REVISION_VERSION << 16) + - (BCM_5710_FW_ENGINEERING_VERSION << 24); - - /* read loaded FW from chip */ - u32 loaded_fw = REG_RD(bp, XSEM_REG_PRAM); - - DP(BNX2X_MSG_SP, "loaded fw %x, my fw %x", - loaded_fw, my_fw); - /* abort nic load if version mismatch */ - if (my_fw != loaded_fw) { - BNX2X_ERR("bnx2x with FW %x already loaded, " - "which mismatches my %x FW. aborting", - loaded_fw, my_fw); + if (!bnx2x_test_firmware_version(bp, true)) { rc = -EBUSY; LOAD_ERROR_EXIT(bp, load_error2); } @@ -1866,7 +1874,6 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) * bnx2x_periodic_task(). */ smp_mb(); - queue_delayed_work(bnx2x_wq, &bp->period_task, 0); } else bp->port.pmf = 0; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 8b163388659a..5c27454d2ec2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -431,6 +431,9 @@ void bnx2x_panic_dump(struct bnx2x *bp); void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl); +/* validate currect fw is loaded */ +bool bnx2x_test_firmware_version(struct bnx2x *bp, bool is_err); + /* dev_close main block */ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h index cd6dfa9eaa3a..b9b263323436 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h @@ -25,31 +25,31 @@ (IRO[149].base + ((funcId) * IRO[149].m1)) #define CSTORM_IGU_MODE_OFFSET (IRO[157].base) #define CSTORM_ISCSI_CQ_SIZE_OFFSET(pfId) \ - (IRO[315].base + ((pfId) * IRO[315].m1)) -#define CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \ (IRO[316].base + ((pfId) * IRO[316].m1)) +#define CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \ + (IRO[317].base + ((pfId) * IRO[317].m1)) #define CSTORM_ISCSI_EQ_CONS_OFFSET(pfId, iscsiEqId) \ - (IRO[308].base + ((pfId) * IRO[308].m1) + ((iscsiEqId) * IRO[308].m2)) + (IRO[309].base + ((pfId) * IRO[309].m1) + ((iscsiEqId) * IRO[309].m2)) #define CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(pfId, iscsiEqId) \ - (IRO[310].base + ((pfId) * IRO[310].m1) + ((iscsiEqId) * IRO[310].m2)) + (IRO[311].base + ((pfId) * IRO[311].m1) + ((iscsiEqId) * IRO[311].m2)) #define CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(pfId, iscsiEqId) \ - (IRO[309].base + ((pfId) * IRO[309].m1) + ((iscsiEqId) * IRO[309].m2)) + (IRO[310].base + ((pfId) * IRO[310].m1) + ((iscsiEqId) * IRO[310].m2)) #define CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_VALID_OFFSET(pfId, iscsiEqId) \ - (IRO[311].base + ((pfId) * IRO[311].m1) + ((iscsiEqId) * IRO[311].m2)) + (IRO[312].base + ((pfId) * IRO[312].m1) + ((iscsiEqId) * IRO[312].m2)) #define CSTORM_ISCSI_EQ_PROD_OFFSET(pfId, iscsiEqId) \ - (IRO[307].base + ((pfId) * IRO[307].m1) + ((iscsiEqId) * IRO[307].m2)) + (IRO[308].base + ((pfId) * IRO[308].m1) + ((iscsiEqId) * IRO[308].m2)) #define CSTORM_ISCSI_EQ_SB_INDEX_OFFSET(pfId, iscsiEqId) \ - (IRO[313].base + ((pfId) * IRO[313].m1) + ((iscsiEqId) * IRO[313].m2)) + (IRO[314].base + ((pfId) * IRO[314].m1) + ((iscsiEqId) * IRO[314].m2)) #define CSTORM_ISCSI_EQ_SB_NUM_OFFSET(pfId, iscsiEqId) \ - (IRO[312].base + ((pfId) * IRO[312].m1) + ((iscsiEqId) * IRO[312].m2)) + (IRO[313].base + ((pfId) * IRO[313].m1) + ((iscsiEqId) * IRO[313].m2)) #define CSTORM_ISCSI_HQ_SIZE_OFFSET(pfId) \ - (IRO[314].base + ((pfId) * IRO[314].m1)) + (IRO[315].base + ((pfId) * IRO[315].m1)) #define CSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \ - (IRO[306].base + ((pfId) * IRO[306].m1)) + (IRO[307].base + ((pfId) * IRO[307].m1)) #define CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \ - (IRO[305].base + ((pfId) * IRO[305].m1)) + (IRO[306].base + ((pfId) * IRO[306].m1)) #define CSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \ - (IRO[304].base + ((pfId) * IRO[304].m1)) + (IRO[305].base + ((pfId) * IRO[305].m1)) #define CSTORM_RECORD_SLOW_PATH_OFFSET(funcId) \ (IRO[151].base + ((funcId) * IRO[151].m1)) #define CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(pfId) \ @@ -96,37 +96,37 @@ #define TSTORM_FUNC_EN_OFFSET(funcId) \ (IRO[103].base + ((funcId) * IRO[103].m1)) #define TSTORM_ISCSI_ERROR_BITMAP_OFFSET(pfId) \ - (IRO[271].base + ((pfId) * IRO[271].m1)) -#define TSTORM_ISCSI_L2_ISCSI_OOO_CID_TABLE_OFFSET(pfId) \ (IRO[272].base + ((pfId) * IRO[272].m1)) -#define TSTORM_ISCSI_L2_ISCSI_OOO_CLIENT_ID_TABLE_OFFSET(pfId) \ +#define TSTORM_ISCSI_L2_ISCSI_OOO_CID_TABLE_OFFSET(pfId) \ (IRO[273].base + ((pfId) * IRO[273].m1)) -#define TSTORM_ISCSI_L2_ISCSI_OOO_PROD_OFFSET(pfId) \ +#define TSTORM_ISCSI_L2_ISCSI_OOO_CLIENT_ID_TABLE_OFFSET(pfId) \ (IRO[274].base + ((pfId) * IRO[274].m1)) +#define TSTORM_ISCSI_L2_ISCSI_OOO_PROD_OFFSET(pfId) \ + (IRO[275].base + ((pfId) * IRO[275].m1)) #define TSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \ - (IRO[270].base + ((pfId) * IRO[270].m1)) + (IRO[271].base + ((pfId) * IRO[271].m1)) #define TSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \ - (IRO[269].base + ((pfId) * IRO[269].m1)) + (IRO[270].base + ((pfId) * IRO[270].m1)) #define TSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \ - (IRO[268].base + ((pfId) * IRO[268].m1)) + (IRO[269].base + ((pfId) * IRO[269].m1)) #define TSTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \ - (IRO[267].base + ((pfId) * IRO[267].m1)) + (IRO[268].base + ((pfId) * IRO[268].m1)) #define TSTORM_ISCSI_TCP_LOCAL_ADV_WND_OFFSET(pfId) \ - (IRO[276].base + ((pfId) * IRO[276].m1)) + (IRO[277].base + ((pfId) * IRO[277].m1)) #define TSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(pfId) \ - (IRO[263].base + ((pfId) * IRO[263].m1)) -#define TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(pfId) \ (IRO[264].base + ((pfId) * IRO[264].m1)) -#define TSTORM_ISCSI_TCP_VARS_MID_LOCAL_MAC_ADDR_OFFSET(pfId) \ +#define TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(pfId) \ (IRO[265].base + ((pfId) * IRO[265].m1)) -#define TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfId) \ +#define TSTORM_ISCSI_TCP_VARS_MID_LOCAL_MAC_ADDR_OFFSET(pfId) \ (IRO[266].base + ((pfId) * IRO[266].m1)) +#define TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(pfId) \ + (IRO[267].base + ((pfId) * IRO[267].m1)) #define TSTORM_MAC_FILTER_CONFIG_OFFSET(pfId) \ (IRO[202].base + ((pfId) * IRO[202].m1)) #define TSTORM_RECORD_SLOW_PATH_OFFSET(funcId) \ (IRO[105].base + ((funcId) * IRO[105].m1)) #define TSTORM_TCP_MAX_CWND_OFFSET(pfId) \ - (IRO[216].base + ((pfId) * IRO[216].m1)) + (IRO[217].base + ((pfId) * IRO[217].m1)) #define TSTORM_VF_TO_PF_OFFSET(funcId) \ (IRO[104].base + ((funcId) * IRO[104].m1)) #define USTORM_AGG_DATA_OFFSET (IRO[206].base) @@ -140,29 +140,29 @@ #define USTORM_ETH_PAUSE_ENABLED_OFFSET(portId) \ (IRO[183].base + ((portId) * IRO[183].m1)) #define USTORM_FCOE_EQ_PROD_OFFSET(pfId) \ - (IRO[317].base + ((pfId) * IRO[317].m1)) + (IRO[318].base + ((pfId) * IRO[318].m1)) #define USTORM_FUNC_EN_OFFSET(funcId) \ (IRO[178].base + ((funcId) * IRO[178].m1)) #define USTORM_ISCSI_CQ_SIZE_OFFSET(pfId) \ - (IRO[281].base + ((pfId) * IRO[281].m1)) -#define USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \ (IRO[282].base + ((pfId) * IRO[282].m1)) +#define USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \ + (IRO[283].base + ((pfId) * IRO[283].m1)) #define USTORM_ISCSI_ERROR_BITMAP_OFFSET(pfId) \ - (IRO[286].base + ((pfId) * IRO[286].m1)) + (IRO[287].base + ((pfId) * IRO[287].m1)) #define USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(pfId) \ - (IRO[283].base + ((pfId) * IRO[283].m1)) + (IRO[284].base + ((pfId) * IRO[284].m1)) #define USTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \ - (IRO[279].base + ((pfId) * IRO[279].m1)) + (IRO[280].base + ((pfId) * IRO[280].m1)) #define USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \ - (IRO[278].base + ((pfId) * IRO[278].m1)) + (IRO[279].base + ((pfId) * IRO[279].m1)) #define USTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \ - (IRO[277].base + ((pfId) * IRO[277].m1)) + (IRO[278].base + ((pfId) * IRO[278].m1)) #define USTORM_ISCSI_R2TQ_SIZE_OFFSET(pfId) \ - (IRO[280].base + ((pfId) * IRO[280].m1)) + (IRO[281].base + ((pfId) * IRO[281].m1)) #define USTORM_ISCSI_RQ_BUFFER_SIZE_OFFSET(pfId) \ - (IRO[284].base + ((pfId) * IRO[284].m1)) -#define USTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \ (IRO[285].base + ((pfId) * IRO[285].m1)) +#define USTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \ + (IRO[286].base + ((pfId) * IRO[286].m1)) #define USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(pfId) \ (IRO[182].base + ((pfId) * IRO[182].m1)) #define USTORM_RECORD_SLOW_PATH_OFFSET(funcId) \ @@ -188,39 +188,39 @@ #define XSTORM_FUNC_EN_OFFSET(funcId) \ (IRO[47].base + ((funcId) * IRO[47].m1)) #define XSTORM_ISCSI_HQ_SIZE_OFFSET(pfId) \ - (IRO[294].base + ((pfId) * IRO[294].m1)) + (IRO[295].base + ((pfId) * IRO[295].m1)) #define XSTORM_ISCSI_LOCAL_MAC_ADDR0_OFFSET(pfId) \ - (IRO[297].base + ((pfId) * IRO[297].m1)) -#define XSTORM_ISCSI_LOCAL_MAC_ADDR1_OFFSET(pfId) \ (IRO[298].base + ((pfId) * IRO[298].m1)) -#define XSTORM_ISCSI_LOCAL_MAC_ADDR2_OFFSET(pfId) \ +#define XSTORM_ISCSI_LOCAL_MAC_ADDR1_OFFSET(pfId) \ (IRO[299].base + ((pfId) * IRO[299].m1)) -#define XSTORM_ISCSI_LOCAL_MAC_ADDR3_OFFSET(pfId) \ +#define XSTORM_ISCSI_LOCAL_MAC_ADDR2_OFFSET(pfId) \ (IRO[300].base + ((pfId) * IRO[300].m1)) -#define XSTORM_ISCSI_LOCAL_MAC_ADDR4_OFFSET(pfId) \ +#define XSTORM_ISCSI_LOCAL_MAC_ADDR3_OFFSET(pfId) \ (IRO[301].base + ((pfId) * IRO[301].m1)) -#define XSTORM_ISCSI_LOCAL_MAC_ADDR5_OFFSET(pfId) \ +#define XSTORM_ISCSI_LOCAL_MAC_ADDR4_OFFSET(pfId) \ (IRO[302].base + ((pfId) * IRO[302].m1)) -#define XSTORM_ISCSI_LOCAL_VLAN_OFFSET(pfId) \ +#define XSTORM_ISCSI_LOCAL_MAC_ADDR5_OFFSET(pfId) \ (IRO[303].base + ((pfId) * IRO[303].m1)) +#define XSTORM_ISCSI_LOCAL_VLAN_OFFSET(pfId) \ + (IRO[304].base + ((pfId) * IRO[304].m1)) #define XSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \ - (IRO[293].base + ((pfId) * IRO[293].m1)) + (IRO[294].base + ((pfId) * IRO[294].m1)) #define XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \ - (IRO[292].base + ((pfId) * IRO[292].m1)) + (IRO[293].base + ((pfId) * IRO[293].m1)) #define XSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \ - (IRO[291].base + ((pfId) * IRO[291].m1)) + (IRO[292].base + ((pfId) * IRO[292].m1)) #define XSTORM_ISCSI_R2TQ_SIZE_OFFSET(pfId) \ - (IRO[296].base + ((pfId) * IRO[296].m1)) + (IRO[297].base + ((pfId) * IRO[297].m1)) #define XSTORM_ISCSI_SQ_SIZE_OFFSET(pfId) \ - (IRO[295].base + ((pfId) * IRO[295].m1)) + (IRO[296].base + ((pfId) * IRO[296].m1)) #define XSTORM_ISCSI_TCP_VARS_ADV_WND_SCL_OFFSET(pfId) \ - (IRO[290].base + ((pfId) * IRO[290].m1)) + (IRO[291].base + ((pfId) * IRO[291].m1)) #define XSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(pfId) \ - (IRO[289].base + ((pfId) * IRO[289].m1)) + (IRO[290].base + ((pfId) * IRO[290].m1)) #define XSTORM_ISCSI_TCP_VARS_TOS_OFFSET(pfId) \ - (IRO[288].base + ((pfId) * IRO[288].m1)) + (IRO[289].base + ((pfId) * IRO[289].m1)) #define XSTORM_ISCSI_TCP_VARS_TTL_OFFSET(pfId) \ - (IRO[287].base + ((pfId) * IRO[287].m1)) + (IRO[288].base + ((pfId) * IRO[288].m1)) #define XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(pfId) \ (IRO[44].base + ((pfId) * IRO[44].m1)) #define XSTORM_RECORD_SLOW_PATH_OFFSET(funcId) \ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index 5d71b7d43237..dbff5915b81a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -1251,6 +1251,9 @@ struct drv_func_mb { #define DRV_MSG_CODE_LINK_STATUS_CHANGED 0x01000000 + #define DRV_MSG_CODE_INITIATE_FLR 0x02000000 + #define REQ_BC_VER_4_INITIATE_FLR 0x00070213 + #define BIOS_MSG_CODE_LIC_CHALLENGE 0xff010000 #define BIOS_MSG_CODE_LIC_RESPONSE 0xff020000 #define BIOS_MSG_CODE_VIRT_MAC_PRIM 0xff030000 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index beb4cdbdb6e1..64392ec410a3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -35,7 +35,6 @@ #define ETH_MAX_PACKET_SIZE 1500 #define ETH_MAX_JUMBO_PACKET_SIZE 9600 #define MDIO_ACCESS_TIMEOUT 1000 -#define BMAC_CONTROL_RX_ENABLE 2 #define WC_LANE_MAX 4 #define I2C_SWITCH_WIDTH 2 #define I2C_BSC0 0 @@ -943,6 +942,12 @@ static int bnx2x_ets_e3b0_sp_pri_to_cos_set(const struct link_params *params, const u8 max_num_of_cos = (port) ? DCBX_E3B0_MAX_NUM_COS_PORT1 : DCBX_E3B0_MAX_NUM_COS_PORT0; + if (pri >= max_num_of_cos) { + DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_pri_to_cos_set invalid " + "parameter Illegal strict priority\n"); + return -EINVAL; + } + if (sp_pri_to_cos[pri] != DCBX_INVALID_COS) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_pri_to_cos_set invalid " "parameter There can't be two COS's with " @@ -950,12 +955,6 @@ static int bnx2x_ets_e3b0_sp_pri_to_cos_set(const struct link_params *params, return -EINVAL; } - if (pri > max_num_of_cos) { - DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_pri_to_cos_set invalid " - "parameter Illegal strict priority\n"); - return -EINVAL; - } - sp_pri_to_cos[pri] = cos_entry; return 0; @@ -1372,7 +1371,14 @@ static void bnx2x_update_pfc_xmac(struct link_params *params, pfc1_val |= XMAC_PFC_CTRL_HI_REG_PFC_REFRESH_EN | XMAC_PFC_CTRL_HI_REG_PFC_STATS_EN | XMAC_PFC_CTRL_HI_REG_RX_PFC_EN | - XMAC_PFC_CTRL_HI_REG_TX_PFC_EN; + XMAC_PFC_CTRL_HI_REG_TX_PFC_EN | + XMAC_PFC_CTRL_HI_REG_FORCE_PFC_XON; + /* Write pause and PFC registers */ + REG_WR(bp, xmac_base + XMAC_REG_PAUSE_CTRL, pause_val); + REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL, pfc0_val); + REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI, pfc1_val); + pfc1_val &= ~XMAC_PFC_CTRL_HI_REG_FORCE_PFC_XON; + } /* Write pause and PFC registers */ @@ -3649,6 +3655,33 @@ static void bnx2x_ext_phy_update_adv_fc(struct bnx2x_phy *phy, if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) { bnx2x_cl22_read(bp, phy, 0x4, &ld_pause); bnx2x_cl22_read(bp, phy, 0x5, &lp_pause); + } else if (CHIP_IS_E3(bp) && + SINGLE_MEDIA_DIRECT(params)) { + u8 lane = bnx2x_get_warpcore_lane(phy, params); + u16 gp_status, gp_mask; + bnx2x_cl45_read(bp, phy, + MDIO_AN_DEVAD, MDIO_WC_REG_GP2_STATUS_GP_2_4, + &gp_status); + gp_mask = (MDIO_WC_REG_GP2_STATUS_GP_2_4_CL73_AN_CMPL | + MDIO_WC_REG_GP2_STATUS_GP_2_4_CL37_LP_AN_CAP) << + lane; + if ((gp_status & gp_mask) == gp_mask) { + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_ADV_PAUSE, &ld_pause); + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_LP_AUTO_NEG, &lp_pause); + } else { + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_CL37_FC_LD, &ld_pause); + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_CL37_FC_LP, &lp_pause); + ld_pause = ((ld_pause & + MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) + << 3); + lp_pause = ((lp_pause & + MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) + << 3); + } } else { bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, @@ -3699,7 +3732,23 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, u16 val16 = 0, lane, bam37 = 0; struct bnx2x *bp = params->bp; DP(NETIF_MSG_LINK, "Enable Auto Negotiation for KR\n"); - + /* Set to default registers that may be overriden by 10G force */ + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7); + bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, + MDIO_WC_REG_PAR_DET_10G_CTRL, 0); + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, 0); + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_XGXSBLK1_LANECTRL0, 0xff); + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_XGXSBLK1_LANECTRL1, 0x5555); + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, + MDIO_WC_REG_IEEE0BLK_AUTONEGNP, 0x0); + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_RX66_CONTROL, 0x7415); + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_SERDESDIGITAL_MISC2, 0x6190); /* Disable Autoneg: re-enable it after adv is done. */ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0); @@ -3945,13 +3994,13 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy, } else { misc1_val |= 0x9; - tap_val = ((0x12 << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) | - (0x2d << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) | - (0x00 << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET)); + tap_val = ((0x0f << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) | + (0x2b << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) | + (0x02 << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET)); tx_driver_val = - ((0x02 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) | + ((0x03 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) | (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) | - (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET)); + (0x06 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET)); } bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_MISC1, misc1_val); @@ -4369,7 +4418,7 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, switch (serdes_net_if) { case PORT_HW_CFG_NET_SERDES_IF_KR: /* Enable KR Auto Neg */ - if (params->loopback_mode == LOOPBACK_NONE) + if (params->loopback_mode != LOOPBACK_EXT) bnx2x_warpcore_enable_AN_KR(phy, params, vars); else { DP(NETIF_MSG_LINK, "Setting KR 10G-Force\n"); @@ -6167,12 +6216,14 @@ int bnx2x_set_led(struct link_params *params, tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); if (params->phy[EXT_PHY1].type == - PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) - EMAC_WR(bp, EMAC_REG_EMAC_LED, tmp & 0xfff1); - else { - EMAC_WR(bp, EMAC_REG_EMAC_LED, - (tmp | EMAC_LED_OVERRIDE)); - } + PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) + tmp &= ~(EMAC_LED_1000MB_OVERRIDE | + EMAC_LED_100MB_OVERRIDE | + EMAC_LED_10MB_OVERRIDE); + else + tmp |= EMAC_LED_OVERRIDE; + + EMAC_WR(bp, EMAC_REG_EMAC_LED, tmp); break; case LED_MODE_OPER: @@ -6227,10 +6278,15 @@ int bnx2x_set_led(struct link_params *params, hw_led_mode); } else if ((params->phy[EXT_PHY1].type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) && - (mode != LED_MODE_OPER)) { + (mode == LED_MODE_ON)) { REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0); tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); - EMAC_WR(bp, EMAC_REG_EMAC_LED, tmp | 0x3); + EMAC_WR(bp, EMAC_REG_EMAC_LED, tmp | + EMAC_LED_OVERRIDE | EMAC_LED_1000MB_OVERRIDE); + /* Break here; otherwise, it'll disable the + * intended override. + */ + break; } else REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode); @@ -6245,13 +6301,9 @@ int bnx2x_set_led(struct link_params *params, LED_BLINK_RATE_VAL_E1X_E2); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_ENA_P0 + port*4, 1); - if ((params->phy[EXT_PHY1].type != - PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE) && - (mode != LED_MODE_OPER)) { - tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); - EMAC_WR(bp, EMAC_REG_EMAC_LED, - (tmp & (~EMAC_LED_OVERRIDE))); - } + tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); + EMAC_WR(bp, EMAC_REG_EMAC_LED, + (tmp & (~EMAC_LED_OVERRIDE))); if (CHIP_IS_E1(bp) && ((speed == SPEED_2500) || @@ -6844,6 +6896,12 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars) SINGLE_MEDIA_DIRECT(params)) && (phy_vars[active_external_phy].fault_detected == 0)); + /* Update the PFC configuration in case it was changed */ + if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) + vars->link_status |= LINK_STATUS_PFC_ENABLED; + else + vars->link_status &= ~LINK_STATUS_PFC_ENABLED; + if (vars->link_up) rc = bnx2x_update_link_up(params, vars, link_10g_plus); else @@ -8031,7 +8089,9 @@ static int bnx2x_verify_sfp_module(struct bnx2x_phy *phy, netdev_err(bp->dev, "Warning: Unqualified SFP+ module detected," " Port %d from %s part number %s\n", params->port, vendor_name, vendor_pn); - phy->flags |= FLAGS_SFP_NOT_APPROVED; + if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) != + PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_WARNING_MSG) + phy->flags |= FLAGS_SFP_NOT_APPROVED; return -EINVAL; } @@ -9091,6 +9151,12 @@ static int bnx2x_8727_config_init(struct bnx2x_phy *phy, tmp2 &= 0xFFEF; bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_OPT_CFG_REG, tmp2); + bnx2x_cl45_read(bp, phy, + MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, + &tmp2); + bnx2x_cl45_write(bp, phy, + MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, + (tmp2 & 0x7fff)); } return 0; @@ -9271,12 +9337,11 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy, MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL, ((1<<5) | (1<<2))); } - DP(NETIF_MSG_LINK, "Enabling 8727 TX laser if SFP is approved\n"); - bnx2x_8727_specific_func(phy, params, ENABLE_TX); - /* If transmitter is disabled, ignore false link up indication */ - bnx2x_cl45_read(bp, phy, - MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &val1); - if (val1 & (1<<15)) { + + if (!(phy->flags & FLAGS_SFP_NOT_APPROVED)) { + DP(NETIF_MSG_LINK, "Enabling 8727 TX laser\n"); + bnx2x_sfp_set_transmitter(params, phy, 1); + } else { DP(NETIF_MSG_LINK, "Tx is disabled\n"); return 0; } @@ -9370,8 +9435,7 @@ static void bnx2x_save_848xx_spirom_version(struct bnx2x_phy *phy, if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD, 0x400f, &fw_ver1); - bnx2x_save_spirom_version(bp, port, - ((fw_ver1 & 0xf000)>>5) | (fw_ver1 & 0x7f), + bnx2x_save_spirom_version(bp, port, fw_ver1 & 0xfff, phy->ver_addr); } else { /* For 32-bit registers in 848xx, access via MDIO2ARM i/f. */ @@ -9794,6 +9858,15 @@ static int bnx2x_84833_hw_reset_phy(struct bnx2x_phy *phy, other_shmem_base_addr)); u32 shmem_base_path[2]; + + /* Work around for 84833 LED failure inside RESET status */ + bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_8481_LEGACY_MII_CTRL, + MDIO_AN_REG_8481_MII_CTRL_FORCE_1G); + bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_8481_1G_100T_EXT_CTRL, + MIDO_AN_REG_8481_EXT_CTRL_FORCE_LEDS_OFF); + shmem_base_path[0] = params->shmem_base; shmem_base_path[1] = other_shmem_base_addr; @@ -10104,7 +10177,7 @@ static void bnx2x_848x3_link_reset(struct bnx2x_phy *phy, u8 port; u16 val16; - if (!(CHIP_IS_E1(bp))) + if (!(CHIP_IS_E1x(bp))) port = BP_PATH(bp); else port = params->port; @@ -10131,7 +10204,7 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy, u16 val; u8 port; - if (!(CHIP_IS_E1(bp))) + if (!(CHIP_IS_E1x(bp))) port = BP_PATH(bp); else port = params->port; @@ -12050,6 +12123,9 @@ int bnx2x_phy_init(struct link_params *params, struct link_vars *vars) bnx2x_emac_init(params, vars); + if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) + vars->link_status |= LINK_STATUS_PFC_ENABLED; + if (params->num_phys == 0) { DP(NETIF_MSG_LINK, "No phy found for initialization !!\n"); return -EINVAL; @@ -12129,10 +12205,10 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars, * Hold it as vars low */ /* clear link led */ + bnx2x_set_mdio_clk(bp, params->chip_id, port); bnx2x_set_led(params, vars, LED_MODE_OFF, 0); if (reset_ext_phy) { - bnx2x_set_mdio_clk(bp, params->chip_id, port); for (phy_index = EXT_PHY1; phy_index < params->num_phys; phy_index++) { if (params->phy[phy_index].link_reset) { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h index 7ba557a610da..763535ee4832 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h @@ -89,6 +89,8 @@ #define PFC_BRB_FULL_LB_XON_THRESHOLD 250 #define MAXVAL(a, b) (((a) > (b)) ? (a) : (b)) + +#define BMAC_CONTROL_RX_ENABLE 2 /***********************************************************/ /* Structs */ /***********************************************************/ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index f7f9aa807264..6af310195bae 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -52,6 +52,7 @@ #include <linux/prefetch.h> #include <linux/zlib.h> #include <linux/io.h> +#include <linux/semaphore.h> #include <linux/stringify.h> #include <linux/vmalloc.h> @@ -211,6 +212,10 @@ static DEFINE_PCI_DEVICE_TABLE(bnx2x_pci_tbl) = { MODULE_DEVICE_TABLE(pci, bnx2x_pci_tbl); +/* Global resources for unloading a previously loaded device */ +#define BNX2X_PREV_WAIT_NEEDED 1 +static DEFINE_SEMAPHORE(bnx2x_prev_sem); +static LIST_HEAD(bnx2x_prev_list); /**************************************************************************** * General service functions ****************************************************************************/ @@ -8812,109 +8817,392 @@ static inline void bnx2x_undi_int_disable(struct bnx2x *bp) bnx2x_undi_int_disable_e1h(bp); } -static void __devinit bnx2x_undi_unload(struct bnx2x *bp) +static void __devinit bnx2x_prev_unload_close_mac(struct bnx2x *bp) { - u32 val; + u32 val, base_addr, offset, mask, reset_reg; + bool mac_stopped = false; + u8 port = BP_PORT(bp); - /* possibly another driver is trying to reset the chip */ - bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_RESET); + reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2); - /* check if doorbell queue is reset */ - if (REG_RD(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET) - & MISC_REGISTERS_RESET_REG_1_RST_DORQ) { + if (!CHIP_IS_E3(bp)) { + val = REG_RD(bp, NIG_REG_BMAC0_REGS_OUT_EN + port * 4); + mask = MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port; + if ((mask & reset_reg) && val) { + u32 wb_data[2]; + BNX2X_DEV_INFO("Disable bmac Rx\n"); + base_addr = BP_PORT(bp) ? NIG_REG_INGRESS_BMAC1_MEM + : NIG_REG_INGRESS_BMAC0_MEM; + offset = CHIP_IS_E2(bp) ? BIGMAC2_REGISTER_BMAC_CONTROL + : BIGMAC_REGISTER_BMAC_CONTROL; - /* - * Check if it is the UNDI driver + /* + * use rd/wr since we cannot use dmae. This is safe + * since MCP won't access the bus due to the request + * to unload, and no function on the path can be + * loaded at this time. + */ + wb_data[0] = REG_RD(bp, base_addr + offset); + wb_data[1] = REG_RD(bp, base_addr + offset + 0x4); + wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; + REG_WR(bp, base_addr + offset, wb_data[0]); + REG_WR(bp, base_addr + offset + 0x4, wb_data[1]); + + } + BNX2X_DEV_INFO("Disable emac Rx\n"); + REG_WR(bp, NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4, 0); + + mac_stopped = true; + } else { + if (reset_reg & MISC_REGISTERS_RESET_REG_2_XMAC) { + BNX2X_DEV_INFO("Disable xmac Rx\n"); + base_addr = BP_PORT(bp) ? GRCBASE_XMAC1 : GRCBASE_XMAC0; + val = REG_RD(bp, base_addr + XMAC_REG_PFC_CTRL_HI); + REG_WR(bp, base_addr + XMAC_REG_PFC_CTRL_HI, + val & ~(1 << 1)); + REG_WR(bp, base_addr + XMAC_REG_PFC_CTRL_HI, + val | (1 << 1)); + REG_WR(bp, base_addr + XMAC_REG_CTRL, 0); + mac_stopped = true; + } + mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; + if (mask & reset_reg) { + BNX2X_DEV_INFO("Disable umac Rx\n"); + base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0; + REG_WR(bp, base_addr + UMAC_REG_COMMAND_CONFIG, 0); + mac_stopped = true; + } + } + + if (mac_stopped) + msleep(20); + +} + +#define BNX2X_PREV_UNDI_PROD_ADDR(p) (BAR_TSTRORM_INTMEM + 0x1508 + ((p) << 4)) +#define BNX2X_PREV_UNDI_RCQ(val) ((val) & 0xffff) +#define BNX2X_PREV_UNDI_BD(val) ((val) >> 16 & 0xffff) +#define BNX2X_PREV_UNDI_PROD(rcq, bd) ((bd) << 16 | (rcq)) + +static void __devinit bnx2x_prev_unload_undi_inc(struct bnx2x *bp, u8 port, + u8 inc) +{ + u16 rcq, bd; + u32 tmp_reg = REG_RD(bp, BNX2X_PREV_UNDI_PROD_ADDR(port)); + + rcq = BNX2X_PREV_UNDI_RCQ(tmp_reg) + inc; + bd = BNX2X_PREV_UNDI_BD(tmp_reg) + inc; + + tmp_reg = BNX2X_PREV_UNDI_PROD(rcq, bd); + REG_WR(bp, BNX2X_PREV_UNDI_PROD_ADDR(port), tmp_reg); + + BNX2X_DEV_INFO("UNDI producer [%d] rings bd -> 0x%04x, rcq -> 0x%04x\n", + port, bd, rcq); +} + +static int __devinit bnx2x_prev_mcp_done(struct bnx2x *bp) +{ + u32 rc = bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE, 0); + if (!rc) { + BNX2X_ERR("MCP response failure, aborting\n"); + return -EBUSY; + } + + return 0; +} + +static bool __devinit bnx2x_prev_is_path_marked(struct bnx2x *bp) +{ + struct bnx2x_prev_path_list *tmp_list; + int rc = false; + + if (down_trylock(&bnx2x_prev_sem)) + return false; + + list_for_each_entry(tmp_list, &bnx2x_prev_list, list) { + if (PCI_SLOT(bp->pdev->devfn) == tmp_list->slot && + bp->pdev->bus->number == tmp_list->bus && + BP_PATH(bp) == tmp_list->path) { + rc = true; + BNX2X_DEV_INFO("Path %d was already cleaned from previous drivers\n", + BP_PATH(bp)); + break; + } + } + + up(&bnx2x_prev_sem); + + return rc; +} + +static int __devinit bnx2x_prev_mark_path(struct bnx2x *bp) +{ + struct bnx2x_prev_path_list *tmp_list; + int rc; + + tmp_list = (struct bnx2x_prev_path_list *) + kmalloc(sizeof(struct bnx2x_prev_path_list), GFP_KERNEL); + if (!tmp_list) { + BNX2X_ERR("Failed to allocate 'bnx2x_prev_path_list'\n"); + return -ENOMEM; + } + + tmp_list->bus = bp->pdev->bus->number; + tmp_list->slot = PCI_SLOT(bp->pdev->devfn); + tmp_list->path = BP_PATH(bp); + + rc = down_interruptible(&bnx2x_prev_sem); + if (rc) { + BNX2X_ERR("Received %d when tried to take lock\n", rc); + kfree(tmp_list); + } else { + BNX2X_DEV_INFO("Marked path [%d] - finished previous unload\n", + BP_PATH(bp)); + list_add(&tmp_list->list, &bnx2x_prev_list); + up(&bnx2x_prev_sem); + } + + return rc; +} + +static bool __devinit bnx2x_can_flr(struct bnx2x *bp) +{ + int pos; + u32 cap; + struct pci_dev *dev = bp->pdev; + + pos = pci_pcie_cap(dev); + if (!pos) + return false; + + pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP, &cap); + if (!(cap & PCI_EXP_DEVCAP_FLR)) + return false; + + return true; +} + +static int __devinit bnx2x_do_flr(struct bnx2x *bp) +{ + int i, pos; + u16 status; + struct pci_dev *dev = bp->pdev; + + /* probe the capability first */ + if (bnx2x_can_flr(bp)) + return -ENOTTY; + + pos = pci_pcie_cap(dev); + if (!pos) + return -ENOTTY; + + /* Wait for Transaction Pending bit clean */ + for (i = 0; i < 4; i++) { + if (i) + msleep((1 << (i - 1)) * 100); + + pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status); + if (!(status & PCI_EXP_DEVSTA_TRPND)) + goto clear; + } + + dev_err(&dev->dev, + "transaction is not cleared; proceeding with reset anyway\n"); + +clear: + if (bp->common.bc_ver < REQ_BC_VER_4_INITIATE_FLR) { + BNX2X_ERR("FLR not supported by BC_VER: 0x%x\n", + bp->common.bc_ver); + return -EINVAL; + } + + bnx2x_fw_command(bp, DRV_MSG_CODE_INITIATE_FLR, 0); + + return 0; +} + +static int __devinit bnx2x_prev_unload_uncommon(struct bnx2x *bp) +{ + int rc; + + BNX2X_DEV_INFO("Uncommon unload Flow\n"); + + /* Test if previous unload process was already finished for this path */ + if (bnx2x_prev_is_path_marked(bp)) + return bnx2x_prev_mcp_done(bp); + + /* If function has FLR capabilities, and existing FW version matches + * the one required, then FLR will be sufficient to clean any residue + * left by previous driver + */ + if (bnx2x_test_firmware_version(bp, false) && bnx2x_can_flr(bp)) + return bnx2x_do_flr(bp); + + /* Close the MCP request, return failure*/ + rc = bnx2x_prev_mcp_done(bp); + if (!rc) + rc = BNX2X_PREV_WAIT_NEEDED; + + return rc; +} + +static int __devinit bnx2x_prev_unload_common(struct bnx2x *bp) +{ + u32 reset_reg, tmp_reg = 0, rc; + /* It is possible a previous function received 'common' answer, + * but hasn't loaded yet, therefore creating a scenario of + * multiple functions receiving 'common' on the same path. + */ + BNX2X_DEV_INFO("Common unload Flow\n"); + + if (bnx2x_prev_is_path_marked(bp)) + return bnx2x_prev_mcp_done(bp); + + reset_reg = REG_RD(bp, MISC_REG_RESET_REG_1); + + /* Reset should be performed after BRB is emptied */ + if (reset_reg & MISC_REGISTERS_RESET_REG_1_RST_BRB1) { + u32 timer_count = 1000; + bool prev_undi = false; + + /* Close the MAC Rx to prevent BRB from filling up */ + bnx2x_prev_unload_close_mac(bp); + + /* Check if the UNDI driver was previously loaded * UNDI driver initializes CID offset for normal bell to 0x7 */ - val = REG_RD(bp, DORQ_REG_NORM_CID_OFST); - if (val == 0x7) { - u32 reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; - /* save our pf_num */ - int orig_pf_num = bp->pf_num; - int port; - u32 swap_en, swap_val, value; - - /* clear the UNDI indication */ - REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0); - - BNX2X_DEV_INFO("UNDI is active! reset device\n"); - - /* try unload UNDI on port 0 */ - bp->pf_num = 0; - bp->fw_seq = - (SHMEM_RD(bp, func_mb[bp->pf_num].drv_mb_header) & - DRV_MSG_SEQ_NUMBER_MASK); - reset_code = bnx2x_fw_command(bp, reset_code, 0); - - /* if UNDI is loaded on the other port */ - if (reset_code != FW_MSG_CODE_DRV_UNLOAD_COMMON) { - - /* send "DONE" for previous unload */ - bnx2x_fw_command(bp, - DRV_MSG_CODE_UNLOAD_DONE, 0); - - /* unload UNDI on port 1 */ - bp->pf_num = 1; - bp->fw_seq = - (SHMEM_RD(bp, func_mb[bp->pf_num].drv_mb_header) & - DRV_MSG_SEQ_NUMBER_MASK); - reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; - - bnx2x_fw_command(bp, reset_code, 0); + reset_reg = REG_RD(bp, MISC_REG_RESET_REG_1); + if (reset_reg & MISC_REGISTERS_RESET_REG_1_RST_DORQ) { + tmp_reg = REG_RD(bp, DORQ_REG_NORM_CID_OFST); + if (tmp_reg == 0x7) { + BNX2X_DEV_INFO("UNDI previously loaded\n"); + prev_undi = true; + /* clear the UNDI indication */ + REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0); } + } + /* wait until BRB is empty */ + tmp_reg = REG_RD(bp, BRB1_REG_NUM_OF_FULL_BLOCKS); + while (timer_count) { + u32 prev_brb = tmp_reg; - bnx2x_undi_int_disable(bp); - port = BP_PORT(bp); - - /* close input traffic and wait for it */ - /* Do not rcv packets to BRB */ - REG_WR(bp, (port ? NIG_REG_LLH1_BRB1_DRV_MASK : - NIG_REG_LLH0_BRB1_DRV_MASK), 0x0); - /* Do not direct rcv packets that are not for MCP to - * the BRB */ - REG_WR(bp, (port ? NIG_REG_LLH1_BRB1_NOT_MCP : - NIG_REG_LLH0_BRB1_NOT_MCP), 0x0); - /* clear AEU */ - REG_WR(bp, (port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : - MISC_REG_AEU_MASK_ATTN_FUNC_0), 0); - msleep(10); - - /* save NIG port swap info */ - swap_val = REG_RD(bp, NIG_REG_PORT_SWAP); - swap_en = REG_RD(bp, NIG_REG_STRAP_OVERRIDE); - /* reset device */ - REG_WR(bp, - GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, - 0xd3ffffff); - - value = 0x1400; - if (CHIP_IS_E3(bp)) { - value |= MISC_REGISTERS_RESET_REG_2_MSTAT0; - value |= MISC_REGISTERS_RESET_REG_2_MSTAT1; - } + tmp_reg = REG_RD(bp, BRB1_REG_NUM_OF_FULL_BLOCKS); + if (!tmp_reg) + break; - REG_WR(bp, - GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, - value); + BNX2X_DEV_INFO("BRB still has 0x%08x\n", tmp_reg); - /* take the NIG out of reset and restore swap values */ - REG_WR(bp, - GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, - MISC_REGISTERS_RESET_REG_1_RST_NIG); - REG_WR(bp, NIG_REG_PORT_SWAP, swap_val); - REG_WR(bp, NIG_REG_STRAP_OVERRIDE, swap_en); + /* reset timer as long as BRB actually gets emptied */ + if (prev_brb > tmp_reg) + timer_count = 1000; + else + timer_count--; - /* send unload done to the MCP */ - bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE, 0); + /* If UNDI resides in memory, manually increment it */ + if (prev_undi) + bnx2x_prev_unload_undi_inc(bp, BP_PORT(bp), 1); - /* restore our func and fw_seq */ - bp->pf_num = orig_pf_num; + udelay(10); } + + if (!timer_count) + BNX2X_ERR("Failed to empty BRB, hope for the best\n"); + } - /* now it's safe to release the lock */ - bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_RESET); + /* No packets are in the pipeline, path is ready for reset */ + bnx2x_reset_common(bp); + + rc = bnx2x_prev_mark_path(bp); + if (rc) { + bnx2x_prev_mcp_done(bp); + return rc; + } + + return bnx2x_prev_mcp_done(bp); +} + +/* previous driver DMAE transaction may have occurred when pre-boot stage ended + * and boot began, or when kdump kernel was loaded. Either case would invalidate + * the addresses of the transaction, resulting in was-error bit set in the pci + * causing all hw-to-host pcie transactions to timeout. If this happened we want + * to clear the interrupt which detected this from the pglueb and the was done + * bit + */ +static void __devinit bnx2x_prev_interrupted_dmae(struct bnx2x *bp) +{ + u32 val = REG_RD(bp, PGLUE_B_REG_PGLUE_B_INT_STS); + if (val & PGLUE_B_PGLUE_B_INT_STS_REG_WAS_ERROR_ATTN) { + BNX2X_ERR("was error bit was found to be set in pglueb upon startup. Clearing"); + REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, 1 << BP_FUNC(bp)); + } +} + +static int __devinit bnx2x_prev_unload(struct bnx2x *bp) +{ + int time_counter = 10; + u32 rc, fw, hw_lock_reg, hw_lock_val; + BNX2X_DEV_INFO("Entering Previous Unload Flow\n"); + + /* clear hw from errors which may have resulted from an interrupted + * dmae transaction. + */ + bnx2x_prev_interrupted_dmae(bp); + + /* Release previously held locks */ + hw_lock_reg = (BP_FUNC(bp) <= 5) ? + (MISC_REG_DRIVER_CONTROL_1 + BP_FUNC(bp) * 8) : + (MISC_REG_DRIVER_CONTROL_7 + (BP_FUNC(bp) - 6) * 8); + + hw_lock_val = (REG_RD(bp, hw_lock_reg)); + if (hw_lock_val) { + if (hw_lock_val & HW_LOCK_RESOURCE_NVRAM) { + BNX2X_DEV_INFO("Release Previously held NVRAM lock\n"); + REG_WR(bp, MCP_REG_MCPR_NVM_SW_ARB, + (MCPR_NVM_SW_ARB_ARB_REQ_CLR1 << BP_PORT(bp))); + } + + BNX2X_DEV_INFO("Release Previously held hw lock\n"); + REG_WR(bp, hw_lock_reg, 0xffffffff); + } else + BNX2X_DEV_INFO("No need to release hw/nvram locks\n"); + + if (MCPR_ACCESS_LOCK_LOCK & REG_RD(bp, MCP_REG_MCPR_ACCESS_LOCK)) { + BNX2X_DEV_INFO("Release previously held alr\n"); + REG_WR(bp, MCP_REG_MCPR_ACCESS_LOCK, 0); + } + + + do { + /* Lock MCP using an unload request */ + fw = bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS, 0); + if (!fw) { + BNX2X_ERR("MCP response failure, aborting\n"); + rc = -EBUSY; + break; + } + + if (fw == FW_MSG_CODE_DRV_UNLOAD_COMMON) { + rc = bnx2x_prev_unload_common(bp); + break; + } + + /* non-common reply from MCP night require looping */ + rc = bnx2x_prev_unload_uncommon(bp); + if (rc != BNX2X_PREV_WAIT_NEEDED) + break; + + msleep(20); + } while (--time_counter); + + if (!time_counter || rc) { + BNX2X_ERR("Failed unloading previous driver, aborting\n"); + rc = -EBUSY; + } + + BNX2X_DEV_INFO("Finished Previous Unload Flow [%d]\n", rc); + + return rc; } static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp) @@ -10100,8 +10388,16 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp) func = BP_FUNC(bp); /* need to reset chip if undi was active */ - if (!BP_NOMCP(bp)) - bnx2x_undi_unload(bp); + if (!BP_NOMCP(bp)) { + /* init fw_seq */ + bp->fw_seq = + SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) & + DRV_MSG_SEQ_NUMBER_MASK; + BNX2X_DEV_INFO("fw_seq 0x%08x\n", bp->fw_seq); + + bnx2x_prev_unload(bp); + } + if (CHIP_REV_IS_FPGA(bp)) dev_err(&bp->pdev->dev, "FPGA detected\n"); @@ -11431,9 +11727,18 @@ static int __init bnx2x_init(void) static void __exit bnx2x_cleanup(void) { + struct list_head *pos, *q; pci_unregister_driver(&bnx2x_pci_driver); destroy_workqueue(bnx2x_wq); + + /* Free globablly allocated resources */ + list_for_each_safe(pos, q, &bnx2x_prev_list) { + struct bnx2x_prev_path_list *tmp = + list_entry(pos, struct bnx2x_prev_path_list, list); + list_del(pos); + kfree(tmp); + } } void bnx2x_notify_link_changed(struct bnx2x *bp) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h index fd7fb4581849..c25803b9c0ca 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h @@ -987,6 +987,7 @@ * clear; 1 = set. Data valid only in addresses 0-4. all the rest are zero. */ #define IGU_REG_WRITE_DONE_PENDING 0x130480 #define MCP_A_REG_MCPR_SCRATCH 0x3a0000 +#define MCP_REG_MCPR_ACCESS_LOCK 0x8009c #define MCP_REG_MCPR_CPU_PROGRAM_COUNTER 0x8501c #define MCP_REG_MCPR_GP_INPUTS 0x800c0 #define MCP_REG_MCPR_GP_OENABLE 0x800c8 @@ -1686,6 +1687,7 @@ [10] rst_dbg; [11] rst_misc_core; [12] rst_dbue (UART); [13] Pci_resetmdio_n; [14] rst_emac0_hard_core; [15] rst_emac1_hard_core; 16] rst_pxp_rq_rd_wr; 31:17] reserved */ +#define MISC_REG_RESET_REG_1 0xa580 #define MISC_REG_RESET_REG_2 0xa590 /* [RW 20] 20 bit GRC address where the scratch-pad of the MCP that is shared with the driver resides */ @@ -5352,6 +5354,7 @@ #define XMAC_CTRL_REG_TX_EN (0x1<<0) #define XMAC_PAUSE_CTRL_REG_RX_PAUSE_EN (0x1<<18) #define XMAC_PAUSE_CTRL_REG_TX_PAUSE_EN (0x1<<17) +#define XMAC_PFC_CTRL_HI_REG_FORCE_PFC_XON (0x1<<1) #define XMAC_PFC_CTRL_HI_REG_PFC_REFRESH_EN (0x1<<0) #define XMAC_PFC_CTRL_HI_REG_PFC_STATS_EN (0x1<<3) #define XMAC_PFC_CTRL_HI_REG_RX_PFC_EN (0x1<<4) @@ -5606,6 +5609,7 @@ /* [RC 32] Parity register #0 read clear */ #define XSEM_REG_XSEM_PRTY_STS_CLR_0 0x280128 #define XSEM_REG_XSEM_PRTY_STS_CLR_1 0x280138 +#define MCPR_ACCESS_LOCK_LOCK (1L<<31) #define MCPR_NVM_ACCESS_ENABLE_EN (1L<<0) #define MCPR_NVM_ACCESS_ENABLE_WR_EN (1L<<1) #define MCPR_NVM_ADDR_NVM_ADDR_VALUE (0xffffffL<<0) @@ -5732,6 +5736,7 @@ #define MISC_REGISTERS_GPIO_PORT_SHIFT 4 #define MISC_REGISTERS_GPIO_SET_POS 8 #define MISC_REGISTERS_RESET_REG_1_CLEAR 0x588 +#define MISC_REGISTERS_RESET_REG_1_RST_BRB1 (0x1<<0) #define MISC_REGISTERS_RESET_REG_1_RST_DORQ (0x1<<19) #define MISC_REGISTERS_RESET_REG_1_RST_HC (0x1<<29) #define MISC_REGISTERS_RESET_REG_1_RST_NIG (0x1<<7) @@ -6816,10 +6821,13 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_AN_REG_8481_10GBASE_T_AN_CTRL 0x0020 #define MDIO_AN_REG_8481_LEGACY_MII_CTRL 0xffe0 +#define MDIO_AN_REG_8481_MII_CTRL_FORCE_1G 0x40 #define MDIO_AN_REG_8481_LEGACY_MII_STATUS 0xffe1 #define MDIO_AN_REG_8481_LEGACY_AN_ADV 0xffe4 #define MDIO_AN_REG_8481_LEGACY_AN_EXPANSION 0xffe6 #define MDIO_AN_REG_8481_1000T_CTRL 0xffe9 +#define MDIO_AN_REG_8481_1G_100T_EXT_CTRL 0xfff0 +#define MIDO_AN_REG_8481_EXT_CTRL_FORCE_LEDS_OFF 0x0008 #define MDIO_AN_REG_8481_EXPANSION_REG_RD_RW 0xfff5 #define MDIO_AN_REG_8481_EXPANSION_REG_ACCESS 0xfff7 #define MDIO_AN_REG_8481_AUX_CTRL 0xfff8 @@ -6939,6 +6947,10 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_WC_REG_GP2_STATUS_GP_2_2 0x81d2 #define MDIO_WC_REG_GP2_STATUS_GP_2_3 0x81d3 #define MDIO_WC_REG_GP2_STATUS_GP_2_4 0x81d4 +#define MDIO_WC_REG_GP2_STATUS_GP_2_4_CL73_AN_CMPL 0x1000 +#define MDIO_WC_REG_GP2_STATUS_GP_2_4_CL37_AN_CMPL 0x0100 +#define MDIO_WC_REG_GP2_STATUS_GP_2_4_CL37_LP_AN_CAP 0x0010 +#define MDIO_WC_REG_GP2_STATUS_GP_2_4_CL37_AN_CAP 0x1 #define MDIO_WC_REG_UC_INFO_B0_DEAD_TRAP 0x81EE #define MDIO_WC_REG_UC_INFO_B1_VERSION 0x81F0 #define MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE 0x81F2 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index 3f52fadee3ed..513573321625 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -3847,7 +3847,7 @@ static bool bnx2x_credit_pool_get_entry( continue; /* If we've got here we are going to find a free entry */ - for (idx = vec * BNX2X_POOL_VEC_SIZE, i = 0; + for (idx = vec * BIT_VEC64_ELEM_SZ, i = 0; i < BIT_VEC64_ELEM_SZ; idx++, i++) if (BIT_VEC64_TEST_BIT(o->pool_mirror, idx)) { diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 4e4bb3875868..ceeab8e852ef 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -879,8 +879,13 @@ static inline unsigned int tg3_has_work(struct tg3_napi *tnapi) if (sblk->status & SD_STATUS_LINK_CHG) work_exists = 1; } - /* check for RX/TX work to do */ - if (sblk->idx[0].tx_consumer != tnapi->tx_cons || + + /* check for TX work to do */ + if (sblk->idx[0].tx_consumer != tnapi->tx_cons) + work_exists = 1; + + /* check for RX work to do */ + if (tnapi->rx_rcb_prod_idx && *(tnapi->rx_rcb_prod_idx) != tnapi->rx_rcb_ptr) work_exists = 1; @@ -2778,7 +2783,9 @@ static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power) if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780 && - (tp->phy_flags & TG3_PHYFLG_MII_SERDES))) + (tp->phy_flags & TG3_PHYFLG_MII_SERDES)) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 && + !tp->pci_fn)) return; if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5784_AX || @@ -6122,6 +6129,9 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget) return work_done; } + if (!tnapi->rx_rcb_prod_idx) + return work_done; + /* run RX thread, within the bounds set by NAPI. * All RX "locking" is done by ensuring outside * code synchronizes with tg3->napi.poll() @@ -7565,6 +7575,12 @@ static int tg3_alloc_consistent(struct tg3 *tp) */ switch (i) { default: + if (tg3_flag(tp, ENABLE_RSS)) { + tnapi->rx_rcb_prod_idx = NULL; + break; + } + /* Fall through */ + case 1: tnapi->rx_rcb_prod_idx = &sblk->idx[0].rx_producer; break; case 2: diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 63bfdd10bd6d..abb6ce7c1b7e 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -1150,6 +1150,48 @@ release_tpsram: } /** + * t3_synchronize_rx - wait for current Rx processing on a port to complete + * @adap: the adapter + * @p: the port + * + * Ensures that current Rx processing on any of the queues associated with + * the given port completes before returning. We do this by acquiring and + * releasing the locks of the response queues associated with the port. + */ +static void t3_synchronize_rx(struct adapter *adap, const struct port_info *p) +{ + int i; + + for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) { + struct sge_rspq *q = &adap->sge.qs[i].rspq; + + spin_lock_irq(&q->lock); + spin_unlock_irq(&q->lock); + } +} + +static void cxgb_vlan_mode(struct net_device *dev, netdev_features_t features) +{ + struct port_info *pi = netdev_priv(dev); + struct adapter *adapter = pi->adapter; + + if (adapter->params.rev > 0) { + t3_set_vlan_accel(adapter, 1 << pi->port_id, + features & NETIF_F_HW_VLAN_RX); + } else { + /* single control for all ports */ + unsigned int i, have_vlans = features & NETIF_F_HW_VLAN_RX; + + for_each_port(adapter, i) + have_vlans |= + adapter->port[i]->features & NETIF_F_HW_VLAN_RX; + + t3_set_vlan_accel(adapter, 1, have_vlans); + } + t3_synchronize_rx(adapter, pi); +} + +/** * cxgb_up - enable the adapter * @adapter: adapter being enabled * @@ -1161,7 +1203,7 @@ release_tpsram: */ static int cxgb_up(struct adapter *adap) { - int err; + int i, err; if (!(adap->flags & FULL_INIT_DONE)) { err = t3_check_fw_version(adap); @@ -1198,6 +1240,9 @@ static int cxgb_up(struct adapter *adap) if (err) goto out; + for_each_port(adap, i) + cxgb_vlan_mode(adap->port[i], adap->port[i]->features); + setup_rss(adap); if (!(adap->flags & NAPI_INIT)) init_napi(adap); @@ -2508,48 +2553,6 @@ static int cxgb_set_mac_addr(struct net_device *dev, void *p) return 0; } -/** - * t3_synchronize_rx - wait for current Rx processing on a port to complete - * @adap: the adapter - * @p: the port - * - * Ensures that current Rx processing on any of the queues associated with - * the given port completes before returning. We do this by acquiring and - * releasing the locks of the response queues associated with the port. - */ -static void t3_synchronize_rx(struct adapter *adap, const struct port_info *p) -{ - int i; - - for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) { - struct sge_rspq *q = &adap->sge.qs[i].rspq; - - spin_lock_irq(&q->lock); - spin_unlock_irq(&q->lock); - } -} - -static void cxgb_vlan_mode(struct net_device *dev, netdev_features_t features) -{ - struct port_info *pi = netdev_priv(dev); - struct adapter *adapter = pi->adapter; - - if (adapter->params.rev > 0) { - t3_set_vlan_accel(adapter, 1 << pi->port_id, - features & NETIF_F_HW_VLAN_RX); - } else { - /* single control for all ports */ - unsigned int i, have_vlans = features & NETIF_F_HW_VLAN_RX; - - for_each_port(adapter, i) - have_vlans |= - adapter->port[i]->features & NETIF_F_HW_VLAN_RX; - - t3_set_vlan_accel(adapter, 1, have_vlans); - } - t3_synchronize_rx(adapter, pi); -} - static netdev_features_t cxgb_fix_features(struct net_device *dev, netdev_features_t features) { @@ -3353,9 +3356,6 @@ static int __devinit init_one(struct pci_dev *pdev, err = sysfs_create_group(&adapter->port[0]->dev.kobj, &cxgb3_attr_group); - for_each_port(adapter, i) - cxgb_vlan_mode(adapter->port[i], adapter->port[i]->features); - print_port_info(adapter, ai); return 0; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 05ff076af06d..b126b98065a9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2000,13 +2000,6 @@ static const struct ethtool_ops cxgb_ethtool_ops = { /* * debugfs support */ - -static int mem_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t mem_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -2050,7 +2043,7 @@ static ssize_t mem_read(struct file *file, char __user *buf, size_t count, static const struct file_operations mem_debugfs_fops = { .owner = THIS_MODULE, - .open = mem_open, + .open = simple_open, .read = mem_read, .llseek = default_llseek, }; diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index b2dc2c81a147..2e09edb9cdf8 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -1259,55 +1259,21 @@ rio_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { int phy_addr; struct netdev_private *np = netdev_priv(dev); - struct mii_data *miidata = (struct mii_data *) &rq->ifr_ifru; - - struct netdev_desc *desc; - int i; + struct mii_ioctl_data *miidata = if_mii(rq); phy_addr = np->phy_addr; switch (cmd) { - case SIOCDEVPRIVATE: - break; - - case SIOCDEVPRIVATE + 1: - miidata->out_value = mii_read (dev, phy_addr, miidata->reg_num); + case SIOCGMIIPHY: + miidata->phy_id = phy_addr; break; - case SIOCDEVPRIVATE + 2: - mii_write (dev, phy_addr, miidata->reg_num, miidata->in_value); + case SIOCGMIIREG: + miidata->val_out = mii_read (dev, phy_addr, miidata->reg_num); break; - case SIOCDEVPRIVATE + 3: - break; - case SIOCDEVPRIVATE + 4: - break; - case SIOCDEVPRIVATE + 5: - netif_stop_queue (dev); + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + mii_write (dev, phy_addr, miidata->reg_num, miidata->val_in); break; - case SIOCDEVPRIVATE + 6: - netif_wake_queue (dev); - break; - case SIOCDEVPRIVATE + 7: - printk - ("tx_full=%x cur_tx=%lx old_tx=%lx cur_rx=%lx old_rx=%lx\n", - netif_queue_stopped(dev), np->cur_tx, np->old_tx, np->cur_rx, - np->old_rx); - break; - case SIOCDEVPRIVATE + 8: - printk("TX ring:\n"); - for (i = 0; i < TX_RING_SIZE; i++) { - desc = &np->tx_ring[i]; - printk - ("%02x:cur:%08x next:%08x status:%08x frag1:%08x frag0:%08x", - i, - (u32) (np->tx_ring_dma + i * sizeof (*desc)), - (u32)le64_to_cpu(desc->next_desc), - (u32)le64_to_cpu(desc->status), - (u32)(le64_to_cpu(desc->fraginfo) >> 32), - (u32)le64_to_cpu(desc->fraginfo)); - printk ("\n"); - } - printk ("\n"); - break; - default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/dlink/dl2k.h b/drivers/net/ethernet/dlink/dl2k.h index ba0adcafa55a..30c2da3de548 100644 --- a/drivers/net/ethernet/dlink/dl2k.h +++ b/drivers/net/ethernet/dlink/dl2k.h @@ -365,13 +365,6 @@ struct ioctl_data { char *data; }; -struct mii_data { - __u16 reserved; - __u16 reg_num; - __u16 in_value; - __u16 out_value; -}; - /* The Rx and Tx buffer descriptors. */ struct netdev_desc { __le64 next_desc; diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c index 9eb815941df5..f7f0bf5d037b 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -356,13 +356,13 @@ static int fsl_pq_mdio_probe(struct platform_device *ofdev) if (prop) tbiaddr = *prop; - } - if (tbiaddr == -1) { - err = -EBUSY; - goto err_free_irqs; - } else { - out_be32(tbipa, tbiaddr); + if (tbiaddr == -1) { + err = -EBUSY; + goto err_free_irqs; + } else { + out_be32(tbipa, tbiaddr); + } } err = of_mdiobus_register(new_bus, np); diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 4e3cd2f8debb..9ac14f804851 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -116,10 +116,10 @@ static struct ucc_geth_info ugeth_primary_info = { .maxGroupAddrInHash = 4, .maxIndAddrInHash = 4, .prel = 7, - .maxFrameLength = 1518, + .maxFrameLength = 1518+16, /* Add extra bytes for VLANs etc. */ .minFrameLength = 64, - .maxD1Length = 1520, - .maxD2Length = 1520, + .maxD1Length = 1520+16, /* Add extra bytes for VLANs etc. */ + .maxD2Length = 1520+16, /* Add extra bytes for VLANs etc. */ .vlantype = 0x8100, .ecamptr = ((uint32_t) NULL), .eventRegMask = UCCE_OTHER, @@ -3945,6 +3945,8 @@ static int ucc_geth_probe(struct platform_device* ofdev) } if (max_speed == SPEED_1000) { + unsigned int snums = qe_get_num_of_snums(); + /* configure muram FIFOs for gigabit operation */ ug_info->uf_info.urfs = UCC_GETH_URFS_GIGA_INIT; ug_info->uf_info.urfet = UCC_GETH_URFET_GIGA_INIT; @@ -3954,11 +3956,11 @@ static int ucc_geth_probe(struct platform_device* ofdev) ug_info->uf_info.utftt = UCC_GETH_UTFTT_GIGA_INIT; ug_info->numThreadsTx = UCC_GETH_NUM_OF_THREADS_4; - /* If QE's snum number is 46 which means we need to support + /* If QE's snum number is 46/76 which means we need to support * 4 UECs at 1000Base-T simultaneously, we need to allocate * more Threads to Rx. */ - if (qe_get_num_of_snums() == 46) + if ((snums == 76) || (snums == 46)) ug_info->numThreadsRx = UCC_GETH_NUM_OF_THREADS_6; else ug_info->numThreadsRx = UCC_GETH_NUM_OF_THREADS_4; diff --git a/drivers/net/ethernet/freescale/ucc_geth.h b/drivers/net/ethernet/freescale/ucc_geth.h index 2e395a2566b8..f71b3e7b12de 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.h +++ b/drivers/net/ethernet/freescale/ucc_geth.h @@ -877,7 +877,7 @@ struct ucc_geth_hardware_statistics { /* Driver definitions */ #define TX_BD_RING_LEN 0x10 -#define RX_BD_RING_LEN 0x10 +#define RX_BD_RING_LEN 0x20 #define TX_RING_MOD_MASK(size) (size-1) #define RX_RING_MOD_MASK(size) (size-1) diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 3516e17a399d..f4d2da0db1b1 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -290,16 +290,18 @@ static void ehea_update_bcmc_registrations(void) arr[i].adh = adapter->handle; arr[i].port_id = port->logical_port_id; - arr[i].reg_type = EHEA_BCMC_SCOPE_ALL | - EHEA_BCMC_MULTICAST | + arr[i].reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_UNTAGGED; + if (mc_entry->macaddr == 0) + arr[i].reg_type |= EHEA_BCMC_SCOPE_ALL; arr[i++].macaddr = mc_entry->macaddr; arr[i].adh = adapter->handle; arr[i].port_id = port->logical_port_id; - arr[i].reg_type = EHEA_BCMC_SCOPE_ALL | - EHEA_BCMC_MULTICAST | + arr[i].reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_VLANID_ALL; + if (mc_entry->macaddr == 0) + arr[i].reg_type |= EHEA_BCMC_SCOPE_ALL; arr[i++].macaddr = mc_entry->macaddr; num_registrations -= 2; } @@ -1838,8 +1840,9 @@ static u64 ehea_multicast_reg_helper(struct ehea_port *port, u64 mc_mac_addr, u64 hret; u8 reg_type; - reg_type = EHEA_BCMC_SCOPE_ALL | EHEA_BCMC_MULTICAST - | EHEA_BCMC_UNTAGGED; + reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_UNTAGGED; + if (mc_mac_addr == 0) + reg_type |= EHEA_BCMC_SCOPE_ALL; hret = ehea_h_reg_dereg_bcmc(port->adapter->handle, port->logical_port_id, @@ -1847,8 +1850,9 @@ static u64 ehea_multicast_reg_helper(struct ehea_port *port, u64 mc_mac_addr, if (hret) goto out; - reg_type = EHEA_BCMC_SCOPE_ALL | EHEA_BCMC_MULTICAST - | EHEA_BCMC_VLANID_ALL; + reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_VLANID_ALL; + if (mc_mac_addr == 0) + reg_type |= EHEA_BCMC_SCOPE_ALL; hret = ehea_h_reg_dereg_bcmc(port->adapter->handle, port->logical_port_id, @@ -1898,7 +1902,7 @@ static void ehea_allmulti(struct net_device *dev, int enable) netdev_err(dev, "failed enabling IFF_ALLMULTI\n"); } - } else + } else { if (!enable) { /* Disable ALLMULTI */ hret = ehea_multicast_reg_helper(port, 0, H_DEREG_BCMC); @@ -1908,6 +1912,7 @@ static void ehea_allmulti(struct net_device *dev, int enable) netdev_err(dev, "failed disabling IFF_ALLMULTI\n"); } + } } static void ehea_add_multicast_entry(struct ehea_port *port, u8 *mc_mac_addr) @@ -1941,11 +1946,7 @@ static void ehea_set_multicast_list(struct net_device *dev) struct netdev_hw_addr *ha; int ret; - if (port->promisc) { - ehea_promiscuous(dev, 1); - return; - } - ehea_promiscuous(dev, 0); + ehea_promiscuous(dev, !!(dev->flags & IFF_PROMISC)); if (dev->flags & IFF_ALLMULTI) { ehea_allmulti(dev, 1); @@ -2463,6 +2464,7 @@ static int ehea_down(struct net_device *dev) return 0; ehea_drop_multicast_list(dev); + ehea_allmulti(dev, 0); ehea_broadcast_reg_helper(port, H_DEREG_BCMC); ehea_free_interrupts(dev); @@ -3261,6 +3263,7 @@ static int __devinit ehea_probe_adapter(struct platform_device *dev, struct ehea_adapter *adapter; const u64 *adapter_handle; int ret; + int i; if (!dev || !dev->dev.of_node) { pr_err("Invalid ibmebus device probed\n"); @@ -3314,17 +3317,9 @@ static int __devinit ehea_probe_adapter(struct platform_device *dev, tasklet_init(&adapter->neq_tasklet, ehea_neq_tasklet, (unsigned long)adapter); - ret = ibmebus_request_irq(adapter->neq->attr.ist1, - ehea_interrupt_neq, IRQF_DISABLED, - "ehea_neq", adapter); - if (ret) { - dev_err(&dev->dev, "requesting NEQ IRQ failed\n"); - goto out_kill_eq; - } - ret = ehea_create_device_sysfs(dev); if (ret) - goto out_free_irq; + goto out_kill_eq; ret = ehea_setup_ports(adapter); if (ret) { @@ -3332,15 +3327,30 @@ static int __devinit ehea_probe_adapter(struct platform_device *dev, goto out_rem_dev_sysfs; } + ret = ibmebus_request_irq(adapter->neq->attr.ist1, + ehea_interrupt_neq, IRQF_DISABLED, + "ehea_neq", adapter); + if (ret) { + dev_err(&dev->dev, "requesting NEQ IRQ failed\n"); + goto out_shutdown_ports; + } + + /* Handle any events that might be pending. */ + tasklet_hi_schedule(&adapter->neq_tasklet); + ret = 0; goto out; +out_shutdown_ports: + for (i = 0; i < EHEA_MAX_PORTS; i++) + if (adapter->port[i]) { + ehea_shutdown_single_port(adapter->port[i]); + adapter->port[i] = NULL; + } + out_rem_dev_sysfs: ehea_remove_device_sysfs(dev); -out_free_irq: - ibmebus_free_irq(adapter->neq->attr.ist1, adapter); - out_kill_eq: ehea_destroy_eq(adapter->neq); diff --git a/drivers/net/ethernet/ibm/ehea/ehea_phyp.h b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h index 52c456ec4d6c..8364815c32ff 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_phyp.h +++ b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h @@ -450,7 +450,7 @@ u64 ehea_h_modify_ehea_port(const u64 adapter_handle, const u16 port_num, void *cb_addr); #define H_REGBCMC_PN EHEA_BMASK_IBM(48, 63) -#define H_REGBCMC_REGTYPE EHEA_BMASK_IBM(61, 63) +#define H_REGBCMC_REGTYPE EHEA_BMASK_IBM(60, 63) #define H_REGBCMC_MACADDR EHEA_BMASK_IBM(16, 63) #define H_REGBCMC_VLANID EHEA_BMASK_IBM(52, 63) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 0e9aec8f6917..37caa8885c2a 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -164,6 +164,8 @@ static int e1000_82547_fifo_workaround(struct e1000_adapter *adapter, static bool e1000_vlan_used(struct e1000_adapter *adapter); static void e1000_vlan_mode(struct net_device *netdev, netdev_features_t features); +static void e1000_vlan_filter_on_off(struct e1000_adapter *adapter, + bool filter_on); static int e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid); static int e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid); static void e1000_restore_vlan(struct e1000_adapter *adapter); @@ -215,7 +217,8 @@ MODULE_DESCRIPTION("Intel(R) PRO/1000 Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); -static int debug = NETIF_MSG_DRV | NETIF_MSG_PROBE; +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK) +static int debug = -1; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); @@ -979,7 +982,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, adapter = netdev_priv(netdev); adapter->netdev = netdev; adapter->pdev = pdev; - adapter->msg_enable = (1 << debug) - 1; + adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); adapter->bars = bars; adapter->need_ioport = need_ioport; @@ -1214,7 +1217,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, if (err) goto err_register; - e1000_vlan_mode(netdev, netdev->features); + e1000_vlan_filter_on_off(adapter, false); /* print bus type/speed/width info */ e_info(probe, "(PCI%s:%dMHz:%d-bit) %pM\n", @@ -3377,7 +3380,7 @@ static void e1000_dump(struct e1000_adapter *adapter) for (i = 0; tx_ring->desc && (i < tx_ring->count); i++) { struct e1000_tx_desc *tx_desc = E1000_TX_DESC(*tx_ring, i); struct e1000_buffer *buffer_info = &tx_ring->buffer_info[i]; - struct my_u { u64 a; u64 b; }; + struct my_u { __le64 a; __le64 b; }; struct my_u *u = (struct my_u *)tx_desc; const char *type; @@ -3421,7 +3424,7 @@ rx_ring_summary: for (i = 0; rx_ring->desc && (i < rx_ring->count); i++) { struct e1000_rx_desc *rx_desc = E1000_RX_DESC(*rx_ring, i); struct e1000_buffer *buffer_info = &rx_ring->buffer_info[i]; - struct my_u { u64 a; u64 b; }; + struct my_u { __le64 a; __le64 b; }; struct my_u *u = (struct my_u *)rx_desc; const char *type; @@ -4770,6 +4773,22 @@ static bool e1000_vlan_used(struct e1000_adapter *adapter) return false; } +static void __e1000_vlan_mode(struct e1000_adapter *adapter, + netdev_features_t features) +{ + struct e1000_hw *hw = &adapter->hw; + u32 ctrl; + + ctrl = er32(CTRL); + if (features & NETIF_F_HW_VLAN_RX) { + /* enable VLAN tag insert/strip */ + ctrl |= E1000_CTRL_VME; + } else { + /* disable VLAN tag insert/strip */ + ctrl &= ~E1000_CTRL_VME; + } + ew32(CTRL, ctrl); +} static void e1000_vlan_filter_on_off(struct e1000_adapter *adapter, bool filter_on) { @@ -4779,6 +4798,7 @@ static void e1000_vlan_filter_on_off(struct e1000_adapter *adapter, if (!test_bit(__E1000_DOWN, &adapter->flags)) e1000_irq_disable(adapter); + __e1000_vlan_mode(adapter, adapter->netdev->features); if (filter_on) { /* enable VLAN receive filtering */ rctl = er32(RCTL); @@ -4799,24 +4819,14 @@ static void e1000_vlan_filter_on_off(struct e1000_adapter *adapter, } static void e1000_vlan_mode(struct net_device *netdev, - netdev_features_t features) + netdev_features_t features) { struct e1000_adapter *adapter = netdev_priv(netdev); - struct e1000_hw *hw = &adapter->hw; - u32 ctrl; if (!test_bit(__E1000_DOWN, &adapter->flags)) e1000_irq_disable(adapter); - ctrl = er32(CTRL); - if (features & NETIF_F_HW_VLAN_RX) { - /* enable VLAN tag insert/strip */ - ctrl |= E1000_CTRL_VME; - } else { - /* disable VLAN tag insert/strip */ - ctrl &= ~E1000_CTRL_VME; - } - ew32(CTRL, ctrl); + __e1000_vlan_mode(adapter, features); if (!test_bit(__E1000_DOWN, &adapter->flags)) e1000_irq_enable(adapter); diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 86cdd4793992..b83897f76ee3 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -161,6 +161,12 @@ struct e1000_info; /* Time to wait before putting the device into D3 if there's no link (in ms). */ #define LINK_TIMEOUT 100 +/* + * Count for polling __E1000_RESET condition every 10-20msec. + * Experimentation has shown the reset can take approximately 210msec. + */ +#define E1000_CHECK_RESET_COUNT 25 + #define DEFAULT_RDTR 0 #define DEFAULT_RADV 8 #define BURST_RDTR 0x20 diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 64c76443a7aa..b461c24945e3 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -1310,10 +1310,6 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state) if (mac_reg & E1000_PHY_CTRL_D0A_LPLU) oem_reg |= HV_OEM_BITS_LPLU; - - /* Set Restart auto-neg to activate the bits */ - if (!hw->phy.ops.check_reset_block(hw)) - oem_reg |= HV_OEM_BITS_RESTART_AN; } else { if (mac_reg & (E1000_PHY_CTRL_GBE_DISABLE | E1000_PHY_CTRL_NOND0A_GBE_DISABLE)) @@ -1324,6 +1320,11 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state) oem_reg |= HV_OEM_BITS_LPLU; } + /* Set Restart auto-neg to activate the bits */ + if ((d0_state || (hw->mac.type != e1000_pchlan)) && + !hw->phy.ops.check_reset_block(hw)) + oem_reg |= HV_OEM_BITS_RESTART_AN; + ret_val = hw->phy.ops.write_reg_locked(hw, HV_OEM_BITS, oem_reg); release: @@ -3682,7 +3683,11 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw) if (hw->mac.type >= e1000_pchlan) { e1000_oem_bits_config_ich8lan(hw, false); - e1000_phy_hw_reset_ich8lan(hw); + + /* Reset PHY to activate OEM bits on 82577/8 */ + if (hw->mac.type == e1000_pchlan) + e1000e_phy_hw_reset_generic(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 7152eb11b7b9..9520a6ac1f30 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -60,6 +60,11 @@ char e1000e_driver_name[] = "e1000e"; const char e1000e_driver_version[] = DRV_VERSION; +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + static void e1000e_disable_aspm(struct pci_dev *pdev, u16 state); static const struct e1000_info *e1000_info_tbl[] = { @@ -1054,6 +1059,13 @@ static void e1000_print_hw_hang(struct work_struct *work) ew32(TIDV, adapter->tx_int_delay | E1000_TIDV_FPD); /* execute the writes immediately */ e1e_flush(); + /* + * Due to rare timing issues, write to TIDV again to ensure + * the write is successful + */ + ew32(TIDV, adapter->tx_int_delay | E1000_TIDV_FPD); + /* execute the writes immediately */ + e1e_flush(); adapter->tx_hang_recheck = true; return; } @@ -3611,6 +3623,16 @@ static void e1000e_flush_descriptors(struct e1000_adapter *adapter) /* execute the writes immediately */ e1e_flush(); + + /* + * due to rare timing issues, write to TIDV/RDTR again to ensure the + * write is successful + */ + ew32(TIDV, adapter->tx_int_delay | E1000_TIDV_FPD); + ew32(RDTR, adapter->rx_int_delay | E1000_RDTR_FPD); + + /* execute the writes immediately */ + e1e_flush(); } static void e1000e_update_stats(struct e1000_adapter *adapter); @@ -3777,7 +3799,7 @@ static int e1000_test_msi_interrupt(struct e1000_adapter *adapter) /* fire an unusual interrupt on the test handler */ ew32(ICS, E1000_ICS_RXSEQ); e1e_flush(); - msleep(50); + msleep(100); e1000_irq_disable(adapter); @@ -3963,6 +3985,10 @@ static int e1000_close(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); struct pci_dev *pdev = adapter->pdev; + int count = E1000_CHECK_RESET_COUNT; + + while (test_bit(__E1000_RESETTING, &adapter->state) && count--) + usleep_range(10000, 20000); WARN_ON(test_bit(__E1000_RESETTING, &adapter->state)); @@ -5467,6 +5493,11 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake, netif_device_detach(netdev); if (netif_running(netdev)) { + int count = E1000_CHECK_RESET_COUNT; + + while (test_bit(__E1000_RESETTING, &adapter->state) && count--) + usleep_range(10000, 20000); + WARN_ON(test_bit(__E1000_RESETTING, &adapter->state)); e1000e_down(adapter); e1000_free_irq(adapter); @@ -6172,7 +6203,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, adapter->hw.adapter = adapter; adapter->hw.mac.type = ei->mac; adapter->max_hw_frame_size = ei->max_hw_frame_size; - adapter->msg_enable = (1 << NETIF_MSG_DRV | NETIF_MSG_PROBE) - 1; + adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); mmio_start = pci_resource_start(pdev, 0); mmio_len = pci_resource_len(pdev, 0); diff --git a/drivers/net/ethernet/intel/e1000e/param.c b/drivers/net/ethernet/intel/e1000e/param.c index ff796e42c3eb..16adeb9418a8 100644 --- a/drivers/net/ethernet/intel/e1000e/param.c +++ b/drivers/net/ethernet/intel/e1000e/param.c @@ -106,7 +106,7 @@ E1000_PARAM(RxAbsIntDelay, "Receive Absolute Interrupt Delay"); /* * Interrupt Throttle Rate (interrupts/sec) * - * Valid Range: 100-100000 (0=off, 1=dynamic, 3=dynamic conservative) + * Valid Range: 100-100000 or one of: 0=off, 1=dynamic, 3=dynamic conservative */ E1000_PARAM(InterruptThrottleRate, "Interrupt Throttling Rate"); #define DEFAULT_ITR 3 @@ -344,53 +344,60 @@ void __devinit e1000e_check_options(struct e1000_adapter *adapter) if (num_InterruptThrottleRate > bd) { adapter->itr = InterruptThrottleRate[bd]; - switch (adapter->itr) { - case 0: - e_info("%s turned off\n", opt.name); - break; - case 1: - e_info("%s set to dynamic mode\n", opt.name); - adapter->itr_setting = adapter->itr; - adapter->itr = 20000; - break; - case 3: - e_info("%s set to dynamic conservative mode\n", - opt.name); - adapter->itr_setting = adapter->itr; - adapter->itr = 20000; - break; - case 4: - e_info("%s set to simplified (2000-8000 ints) " - "mode\n", opt.name); - adapter->itr_setting = 4; - break; - default: - /* - * Save the setting, because the dynamic bits - * change itr. - */ - if (e1000_validate_option(&adapter->itr, &opt, - adapter) && - (adapter->itr == 3)) { - /* - * In case of invalid user value, - * default to conservative mode. - */ - adapter->itr_setting = adapter->itr; - adapter->itr = 20000; - } else { - /* - * Clear the lower two bits because - * they are used as control. - */ - adapter->itr_setting = - adapter->itr & ~3; - } - break; - } + + /* + * Make sure a message is printed for non-special + * values. And in case of an invalid option, display + * warning, use default and got through itr/itr_setting + * adjustment logic below + */ + if ((adapter->itr > 4) && + e1000_validate_option(&adapter->itr, &opt, adapter)) + adapter->itr = opt.def; } else { - adapter->itr_setting = opt.def; + /* + * If no option specified, use default value and go + * through the logic below to adjust itr/itr_setting + */ + adapter->itr = opt.def; + + /* + * Make sure a message is printed for non-special + * default values + */ + if (adapter->itr > 40) + e_info("%s set to default %d\n", opt.name, + adapter->itr); + } + + adapter->itr_setting = adapter->itr; + switch (adapter->itr) { + case 0: + e_info("%s turned off\n", opt.name); + break; + case 1: + e_info("%s set to dynamic mode\n", opt.name); + adapter->itr = 20000; + break; + case 3: + e_info("%s set to dynamic conservative mode\n", + opt.name); adapter->itr = 20000; + break; + case 4: + e_info("%s set to simplified (2000-8000 ints) mode\n", + opt.name); + break; + default: + /* + * Save the setting, because the dynamic bits + * change itr. + * + * Clear the lower two bits because + * they are used as control. + */ + adapter->itr_setting &= ~3; + break; } } { /* Interrupt Mode */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index c4902411d749..8683ca4748c8 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -238,6 +238,11 @@ MODULE_DESCRIPTION("Intel(R) Gigabit Ethernet Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + struct igb_reg_info { u32 ofs; char *name; @@ -1106,9 +1111,12 @@ msi_only: adapter->flags |= IGB_FLAG_HAS_MSI; out: /* Notify the stack of the (possibly) reduced queue counts. */ + rtnl_lock(); netif_set_real_num_tx_queues(adapter->netdev, adapter->num_tx_queues); - return netif_set_real_num_rx_queues(adapter->netdev, - adapter->num_rx_queues); + err = netif_set_real_num_rx_queues(adapter->netdev, + adapter->num_rx_queues); + rtnl_unlock(); + return err; } /** @@ -1893,7 +1901,7 @@ static int __devinit igb_probe(struct pci_dev *pdev, adapter->pdev = pdev; hw = &adapter->hw; hw->back = adapter; - adapter->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE; + adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); mmio_start = pci_resource_start(pdev, 0); mmio_len = pci_resource_len(pdev, 0); @@ -2766,8 +2774,6 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, txdctl |= E1000_TXDCTL_QUEUE_ENABLE; wr32(E1000_TXDCTL(reg_idx), txdctl); - - netdev_tx_reset_queue(txring_txq(ring)); } /** @@ -3277,6 +3283,8 @@ static void igb_clean_tx_ring(struct igb_ring *tx_ring) igb_unmap_and_free_tx_resource(tx_ring, buffer_info); } + netdev_tx_reset_queue(txring_txq(tx_ring)); + size = sizeof(struct igb_tx_buffer) * tx_ring->count; memset(tx_ring->tx_buffer_info, 0, size); @@ -6791,18 +6799,7 @@ static int igb_resume(struct device *dev) pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); - if (!rtnl_is_locked()) { - /* - * shut up ASSERT_RTNL() warning in - * netif_set_real_num_tx/rx_queues. - */ - rtnl_lock(); - err = igb_init_interrupt_scheme(adapter); - rtnl_unlock(); - } else { - err = igb_init_interrupt_scheme(adapter); - } - if (err) { + if (igb_init_interrupt_scheme(adapter)) { dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); return -ENOMEM; } diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 217c143686d2..8ec74b07f940 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -55,6 +55,11 @@ static const char igbvf_driver_string[] = static const char igbvf_copyright[] = "Copyright (c) 2009 - 2012 Intel Corporation."; +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + static int igbvf_poll(struct napi_struct *napi, int budget); static void igbvf_reset(struct igbvf_adapter *); static void igbvf_set_interrupt_capability(struct igbvf_adapter *); @@ -2649,7 +2654,7 @@ static int __devinit igbvf_probe(struct pci_dev *pdev, adapter->flags = ei->flags; adapter->hw.back = adapter; adapter->hw.mac.type = ei->mac; - adapter->msg_enable = (1 << NETIF_MSG_DRV | NETIF_MSG_PROBE) - 1; + adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); /* PCI config space info */ @@ -2726,14 +2731,14 @@ static int __devinit igbvf_probe(struct pci_dev *pdev, netdev->addr_len); } - if (!is_valid_ether_addr(netdev->perm_addr)) { + if (!is_valid_ether_addr(netdev->dev_addr)) { dev_err(&pdev->dev, "Invalid MAC Address: %pM\n", netdev->dev_addr); err = -EIO; goto err_hw_init; } - memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len); + memcpy(netdev->perm_addr, netdev->dev_addr, netdev->addr_len); setup_timer(&adapter->watchdog_timer, &igbvf_watchdog, (unsigned long) adapter); diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index 82aaa792cbf3..5fce363d810a 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c @@ -134,8 +134,8 @@ MODULE_DESCRIPTION("Intel(R) PRO/10GbE Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); -#define DEFAULT_DEBUG_LEVEL_SHIFT 3 -static int debug = DEFAULT_DEBUG_LEVEL_SHIFT; +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK) +static int debug = -1; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); @@ -442,7 +442,7 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.back = adapter; - adapter->msg_enable = netif_msg_init(debug, DEFAULT_DEBUG_LEVEL_SHIFT); + adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); adapter->hw.hw_addr = pci_ioremap_bar(pdev, BAR_0); if (!adapter->hw.hw_addr) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 80e26ff30ebf..81b155589532 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -544,7 +544,7 @@ struct ixgbe_fdir_filter { u16 action; }; -enum ixbge_state_t { +enum ixgbe_state_t { __IXGBE_TESTING, __IXGBE_RESETTING, __IXGBE_DOWN, @@ -574,9 +574,6 @@ extern struct ixgbe_info ixgbe_82599_info; extern struct ixgbe_info ixgbe_X540_info; #ifdef CONFIG_IXGBE_DCB extern const struct dcbnl_rtnl_ops dcbnl_ops; -extern int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg, - struct ixgbe_dcb_config *dst_dcb_cfg, - int tc_max); #endif extern char ixgbe_driver_name[]; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index dde65f951400..32e5c02ff6d0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -44,62 +44,102 @@ #define DCB_NO_HW_CHG 1 /* DCB configuration did not change */ #define DCB_HW_CHG 2 /* DCB configuration changed, no reset */ -int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg, - struct ixgbe_dcb_config *dst_dcb_cfg, int tc_max) +static int ixgbe_copy_dcb_cfg(struct ixgbe_adapter *adapter, int tc_max) { - struct tc_configuration *src_tc_cfg = NULL; - struct tc_configuration *dst_tc_cfg = NULL; - int i; + struct ixgbe_dcb_config *scfg = &adapter->temp_dcb_cfg; + struct ixgbe_dcb_config *dcfg = &adapter->dcb_cfg; + struct tc_configuration *src = NULL; + struct tc_configuration *dst = NULL; + int i, j; + int tx = DCB_TX_CONFIG; + int rx = DCB_RX_CONFIG; + int changes = 0; +#ifdef IXGBE_FCOE + struct dcb_app app = { + .selector = DCB_APP_IDTYPE_ETHTYPE, + .protocol = ETH_P_FCOE, + }; + u8 up = dcb_getapp(adapter->netdev, &app); - if (!src_dcb_cfg || !dst_dcb_cfg) - return -EINVAL; + if (up && !(up & (1 << adapter->fcoe.up))) + changes |= BIT_APP_UPCHG; +#endif for (i = DCB_PG_ATTR_TC_0; i < tc_max + DCB_PG_ATTR_TC_0; i++) { - src_tc_cfg = &src_dcb_cfg->tc_config[i - DCB_PG_ATTR_TC_0]; - dst_tc_cfg = &dst_dcb_cfg->tc_config[i - DCB_PG_ATTR_TC_0]; + src = &scfg->tc_config[i - DCB_PG_ATTR_TC_0]; + dst = &dcfg->tc_config[i - DCB_PG_ATTR_TC_0]; - dst_tc_cfg->path[DCB_TX_CONFIG].prio_type = - src_tc_cfg->path[DCB_TX_CONFIG].prio_type; + if (dst->path[tx].prio_type != src->path[tx].prio_type) { + dst->path[tx].prio_type = src->path[tx].prio_type; + changes |= BIT_PG_TX; + } - dst_tc_cfg->path[DCB_TX_CONFIG].bwg_id = - src_tc_cfg->path[DCB_TX_CONFIG].bwg_id; + if (dst->path[tx].bwg_id != src->path[tx].bwg_id) { + dst->path[tx].bwg_id = src->path[tx].bwg_id; + changes |= BIT_PG_TX; + } - dst_tc_cfg->path[DCB_TX_CONFIG].bwg_percent = - src_tc_cfg->path[DCB_TX_CONFIG].bwg_percent; + if (dst->path[tx].bwg_percent != src->path[tx].bwg_percent) { + dst->path[tx].bwg_percent = src->path[tx].bwg_percent; + changes |= BIT_PG_TX; + } - dst_tc_cfg->path[DCB_TX_CONFIG].up_to_tc_bitmap = - src_tc_cfg->path[DCB_TX_CONFIG].up_to_tc_bitmap; + if (dst->path[tx].up_to_tc_bitmap != + src->path[tx].up_to_tc_bitmap) { + dst->path[tx].up_to_tc_bitmap = + src->path[tx].up_to_tc_bitmap; + changes |= (BIT_PG_TX | BIT_PFC | BIT_APP_UPCHG); + } - dst_tc_cfg->path[DCB_RX_CONFIG].prio_type = - src_tc_cfg->path[DCB_RX_CONFIG].prio_type; + if (dst->path[rx].prio_type != src->path[rx].prio_type) { + dst->path[rx].prio_type = src->path[rx].prio_type; + changes |= BIT_PG_RX; + } - dst_tc_cfg->path[DCB_RX_CONFIG].bwg_id = - src_tc_cfg->path[DCB_RX_CONFIG].bwg_id; + if (dst->path[rx].bwg_id != src->path[rx].bwg_id) { + dst->path[rx].bwg_id = src->path[rx].bwg_id; + changes |= BIT_PG_RX; + } - dst_tc_cfg->path[DCB_RX_CONFIG].bwg_percent = - src_tc_cfg->path[DCB_RX_CONFIG].bwg_percent; + if (dst->path[rx].bwg_percent != src->path[rx].bwg_percent) { + dst->path[rx].bwg_percent = src->path[rx].bwg_percent; + changes |= BIT_PG_RX; + } - dst_tc_cfg->path[DCB_RX_CONFIG].up_to_tc_bitmap = - src_tc_cfg->path[DCB_RX_CONFIG].up_to_tc_bitmap; + if (dst->path[rx].up_to_tc_bitmap != + src->path[rx].up_to_tc_bitmap) { + dst->path[rx].up_to_tc_bitmap = + src->path[rx].up_to_tc_bitmap; + changes |= (BIT_PG_RX | BIT_PFC | BIT_APP_UPCHG); + } } for (i = DCB_PG_ATTR_BW_ID_0; i < DCB_PG_ATTR_BW_ID_MAX; i++) { - dst_dcb_cfg->bw_percentage[DCB_TX_CONFIG] - [i-DCB_PG_ATTR_BW_ID_0] = src_dcb_cfg->bw_percentage - [DCB_TX_CONFIG][i-DCB_PG_ATTR_BW_ID_0]; - dst_dcb_cfg->bw_percentage[DCB_RX_CONFIG] - [i-DCB_PG_ATTR_BW_ID_0] = src_dcb_cfg->bw_percentage - [DCB_RX_CONFIG][i-DCB_PG_ATTR_BW_ID_0]; + j = i - DCB_PG_ATTR_BW_ID_0; + if (dcfg->bw_percentage[tx][j] != scfg->bw_percentage[tx][j]) { + dcfg->bw_percentage[tx][j] = scfg->bw_percentage[tx][j]; + changes |= BIT_PG_TX; + } + if (dcfg->bw_percentage[rx][j] != scfg->bw_percentage[rx][j]) { + dcfg->bw_percentage[rx][j] = scfg->bw_percentage[rx][j]; + changes |= BIT_PG_RX; + } } for (i = DCB_PFC_UP_ATTR_0; i < DCB_PFC_UP_ATTR_MAX; i++) { - dst_dcb_cfg->tc_config[i - DCB_PFC_UP_ATTR_0].dcb_pfc = - src_dcb_cfg->tc_config[i - DCB_PFC_UP_ATTR_0].dcb_pfc; + j = i - DCB_PFC_UP_ATTR_0; + if (dcfg->tc_config[j].dcb_pfc != scfg->tc_config[j].dcb_pfc) { + dcfg->tc_config[j].dcb_pfc = scfg->tc_config[j].dcb_pfc; + changes |= BIT_PFC; + } } - dst_dcb_cfg->pfc_mode_enable = src_dcb_cfg->pfc_mode_enable; + if (dcfg->pfc_mode_enable != scfg->pfc_mode_enable) { + dcfg->pfc_mode_enable = scfg->pfc_mode_enable; + changes |= BIT_PFC; + } - return 0; + return changes; } static u8 ixgbe_dcbnl_get_state(struct net_device *netdev) @@ -179,20 +219,6 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc, if (up_map != DCB_ATTR_VALUE_UNDEFINED) adapter->temp_dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap = up_map; - - if ((adapter->temp_dcb_cfg.tc_config[tc].path[0].prio_type != - adapter->dcb_cfg.tc_config[tc].path[0].prio_type) || - (adapter->temp_dcb_cfg.tc_config[tc].path[0].bwg_id != - adapter->dcb_cfg.tc_config[tc].path[0].bwg_id) || - (adapter->temp_dcb_cfg.tc_config[tc].path[0].bwg_percent != - adapter->dcb_cfg.tc_config[tc].path[0].bwg_percent) || - (adapter->temp_dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap != - adapter->dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap)) - adapter->dcb_set_bitmap |= BIT_PG_TX; - - if (adapter->temp_dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap != - adapter->dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap) - adapter->dcb_set_bitmap |= BIT_PFC | BIT_APP_UPCHG; } static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id, @@ -201,10 +227,6 @@ static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id, struct ixgbe_adapter *adapter = netdev_priv(netdev); adapter->temp_dcb_cfg.bw_percentage[0][bwg_id] = bw_pct; - - if (adapter->temp_dcb_cfg.bw_percentage[0][bwg_id] != - adapter->dcb_cfg.bw_percentage[0][bwg_id]) - adapter->dcb_set_bitmap |= BIT_PG_TX; } static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc, @@ -223,20 +245,6 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc, if (up_map != DCB_ATTR_VALUE_UNDEFINED) adapter->temp_dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap = up_map; - - if ((adapter->temp_dcb_cfg.tc_config[tc].path[1].prio_type != - adapter->dcb_cfg.tc_config[tc].path[1].prio_type) || - (adapter->temp_dcb_cfg.tc_config[tc].path[1].bwg_id != - adapter->dcb_cfg.tc_config[tc].path[1].bwg_id) || - (adapter->temp_dcb_cfg.tc_config[tc].path[1].bwg_percent != - adapter->dcb_cfg.tc_config[tc].path[1].bwg_percent) || - (adapter->temp_dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap != - adapter->dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap)) - adapter->dcb_set_bitmap |= BIT_PG_RX; - - if (adapter->temp_dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap != - adapter->dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap) - adapter->dcb_set_bitmap |= BIT_PFC; } static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id, @@ -245,10 +253,6 @@ static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id, struct ixgbe_adapter *adapter = netdev_priv(netdev); adapter->temp_dcb_cfg.bw_percentage[1][bwg_id] = bw_pct; - - if (adapter->temp_dcb_cfg.bw_percentage[1][bwg_id] != - adapter->dcb_cfg.bw_percentage[1][bwg_id]) - adapter->dcb_set_bitmap |= BIT_PG_RX; } static void ixgbe_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc, @@ -298,10 +302,8 @@ static void ixgbe_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority, adapter->temp_dcb_cfg.tc_config[priority].dcb_pfc = setting; if (adapter->temp_dcb_cfg.tc_config[priority].dcb_pfc != - adapter->dcb_cfg.tc_config[priority].dcb_pfc) { - adapter->dcb_set_bitmap |= BIT_PFC; + adapter->dcb_cfg.tc_config[priority].dcb_pfc) adapter->temp_dcb_cfg.pfc_mode_enable = true; - } } static void ixgbe_dcbnl_get_pfc_cfg(struct net_device *netdev, int priority, @@ -336,31 +338,17 @@ static void ixgbe_dcbnl_devreset(struct net_device *dev) static u8 ixgbe_dcbnl_set_all(struct net_device *netdev) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - int ret, i; -#ifdef IXGBE_FCOE - struct dcb_app app = { - .selector = DCB_APP_IDTYPE_ETHTYPE, - .protocol = ETH_P_FCOE, - }; - u8 up; - - /* In IEEE mode, use the IEEE Ethertype selector value */ - if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) { - app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE; - up = dcb_ieee_getapp_mask(netdev, &app); - } else { - up = dcb_getapp(netdev, &app); - } -#endif + int ret = DCB_NO_HW_CHG; + int i; /* Fail command if not in CEE mode */ if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE)) - return 1; + return ret; - ret = ixgbe_copy_dcb_cfg(&adapter->temp_dcb_cfg, &adapter->dcb_cfg, - MAX_TRAFFIC_CLASS); - if (ret) - return DCB_NO_HW_CHG; + adapter->dcb_set_bitmap |= ixgbe_copy_dcb_cfg(adapter, + MAX_TRAFFIC_CLASS); + if (!adapter->dcb_set_bitmap) + return ret; if (adapter->dcb_cfg.pfc_mode_enable) { switch (adapter->hw.mac.type) { @@ -420,6 +408,8 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev) for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) netdev_set_prio_tc_map(netdev, i, prio_tc[i]); + + ret = DCB_HW_CHG_RST; } if (adapter->dcb_set_bitmap & BIT_PFC) { @@ -430,7 +420,8 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev) DCB_TX_CONFIG, prio_tc); ixgbe_dcb_unpack_pfc(&adapter->dcb_cfg, &pfc_en); ixgbe_dcb_hw_pfc_config(&adapter->hw, pfc_en, prio_tc); - ret = DCB_HW_CHG; + if (ret != DCB_HW_CHG_RST) + ret = DCB_HW_CHG; } if (adapter->dcb_cfg.pfc_mode_enable) @@ -441,8 +432,13 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev) * FCoE is using changes. This happens if the APP info * changes or the up2tc mapping is updated. */ - if ((up && !(up & (1 << adapter->fcoe.up))) || - (adapter->dcb_set_bitmap & BIT_APP_UPCHG)) { + if (adapter->dcb_set_bitmap & BIT_APP_UPCHG) { + struct dcb_app app = { + .selector = DCB_APP_IDTYPE_ETHTYPE, + .protocol = ETH_P_FCOE, + }; + u8 up = dcb_getapp(netdev, &app); + adapter->fcoe.up = ffs(up) - 1; ixgbe_dcbnl_devreset(netdev); ret = DCB_HW_CHG_RST; @@ -531,9 +527,6 @@ static void ixgbe_dcbnl_setpfcstate(struct net_device *netdev, u8 state) struct ixgbe_adapter *adapter = netdev_priv(netdev); adapter->temp_dcb_cfg.pfc_mode_enable = state; - if (adapter->temp_dcb_cfg.pfc_mode_enable != - adapter->dcb_cfg.pfc_mode_enable) - adapter->dcb_set_bitmap |= BIT_PFC; } /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 31a2bf76a346..cfe7d269590c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1780,6 +1780,8 @@ static u16 ixgbe_clean_test_rings(struct ixgbe_ring *rx_ring, rx_desc = IXGBE_RX_DESC(rx_ring, rx_ntc); } + netdev_tx_reset_queue(txring_txq(tx_ring)); + /* re-map buffers to ring, store next to clean values */ ixgbe_alloc_rx_buffers(rx_ring, count); rx_ring->next_to_clean = rx_ntc; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index 77ea4b716535..bc07933d67da 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -437,6 +437,7 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter, */ if ((fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA) && (fctl & FC_FC_END_SEQ)) { + skb_linearize(skb); crc = (struct fcoe_crc_eof *)skb_put(skb, sizeof(*crc)); crc->fcoe_eof = FC_EOF_T; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index 027d7a75be39..ed1b47dc0834 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -622,6 +622,16 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, int v_idx, if (adapter->hw.mac.type == ixgbe_mac_82599EB) set_bit(__IXGBE_RX_CSUM_UDP_ZERO_ERR, &ring->state); +#ifdef IXGBE_FCOE + if (adapter->netdev->features & NETIF_F_FCOE_MTU) { + struct ixgbe_ring_feature *f; + f = &adapter->ring_feature[RING_F_FCOE]; + if ((rxr_idx >= f->mask) && + (rxr_idx < f->mask + f->indices)) + set_bit(__IXGBE_RX_FCOE_BUFSZ, &ring->state); + } + +#endif /* IXGBE_FCOE */ /* apply Rx specific ring traits */ ring->count = adapter->rx_ring_count; ring->queue_index = rxr_idx; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 398fc223cab9..467948e9ecd9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -63,8 +63,8 @@ static char ixgbe_default_device_descr[] = "Intel(R) 10 Gigabit Network Connection"; #endif #define MAJ 3 -#define MIN 6 -#define BUILD 7 +#define MIN 8 +#define BUILD 21 #define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \ __stringify(BUILD) "-k" const char ixgbe_driver_version[] = DRV_VERSION; @@ -141,13 +141,16 @@ module_param(allow_unsupported_sfp, uint, 0); MODULE_PARM_DESC(allow_unsupported_sfp, "Allow unsupported and untested SFP+ modules on 82599-based adapters"); +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>"); MODULE_DESCRIPTION("Intel(R) 10 Gigabit PCI Express Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); -#define DEFAULT_DEBUG_LEVEL_SHIFT 3 - static void ixgbe_service_event_schedule(struct ixgbe_adapter *adapter) { if (!test_bit(__IXGBE_DOWN, &adapter->state) && @@ -2668,8 +2671,6 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, /* enable queue */ IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); - netdev_tx_reset_queue(txring_txq(ring)); - /* TXDCTL.EN will return 0 on 82598 if link is down, so skip it */ if (hw->mac.type == ixgbe_mac_82598EB && !(IXGBE_READ_REG(hw, IXGBE_LINKS) & IXGBE_LINKS_UP)) @@ -3151,14 +3152,6 @@ static void ixgbe_set_rx_buffer_len(struct ixgbe_adapter *adapter) set_ring_rsc_enabled(rx_ring); else clear_ring_rsc_enabled(rx_ring); -#ifdef IXGBE_FCOE - if (netdev->features & NETIF_F_FCOE_MTU) { - struct ixgbe_ring_feature *f; - f = &adapter->ring_feature[RING_F_FCOE]; - if ((i >= f->mask) && (i < f->mask + f->indices)) - set_bit(__IXGBE_RX_FCOE_BUFSZ, &rx_ring->state); - } -#endif /* IXGBE_FCOE */ } } @@ -4172,6 +4165,8 @@ static void ixgbe_clean_tx_ring(struct ixgbe_ring *tx_ring) ixgbe_unmap_and_free_tx_resource(tx_ring, tx_buffer_info); } + netdev_tx_reset_queue(txring_txq(tx_ring)); + size = sizeof(struct ixgbe_tx_buffer) * tx_ring->count; memset(tx_ring->tx_buffer_info, 0, size); @@ -4423,8 +4418,8 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter) adapter->dcb_cfg.pfc_mode_enable = false; adapter->dcb_set_bitmap = 0x00; adapter->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE; - ixgbe_copy_dcb_cfg(&adapter->dcb_cfg, &adapter->temp_dcb_cfg, - MAX_TRAFFIC_CLASS); + memcpy(&adapter->temp_dcb_cfg, &adapter->dcb_cfg, + sizeof(adapter->temp_dcb_cfg)); #endif @@ -4833,7 +4828,9 @@ static int ixgbe_resume(struct pci_dev *pdev) pci_wake_from_d3(pdev, false); + rtnl_lock(); err = ixgbe_init_interrupt_scheme(adapter); + rtnl_unlock(); if (err) { e_dev_err("Cannot initialize interrupts for device\n"); return err; @@ -4869,17 +4866,15 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake) netif_device_detach(netdev); if (netif_running(netdev)) { + rtnl_lock(); ixgbe_down(adapter); ixgbe_free_irq(adapter); ixgbe_free_all_tx_resources(adapter); ixgbe_free_all_rx_resources(adapter); + rtnl_unlock(); } ixgbe_clear_interrupt_scheme(adapter); -#ifdef CONFIG_DCB - kfree(adapter->ixgbe_ieee_pfc); - kfree(adapter->ixgbe_ieee_ets); -#endif #ifdef CONFIG_PM retval = pci_save_state(pdev); @@ -4890,6 +4885,16 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake) if (wufc) { ixgbe_set_rx_mode(netdev); + /* + * enable the optics for both mult-speed fiber and + * 82599 SFP+ fiber as we can WoL. + */ + if (hw->mac.ops.enable_tx_laser && + (hw->phy.multispeed_fiber || + (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber && + hw->mac.type == ixgbe_mac_82599EB))) + hw->mac.ops.enable_tx_laser(hw); + /* turn on all-multi mode if wake on multicast is enabled */ if (wufc & IXGBE_WUFC_MC) { fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL); @@ -6834,7 +6839,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, adapter->pdev = pdev; hw = &adapter->hw; hw->back = adapter; - adapter->msg_enable = (1 << DEFAULT_DEBUG_LEVEL_SHIFT) - 1; + adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); hw->hw_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); @@ -7217,6 +7222,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) ixgbe_release_hw_control(adapter); +#ifdef CONFIG_DCB + kfree(adapter->ixgbe_ieee_pfc); + kfree(adapter->ixgbe_ieee_ets); + +#endif iounmap(adapter->hw.hw_addr); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 581c65976bb4..307611ae831d 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -91,7 +91,10 @@ MODULE_DESCRIPTION("Intel(R) 82599 Virtual Function Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); -#define DEFAULT_DEBUG_LEVEL_SHIFT 3 +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); /* forward decls */ static void ixgbevf_set_itr_msix(struct ixgbevf_q_vector *q_vector); @@ -3367,7 +3370,7 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, adapter->pdev = pdev; hw = &adapter->hw; hw->back = adapter; - adapter->msg_enable = (1 << DEFAULT_DEBUG_LEVEL_SHIFT) - 1; + adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); /* * call save state here in standalone driver because it relies on diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 423a1a2a702e..487a6c8bd4ec 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -1767,13 +1767,14 @@ static int sky2_open(struct net_device *dev) sky2_hw_up(sky2); + /* Enable interrupts from phy/mac for port */ + imask = sky2_read32(hw, B0_IMSK); + if (hw->chip_id == CHIP_ID_YUKON_OPT || hw->chip_id == CHIP_ID_YUKON_PRM || hw->chip_id == CHIP_ID_YUKON_OP_2) imask |= Y2_IS_PHY_QLNK; /* enable PHY Quick Link */ - /* Enable interrupts from phy/mac for port */ - imask = sky2_read32(hw, B0_IMSK); imask |= portirq_msk[port]; sky2_write32(hw, B0_IMSK, imask); sky2_read32(hw, B0_IMSK); @@ -2468,6 +2469,17 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) return err; } +static inline bool needs_copy(const struct rx_ring_info *re, + unsigned length) +{ +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + /* Some architectures need the IP header to be aligned */ + if (!IS_ALIGNED(re->data_addr + ETH_HLEN, sizeof(u32))) + return true; +#endif + return length < copybreak; +} + /* For small just reuse existing skb for next receive */ static struct sk_buff *receive_copy(struct sky2_port *sky2, const struct rx_ring_info *re, @@ -2482,8 +2494,13 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2, skb_copy_from_linear_data(re->skb, skb->data, length); skb->ip_summed = re->skb->ip_summed; skb->csum = re->skb->csum; + skb->rxhash = re->skb->rxhash; + skb->vlan_tci = re->skb->vlan_tci; + pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr, length, PCI_DMA_FROMDEVICE); + re->skb->vlan_tci = 0; + re->skb->rxhash = 0; re->skb->ip_summed = CHECKSUM_NONE; skb_put(skb, length); } @@ -2568,9 +2585,6 @@ static struct sk_buff *sky2_receive(struct net_device *dev, struct sk_buff *skb = NULL; u16 count = (status & GMR_FS_LEN) >> 16; - if (status & GMR_FS_VLAN) - count -= VLAN_HLEN; /* Account for vlan tag */ - netif_printk(sky2, rx_status, KERN_DEBUG, dev, "rx slot %u status 0x%x len %d\n", sky2->rx_next, status, length); @@ -2578,6 +2592,9 @@ static struct sk_buff *sky2_receive(struct net_device *dev, sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; prefetch(sky2->rx_ring + sky2->rx_next); + if (vlan_tx_tag_present(re->skb)) + count -= VLAN_HLEN; /* Account for vlan tag */ + /* This chip has hardware problems that generates bogus status. * So do only marginal checking and expect higher level protocols * to handle crap frames. @@ -2598,7 +2615,7 @@ static struct sk_buff *sky2_receive(struct net_device *dev, goto error; okay: - if (length < copybreak) + if (needs_copy(re, length)) skb = receive_copy(sky2, re, length); else skb = receive_new(sky2, re, length); @@ -2635,11 +2652,8 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last) } static inline void sky2_skb_rx(const struct sky2_port *sky2, - u32 status, struct sk_buff *skb) + struct sk_buff *skb) { - if (status & GMR_FS_VLAN) - __vlan_hwaccel_put_tag(skb, be16_to_cpu(sky2->rx_tag)); - if (skb->ip_summed == CHECKSUM_NONE) netif_receive_skb(skb); else @@ -2693,6 +2707,14 @@ static void sky2_rx_checksum(struct sky2_port *sky2, u32 status) } } +static void sky2_rx_tag(struct sky2_port *sky2, u16 length) +{ + struct sk_buff *skb; + + skb = sky2->rx_ring[sky2->rx_next].skb; + __vlan_hwaccel_put_tag(skb, be16_to_cpu(length)); +} + static void sky2_rx_hash(struct sky2_port *sky2, u32 status) { struct sk_buff *skb; @@ -2751,8 +2773,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) } skb->protocol = eth_type_trans(skb, dev); - - sky2_skb_rx(sky2, status, skb); + sky2_skb_rx(sky2, skb); /* Stop after net poll weight */ if (++work_done >= to_do) @@ -2760,11 +2781,11 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) break; case OP_RXVLAN: - sky2->rx_tag = length; + sky2_rx_tag(sky2, length); break; case OP_RXCHKSVLAN: - sky2->rx_tag = length; + sky2_rx_tag(sky2, length); /* fall through */ case OP_RXCHKS: if (likely(dev->features & NETIF_F_RXCSUM)) diff --git a/drivers/net/ethernet/marvell/sky2.h b/drivers/net/ethernet/marvell/sky2.h index ff6f58bf822a..3c896ce80b71 100644 --- a/drivers/net/ethernet/marvell/sky2.h +++ b/drivers/net/ethernet/marvell/sky2.h @@ -2241,7 +2241,6 @@ struct sky2_port { u16 rx_pending; u16 rx_data_size; u16 rx_nfrags; - u16 rx_tag; struct { unsigned long last; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 9e2b911a1230..d69fee41f24a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -83,8 +83,9 @@ #define MLX4_EN_WATCHDOG_TIMEOUT (15 * HZ) -#define MLX4_EN_ALLOC_ORDER 2 -#define MLX4_EN_ALLOC_SIZE (PAGE_SIZE << MLX4_EN_ALLOC_ORDER) +/* Use the maximum between 16384 and a single page */ +#define MLX4_EN_ALLOC_SIZE PAGE_ALIGN(16384) +#define MLX4_EN_ALLOC_ORDER get_order(MLX4_EN_ALLOC_SIZE) #define MLX4_EN_MAX_LRO_DESCRIPTORS 32 diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index c722aa607d07..5e313e9a252f 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -618,10 +618,8 @@ static void ks8851_irq_work(struct work_struct *work) netif_dbg(ks, intr, ks->netdev, "%s: status 0x%04x\n", __func__, status); - if (status & IRQ_LCI) { - /* should do something about checking link status */ + if (status & IRQ_LCI) handled |= IRQ_LCI; - } if (status & IRQ_LDI) { u16 pmecr = ks8851_rdreg16(ks, KS_PMECR); @@ -684,6 +682,9 @@ static void ks8851_irq_work(struct work_struct *work) mutex_unlock(&ks->lock); + if (status & IRQ_LCI) + mii_check_link(&ks->mii); + if (status & IRQ_TXI) netif_wake_queue(ks->netdev); @@ -889,16 +890,17 @@ static int ks8851_net_stop(struct net_device *dev) netif_stop_queue(dev); mutex_lock(&ks->lock); + /* turn off the IRQs and ack any outstanding */ + ks8851_wrreg16(ks, KS_IER, 0x0000); + ks8851_wrreg16(ks, KS_ISR, 0xffff); + mutex_unlock(&ks->lock); /* stop any outstanding work */ flush_work(&ks->irq_work); flush_work(&ks->tx_work); flush_work(&ks->rxctrl_work); - /* turn off the IRQs and ack any outstanding */ - ks8851_wrreg16(ks, KS_IER, 0x0000); - ks8851_wrreg16(ks, KS_ISR, 0xffff); - + mutex_lock(&ks->lock); /* shutdown RX process */ ks8851_wrreg16(ks, KS_RXCR1, 0x0000); @@ -907,6 +909,7 @@ static int ks8851_net_stop(struct net_device *dev) /* set powermode to soft power down to save power */ ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); + mutex_unlock(&ks->lock); /* ensure any queued tx buffers are dumped */ while (!skb_queue_empty(&ks->txq)) { @@ -918,7 +921,6 @@ static int ks8851_net_stop(struct net_device *dev) dev_kfree_skb(txb); } - mutex_unlock(&ks->lock); return 0; } @@ -1418,6 +1420,7 @@ static int __devinit ks8851_probe(struct spi_device *spi) struct net_device *ndev; struct ks8851_net *ks; int ret; + unsigned cider; ndev = alloc_etherdev(sizeof(struct ks8851_net)); if (!ndev) @@ -1484,8 +1487,8 @@ static int __devinit ks8851_probe(struct spi_device *spi) ks8851_soft_reset(ks, GRR_GSR); /* simple check for a valid chip being connected to the bus */ - - if ((ks8851_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) { + cider = ks8851_rdreg16(ks, KS_CIDER); + if ((cider & ~CIDER_REV_MASK) != CIDER_ID) { dev_err(&spi->dev, "failed to read device ID\n"); ret = -ENODEV; goto err_id; @@ -1516,15 +1519,14 @@ static int __devinit ks8851_probe(struct spi_device *spi) } netdev_info(ndev, "revision %d, MAC %pM, IRQ %d, %s EEPROM\n", - CIDER_REV_GET(ks8851_rdreg16(ks, KS_CIDER)), - ndev->dev_addr, ndev->irq, + CIDER_REV_GET(cider), ndev->dev_addr, ndev->irq, ks->rc_ccr & CCR_EEPROM ? "has" : "no"); return 0; err_netdev: - free_irq(ndev->irq, ndev); + free_irq(ndev->irq, ks); err_id: err_irq: diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index b8104d9f4081..5ffde23ac8fb 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -40,7 +40,7 @@ #define DRV_NAME "ks8851_mll" static u8 KS_DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x86, 0x95, 0x11 }; -#define MAX_RECV_FRAMES 32 +#define MAX_RECV_FRAMES 255 #define MAX_BUF_SIZE 2048 #define TX_BUF_SIZE 2000 #define RX_BUF_SIZE 2000 diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index ef723b185d85..eaf9ff0262a9 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -5675,7 +5675,7 @@ static int netdev_set_mac_address(struct net_device *dev, void *addr) memcpy(hw->override_addr, mac->sa_data, ETH_ALEN); } - memcpy(dev->dev_addr, mac->sa_data, MAX_ADDR_LEN); + memcpy(dev->dev_addr, mac->sa_data, ETH_ALEN); interrupt = hw_block_intr(hw); diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 69444247c20b..6dfc26d85e47 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -1441,7 +1441,7 @@ static int lpc_eth_drv_probe(struct platform_device *pdev) } #endif if (!is_valid_ether_addr(ndev->dev_addr)) - dev_hw_addr_random(ndev, ndev->dev_addr); + eth_hw_addr_random(ndev); /* Reset the ethernet controller */ __lpc_eth_reset(pldat); diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index abc79076f867..b3287c0fe279 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -958,6 +958,11 @@ static inline void cp_start_hw (struct cp_private *cp) cpw8(Cmd, RxOn | TxOn); } +static void cp_enable_irq(struct cp_private *cp) +{ + cpw16_f(IntrMask, cp_intr_mask); +} + static void cp_init_hw (struct cp_private *cp) { struct net_device *dev = cp->dev; @@ -997,8 +1002,6 @@ static void cp_init_hw (struct cp_private *cp) cpw16(MultiIntr, 0); - cpw16_f(IntrMask, cp_intr_mask); - cpw8_f(Cfg9346, Cfg9346_Lock); } @@ -1130,6 +1133,8 @@ static int cp_open (struct net_device *dev) if (rc) goto err_out_hw; + cp_enable_irq(cp); + netif_carrier_off(dev); mii_check_media(&cp->mii_if, netif_msg_link(cp), true); netif_start_queue(dev); @@ -2031,6 +2036,7 @@ static int cp_resume (struct pci_dev *pdev) /* FIXME: sh*t may happen if the Rx ring buffer is depleted */ cp_init_rings_index (cp); cp_init_hw (cp); + cp_enable_irq(cp); netif_start_queue (dev); spin_lock_irqsave (&cp->lock, flags); diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 7b23554f80b6..ce6b44d1f252 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -61,8 +61,12 @@ #define R8169_MSG_DEFAULT \ (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN) -#define TX_BUFFS_AVAIL(tp) \ - (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx - 1) +#define TX_SLOTS_AVAIL(tp) \ + (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx) + +/* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */ +#define TX_FRAGS_READY_FOR(tp,nr_frags) \ + (TX_SLOTS_AVAIL(tp) >= (nr_frags + 1)) /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */ @@ -5115,7 +5119,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, u32 opts[2]; int frags; - if (unlikely(TX_BUFFS_AVAIL(tp) < skb_shinfo(skb)->nr_frags)) { + if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) { netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n"); goto err_stop_0; } @@ -5169,7 +5173,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, mmiowb(); - if (TX_BUFFS_AVAIL(tp) < MAX_SKB_FRAGS) { + if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) { /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must * not miss a ring update when it notices a stopped queue. */ @@ -5183,7 +5187,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, * can't. */ smp_mb(); - if (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS) + if (TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) netif_wake_queue(dev); } @@ -5306,7 +5310,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp) */ smp_mb(); if (netif_queue_stopped(dev) && - (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS)) { + TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) { netif_wake_queue(dev); } /* @@ -5810,7 +5814,10 @@ static void __rtl8169_resume(struct net_device *dev) rtl_pll_power_up(tp); + rtl_lock_work(tp); + napi_enable(&tp->napi); set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags); + rtl_unlock_work(tp); rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING); } diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig index 9755b49bbefb..3fb2355af37e 100644 --- a/drivers/net/ethernet/renesas/Kconfig +++ b/drivers/net/ethernet/renesas/Kconfig @@ -7,7 +7,8 @@ config SH_ETH depends on SUPERH && \ (CPU_SUBTYPE_SH7710 || CPU_SUBTYPE_SH7712 || \ CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7619 || \ - CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7757) + CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7734 || \ + CPU_SUBTYPE_SH7757) select CRC32 select NET_CORE select MII @@ -16,4 +17,4 @@ config SH_ETH ---help--- Renesas SuperH Ethernet device driver. This driver supporting CPUs are: - - SH7710, SH7712, SH7763, SH7619, SH7724, and SH7757. + - SH7619, SH7710, SH7712, SH7724, SH7734, SH7763 and SH7757. diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 8615961c1287..d63e09b29a96 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1,8 +1,8 @@ /* * SuperH Ethernet device driver * - * Copyright (C) 2006-2008 Nobuhiro Iwamatsu - * Copyright (C) 2008-2009 Renesas Solutions Corp. + * Copyright (C) 2006-2012 Nobuhiro Iwamatsu + * Copyright (C) 2008-2012 Renesas Solutions Corp. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -38,6 +38,7 @@ #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/if_vlan.h> +#include <linux/clk.h> #include <linux/sh_eth.h> #include "sh_eth.h" @@ -279,8 +280,9 @@ static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp) return &sh_eth_my_cpu_data; } -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) +#elif defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) #define SH_ETH_HAS_TSU 1 +static void sh_eth_reset_hw_crc(struct net_device *ndev); static void sh_eth_chip_reset(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -314,6 +316,9 @@ static void sh_eth_reset(struct net_device *ndev) sh_eth_write(ndev, 0x0, RDFAR); sh_eth_write(ndev, 0x0, RDFXR); sh_eth_write(ndev, 0x0, RDFFR); + + /* Reset HW CRC register */ + sh_eth_reset_hw_crc(ndev); } static void sh_eth_set_duplex(struct net_device *ndev) @@ -370,8 +375,17 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .no_trimd = 1, .no_ade = 1, .tsu = 1, +#if defined(CONFIG_CPU_SUBTYPE_SH7734) + .hw_crc = 1, +#endif }; +static void sh_eth_reset_hw_crc(struct net_device *ndev) +{ + if (sh_eth_my_cpu_data.hw_crc) + sh_eth_write(ndev, 0x0, CSMR); +} + #elif defined(CONFIG_CPU_SUBTYPE_SH7619) #define SH_ETH_RESET_DEFAULT 1 static struct sh_eth_cpu_data sh_eth_my_cpu_data = { @@ -790,7 +804,7 @@ static int sh_eth_dev_init(struct net_device *ndev) /* all sh_eth int mask */ sh_eth_write(ndev, 0, EESIPR); -#if defined(__LITTLE_ENDIAN__) +#if defined(__LITTLE_ENDIAN) if (mdp->cd->hw_swap) sh_eth_write(ndev, EDMR_EL, EDMR); else diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 57dc26261116..0fa14afce23d 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -1,8 +1,8 @@ /* * SuperH Ethernet device driver * - * Copyright (C) 2006-2008 Nobuhiro Iwamatsu - * Copyright (C) 2008-2011 Renesas Solutions Corp. + * Copyright (C) 2006-2012 Nobuhiro Iwamatsu + * Copyright (C) 2008-2012 Renesas Solutions Corp. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -98,6 +98,8 @@ enum { CEECR, MAFCR, RTRATE, + CSMR, + RMII_MII, /* TSU Absolute address */ ARSTR, @@ -172,6 +174,7 @@ static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = { [RMCR] = 0x0458, [RPADIR] = 0x0460, [FCFTR] = 0x0468, + [CSMR] = 0x04E4, [ECMR] = 0x0500, [ECSR] = 0x0510, @@ -200,6 +203,7 @@ static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = { [CERCR] = 0x0768, [CEECR] = 0x0770, [MAFCR] = 0x0778, + [RMII_MII] = 0x0790, [ARSTR] = 0x0000, [TSU_CTRST] = 0x0004, @@ -377,7 +381,7 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = { /* * Register's bits */ -#ifdef CONFIG_CPU_SUBTYPE_SH7763 +#if defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) /* EDSR */ enum EDSR_BIT { EDSR_ENT = 0x01, EDSR_ENR = 0x02, @@ -689,7 +693,7 @@ enum TSU_FWSLC_BIT { */ struct sh_eth_txdesc { u32 status; /* TD0 */ -#if defined(CONFIG_CPU_LITTLE_ENDIAN) +#if defined(__LITTLE_ENDIAN) u16 pad0; /* TD1 */ u16 buffer_length; /* TD1 */ #else @@ -706,7 +710,7 @@ struct sh_eth_txdesc { */ struct sh_eth_rxdesc { u32 status; /* RD0 */ -#if defined(CONFIG_CPU_LITTLE_ENDIAN) +#if defined(__LITTLE_ENDIAN) u16 frame_length; /* RD1 */ u16 buffer_length; /* RD1 */ #else @@ -751,6 +755,7 @@ struct sh_eth_cpu_data { unsigned rpadir:1; /* E-DMAC have RPADIR */ unsigned no_trimd:1; /* E-DMAC DO NOT have TRIMD */ unsigned no_ade:1; /* E-DMAC DO NOT have ADE bit in EESR */ + unsigned hw_crc:1; /* E-DMAC have CSMR */ }; struct sh_eth_private { diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 3cbfbffe3f00..4a0005342e65 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -1349,7 +1349,7 @@ static int efx_probe_interrupts(struct efx_nic *efx) } /* RSS might be usable on VFs even if it is disabled on the PF */ - efx->rss_spread = (efx->n_rx_channels > 1 ? + efx->rss_spread = ((efx->n_rx_channels > 1 || !efx_sriov_wanted(efx)) ? efx->n_rx_channels : efx_vf_size(efx)); return 0; diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 4a6971027076..cd3defb11ffb 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1166,10 +1166,8 @@ smsc911x_rx_counterrors(struct net_device *dev, unsigned int rxstat) /* Quickly dumps bad packets */ static void -smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes) +smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktwords) { - unsigned int pktwords = (pktbytes + NET_IP_ALIGN + 3) >> 2; - if (likely(pktwords >= 4)) { unsigned int timeout = 500; unsigned int val; @@ -1233,7 +1231,7 @@ static int smsc911x_poll(struct napi_struct *napi, int budget) continue; } - skb = netdev_alloc_skb(dev, pktlength + NET_IP_ALIGN); + skb = netdev_alloc_skb(dev, pktwords << 2); if (unlikely(!skb)) { SMSC_WARN(pdata, rx_err, "Unable to allocate skb for rx packet"); @@ -1243,14 +1241,12 @@ static int smsc911x_poll(struct napi_struct *napi, int budget) break; } - skb->data = skb->head; - skb_reset_tail_pointer(skb); + pdata->ops->rx_readfifo(pdata, + (unsigned int *)skb->data, pktwords); /* Align IP on 16B boundary */ skb_reserve(skb, NET_IP_ALIGN); skb_put(skb, pktlength - 4); - pdata->ops->rx_readfifo(pdata, - (unsigned int *)skb->head, pktwords); skb->protocol = eth_type_trans(skb, dev); skb_checksum_none_assert(skb); netif_receive_skb(skb); @@ -1565,7 +1561,7 @@ static int smsc911x_open(struct net_device *dev) smsc911x_reg_write(pdata, FIFO_INT, temp); /* set RX Data offset to 2 bytes for alignment */ - smsc911x_reg_write(pdata, RX_CFG, (2 << 8)); + smsc911x_reg_write(pdata, RX_CFG, (NET_IP_ALIGN << 8)); /* enable NAPI polling before enabling RX interrupts */ napi_enable(&pdata->napi); @@ -2382,7 +2378,6 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) SET_NETDEV_DEV(dev, &pdev->dev); pdata = netdev_priv(dev); - dev->irq = irq_res->start; irq_flags = irq_res->flags & IRQF_TRIGGER_MASK; pdata->ioaddr = ioremap_nocache(res->start, res_size); @@ -2446,7 +2441,7 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) if (retval) { SMSC_WARN(pdata, probe, "Unable to claim requested irq: %d", dev->irq); - goto out_free_irq; + goto out_disable_resources; } retval = register_netdev(dev); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e85ffbd54830..48d56da62f08 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1737,10 +1737,12 @@ static int stmmac_hw_init(struct stmmac_priv *priv) struct mac_device_info *mac; /* Identify the MAC HW device */ - if (priv->plat->has_gmac) + if (priv->plat->has_gmac) { + priv->dev->priv_flags |= IFF_UNICAST_FLT; mac = dwmac1000_setup(priv->ioaddr); - else + } else { mac = dwmac100_setup(priv->ioaddr); + } if (!mac) return -ENOMEM; diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 558409ff4058..4ba969096717 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -2339,7 +2339,7 @@ static int gem_suspend(struct pci_dev *pdev, pm_message_t state) netif_device_detach(dev); /* Switch off chip, remember WOL setting */ - gp->asleep_wol = gp->wake_on_lan; + gp->asleep_wol = !!gp->wake_on_lan; gem_do_stop(dev, gp->asleep_wol); /* Unlock the network stack */ diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 174a3348f676..08aff1a2087c 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1511,7 +1511,7 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd) static int match_first_device(struct device *dev, void *data) { - return 1; + return !strncmp(dev_name(dev), "davinci_mdio", 12); } /** diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 2757c7d6e633..e4e47088e26b 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -181,6 +181,11 @@ static inline int wait_for_user_access(struct davinci_mdio_data *data) __davinci_mdio_reset(data); return -EAGAIN; } + + reg = __raw_readl(®s->user[0].access); + if ((reg & USERACCESS_GO) == 0) + return 0; + dev_err(data->dev, "timed out waiting for user access\n"); return -ETIMEDOUT; } diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 817ad3bc4957..efd36691ce54 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -228,7 +228,7 @@ tlan_get_skb(const struct tlan_list *tag) unsigned long addr; addr = tag->buffer[9].address; - addr |= (tag->buffer[8].address << 16) << 16; + addr |= ((unsigned long) tag->buffer[8].address << 16) << 16; return (struct sk_buff *) addr; } diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c index 261356c2dc99..3d501ec7fad7 100644 --- a/drivers/net/ethernet/tile/tilepro.c +++ b/drivers/net/ethernet/tile/tilepro.c @@ -342,6 +342,21 @@ inline int __netio_fastio1(u32 fastio_index, u32 arg0) } +static void tile_net_return_credit(struct tile_net_cpu *info) +{ + struct tile_netio_queue *queue = &info->queue; + netio_queue_user_impl_t *qup = &queue->__user_part; + + /* Return four credits after every fourth packet. */ + if (--qup->__receive_credit_remaining == 0) { + u32 interval = qup->__receive_credit_interval; + qup->__receive_credit_remaining = interval; + __netio_fastio_return_credits(qup->__fastio_index, interval); + } +} + + + /* * Provide a linux buffer to LIPP. */ @@ -433,7 +448,7 @@ static bool tile_net_provide_needed_buffer(struct tile_net_cpu *info, struct sk_buff **skb_ptr; /* Request 96 extra bytes for alignment purposes. */ - skb = netdev_alloc_skb(info->napi->dev, len + padding); + skb = netdev_alloc_skb(info->napi.dev, len + padding); if (skb == NULL) return false; @@ -864,19 +879,11 @@ static bool tile_net_poll_aux(struct tile_net_cpu *info, int index) stats->rx_packets++; stats->rx_bytes += len; - - if (small) - info->num_needed_small_buffers++; - else - info->num_needed_large_buffers++; } - /* Return four credits after every fourth packet. */ - if (--qup->__receive_credit_remaining == 0) { - u32 interval = qup->__receive_credit_interval; - qup->__receive_credit_remaining = interval; - __netio_fastio_return_credits(qup->__fastio_index, interval); - } + /* ISSUE: It would be nice to defer this until the packet has */ + /* actually been processed. */ + tile_net_return_credit(info); /* Consume this packet. */ qup->__packet_receive_read = index2; @@ -1543,7 +1550,7 @@ static int tile_net_drain_lipp_buffers(struct tile_net_priv *priv) /* Drain all the LIPP buffers. */ while (true) { - int buffer; + unsigned int buffer; /* NOTE: This should never fail. */ if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&buffer, @@ -1707,7 +1714,7 @@ static unsigned int tile_net_tx_frags(lepp_frag_t *frags, if (!hash_default) { void *va = pfn_to_kaddr(pfn) + f->page_offset; BUG_ON(PageHighMem(skb_frag_page(f))); - finv_buffer_remote(va, f->size, 0); + finv_buffer_remote(va, skb_frag_size(f), 0); } cpa = ((phys_addr_t)pfn << PAGE_SHIFT) + f->page_offset; @@ -1735,8 +1742,8 @@ static unsigned int tile_net_tx_frags(lepp_frag_t *frags, * Sometimes, if "sendfile()" requires copying, we will be called with * "data" containing the header and payload, with "frags" being empty. * - * In theory, "sh->nr_frags" could be 3, but in practice, it seems - * that this will never actually happen. + * Sometimes, for example when using NFS over TCP, a single segment can + * span 3 fragments, which must be handled carefully in LEPP. * * See "emulate_large_send_offload()" for some reference code, which * does not handle checksumming. @@ -1844,10 +1851,8 @@ static int tile_net_tx_tso(struct sk_buff *skb, struct net_device *dev) spin_lock_irqsave(&priv->eq_lock, irqflags); - /* - * Handle completions if needed to make room. - * HACK: Spin until there is sufficient room. - */ + /* Handle completions if needed to make room. */ + /* NOTE: Return NETDEV_TX_BUSY if there is still no room. */ if (lepp_num_free_comp_slots(eq) == 0) { nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 0); if (nolds == 0) { @@ -1861,6 +1866,7 @@ busy: cmd_tail = eq->cmd_tail; /* Prepare to advance, detecting full queue. */ + /* NOTE: Return NETDEV_TX_BUSY if the queue is full. */ cmd_next = cmd_tail + cmd_size; if (cmd_tail < cmd_head && cmd_next >= cmd_head) goto busy; @@ -2023,10 +2029,8 @@ static int tile_net_tx(struct sk_buff *skb, struct net_device *dev) spin_lock_irqsave(&priv->eq_lock, irqflags); - /* - * Handle completions if needed to make room. - * HACK: Spin until there is sufficient room. - */ + /* Handle completions if needed to make room. */ + /* NOTE: Return NETDEV_TX_BUSY if there is still no room. */ if (lepp_num_free_comp_slots(eq) == 0) { nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 0); if (nolds == 0) { @@ -2040,6 +2044,7 @@ busy: cmd_tail = eq->cmd_tail; /* Copy the commands, or fail. */ + /* NOTE: Return NETDEV_TX_BUSY if the queue is full. */ for (i = 0; i < num_frags; i++) { /* Prepare to advance, detecting full queue. */ @@ -2261,6 +2266,23 @@ static int tile_net_get_mac(struct net_device *dev) return 0; } + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ +static void tile_net_netpoll(struct net_device *dev) +{ + struct tile_net_priv *priv = netdev_priv(dev); + disable_percpu_irq(priv->intr_id); + tile_net_handle_ingress_interrupt(priv->intr_id, dev); + enable_percpu_irq(priv->intr_id, 0); +} +#endif + + static const struct net_device_ops tile_net_ops = { .ndo_open = tile_net_open, .ndo_stop = tile_net_stop, @@ -2269,7 +2291,10 @@ static const struct net_device_ops tile_net_ops = { .ndo_get_stats = tile_net_get_stats, .ndo_change_mtu = tile_net_change_mtu, .ndo_tx_timeout = tile_net_tx_timeout, - .ndo_set_mac_address = tile_net_set_mac_address + .ndo_set_mac_address = tile_net_set_mac_address, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = tile_net_netpoll, +#endif }; @@ -2409,7 +2434,7 @@ static void tile_net_cleanup(void) */ static int tile_net_init_module(void) { - pr_info("Tilera IPP Net Driver\n"); + pr_info("Tilera Network Driver\n"); tile_net_devs[0] = tile_net_dev_init("xgbe0"); tile_net_devs[1] = tile_net_dev_init("xgbe1"); diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index 39b8cf3dafcd..fcfa01f7ceb6 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -503,30 +503,32 @@ static int rhine_vlan_rx_add_vid(struct net_device *dev, unsigned short vid); static int rhine_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid); static void rhine_restart_tx(struct net_device *dev); -static void rhine_wait_bit(struct rhine_private *rp, u8 reg, u8 mask, bool high) +static void rhine_wait_bit(struct rhine_private *rp, u8 reg, u8 mask, bool low) { void __iomem *ioaddr = rp->base; int i; for (i = 0; i < 1024; i++) { - if (high ^ !!(ioread8(ioaddr + reg) & mask)) + bool has_mask_bits = !!(ioread8(ioaddr + reg) & mask); + + if (low ^ has_mask_bits) break; udelay(10); } if (i > 64) { netif_dbg(rp, hw, rp->dev, "%s bit wait (%02x/%02x) cycle " - "count: %04d\n", high ? "high" : "low", reg, mask, i); + "count: %04d\n", low ? "low" : "high", reg, mask, i); } } static void rhine_wait_bit_high(struct rhine_private *rp, u8 reg, u8 mask) { - rhine_wait_bit(rp, reg, mask, true); + rhine_wait_bit(rp, reg, mask, false); } static void rhine_wait_bit_low(struct rhine_private *rp, u8 reg, u8 mask) { - rhine_wait_bit(rp, reg, mask, false); + rhine_wait_bit(rp, reg, mask, true); } static u32 rhine_get_events(struct rhine_private *rp) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index cc83af083fd7..44b8d2bad8c3 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -2,9 +2,7 @@ * Definitions for Xilinx Axi Ethernet device driver. * * Copyright (c) 2009 Secret Lab Technologies, Ltd. - * Copyright (c) 2010 Xilinx, Inc. All rights reserved. - * Copyright (c) 2012 Daniel Borkmann, <daniel.borkmann@tik.ee.ethz.ch> - * Copyright (c) 2012 Ariane Keller, <ariane.keller@tik.ee.ethz.ch> + * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. */ #ifndef XILINX_AXIENET_H diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 2fcbeba6814b..9c365e192a31 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -4,9 +4,9 @@ * Copyright (c) 2008 Nissin Systems Co., Ltd., Yoshio Kashiwagi * Copyright (c) 2005-2008 DLA Systems, David H. Lynch Jr. <dhlii@dlasys.net> * Copyright (c) 2008-2009 Secret Lab Technologies Ltd. - * Copyright (c) 2010 Xilinx, Inc. All rights reserved. - * Copyright (c) 2012 Daniel Borkmann, <daniel.borkmann@tik.ee.ethz.ch> - * Copyright (c) 2012 Ariane Keller, <ariane.keller@tik.ee.ethz.ch> + * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (c) 2010 - 2011 PetaLogix + * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. * * This is a driver for the Xilinx Axi Ethernet which is used in the Virtex6 * and Spartan6. diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index d70b6e79f6c0..e90e1f46121e 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -2,9 +2,9 @@ * MDIO bus driver for the Xilinx Axi Ethernet device * * Copyright (c) 2009 Secret Lab Technologies, Ltd. - * Copyright (c) 2010 Xilinx, Inc. All rights reserved. - * Copyright (c) 2012 Daniel Borkmann, <daniel.borkmann@tik.ee.ethz.ch> - * Copyright (c) 2012 Ariane Keller, <ariane.keller@tik.ee.ethz.ch> + * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (c) 2010 - 2011 PetaLogix + * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. */ #include <linux/of_address.h> diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index dd294783b5c5..2d59138db7f3 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -44,6 +44,7 @@ struct net_device_context { /* point back to our device context */ struct hv_device *device_ctx; struct delayed_work dwork; + struct work_struct work; }; @@ -51,30 +52,22 @@ static int ring_size = 128; module_param(ring_size, int, S_IRUGO); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); -struct set_multicast_work { - struct work_struct work; - struct net_device *net; -}; - static void do_set_multicast(struct work_struct *w) { - struct set_multicast_work *swk = - container_of(w, struct set_multicast_work, work); - struct net_device *net = swk->net; - - struct net_device_context *ndevctx = netdev_priv(net); + struct net_device_context *ndevctx = + container_of(w, struct net_device_context, work); struct netvsc_device *nvdev; struct rndis_device *rdev; nvdev = hv_get_drvdata(ndevctx->device_ctx); - if (nvdev == NULL) - goto out; + if (nvdev == NULL || nvdev->ndev == NULL) + return; rdev = nvdev->extension; if (rdev == NULL) - goto out; + return; - if (net->flags & IFF_PROMISC) + if (nvdev->ndev->flags & IFF_PROMISC) rndis_filter_set_packet_filter(rdev, NDIS_PACKET_TYPE_PROMISCUOUS); else @@ -82,21 +75,13 @@ static void do_set_multicast(struct work_struct *w) NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_DIRECTED); - -out: - kfree(w); } static void netvsc_set_multicast_list(struct net_device *net) { - struct set_multicast_work *swk = - kmalloc(sizeof(struct set_multicast_work), GFP_ATOMIC); - if (swk == NULL) - return; + struct net_device_context *net_device_ctx = netdev_priv(net); - swk->net = net; - INIT_WORK(&swk->work, do_set_multicast); - schedule_work(&swk->work); + schedule_work(&net_device_ctx->work); } static int netvsc_open(struct net_device *net) @@ -125,6 +110,8 @@ static int netvsc_close(struct net_device *net) netif_tx_disable(net); + /* Make sure netvsc_set_multicast_list doesn't re-enable filter! */ + cancel_work_sync(&net_device_ctx->work); ret = rndis_filter_close(device_obj); if (ret != 0) netdev_err(net, "unable to close device (ret %d).\n", ret); @@ -335,6 +322,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) nvdev->start_remove = true; cancel_delayed_work_sync(&ndevctx->dwork); + cancel_work_sync(&ndevctx->work); netif_tx_disable(ndev); rndis_filter_device_remove(hdev); @@ -403,6 +391,7 @@ static int netvsc_probe(struct hv_device *dev, net_device_ctx->device_ctx = dev; hv_set_drvdata(dev, net); INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); + INIT_WORK(&net_device_ctx->work, do_set_multicast); net->netdev_ops = &device_ops; @@ -456,6 +445,7 @@ static int netvsc_remove(struct hv_device *dev) ndev_ctx = netdev_priv(net); cancel_delayed_work_sync(&ndev_ctx->dwork); + cancel_work_sync(&ndev_ctx->work); /* Stop outbound asap */ netif_tx_disable(net); diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index a0d1913a58d3..e25067552b20 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -147,7 +147,7 @@ static void sa1100_irda_dma_start(struct sa1100_buf *buf, struct dma_async_tx_descriptor *desc; struct dma_chan *chan = buf->chan; - desc = chan->device->device_prep_slave_sg(chan, &buf->sg, 1, dir, + desc = dmaengine_prep_slave_sg(chan, &buf->sg, 1, dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (desc) { desc->callback = cb; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index f975afdc315c..025367a94add 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -259,7 +259,7 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev) xmit_world: skb->ip_summed = ip_summed; - skb_set_dev(skb, vlan->lowerdev); + skb->dev = vlan->lowerdev; return dev_queue_xmit(skb); } diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 0427c6561c84..cb8fd5069dbe 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -1,5 +1,6 @@ #include <linux/etherdevice.h> #include <linux/if_macvlan.h> +#include <linux/if_vlan.h> #include <linux/interrupt.h> #include <linux/nsproxy.h> #include <linux/compat.h> @@ -759,6 +760,8 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, struct macvlan_dev *vlan; int ret; int vnet_hdr_len = 0; + int vlan_offset = 0; + int copied; if (q->flags & IFF_VNET_HDR) { struct virtio_net_hdr vnet_hdr; @@ -773,18 +776,48 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, if (memcpy_toiovecend(iv, (void *)&vnet_hdr, 0, sizeof(vnet_hdr))) return -EFAULT; } + copied = vnet_hdr_len; + + if (!vlan_tx_tag_present(skb)) + len = min_t(int, skb->len, len); + else { + int copy; + struct { + __be16 h_vlan_proto; + __be16 h_vlan_TCI; + } veth; + veth.h_vlan_proto = htons(ETH_P_8021Q); + veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); + + vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); + len = min_t(int, skb->len + VLAN_HLEN, len); + + copy = min_t(int, vlan_offset, len); + ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy); + len -= copy; + copied += copy; + if (ret || !len) + goto done; + + copy = min_t(int, sizeof(veth), len); + ret = memcpy_toiovecend(iv, (void *)&veth, copied, copy); + len -= copy; + copied += copy; + if (ret || !len) + goto done; + } - len = min_t(int, skb->len, len); - - ret = skb_copy_datagram_const_iovec(skb, 0, iv, vnet_hdr_len, len); + ret = skb_copy_datagram_const_iovec(skb, vlan_offset, iv, copied, len); + copied += len; +done: rcu_read_lock_bh(); vlan = rcu_dereference_bh(q->vlan); if (vlan) - macvlan_count_rx(vlan, len, ret == 0, 0); + macvlan_count_rx(vlan, copied - vnet_hdr_len, ret == 0, 0); rcu_read_unlock_bh(); - return ret ? ret : (len + vnet_hdr_len); + return ret ? ret : copied; } static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index 0856e1b7a849..5ac46f5226f3 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -40,6 +40,7 @@ MODULE_LICENSE("GPL"); #define IP1001_PHASE_SEL_MASK 3 /* IP1001 RX/TXPHASE_SEL */ #define IP1001_APS_ON 11 /* IP1001 APS Mode bit */ #define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */ +#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */ static int ip175c_config_init(struct phy_device *phydev) { @@ -162,7 +163,8 @@ static int ip101a_g_config_init(struct phy_device *phydev) /* Enable Auto Power Saving mode */ c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); c |= IP101A_G_APS_ON; - return c; + + return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); } static int ip175c_read_status(struct phy_device *phydev) @@ -184,6 +186,15 @@ static int ip175c_config_aneg(struct phy_device *phydev) return 0; } +static int ip101a_g_ack_interrupt(struct phy_device *phydev) +{ + int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); + if (err < 0) + return err; + + return 0; +} + static struct phy_driver ip175c_driver = { .phy_id = 0x02430d80, .name = "ICPlus IP175C", @@ -203,7 +214,6 @@ static struct phy_driver ip1001_driver = { .phy_id_mask = 0x0ffffff0, .features = PHY_GBIT_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause, - .flags = PHY_HAS_INTERRUPT, .config_init = &ip1001_config_init, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, @@ -219,6 +229,7 @@ static struct phy_driver ip101a_g_driver = { .features = PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause, .flags = PHY_HAS_INTERRUPT, + .ack_interrupt = ip101a_g_ack_interrupt, .config_init = &ip101a_g_config_init, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 159da2905fe9..21d7151fb0ab 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -968,7 +968,6 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) proto = npindex_to_proto[npi]; put_unaligned_be16(proto, pp); - netif_stop_queue(dev); skb_queue_tail(&ppp->file.xq, skb); ppp_xmit_process(ppp); return NETDEV_TX_OK; @@ -1063,6 +1062,8 @@ ppp_xmit_process(struct ppp *ppp) code that we can accept some more. */ if (!ppp->xmit_pending && !skb_peek(&ppp->file.xq)) netif_wake_queue(ppp->dev); + else + netif_stop_queue(ppp->dev); } ppp_xmit_unlock(ppp); } diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index a57f05726b57..91d25888a1b9 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -375,8 +375,8 @@ static void rionet_remove(struct rio_dev *rdev) struct net_device *ndev = rio_get_drvdata(rdev); struct rionet_peer *peer, *tmp; - free_pages((unsigned long)rionet_active, rdev->net->hport->sys_size ? - __fls(sizeof(void *)) + 4 : 0); + free_pages((unsigned long)rionet_active, get_order(sizeof(void *) * + RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size))); unregister_netdev(ndev); free_netdev(ndev); @@ -432,15 +432,16 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) int rc = 0; struct rionet_private *rnet; u16 device_id; + const size_t rionet_active_bytes = sizeof(void *) * + RIO_MAX_ROUTE_ENTRIES(mport->sys_size); rionet_active = (struct rio_dev **)__get_free_pages(GFP_KERNEL, - mport->sys_size ? __fls(sizeof(void *)) + 4 : 0); + get_order(rionet_active_bytes)); if (!rionet_active) { rc = -ENOMEM; goto out; } - memset((void *)rionet_active, 0, sizeof(void *) * - RIO_MAX_ROUTE_ENTRIES(mport->sys_size)); + memset((void *)rionet_active, 0, rionet_active_bytes); /* Set up private area */ rnet = netdev_priv(ndev); diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 5ee032cafade..42b5151aa78a 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -355,7 +355,7 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, u32 packet_len; u32 padbytes = 0xffff0000; - padlen = ((skb->len + 4) % 512) ? 0 : 4; + padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; if ((!skb_cloned(skb)) && ((headroom + tailroom) >= (4 + padlen))) { @@ -377,7 +377,7 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, cpu_to_le32s(&packet_len); skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); - if ((skb->len % 512) == 0) { + if (padlen) { cpu_to_le32s(&padbytes); memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); skb_put(skb, sizeof(padbytes)); diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 3886b30ed373..3e41b00c6806 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -165,13 +165,13 @@ static void rx_complete(struct urb *req) memcpy(skb_put(skb, 1), page_address(page), 1); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 1, req->actual_length, - req->actual_length); + PAGE_SIZE); page = NULL; } } else { skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 0, req->actual_length, - req->actual_length); + PAGE_SIZE); page = NULL; } if (req->actual_length < PAGE_SIZE) diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c index 439690be519f..685a4e22c768 100644 --- a/drivers/net/usb/cdc_eem.c +++ b/drivers/net/usb/cdc_eem.c @@ -93,6 +93,7 @@ static int eem_bind(struct usbnet *dev, struct usb_interface *intf) /* no jumbogram (16K) support for now */ dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN; + dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; return 0; } diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 90a30026a931..00880edba048 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -83,6 +83,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) struct cdc_state *info = (void *) &dev->data; int status; int rndis; + bool android_rndis_quirk = false; struct usb_driver *driver = driver_of(intf); struct usb_cdc_mdlm_desc *desc = NULL; struct usb_cdc_mdlm_detail_desc *detail = NULL; @@ -195,6 +196,11 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) info->control, info->u->bSlaveInterface0, info->data); + /* fall back to hard-wiring for RNDIS */ + if (rndis) { + android_rndis_quirk = true; + goto next_desc; + } goto bad_desc; } if (info->control != intf) { @@ -271,11 +277,15 @@ next_desc: /* Microsoft ActiveSync based and some regular RNDIS devices lack the * CDC descriptors, so we'll hard-wire the interfaces and not check * for descriptors. + * + * Some Android RNDIS devices have a CDC Union descriptor pointing + * to non-existing interfaces. Ignore that and attempt the same + * hard-wired 0 and 1 interfaces. */ - if (rndis && !info->u) { + if (rndis && (!info->u || android_rndis_quirk)) { info->control = usb_ifnum_to_if(dev->udev, 0); info->data = usb_ifnum_to_if(dev->udev, 1); - if (!info->control || !info->data) { + if (!info->control || !info->data || info->control != intf) { dev_dbg(&intf->dev, "rndis: master #0/%p slave #1/%p\n", info->control, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 552d24bf862e..d316503b35d4 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -365,6 +365,27 @@ static const struct driver_info qmi_wwan_force_int4 = { .data = BIT(4), /* interface whitelist bitmap */ }; +/* Sierra Wireless provide equally useless interface descriptors + * Devices in QMI mode can be switched between two different + * configurations: + * a) USB interface #8 is QMI/wwan + * b) USB interfaces #8, #19 and #20 are QMI/wwan + * + * Both configurations provide a number of other interfaces (serial++), + * some of which have the same endpoint configuration as we expect, so + * a whitelist or blacklist is necessary. + * + * FIXME: The below whitelist should include BIT(20). It does not + * because I cannot get it to work... + */ +static const struct driver_info qmi_wwan_sierra = { + .description = "Sierra Wireless wwan/QMI device", + .flags = FLAG_WWAN, + .bind = qmi_wwan_bind_gobi, + .unbind = qmi_wwan_unbind_shared, + .manage_power = qmi_wwan_manage_power, + .data = BIT(8) | BIT(19), /* interface whitelist bitmap */ +}; #define HUAWEI_VENDOR_ID 0x12D1 #define QMI_GOBI_DEVICE(vend, prod) \ @@ -445,6 +466,15 @@ static const struct usb_device_id products[] = { .bInterfaceProtocol = 0xff, .driver_info = (unsigned long)&qmi_wwan_force_int4, }, + { /* Sierra Wireless MC77xx in QMI mode */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1199, + .idProduct = 0x68a2, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .driver_info = (unsigned long)&qmi_wwan_sierra, + }, {QMI_GOBI_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ {QMI_GOBI_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ {QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */ diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 6dda2fe5b15b..d363b31053da 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -85,32 +85,6 @@ #define INT_CRERR_CNT 0x06 #define INT_COL_CNT 0x07 -/* Transmit status register errors */ -#define TSR_ECOL (1<<5) -#define TSR_LCOL (1<<4) -#define TSR_LOSS_CRS (1<<3) -#define TSR_JBR (1<<2) -#define TSR_ERRORS (TSR_ECOL | TSR_LCOL | TSR_LOSS_CRS | TSR_JBR) -/* Receive status register errors */ -#define RSR_CRC (1<<2) -#define RSR_FAE (1<<1) -#define RSR_ERRORS (RSR_CRC | RSR_FAE) - -/* Media status register definitions */ -#define MSR_DUPLEX (1<<4) -#define MSR_SPEED (1<<3) -#define MSR_LINK (1<<2) - -/* Interrupt pipe data */ -#define INT_TSR 0x00 -#define INT_RSR 0x01 -#define INT_MSR 0x02 -#define INT_WAKSR 0x03 -#define INT_TXOK_CNT 0x04 -#define INT_RXLOST_CNT 0x05 -#define INT_CRERR_CNT 0x06 -#define INT_COL_CNT 0x07 - #define RTL8150_MTU 1540 #define RTL8150_TX_TIMEOUT (HZ) diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 187d01ccb973..00103a8c5e04 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -98,7 +98,7 @@ static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index, if (unlikely(ret < 0)) netdev_warn(dev->net, - "Failed to read register index 0x%08x", index); + "Failed to read reg index 0x%08x: %d", index, ret); le32_to_cpus(buf); *data = *buf; @@ -128,7 +128,7 @@ static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, if (unlikely(ret < 0)) netdev_warn(dev->net, - "Failed to write register index 0x%08x", index); + "Failed to write reg index 0x%08x: %d", index, ret); kfree(buf); @@ -171,7 +171,7 @@ static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) idx &= dev->mii.reg_num_mask; addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) - | MII_ACCESS_READ; + | MII_ACCESS_READ | MII_ACCESS_BUSY; ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); check_warn_goto_done(ret, "Error writing MII_ACCESS"); @@ -210,7 +210,7 @@ static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, idx &= dev->mii.reg_num_mask; addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) - | MII_ACCESS_WRITE; + | MII_ACCESS_WRITE | MII_ACCESS_BUSY; ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); check_warn_goto_done(ret, "Error writing MII_ACCESS"); @@ -508,9 +508,10 @@ static int smsc75xx_link_reset(struct usbnet *dev) u16 lcladv, rmtadv; int ret; - /* clear interrupt status */ + /* read and write to clear phy interrupt status */ ret = smsc75xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); check_warn_return(ret, "Error reading PHY_INT_SRC"); + smsc75xx_mdio_write(dev->net, mii->phy_id, PHY_INT_SRC, 0xffff); ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); check_warn_return(ret, "Error writing INT_STS"); @@ -643,7 +644,7 @@ static int smsc75xx_set_mac_address(struct usbnet *dev) static int smsc75xx_phy_initialize(struct usbnet *dev) { - int bmcr, timeout = 0; + int bmcr, ret, timeout = 0; /* Initialize MII structure */ dev->mii.dev = dev->net; @@ -651,6 +652,7 @@ static int smsc75xx_phy_initialize(struct usbnet *dev) dev->mii.mdio_write = smsc75xx_mdio_write; dev->mii.phy_id_mask = 0x1f; dev->mii.reg_num_mask = 0x1f; + dev->mii.supports_gmii = 1; dev->mii.phy_id = SMSC75XX_INTERNAL_PHY_ID; /* reset phy and wait for reset to complete */ @@ -661,7 +663,7 @@ static int smsc75xx_phy_initialize(struct usbnet *dev) bmcr = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); check_warn_return(bmcr, "Error reading MII_BMCR"); timeout++; - } while ((bmcr & MII_BMCR) && (timeout < 100)); + } while ((bmcr & BMCR_RESET) && (timeout < 100)); if (timeout >= 100) { netdev_warn(dev->net, "timeout on PHY Reset"); @@ -671,10 +673,13 @@ static int smsc75xx_phy_initialize(struct usbnet *dev) smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000, + ADVERTISE_1000FULL); - /* read to clear */ - smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); - check_warn_return(bmcr, "Error reading PHY_INT_SRC"); + /* read and write to clear phy interrupt status */ + ret = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); + check_warn_return(ret, "Error reading PHY_INT_SRC"); + smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_SRC, 0xffff); smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, PHY_INT_MASK_DEFAULT); @@ -946,6 +951,14 @@ static int smsc75xx_reset(struct usbnet *dev) ret = smsc75xx_write_reg(dev, INT_EP_CTL, buf); check_warn_return(ret, "Failed to write INT_EP_CTL: %d", ret); + /* allow mac to detect speed and duplex from phy */ + ret = smsc75xx_read_reg(dev, MAC_CR, &buf); + check_warn_return(ret, "Failed to read MAC_CR: %d", ret); + + buf |= (MAC_CR_ADD | MAC_CR_ASD); + ret = smsc75xx_write_reg(dev, MAC_CR, buf); + check_warn_return(ret, "Failed to write MAC_CR: %d", ret); + ret = smsc75xx_read_reg(dev, MAC_TX, &buf); check_warn_return(ret, "Failed to read MAC_TX: %d", ret); @@ -1051,6 +1064,7 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->ethtool_ops = &smsc75xx_ethtool_ops; dev->net->flags |= IFF_MULTICAST; dev->net->hard_header_len += SMSC75XX_TX_OVERHEAD; + dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; return 0; } @@ -1211,7 +1225,7 @@ static const struct driver_info smsc75xx_info = { .rx_fixup = smsc75xx_rx_fixup, .tx_fixup = smsc75xx_tx_fixup, .status = smsc75xx_status, - .flags = FLAG_ETHER | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, }; static const struct usb_device_id products[] = { diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 5f19f84d3494..94ae66999f59 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1017,6 +1017,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->ethtool_ops = &smsc95xx_ethtool_ops; dev->net->flags |= IFF_MULTICAST; dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM; + dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; return 0; } @@ -1191,7 +1192,7 @@ static const struct driver_info smsc95xx_info = { .rx_fixup = smsc95xx_rx_fixup, .tx_fixup = smsc95xx_tx_fixup, .status = smsc95xx_status, - .flags = FLAG_ETHER | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, }; static const struct usb_device_id products[] = { diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index b7b3f5b0d406..2d927fb4adf4 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -210,6 +210,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf) } else { usb_fill_int_urb(dev->interrupt, dev->udev, pipe, buf, maxp, intr_complete, dev, period); + dev->interrupt->transfer_flags |= URB_FREE_BUFFER; dev_dbg(&intf->dev, "status ep%din, %d bytes period %d\n", usb_pipeendpoint(pipe), maxp, period); @@ -1443,7 +1444,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) status = register_netdev (net); if (status) - goto out3; + goto out4; netif_info(dev, probe, dev->net, "register '%s' at usb-%s-%s, %s, %pM\n", udev->dev.driver->name, @@ -1461,6 +1462,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) return 0; +out4: + usb_free_urb(dev->interrupt); out3: if (info->unbind) info->unbind (dev, udev); diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c index c3197ce0e2ad..34db195fb8b0 100644 --- a/drivers/net/usb/zaurus.c +++ b/drivers/net/usb/zaurus.c @@ -337,6 +337,11 @@ static const struct usb_device_id products [] = { .driver_info = ZAURUS_PXA_INFO, }, { + /* Motorola Rokr E6 */ + USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x6027, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &bogus_mdlm_info, +}, { /* Motorola MOTOMAGX phones */ USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x6425, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 019da012669f..af8acc85f4bb 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -625,16 +625,16 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) /* This can happen with OOM and indirect buffers. */ if (unlikely(capacity < 0)) { - if (net_ratelimit()) { - if (likely(capacity == -ENOMEM)) { + if (likely(capacity == -ENOMEM)) { + if (net_ratelimit()) dev_warn(&dev->dev, "TX queue failure: out of memory\n"); - } else { - dev->stats.tx_fifo_errors++; + } else { + dev->stats.tx_fifo_errors++; + if (net_ratelimit()) dev_warn(&dev->dev, "Unexpected TX queue failure: %d\n", capacity); - } } dev->stats.tx_dropped++; kfree_skb(skb); diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index ebb9f24eefb5..1a623183cbe5 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -2483,6 +2483,7 @@ fst_add_one(struct pci_dev *pdev, const struct pci_device_id *ent) pr_err("Control memory remap failed\n"); pci_release_regions(pdev); pci_disable_device(pdev); + iounmap(card->mem); kfree(card); return -ENODEV; } diff --git a/drivers/net/wimax/i2400m/debugfs.c b/drivers/net/wimax/i2400m/debugfs.c index 129ba36bd04d..4b66ab1d0e5c 100644 --- a/drivers/net/wimax/i2400m/debugfs.c +++ b/drivers/net/wimax/i2400m/debugfs.c @@ -53,17 +53,6 @@ struct dentry *debugfs_create_netdev_queue_stopped( &fops_netdev_queue_stopped); } - -/* - * inode->i_private has the @data argument to debugfs_create_file() - */ -static -int i2400m_stats_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - return 0; -} - /* * We don't allow partial reads of this file, as then the reader would * get weirdly confused data as it is updated. @@ -117,7 +106,7 @@ ssize_t i2400m_rx_stats_write(struct file *filp, const char __user *buffer, static const struct file_operations i2400m_rx_stats_fops = { .owner = THIS_MODULE, - .open = i2400m_stats_open, + .open = simple_open, .read = i2400m_rx_stats_read, .write = i2400m_rx_stats_write, .llseek = default_llseek, @@ -170,7 +159,7 @@ ssize_t i2400m_tx_stats_write(struct file *filp, const char __user *buffer, static const struct file_operations i2400m_tx_stats_fops = { .owner = THIS_MODULE, - .open = i2400m_stats_open, + .open = simple_open, .read = i2400m_tx_stats_read, .write = i2400m_tx_stats_write, .llseek = default_llseek, diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c index 63e4b709efa9..1d76ae855f07 100644 --- a/drivers/net/wimax/i2400m/netdev.c +++ b/drivers/net/wimax/i2400m/netdev.c @@ -597,7 +597,8 @@ static void i2400m_get_drvinfo(struct net_device *net_dev, struct i2400m *i2400m = net_dev_to_i2400m(net_dev); strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver) - 1); - strncpy(info->fw_version, i2400m->fw_name, sizeof(info->fw_version) - 1); + strncpy(info->fw_version, + i2400m->fw_name ? : "", sizeof(info->fw_version) - 1); if (net_dev->dev.parent) strncpy(info->bus_info, dev_name(net_dev->dev.parent), sizeof(info->bus_info) - 1); diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 2c1b8b687646..29b1e033a10b 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c @@ -339,6 +339,23 @@ int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) return result; } +static void i2400mu_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *info) +{ + struct i2400m *i2400m = net_dev_to_i2400m(net_dev); + struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); + struct usb_device *udev = i2400mu->usb_dev; + + strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver) - 1); + strncpy(info->fw_version, + i2400m->fw_name ? : "", sizeof(info->fw_version) - 1); + usb_make_path(udev, info->bus_info, sizeof(info->bus_info)); +} + +static const struct ethtool_ops i2400mu_ethtool_ops = { + .get_drvinfo = i2400mu_get_drvinfo, + .get_link = ethtool_op_get_link, +}; static void i2400mu_netdev_setup(struct net_device *net_dev) @@ -347,6 +364,7 @@ void i2400mu_netdev_setup(struct net_device *net_dev) struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); i2400mu_init(i2400mu); i2400m_netdev_setup(net_dev); + net_dev->ethtool_ops = &i2400mu_ethtool_ops; } diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index 8faa129da5a0..aec33cc207fd 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -19,6 +19,7 @@ #include <linux/nl80211.h> #include <linux/platform_device.h> #include <linux/etherdevice.h> +#include <linux/export.h> #include <ar231x_platform.h> #include "ath5k.h" #include "debug.h" @@ -119,7 +120,7 @@ static int ath_ahb_probe(struct platform_device *pdev) if (res == NULL) { dev_err(&pdev->dev, "no IRQ resource found\n"); ret = -ENXIO; - goto err_out; + goto err_iounmap; } irq = res->start; @@ -128,7 +129,7 @@ static int ath_ahb_probe(struct platform_device *pdev) if (hw == NULL) { dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); ret = -ENOMEM; - goto err_out; + goto err_iounmap; } ah = hw->priv; @@ -185,6 +186,8 @@ static int ath_ahb_probe(struct platform_device *pdev) err_free_hw: ieee80211_free_hw(hw); platform_set_drvdata(pdev, NULL); + err_iounmap: + iounmap(mem); err_out: return ret; } @@ -217,6 +220,7 @@ static int ath_ahb_remove(struct platform_device *pdev) } ath5k_deinit_ah(ah); + iounmap(ah->iobase); platform_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 8c5ce8b0c734..e5e8f45d86ac 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -71,13 +71,6 @@ static unsigned int ath5k_debug; module_param_named(debug, ath5k_debug, uint, 0); -static int ath5k_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - - /* debugfs: registers */ struct reg { @@ -265,7 +258,7 @@ static ssize_t write_file_beacon(struct file *file, static const struct file_operations fops_beacon = { .read = read_file_beacon, .write = write_file_beacon, - .open = ath5k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -285,7 +278,7 @@ static ssize_t write_file_reset(struct file *file, static const struct file_operations fops_reset = { .write = write_file_reset, - .open = ath5k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = noop_llseek, }; @@ -365,7 +358,7 @@ static ssize_t write_file_debug(struct file *file, static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, - .open = ath5k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -477,7 +470,7 @@ static ssize_t write_file_antenna(struct file *file, static const struct file_operations fops_antenna = { .read = read_file_antenna, .write = write_file_antenna, - .open = ath5k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -532,7 +525,7 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, static const struct file_operations fops_misc = { .read = read_file_misc, - .open = ath5k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, }; @@ -647,7 +640,7 @@ static ssize_t write_file_frameerrors(struct file *file, static const struct file_operations fops_frameerrors = { .read = read_file_frameerrors, .write = write_file_frameerrors, - .open = ath5k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -810,7 +803,7 @@ static ssize_t write_file_ani(struct file *file, static const struct file_operations fops_ani = { .read = read_file_ani, .write = write_file_ani, - .open = ath5k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -881,7 +874,7 @@ static ssize_t write_file_queue(struct file *file, static const struct file_operations fops_queue = { .read = read_file_queue, .write = write_file_queue, - .open = ath5k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 552adb3f80d0..d01403a263ff 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -217,12 +217,6 @@ void dump_cred_dist_stats(struct htc_target *target) target->credit_info->cur_free_credits); } -static int ath6kl_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) { switch (war) { @@ -263,7 +257,7 @@ static ssize_t read_file_war_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_war_stats = { .read = read_file_war_stats, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -488,7 +482,7 @@ static ssize_t ath6kl_fwlog_mask_write(struct file *file, } static const struct file_operations fops_fwlog_mask = { - .open = ath6kl_debugfs_open, + .open = simple_open, .read = ath6kl_fwlog_mask_read, .write = ath6kl_fwlog_mask_write, .owner = THIS_MODULE, @@ -634,7 +628,7 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_tgt_stats = { .read = read_file_tgt_stats, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -699,7 +693,7 @@ static ssize_t read_file_credit_dist_stats(struct file *file, static const struct file_operations fops_credit_dist_stats = { .read = read_file_credit_dist_stats, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -802,7 +796,7 @@ static ssize_t ath6kl_endpoint_stats_write(struct file *file, } static const struct file_operations fops_endpoint_stats = { - .open = ath6kl_debugfs_open, + .open = simple_open, .read = ath6kl_endpoint_stats_read, .write = ath6kl_endpoint_stats_write, .owner = THIS_MODULE, @@ -875,7 +869,7 @@ static ssize_t ath6kl_regread_write(struct file *file, static const struct file_operations fops_diag_reg_read = { .read = ath6kl_regread_read, .write = ath6kl_regread_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -999,7 +993,7 @@ static ssize_t ath6kl_lrssi_roam_read(struct file *file, static const struct file_operations fops_lrssi_roam_threshold = { .read = ath6kl_lrssi_roam_read, .write = ath6kl_lrssi_roam_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1061,7 +1055,7 @@ static ssize_t ath6kl_regwrite_write(struct file *file, static const struct file_operations fops_diag_reg_write = { .read = ath6kl_regwrite_read, .write = ath6kl_regwrite_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1166,7 +1160,7 @@ static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf, static const struct file_operations fops_roam_table = { .read = ath6kl_roam_table_read, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1204,7 +1198,7 @@ static ssize_t ath6kl_force_roam_write(struct file *file, static const struct file_operations fops_force_roam = { .write = ath6kl_force_roam_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1244,7 +1238,7 @@ static ssize_t ath6kl_roam_mode_write(struct file *file, static const struct file_operations fops_roam_mode = { .write = ath6kl_roam_mode_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1286,7 +1280,7 @@ static ssize_t ath6kl_keepalive_write(struct file *file, } static const struct file_operations fops_keepalive = { - .open = ath6kl_debugfs_open, + .open = simple_open, .read = ath6kl_keepalive_read, .write = ath6kl_keepalive_write, .owner = THIS_MODULE, @@ -1331,7 +1325,7 @@ static ssize_t ath6kl_disconnect_timeout_write(struct file *file, } static const struct file_operations fops_disconnect_timeout = { - .open = ath6kl_debugfs_open, + .open = simple_open, .read = ath6kl_disconnect_timeout_read, .write = ath6kl_disconnect_timeout_write, .owner = THIS_MODULE, @@ -1512,7 +1506,7 @@ static ssize_t ath6kl_create_qos_write(struct file *file, static const struct file_operations fops_create_qos = { .write = ath6kl_create_qos_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1560,7 +1554,7 @@ static ssize_t ath6kl_delete_qos_write(struct file *file, static const struct file_operations fops_delete_qos = { .write = ath6kl_delete_qos_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1593,7 +1587,7 @@ static ssize_t ath6kl_bgscan_int_write(struct file *file, static const struct file_operations fops_bgscan_int = { .write = ath6kl_bgscan_int_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1651,7 +1645,7 @@ static ssize_t ath6kl_listen_int_read(struct file *file, static const struct file_operations fops_listen_int = { .read = ath6kl_listen_int_read, .write = ath6kl_listen_int_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1711,7 +1705,7 @@ static ssize_t ath6kl_power_params_write(struct file *file, static const struct file_operations fops_power_params = { .write = ath6kl_power_params_write, - .open = ath6kl_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index d7d8e9199140..aba088005b22 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -869,7 +869,7 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, ar5008_hw_set_channel_regs(ah, chan); ar5008_hw_init_chain_masks(ah); ath9k_olc_init(ah); - ath9k_hw_apply_txpower(ah, chan); + ath9k_hw_apply_txpower(ah, chan, false); /* Write analog registers */ if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index 59647a3ceb7f..3d400e8d6535 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -54,7 +54,7 @@ void ar9003_paprd_enable(struct ath_hw *ah, bool val) if (val) { ah->paprd_table_write_done = true; - ath9k_hw_apply_txpower(ah, chan); + ath9k_hw_apply_txpower(ah, chan, false); } REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B0, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index bc992b237ae5..600aca9fe6b1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -373,7 +373,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, else spur_subchannel_sd = 0; - spur_freq_sd = (freq_offset << 9) / 11; + spur_freq_sd = ((freq_offset + 10) << 9) / 11; } else { if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, @@ -382,7 +382,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, else spur_subchannel_sd = 1; - spur_freq_sd = (freq_offset << 9) / 11; + spur_freq_sd = ((freq_offset - 10) << 9) / 11; } @@ -694,7 +694,7 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, ar9003_hw_override_ini(ah); ar9003_hw_set_channel_regs(ah, chan); ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); - ath9k_hw_apply_txpower(ah, chan); + ath9k_hw_apply_txpower(ah, chan, false); if (AR_SREV_9462(ah)) { if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0, diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 2f4b48e6fb03..e5cceb077574 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -20,7 +20,6 @@ /* Common calibration code */ -#define ATH9K_NF_TOO_HIGH -60 static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) { @@ -346,10 +345,10 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) "NF calibrated [%s] [chain %d] is %d\n", (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); - if (nf[i] > ATH9K_NF_TOO_HIGH) { + if (nf[i] > limit->max) { ath_dbg(common, CALIBRATE, "NF[%d] (%d) > MAX (%d), correcting to MAX\n", - i, nf[i], ATH9K_NF_TOO_HIGH); + i, nf[i], limit->max); nf[i] = limit->max; } else if (nf[i] < limit->min) { ath_dbg(common, CALIBRATE, diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 35d1c8e91d1c..ff47b32ecaf4 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -26,11 +26,6 @@ #define REG_READ_D(_ah, _reg) \ ath9k_hw_common(_ah)->ops->read((_ah), (_reg)) -static int ath9k_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} static ssize_t ath9k_debugfs_read_buf(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -83,7 +78,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf, static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -129,7 +124,7 @@ static ssize_t write_file_tx_chainmask(struct file *file, const char __user *use static const struct file_operations fops_tx_chainmask = { .read = read_file_tx_chainmask, .write = write_file_tx_chainmask, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -172,7 +167,7 @@ static ssize_t write_file_rx_chainmask(struct file *file, const char __user *use static const struct file_operations fops_rx_chainmask = { .read = read_file_rx_chainmask, .write = write_file_rx_chainmask, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -223,7 +218,7 @@ static ssize_t write_file_disable_ani(struct file *file, static const struct file_operations fops_disable_ani = { .read = read_file_disable_ani, .write = write_file_disable_ani, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -324,7 +319,7 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, static const struct file_operations fops_dma = { .read = read_file_dma, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -446,7 +441,7 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, static const struct file_operations fops_interrupt = { .read = read_file_interrupt, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -852,28 +847,28 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, static const struct file_operations fops_xmit = { .read = read_file_xmit, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_stations = { .read = read_file_stations, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_misc = { .read = read_file_misc, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_reset = { .read = read_file_reset, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1016,7 +1011,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) static const struct file_operations fops_recv = { .read = read_file_recv, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1055,7 +1050,7 @@ static ssize_t write_file_regidx(struct file *file, const char __user *user_buf, static const struct file_operations fops_regidx = { .read = read_file_regidx, .write = write_file_regidx, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1102,7 +1097,7 @@ static ssize_t write_file_regval(struct file *file, const char __user *user_buf, static const struct file_operations fops_regval = { .read = read_file_regval, .write = write_file_regval, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1191,7 +1186,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf, static const struct file_operations fops_dump_nfcal = { .read = read_file_dump_nfcal, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1219,7 +1214,7 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, static const struct file_operations fops_base_eeprom = { .read = read_file_base_eeprom, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1247,7 +1242,7 @@ static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, static const struct file_operations fops_modal_eeprom = { .read = read_file_modal_eeprom, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index 106d031d834a..4364c103ed33 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -60,16 +60,9 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, return retval; } -static int ath9k_dfs_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - - return 0; -} - static const struct file_operations fops_dfs_stats = { .read = read_file_dfs, - .open = ath9k_dfs_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index f272236d8053..b34e8b2990b1 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -824,6 +824,8 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, regulatory->max_power_level = ratesArray[i]; } + ath9k_hw_update_regulatory_maxpower(ah); + if (test) return; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index d3ff33c71aa5..3035deb7a0cd 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -16,12 +16,6 @@ #include "htc.h" -static int ath9k_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -75,7 +69,7 @@ static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_tgt_int_stats = { .read = read_file_tgt_int_stats, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -145,7 +139,7 @@ static ssize_t read_file_tgt_tx_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_tgt_tx_stats = { .read = read_file_tgt_tx_stats, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -191,7 +185,7 @@ static ssize_t read_file_tgt_rx_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_tgt_rx_stats = { .read = read_file_tgt_rx_stats, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -243,7 +237,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, static const struct file_operations fops_xmit = { .read = read_file_xmit, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -364,7 +358,7 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, static const struct file_operations fops_recv = { .read = read_file_recv, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -399,7 +393,7 @@ static ssize_t read_file_slot(struct file *file, char __user *user_buf, static const struct file_operations fops_slot = { .read = read_file_slot, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -446,7 +440,7 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf, static const struct file_operations fops_queue = { .read = read_file_queue, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -487,7 +481,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf, static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -636,7 +630,7 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, static const struct file_operations fops_base_eeprom = { .read = read_file_base_eeprom, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -917,7 +911,7 @@ static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, static const struct file_operations fops_modal_eeprom = { .read = read_file_modal_eeprom, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 6c69e4e8b1cb..fa84e37bf091 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1454,7 +1454,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, return false; } ath9k_hw_set_clockrate(ah); - ath9k_hw_apply_txpower(ah, chan); + ath9k_hw_apply_txpower(ah, chan, false); ath9k_hw_rfbus_done(ah); if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) @@ -2652,7 +2652,8 @@ static int get_antenna_gain(struct ath_hw *ah, struct ath9k_channel *chan) return ah->eep_ops->get_eeprom(ah, gain_param); } -void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan) +void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan, + bool test) { struct ath_regulatory *reg = ath9k_hw_regulatory(ah); struct ieee80211_channel *channel; @@ -2673,7 +2674,7 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan) ah->eep_ops->set_txpower(ah, chan, ath9k_regd_get_ctl(reg, chan), - ant_reduction, new_pwr, false); + ant_reduction, new_pwr, test); } void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test) @@ -2686,7 +2687,7 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test) if (test) channel->max_power = MAX_RATE_POWER / 2; - ath9k_hw_apply_txpower(ah, chan); + ath9k_hw_apply_txpower(ah, chan, test); if (test) channel->max_power = DIV_ROUND_UP(reg->max_power_level, 2); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index aa1680a0c7fd..e88f182ff45c 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -985,7 +985,8 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len); /* PHY */ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled, u32 *coef_mantissa, u32 *coef_exponent); -void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan); +void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan, + bool test); /* * Code Specific to AR5008, AR9001 or AR9002, diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 60159f4ee532..cb006458fc4b 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -680,7 +680,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->queues = 4; hw->max_rates = 4; hw->channel_change_time = 5000; - hw->max_listen_interval = 10; + hw->max_listen_interval = 1; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 38794850f005..798ea57252b4 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -118,15 +118,13 @@ void ath9k_ps_restore(struct ath_softc *sc) if (--sc->ps_usecount != 0) goto unlock; - if (sc->ps_flags & PS_WAIT_FOR_TX_ACK) - goto unlock; - - if (sc->ps_idle) + if (sc->ps_idle && (sc->ps_flags & PS_WAIT_FOR_TX_ACK)) mode = ATH9K_PM_FULL_SLEEP; else if (sc->ps_enabled && !(sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | - PS_WAIT_FOR_PSPOLL_DATA))) + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK))) mode = ATH9K_PM_NETWORK_SLEEP; else goto unlock; @@ -640,7 +638,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, an->sta = sta; an->vif = vif; - if (sta->ht_cap.ht_supported) { + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { ath_tx_node_init(sc, an); an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor); @@ -659,7 +657,7 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) an->sta = NULL; #endif - if (sta->ht_cap.ht_supported) + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) ath_tx_node_cleanup(sc, an); } @@ -1550,6 +1548,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &hw->conf; + bool reset_channel = false; ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); @@ -1558,6 +1557,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); if (sc->ps_idle) ath_cancel_work(sc); + else + /* + * The chip needs a reset to properly wake up from + * full sleep + */ + reset_channel = ah->chip_fullsleep; } /* @@ -1586,7 +1591,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; int old_pos = -1; diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 4f848493fece..08bb45532701 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1480,12 +1480,6 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, #ifdef CONFIG_ATH9K_DEBUGFS -static int ath9k_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1553,7 +1547,7 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, static const struct file_operations fops_rcstat = { .read = read_file_rcstat, - .open = ath9k_debugfs_open, + .open = simple_open, .owner = THIS_MODULE }; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index f4ae3ba994a8..1c4583c7ff7c 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1913,13 +1913,13 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) if (sc->rx.frag) { int space = skb->len - skb_tailroom(hdr_skb); - sc->rx.frag = NULL; - if (pskb_expand_head(hdr_skb, 0, space, GFP_ATOMIC) < 0) { dev_kfree_skb(skb); goto requeue_drop_frag; } + sc->rx.frag = NULL; + skb_copy_from_linear_data(skb, skb_put(hdr_skb, skb->len), skb->len); dev_kfree_skb_any(skb); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 834e6bc45e8b..23eaa1b26ebe 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1820,6 +1820,7 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_frame_info *fi = get_frame_info(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ath_buf *bf; + int fragno; u16 seqno; bf = ath_tx_get_buffer(sc); @@ -1831,9 +1832,16 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, ATH_TXBUF_RESET(bf); if (tid) { + fragno = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; seqno = tid->seq_next; hdr->seq_ctrl = cpu_to_le16(tid->seq_next << IEEE80211_SEQ_SEQ_SHIFT); - INCR(tid->seq_next, IEEE80211_SEQ_MAX); + + if (fragno) + hdr->seq_ctrl |= cpu_to_le16(fragno); + + if (!ieee80211_has_morefrags(hdr->frame_control)) + INCR(tid->seq_next, IEEE80211_SEQ_MAX); + bf->bf_state.seqno = seqno; } diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c index 3c164226687f..93fe6003a493 100644 --- a/drivers/net/wireless/ath/carl9170/debug.c +++ b/drivers/net/wireless/ath/carl9170/debug.c @@ -48,11 +48,6 @@ #define ADD(buf, off, max, fmt, args...) \ off += snprintf(&buf[off], max - off, fmt, ##args); -static int carl9170_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} struct carl9170_debugfs_fops { unsigned int read_bufsize; @@ -178,7 +173,7 @@ static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\ .attr = _attr, \ .req_dev_state = _dstate, \ .fops = { \ - .open = carl9170_debugfs_open, \ + .open = simple_open, \ .read = carl9170_debugfs_read, \ .write = carl9170_debugfs_write, \ .owner = THIS_MODULE \ diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c index e751fdee89b2..e807bd930647 100644 --- a/drivers/net/wireless/b43/debugfs.c +++ b/drivers/net/wireless/b43/debugfs.c @@ -500,12 +500,6 @@ out: #undef fappend -static int b43_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -624,7 +618,7 @@ out_unlock: .read = _read, \ .write = _write, \ .fops = { \ - .open = b43_debugfs_open, \ + .open = simple_open, \ .read = b43_debugfs_read, \ .write = b43_debugfs_write, \ .llseek = generic_file_llseek, \ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index c79e6638c88d..e4d6dc2e37d1 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4827,8 +4827,14 @@ static int b43_op_start(struct ieee80211_hw *hw) out_mutex_unlock: mutex_unlock(&wl->mutex); - /* reload configuration */ - b43_op_config(hw, ~0); + /* + * Configuration may have been overwritten during initialization. + * Reload the configuration, but only if initialization was + * successful. Reloading the configuration after a failed init + * may hang the system. + */ + if (!err) + b43_op_config(hw, ~0); return err; } diff --git a/drivers/net/wireless/b43legacy/debugfs.c b/drivers/net/wireless/b43legacy/debugfs.c index 5e28ad0d6d17..1965edb765a2 100644 --- a/drivers/net/wireless/b43legacy/debugfs.c +++ b/drivers/net/wireless/b43legacy/debugfs.c @@ -197,12 +197,6 @@ static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size #undef fappend -static int b43legacy_debugfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -331,7 +325,7 @@ out_unlock: .read = _read, \ .write = _write, \ .fops = { \ - .open = b43legacy_debugfs_open, \ + .open = simple_open, \ .read = b43legacy_debugfs_read, \ .write = b43legacy_debugfs_write, \ .llseek = generic_file_llseek, \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index 4688904908ec..758c115b556e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -108,9 +108,15 @@ static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, sdio_release_host(sdfunc); } } else if (regaddr == SDIO_CCCR_ABORT) { + sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func), + GFP_KERNEL); + if (!sdfunc) + return -ENOMEM; + sdfunc->num = 0; sdio_claim_host(sdfunc); sdio_writeb(sdfunc, *byte, regaddr, &err_ret); sdio_release_host(sdfunc); + kfree(sdfunc); } else if (regaddr < 0xF0) { brcmf_dbg(ERROR, "F0 Wr:0x%02x: write disallowed\n", regaddr); err_ret = -EPERM; @@ -486,7 +492,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, kfree(bus_if); return -ENOMEM; } - sdiodev->func[0] = func->card->sdio_func[0]; + sdiodev->func[0] = func; sdiodev->func[1] = func; sdiodev->bus_if = bus_if; bus_if->bus_priv.sdio = sdiodev; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 2bf5dda29291..e2b34e1563f4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -574,6 +574,8 @@ struct brcmf_sdio { struct task_struct *dpc_tsk; struct completion dpc_wait; + struct list_head dpc_tsklst; + spinlock_t dpc_tl_lock; struct semaphore sdsem; @@ -2594,29 +2596,59 @@ clkwait: return resched; } +static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus) +{ + struct list_head *new_hd; + unsigned long flags; + + if (in_interrupt()) + new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC); + else + new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (new_hd == NULL) + return; + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_add_tail(new_hd, &bus->dpc_tsklst); + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); +} + static int brcmf_sdbrcm_dpc_thread(void *data) { struct brcmf_sdio *bus = (struct brcmf_sdio *) data; + struct list_head *cur_hd, *tmp_hd; + unsigned long flags; allow_signal(SIGTERM); /* Run until signal received */ while (1) { if (kthread_should_stop()) break; - if (!wait_for_completion_interruptible(&bus->dpc_wait)) { - /* Call bus dpc unless it indicated down - (then clean stop) */ - if (bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN) { - if (brcmf_sdbrcm_dpc(bus)) - complete(&bus->dpc_wait); - } else { + + if (list_empty(&bus->dpc_tsklst)) + if (wait_for_completion_interruptible(&bus->dpc_wait)) + break; + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) { + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); + + if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { /* after stopping the bus, exit thread */ brcmf_sdbrcm_bus_stop(bus->sdiodev->dev); bus->dpc_tsk = NULL; + spin_lock_irqsave(&bus->dpc_tl_lock, flags); break; } - } else - break; + + if (brcmf_sdbrcm_dpc(bus)) + brcmf_sdbrcm_adddpctsk(bus); + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_del(cur_hd); + kfree(cur_hd); + } + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } return 0; } @@ -2669,8 +2701,10 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) /* Schedule DPC if needed to send queued packet(s) */ if (!bus->dpc_sched) { bus->dpc_sched = true; - if (bus->dpc_tsk) + if (bus->dpc_tsk) { + brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); + } } return ret; @@ -3514,8 +3548,10 @@ void brcmf_sdbrcm_isr(void *arg) brcmf_dbg(ERROR, "isr w/o interrupt configured!\n"); bus->dpc_sched = true; - if (bus->dpc_tsk) + if (bus->dpc_tsk) { + brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); + } } static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) @@ -3559,8 +3595,10 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->ipend = true; bus->dpc_sched = true; - if (bus->dpc_tsk) + if (bus->dpc_tsk) { + brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); + } } } @@ -3897,6 +3935,8 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) } /* Initialize DPC thread */ init_completion(&bus->dpc_wait); + INIT_LIST_HEAD(&bus->dpc_tsklst); + spin_lock_init(&bus->dpc_tl_lock); bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread, bus, "brcmf_dpc"); if (IS_ERR(bus->dpc_tsk)) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 231ddf4a674f..b4d92792c502 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -847,8 +847,7 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) */ if (!(txs->status & TX_STATUS_AMPDU) && (txs->status & TX_STATUS_INTERMEDIATE)) { - wiphy_err(wlc->wiphy, "%s: INTERMEDIATE but not AMPDU\n", - __func__); + BCMMSG(wlc->wiphy, "INTERMEDIATE but not AMPDU\n"); return false; } @@ -7614,6 +7613,7 @@ brcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh, { int len_mpdu; struct ieee80211_rx_status rx_status; + struct ieee80211_hdr *hdr; memset(&rx_status, 0, sizeof(rx_status)); prep_mac80211_status(wlc, rxh, p, &rx_status); @@ -7623,6 +7623,13 @@ brcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh, skb_pull(p, D11_PHY_HDR_LEN); __skb_trim(p, len_mpdu); + /* unmute transmit */ + if (wlc->hw->suspended_fifos) { + hdr = (struct ieee80211_hdr *)p->data; + if (ieee80211_is_beacon(hdr->frame_control)) + brcms_b_mute(wlc->hw, false); + } + memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p); } diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 4fcdac63a300..1779db3aa2b0 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -2191,6 +2191,7 @@ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) { int rc = 0; unsigned long flags; + unsigned long now, end; spin_lock_irqsave(&priv->lock, flags); if (priv->status & STATUS_HCMD_ACTIVE) { @@ -2232,10 +2233,20 @@ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) } spin_unlock_irqrestore(&priv->lock, flags); + now = jiffies; + end = now + HOST_COMPLETE_TIMEOUT; +again: rc = wait_event_interruptible_timeout(priv->wait_command_queue, !(priv-> status & STATUS_HCMD_ACTIVE), - HOST_COMPLETE_TIMEOUT); + end - now); + if (rc < 0) { + now = jiffies; + if (time_before(now, end)) + goto again; + rc = 0; + } + if (rc == 0) { spin_lock_irqsave(&priv->lock, flags); if (priv->status & STATUS_HCMD_ACTIVE) { @@ -11507,9 +11518,9 @@ static int ipw_wdev_init(struct net_device *dev) rc = -ENOMEM; goto out; } - /* translate geo->bg to a_band.channels */ + /* translate geo->a to a_band.channels */ for (i = 0; i < geo->a_channels; i++) { - a_band->channels[i].band = IEEE80211_BAND_2GHZ; + a_band->channels[i].band = IEEE80211_BAND_5GHZ; a_band->channels[i].center_freq = geo->a[i].freq; a_band->channels[i].hw_value = geo->a[i].channel; a_band->channels[i].max_power = geo->a[i].max_power; diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index 0c1209390169..faec40467208 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -2673,8 +2673,6 @@ il3945_bg_restart(struct work_struct *data) if (test_and_clear_bit(S_FW_ERROR, &il->status)) { mutex_lock(&il->mutex); - /* FIXME: vif can be dereferenced */ - il->vif = NULL; il->is_open = 0; mutex_unlock(&il->mutex); il3945_down(il); diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index 70bee1a4d876..4b10157d8686 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -821,12 +821,6 @@ out: } #ifdef CONFIG_MAC80211_DEBUGFS -static int -il3945_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} static ssize_t il3945_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, @@ -862,7 +856,7 @@ il3945_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, static const struct file_operations rs_sta_dbgfs_stats_table_ops = { .read = il3945_sta_dbgfs_stats_table_read, - .open = il3945_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 17f1c6853182..c46275a92565 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5652,8 +5652,6 @@ il4965_bg_restart(struct work_struct *data) if (test_and_clear_bit(S_FW_ERROR, &il->status)) { mutex_lock(&il->mutex); - /* FIXME: do we dereference vif without mutex locked ? */ - il->vif = NULL; il->is_open = 0; __il4965_down(il); diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c index d7e2856e41d3..11ab1247fae1 100644 --- a/drivers/net/wireless/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/iwlegacy/4965-rs.c @@ -2518,12 +2518,6 @@ il4965_rs_free_sta(void *il_r, struct ieee80211_sta *sta, void *il_sta) } #ifdef CONFIG_MAC80211_DEBUGFS -static int -il4965_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) @@ -2695,7 +2689,7 @@ il4965_rs_sta_dbgfs_scale_table_read(struct file *file, char __user *user_buf, static const struct file_operations rs_sta_dbgfs_scale_table_ops = { .write = il4965_rs_sta_dbgfs_scale_table_write, .read = il4965_rs_sta_dbgfs_scale_table_read, - .open = il4965_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -2740,7 +2734,7 @@ il4965_rs_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, static const struct file_operations rs_sta_dbgfs_stats_table_ops = { .read = il4965_rs_sta_dbgfs_stats_table_read, - .open = il4965_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -2768,7 +2762,7 @@ il4965_rs_sta_dbgfs_rate_scale_data_read(struct file *file, static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { .read = il4965_rs_sta_dbgfs_rate_scale_data_read, - .open = il4965_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index e5ac04739bcc..eaf249452e51 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -4508,6 +4508,7 @@ il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct il_priv *il = hw->priv; int err; + bool reset; mutex_lock(&il->mutex); D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); @@ -4518,7 +4519,12 @@ il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) goto out; } - if (il->vif) { + /* + * We do not support multiple virtual interfaces, but on hardware reset + * we have to add the same interface again. + */ + reset = (il->vif == vif); + if (il->vif && !reset) { err = -EOPNOTSUPP; goto out; } @@ -4528,8 +4534,11 @@ il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) err = il_set_mode(il); if (err) { - il->vif = NULL; - il->iw_mode = NL80211_IFTYPE_STATION; + IL_WARN("Fail to set mode %d\n", vif->type); + if (!reset) { + il->vif = NULL; + il->iw_mode = NL80211_IFTYPE_STATION; + } } out: @@ -5279,9 +5288,9 @@ il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, D_MAC80211("BSSID %pM\n", bss_conf->bssid); /* - * If there is currently a HW scan going on in the - * background then we need to cancel it else the RXON - * below/in post_associate will fail. + * If there is currently a HW scan going on in the background, + * then we need to cancel it, otherwise sometimes we are not + * able to authenticate (FIXME: why ?) */ if (il_scan_cancel_timeout(il, 100)) { D_MAC80211("leave - scan abort failed\n"); @@ -5290,14 +5299,10 @@ il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } /* mac80211 only sets assoc when in STATION mode */ - if (vif->type == NL80211_IFTYPE_ADHOC || bss_conf->assoc) { - memcpy(il->staging.bssid_addr, bss_conf->bssid, - ETH_ALEN); + memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); - /* currently needed in a few places */ - memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); - } else - il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + /* FIXME: currently needed in a few places */ + memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); } /* diff --git a/drivers/net/wireless/iwlegacy/debug.c b/drivers/net/wireless/iwlegacy/debug.c index 229849150aac..eff26501d60a 100644 --- a/drivers/net/wireless/iwlegacy/debug.c +++ b/drivers/net/wireless/iwlegacy/debug.c @@ -160,18 +160,12 @@ static ssize_t il_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos); -static int -il_dbgfs_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} #define DEBUGFS_READ_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ static const struct file_operations il_dbgfs_##name##_ops = { \ .read = il_dbgfs_##name##_read, \ - .open = il_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -179,7 +173,7 @@ static const struct file_operations il_dbgfs_##name##_ops = { \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations il_dbgfs_##name##_ops = { \ .write = il_dbgfs_##name##_write, \ - .open = il_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -189,7 +183,7 @@ static const struct file_operations il_dbgfs_##name##_ops = { \ static const struct file_operations il_dbgfs_##name##_ops = { \ .write = il_dbgfs_##name##_write, \ .read = il_dbgfs_##name##_read, \ - .open = il_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 5b0d888f746b..8d80e233bc7a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -46,8 +46,8 @@ #include "iwl-prph.h" /* Highest firmware API version supported */ -#define IWL1000_UCODE_API_MAX 6 -#define IWL100_UCODE_API_MAX 6 +#define IWL1000_UCODE_API_MAX 5 +#define IWL100_UCODE_API_MAX 5 /* Oldest version we won't warn about */ #define IWL1000_UCODE_API_OK 5 @@ -226,5 +226,5 @@ const struct iwl_cfg iwl100_bg_cfg = { IWL_DEVICE_100, }; -MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index 5635b9e2c69e..ea108622e0bd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -51,10 +51,10 @@ #define IWL135_UCODE_API_MAX 6 /* Oldest version we won't warn about */ -#define IWL2030_UCODE_API_OK 5 -#define IWL2000_UCODE_API_OK 5 -#define IWL105_UCODE_API_OK 5 -#define IWL135_UCODE_API_OK 5 +#define IWL2030_UCODE_API_OK 6 +#define IWL2000_UCODE_API_OK 6 +#define IWL105_UCODE_API_OK 6 +#define IWL135_UCODE_API_OK 6 /* Lowest firmware API version supported */ #define IWL2030_UCODE_API_MIN 5 @@ -328,7 +328,7 @@ const struct iwl_cfg iwl135_bgn_cfg = { .ht_params = &iwl2000_ht_params, }; -MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_OK)); +MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_OK)); +MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index a805e97b89af..de0920c74cdd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -51,6 +51,10 @@ #define IWL5000_UCODE_API_MAX 5 #define IWL5150_UCODE_API_MAX 2 +/* Oldest version we won't warn about */ +#define IWL5000_UCODE_API_OK 5 +#define IWL5150_UCODE_API_OK 2 + /* Lowest firmware API version supported */ #define IWL5000_UCODE_API_MIN 1 #define IWL5150_UCODE_API_MIN 1 @@ -326,6 +330,7 @@ static const struct iwl_ht_params iwl5000_ht_params = { #define IWL_DEVICE_5000 \ .fw_name_pre = IWL5000_FW_PRE, \ .ucode_api_max = IWL5000_UCODE_API_MAX, \ + .ucode_api_ok = IWL5000_UCODE_API_OK, \ .ucode_api_min = IWL5000_UCODE_API_MIN, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ @@ -371,6 +376,7 @@ const struct iwl_cfg iwl5350_agn_cfg = { .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, + .ucode_api_ok = IWL5000_UCODE_API_OK, .ucode_api_min = IWL5000_UCODE_API_MIN, .max_inst_size = IWLAGN_RTC_INST_SIZE, .max_data_size = IWLAGN_RTC_DATA_SIZE, @@ -386,6 +392,7 @@ const struct iwl_cfg iwl5350_agn_cfg = { #define IWL_DEVICE_5150 \ .fw_name_pre = IWL5150_FW_PRE, \ .ucode_api_max = IWL5150_UCODE_API_MAX, \ + .ucode_api_ok = IWL5150_UCODE_API_OK, \ .ucode_api_min = IWL5150_UCODE_API_MIN, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ @@ -409,5 +416,5 @@ const struct iwl_cfg iwl5150_abg_cfg = { IWL_DEVICE_5150, }; -MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 64060cd738b5..f0c91505a7f7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -53,6 +53,8 @@ /* Oldest version we won't warn about */ #define IWL6000_UCODE_API_OK 4 #define IWL6000G2_UCODE_API_OK 5 +#define IWL6050_UCODE_API_OK 5 +#define IWL6000G2B_UCODE_API_OK 6 /* Lowest firmware API version supported */ #define IWL6000_UCODE_API_MIN 4 @@ -388,7 +390,7 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = { #define IWL_DEVICE_6030 \ .fw_name_pre = IWL6030_FW_PRE, \ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000G2_UCODE_API_OK, \ + .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ @@ -557,6 +559,6 @@ const struct iwl_cfg iwl6000_3agn_cfg = { }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_OK)); +MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_OK)); +MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 53f8c51cfcdb..7e590b349dd7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -3083,11 +3083,6 @@ static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta, } #ifdef CONFIG_MAC80211_DEBUGFS -static int open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, u32 *rate_n_flags, int index) { @@ -3226,7 +3221,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, static const struct file_operations rs_sta_dbgfs_scale_table_ops = { .write = rs_sta_dbgfs_scale_table_write, .read = rs_sta_dbgfs_scale_table_read, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, @@ -3269,7 +3264,7 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, static const struct file_operations rs_sta_dbgfs_stats_table_ops = { .read = rs_sta_dbgfs_stats_table_read, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -3295,7 +3290,7 @@ static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file, static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { .read = rs_sta_dbgfs_rate_scale_data_read, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c index f4b84d1596e3..22474608a70b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c @@ -773,8 +773,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, struct sk_buff *skb; __le16 fc = hdr->frame_control; struct iwl_rxon_context *ctx; - struct page *p; - int offset; + unsigned int hdrlen, fraglen; /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { @@ -788,16 +787,24 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats)) return; - skb = dev_alloc_skb(128); + /* Dont use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); if (!skb) { - IWL_ERR(priv, "dev_alloc_skb failed\n"); + IWL_ERR(priv, "alloc_skb failed\n"); return; } + hdrlen = min_t(unsigned int, len, skb_tailroom(skb)); + memcpy(skb_put(skb, hdrlen), hdr, hdrlen); + fraglen = len - hdrlen; - offset = (void *)hdr - rxb_addr(rxb); - p = rxb_steal_page(rxb); - skb_add_rx_frag(skb, 0, p, offset, len, len); + if (fraglen) { + int offset = (void *)hdr + hdrlen - rxb_addr(rxb); + skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, + fraglen, rxb->truesize); + } iwl_update_stats(priv, false, fc, len); /* diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index f1226dbf789d..2a9a16f901c3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -863,7 +863,6 @@ static void iwl_bg_run_time_calib_work(struct work_struct *work) void iwlagn_prepare_restart(struct iwl_priv *priv) { - struct iwl_rxon_context *ctx; bool bt_full_concurrent; u8 bt_ci_compliance; u8 bt_load; @@ -872,8 +871,6 @@ void iwlagn_prepare_restart(struct iwl_priv *priv) lockdep_assert_held(&priv->mutex); - for_each_context(priv, ctx) - ctx->vif = NULL; priv->is_open = 0; /* diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index b7b1c04f2fba..2bbaebd99ad4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -84,17 +84,11 @@ static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ size_t count, loff_t *ppos); -static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - #define DEBUGFS_READ_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -102,7 +96,7 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -113,7 +107,7 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .read = iwl_dbgfs_##name##_read, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h index 90208094b8eb..74bce97a8600 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-fh.h @@ -104,15 +104,29 @@ * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04 * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte * aligned (address bits 0-7 must be 0). + * Later devices have 20 (5000 series) or 30 (higher) queues, but the registers + * for them are in different places. * * Bit fields in each pointer register: * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned */ -#define FH_MEM_CBBC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) -#define FH_MEM_CBBC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10) - -/* Find TFD CB base pointer for given queue (range 0-15). */ -#define FH_MEM_CBBC_QUEUE(x) (FH_MEM_CBBC_LOWER_BOUND + (x) * 0x4) +#define FH_MEM_CBBC_0_15_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) +#define FH_MEM_CBBC_0_15_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10) +#define FH_MEM_CBBC_16_19_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBF0) +#define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) +#define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20) +#define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80) + +/* Find TFD CB base pointer for given queue */ +static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) +{ + if (chnl < 16) + return FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl; + if (chnl < 20) + return FH_MEM_CBBC_16_19_LOWER_BOUND + 4 * (chnl - 16); + WARN_ON_ONCE(chnl >= 32); + return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20); +} /** diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c index b6805f8e9a01..c24a7134a6f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c +++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c @@ -1244,6 +1244,7 @@ static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, struct iwl_rxon_context *tmp, *ctx = NULL; int err; enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); + bool reset = false; IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", viftype, vif->addr); @@ -1265,6 +1266,13 @@ static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, tmp->interface_modes | tmp->exclusive_interface_modes; if (tmp->vif) { + /* On reset we need to add the same interface again */ + if (tmp->vif == vif) { + reset = true; + ctx = tmp; + break; + } + /* check if this busy context is exclusive */ if (tmp->exclusive_interface_modes & BIT(tmp->vif->type)) { @@ -1291,7 +1299,7 @@ static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, ctx->vif = vif; err = iwl_setup_interface(priv, ctx); - if (!err) + if (!err || reset) goto out; ctx->vif = NULL; diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 75dc20bd965b..3b1069290fa9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -223,12 +223,33 @@ #define SCD_AIT (SCD_BASE + 0x0c) #define SCD_TXFACT (SCD_BASE + 0x10) #define SCD_ACTIVE (SCD_BASE + 0x14) -#define SCD_QUEUE_WRPTR(x) (SCD_BASE + 0x18 + (x) * 4) -#define SCD_QUEUE_RDPTR(x) (SCD_BASE + 0x68 + (x) * 4) #define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8) #define SCD_AGGR_SEL (SCD_BASE + 0x248) #define SCD_INTERRUPT_MASK (SCD_BASE + 0x108) -#define SCD_QUEUE_STATUS_BITS(x) (SCD_BASE + 0x10c + (x) * 4) + +static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x18 + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x284 + (chnl - 20) * 4; +} + +static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x68 + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x2B4 + (chnl - 20) * 4; +} + +static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x10c + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x384 + (chnl - 20) * 4; +} /*********************** END TX SCHEDULER *************************************/ diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c index 8b1a7988e176..aa7aea168138 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c @@ -374,8 +374,9 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, if (WARN_ON(!rxb)) return; + rxcb.truesize = PAGE_SIZE << hw_params(trans).rx_page_order; dma_unmap_page(trans->dev, rxb->page_dma, - PAGE_SIZE << hw_params(trans).rx_page_order, + rxcb.truesize, DMA_FROM_DEVICE); rxcb._page = rxb->page; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index b4f796c82e1e..4d7b30d3e648 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -1898,17 +1898,11 @@ static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ size_t count, loff_t *ppos); -static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - #define DEBUGFS_READ_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -1916,7 +1910,7 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -1926,7 +1920,7 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .read = iwl_dbgfs_##name##_read, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 0c81cbaa8088..fdf97886a5e4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -260,6 +260,7 @@ static inline void iwl_free_resp(struct iwl_host_cmd *cmd) struct iwl_rx_cmd_buffer { struct page *_page; + unsigned int truesize; }; static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r) diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c index 87eef5773a02..b6199d124bb9 100644 --- a/drivers/net/wireless/iwmc3200wifi/debugfs.c +++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c @@ -99,12 +99,6 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules, iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write, "%llu\n"); -static int iwm_generic_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - return 0; -} - static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) @@ -401,28 +395,28 @@ out: static const struct file_operations iwm_debugfs_txq_fops = { .owner = THIS_MODULE, - .open = iwm_generic_open, + .open = simple_open, .read = iwm_debugfs_txq_read, .llseek = default_llseek, }; static const struct file_operations iwm_debugfs_tx_credit_fops = { .owner = THIS_MODULE, - .open = iwm_generic_open, + .open = simple_open, .read = iwm_debugfs_tx_credit_read, .llseek = default_llseek, }; static const struct file_operations iwm_debugfs_rx_ticket_fops = { .owner = THIS_MODULE, - .open = iwm_generic_open, + .open = simple_open, .read = iwm_debugfs_rx_ticket_read, .llseek = default_llseek, }; static const struct file_operations iwm_debugfs_fw_err_fops = { .owner = THIS_MODULE, - .open = iwm_generic_open, + .open = simple_open, .read = iwm_debugfs_fw_err_read, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c index 764b40dd24ad..0042f204b07f 100644 --- a/drivers/net/wireless/iwmc3200wifi/sdio.c +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c @@ -264,13 +264,6 @@ static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count) return ret; } -/* debugfs hooks */ -static int iwm_debugfs_sdio_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - return 0; -} - static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { @@ -363,7 +356,7 @@ err: static const struct file_operations iwm_debugfs_sdio_fops = { .owner = THIS_MODULE, - .open = iwm_debugfs_sdio_open, + .open = simple_open, .read = iwm_debugfs_sdio_read, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 3fa1ecebadfd..2fa879b015b6 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -103,7 +103,7 @@ static const u32 cipher_suites[] = { * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1 * in the firmware spec */ -static u8 lbs_auth_to_authtype(enum nl80211_auth_type auth_type) +static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type) { int ret = -ENOTSUPP; @@ -1411,7 +1411,12 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, goto done; } - lbs_set_authtype(priv, sme); + ret = lbs_set_authtype(priv, sme); + if (ret == -ENOTSUPP) { + wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type); + goto done; + } + lbs_set_radio(priv, preamble, 1); /* Do the actual association */ diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index c192671610fc..a06cc283e23d 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c @@ -21,12 +21,6 @@ static char *szStates[] = { static void lbs_debug_init(struct lbs_private *priv); #endif -static int open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t write_file_dummy(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -696,7 +690,7 @@ out_unlock: #define FOPS(fread, fwrite) { \ .owner = THIS_MODULE, \ - .open = open_file_generic, \ + .open = simple_open, \ .read = (fread), \ .write = (fwrite), \ .llseek = generic_file_llseek, \ @@ -962,7 +956,7 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, static const struct file_operations lbs_debug_fops = { .owner = THIS_MODULE, - .open = open_file_generic, + .open = simple_open, .write = lbs_debugfs_write, .read = lbs_debugfs_read, .llseek = default_llseek, diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index d26a78b6b3c4..1a845074c52a 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -140,18 +140,6 @@ static struct mwifiex_debug_data items[] = { static int num_of_items = ARRAY_SIZE(items); /* - * Generic proc file open handler. - * - * This function is called every time a file is accessed for read or write. - */ -static int -mwifiex_open_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -/* * Proc info file read handler. * * This function is called when the 'info' file is opened for reading. @@ -676,19 +664,19 @@ done: static const struct file_operations mwifiex_dfs_##name##_fops = { \ .read = mwifiex_##name##_read, \ .write = mwifiex_##name##_write, \ - .open = mwifiex_open_generic, \ + .open = simple_open, \ }; #define MWIFIEX_DFS_FILE_READ_OPS(name) \ static const struct file_operations mwifiex_dfs_##name##_fops = { \ .read = mwifiex_##name##_read, \ - .open = mwifiex_open_generic, \ + .open = simple_open, \ }; #define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ static const struct file_operations mwifiex_dfs_##name##_fops = { \ .write = mwifiex_##name##_write, \ - .open = mwifiex_open_generic, \ + .open = simple_open, \ }; diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index 445ff21772e2..2f218f9a3fd3 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h @@ -48,15 +48,15 @@ #define PCIE_HOST_INT_STATUS_MASK 0xC3C #define PCIE_SCRATCH_2_REG 0xC40 #define PCIE_SCRATCH_3_REG 0xC44 -#define PCIE_SCRATCH_4_REG 0xCC0 -#define PCIE_SCRATCH_5_REG 0xCC4 -#define PCIE_SCRATCH_6_REG 0xCC8 -#define PCIE_SCRATCH_7_REG 0xCCC -#define PCIE_SCRATCH_8_REG 0xCD0 -#define PCIE_SCRATCH_9_REG 0xCD4 -#define PCIE_SCRATCH_10_REG 0xCD8 -#define PCIE_SCRATCH_11_REG 0xCDC -#define PCIE_SCRATCH_12_REG 0xCE0 +#define PCIE_SCRATCH_4_REG 0xCD0 +#define PCIE_SCRATCH_5_REG 0xCD4 +#define PCIE_SCRATCH_6_REG 0xCD8 +#define PCIE_SCRATCH_7_REG 0xCDC +#define PCIE_SCRATCH_8_REG 0xCE0 +#define PCIE_SCRATCH_9_REG 0xCE4 +#define PCIE_SCRATCH_10_REG 0xCE8 +#define PCIE_SCRATCH_11_REG 0xCEC +#define PCIE_SCRATCH_12_REG 0xCF0 #define CPU_INTR_DNLD_RDY BIT(0) #define CPU_INTR_DOOR_BELL BIT(1) diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index dd6c64ac406e..88e3ad2d1db8 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -1336,6 +1336,10 @@ static void qbuf_scan(struct orinoco_private *priv, void *buf, unsigned long flags; sd = kmalloc(sizeof(*sd), GFP_ATOMIC); + if (!sd) { + printk(KERN_ERR "%s: failed to alloc memory\n", __func__); + return; + } sd->buf = buf; sd->len = len; sd->type = type; @@ -1353,6 +1357,10 @@ static void qabort_scan(struct orinoco_private *priv) unsigned long flags; sd = kmalloc(sizeof(*sd), GFP_ATOMIC); + if (!sd) { + printk(KERN_ERR "%s: failed to alloc memory\n", __func__); + return; + } sd->len = -1; /* Abort */ spin_lock_irqsave(&priv->scan_lock, flags); diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index cd490abced91..001735f7a661 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -163,7 +163,13 @@ static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, /* Reschedule urb to read TX status again instantly */ return true; - } else if (rt2800usb_txstatus_pending(rt2x00dev)) { + } + + /* Check if there is any entry that timedout waiting on TX status */ + if (rt2800usb_txstatus_timeout(rt2x00dev)) + queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); + + if (rt2800usb_txstatus_pending(rt2x00dev)) { /* Read register after 250 us */ hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 250000), HRTIMER_MODE_REL); @@ -178,7 +184,7 @@ stop_reading: * here again if status reading is needed. */ if (rt2800usb_txstatus_pending(rt2x00dev) && - test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) + !test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) return true; else return false; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index fc9901e027c1..90cc5e772650 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1062,11 +1062,6 @@ static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) set_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags); - /* - * Register the extra components. - */ - rt2x00rfkill_register(rt2x00dev); - return 0; } @@ -1210,6 +1205,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) rt2x00link_register(rt2x00dev); rt2x00leds_register(rt2x00dev); rt2x00debug_register(rt2x00dev); + rt2x00rfkill_register(rt2x00dev); return 0; diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 510023554e5f..e54488db0e10 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -838,7 +838,10 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw, __le16 fc = hdr->frame_control; txrate = ieee80211_get_tx_rate(hw, info); - tcb_desc->hw_rate = txrate->hw_value; + if (txrate) + tcb_desc->hw_rate = txrate->hw_value; + else + tcb_desc->hw_rate = 0; if (ieee80211_is_data(fc)) { /* diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 07dd38efe62a..cc15fdb36060 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -912,8 +912,13 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); ring = &rtlpci->tx_ring[BEACON_QUEUE]; pskb = __skb_dequeue(&ring->queue); - if (pskb) + if (pskb) { + struct rtl_tx_desc *entry = &ring->desc[ring->idx]; + pci_unmap_single(rtlpci->pdev, rtlpriv->cfg->ops->get_desc( + (u8 *) entry, true, HW_DESC_TXBUFF_ADDR), + pskb->len, PCI_DMA_TODEVICE); kfree_skb(pskb); + } /*NB: the beacon data buffer must be 32-bit aligned. */ pskb = ieee80211_beacon_get(hw, mac->vif); @@ -1936,6 +1941,7 @@ void rtl_pci_disconnect(struct pci_dev *pdev) rtl_deinit_deferred_work(hw); rtlpriv->intf_ops->adapter_stop(hw); } + rtlpriv->cfg->ops->disable_interrupt(hw); /*deinit rfkill */ rtl_deinit_rfkill(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 1eec3a06d1f3..4c016241f340 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c @@ -1893,7 +1893,7 @@ void rtl92c_phy_set_io(struct ieee80211_hw *hw) break; case IO_CMD_PAUSE_DM_BY_SCAN: rtlphy->initgain_backup.xaagccore1 = dm_digtable.cur_igvalue; - dm_digtable.cur_igvalue = 0x17; + dm_digtable.cur_igvalue = 0x37; rtl92c_dm_write_dig(hw); break; default: diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c index 34591eeb8376..28fc5fb8057b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c @@ -3077,7 +3077,7 @@ static void rtl92d_phy_set_io(struct ieee80211_hw *hw) break; case IO_CMD_PAUSE_DM_BY_SCAN: rtlphy->initgain_backup.xaagccore1 = de_digtable.cur_igvalue; - de_digtable.cur_igvalue = 0x17; + de_digtable.cur_igvalue = 0x37; rtl92d_dm_write_dig(hw); break; default: diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c index 4898c502974d..480862c07f92 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c @@ -91,7 +91,6 @@ static int rtl92d_init_sw_vars(struct ieee80211_hw *hw) u8 tid; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - static int header_print; rtlpriv->dm.dm_initialgain_enable = true; rtlpriv->dm.dm_flag = 0; @@ -171,10 +170,6 @@ static int rtl92d_init_sw_vars(struct ieee80211_hw *hw) for (tid = 0; tid < 8; tid++) skb_queue_head_init(&rtlpriv->mac80211.skb_waitq[tid]); - /* Only load firmware for first MAC */ - if (header_print) - return 0; - /* for firmware buf */ rtlpriv->rtlhal.pfirmware = vzalloc(0x8000); if (!rtlpriv->rtlhal.pfirmware) { @@ -186,7 +181,6 @@ static int rtl92d_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->max_fw_size = 0x8000; pr_info("Driver for Realtek RTL8192DE WLAN interface\n"); pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name); - header_print++; /* request fw */ err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index 2e1e352864bb..d04dbda13f5a 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -124,46 +124,38 @@ static int _usbctrl_vendorreq_sync_read(struct usb_device *udev, u8 request, return status; } -static u32 _usb_read_sync(struct usb_device *udev, u32 addr, u16 len) +static u32 _usb_read_sync(struct rtl_priv *rtlpriv, u32 addr, u16 len) { + struct device *dev = rtlpriv->io.dev; + struct usb_device *udev = to_usb_device(dev); u8 request; u16 wvalue; u16 index; - u32 *data; - u32 ret; + __le32 *data = &rtlpriv->usb_data[rtlpriv->usb_data_index]; - data = kmalloc(sizeof(u32), GFP_KERNEL); - if (!data) - return -ENOMEM; request = REALTEK_USB_VENQT_CMD_REQ; index = REALTEK_USB_VENQT_CMD_IDX; /* n/a */ wvalue = (u16)addr; _usbctrl_vendorreq_sync_read(udev, request, wvalue, index, data, len); - ret = le32_to_cpu(*data); - kfree(data); - return ret; + if (++rtlpriv->usb_data_index >= RTL_USB_MAX_RX_COUNT) + rtlpriv->usb_data_index = 0; + return le32_to_cpu(*data); } static u8 _usb_read8_sync(struct rtl_priv *rtlpriv, u32 addr) { - struct device *dev = rtlpriv->io.dev; - - return (u8)_usb_read_sync(to_usb_device(dev), addr, 1); + return (u8)_usb_read_sync(rtlpriv, addr, 1); } static u16 _usb_read16_sync(struct rtl_priv *rtlpriv, u32 addr) { - struct device *dev = rtlpriv->io.dev; - - return (u16)_usb_read_sync(to_usb_device(dev), addr, 2); + return (u16)_usb_read_sync(rtlpriv, addr, 2); } static u32 _usb_read32_sync(struct rtl_priv *rtlpriv, u32 addr) { - struct device *dev = rtlpriv->io.dev; - - return _usb_read_sync(to_usb_device(dev), addr, 4); + return _usb_read_sync(rtlpriv, addr, 4); } static void _usb_write_async(struct usb_device *udev, u32 addr, u32 val, @@ -955,6 +947,11 @@ int __devinit rtl_usb_probe(struct usb_interface *intf, return -ENOMEM; } rtlpriv = hw->priv; + rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32), + GFP_KERNEL); + if (!rtlpriv->usb_data) + return -ENOMEM; + rtlpriv->usb_data_index = 0; init_completion(&rtlpriv->firmware_loading_complete); SET_IEEE80211_DEV(hw, &intf->dev); udev = interface_to_usbdev(intf); @@ -1025,6 +1022,7 @@ void rtl_usb_disconnect(struct usb_interface *intf) /* rtl_deinit_rfkill(hw); */ rtl_usb_deinit(hw); rtl_deinit_core(hw); + kfree(rtlpriv->usb_data); rtlpriv->cfg->ops->deinit_sw_leds(hw); rtlpriv->cfg->ops->deinit_sw_vars(hw); _rtl_usb_io_handler_release(hw); diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index b591614c3b9b..28ebc69218a3 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -67,7 +67,7 @@ #define QOS_QUEUE_NUM 4 #define RTL_MAC80211_NUM_QUEUE 5 #define REALTEK_USB_VENQT_MAX_BUF_SIZE 254 - +#define RTL_USB_MAX_RX_COUNT 100 #define QBSS_LOAD_SIZE 5 #define MAX_WMMELE_LENGTH 64 @@ -1629,6 +1629,10 @@ struct rtl_priv { interface or hardware */ unsigned long status; + /* data buffer pointer for USB reads */ + __le32 *usb_data; + int usb_data_index; + /*This must be the last item so that it points to the data allocated beyond this structure like: diff --git a/drivers/net/wireless/wl1251/debugfs.c b/drivers/net/wireless/wl1251/debugfs.c index 6c274007d200..448da1f8c22f 100644 --- a/drivers/net/wireless/wl1251/debugfs.c +++ b/drivers/net/wireless/wl1251/debugfs.c @@ -47,7 +47,7 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \ \ static const struct file_operations name## _ops = { \ .read = name## _read, \ - .open = wl1251_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -84,7 +84,7 @@ static ssize_t sub## _ ##name## _read(struct file *file, \ \ static const struct file_operations sub## _ ##name## _ops = { \ .read = sub## _ ##name## _read, \ - .open = wl1251_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -117,12 +117,6 @@ out: mutex_unlock(&wl->mutex); } -static int wl1251_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, out_of_mem, 20, "%u"); @@ -235,7 +229,7 @@ static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, static const struct file_operations tx_queue_len_ops = { .read = tx_queue_len_read, - .open = wl1251_open_file_generic, + .open = simple_open, .llseek = generic_file_llseek, }; @@ -257,7 +251,7 @@ static ssize_t tx_queue_status_read(struct file *file, char __user *userbuf, static const struct file_operations tx_queue_status_ops = { .read = tx_queue_status_read, - .open = wl1251_open_file_generic, + .open = simple_open, .llseek = generic_file_llseek, }; diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index 41302c7b1ad0..d1afb8e3b2ef 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -479,6 +479,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->tx_work); cancel_work_sync(&wl->filter_work); + cancel_delayed_work_sync(&wl->elp_work); mutex_lock(&wl->mutex); diff --git a/drivers/net/wireless/wl1251/sdio.c b/drivers/net/wireless/wl1251/sdio.c index f78694295c39..1b851f650e07 100644 --- a/drivers/net/wireless/wl1251/sdio.c +++ b/drivers/net/wireless/wl1251/sdio.c @@ -315,8 +315,8 @@ static void __devexit wl1251_sdio_remove(struct sdio_func *func) if (wl->irq) free_irq(wl->irq, wl); - kfree(wl_sdio); wl1251_free_hw(wl); + kfree(wl_sdio); sdio_claim_host(func); sdio_release_irq(func); diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index e1cf72765965..564d49575c94 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -63,7 +63,7 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \ \ static const struct file_operations name## _ops = { \ .read = name## _read, \ - .open = wl1271_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -96,7 +96,7 @@ static ssize_t sub## _ ##name## _read(struct file *file, \ \ static const struct file_operations sub## _ ##name## _ops = { \ .read = sub## _ ##name## _read, \ - .open = wl1271_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -126,12 +126,6 @@ out: mutex_unlock(&wl->mutex); } -static int wl1271_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u"); DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u"); @@ -243,7 +237,7 @@ static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, static const struct file_operations tx_queue_len_ops = { .read = tx_queue_len_read, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -289,7 +283,7 @@ static ssize_t gpio_power_write(struct file *file, static const struct file_operations gpio_power_ops = { .read = gpio_power_read, .write = gpio_power_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -308,7 +302,7 @@ static ssize_t start_recovery_write(struct file *file, static const struct file_operations start_recovery_ops = { .write = start_recovery_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -372,7 +366,7 @@ out: static const struct file_operations dynamic_ps_timeout_ops = { .read = dynamic_ps_timeout_read, .write = dynamic_ps_timeout_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -441,7 +435,7 @@ out: static const struct file_operations forced_ps_ops = { .read = forced_ps_read, .write = forced_ps_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -483,7 +477,7 @@ static ssize_t split_scan_timeout_write(struct file *file, static const struct file_operations split_scan_timeout_ops = { .read = split_scan_timeout_read, .write = split_scan_timeout_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -566,7 +560,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, static const struct file_operations driver_state_ops = { .read = driver_state_read, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -675,7 +669,7 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, static const struct file_operations vifs_state_ops = { .read = vifs_state_read, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -733,7 +727,7 @@ static ssize_t dtim_interval_write(struct file *file, static const struct file_operations dtim_interval_ops = { .read = dtim_interval_read, .write = dtim_interval_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -791,7 +785,7 @@ static ssize_t suspend_dtim_interval_write(struct file *file, static const struct file_operations suspend_dtim_interval_ops = { .read = suspend_dtim_interval_read, .write = suspend_dtim_interval_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -849,7 +843,7 @@ static ssize_t beacon_interval_write(struct file *file, static const struct file_operations beacon_interval_ops = { .read = beacon_interval_read, .write = beacon_interval_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -904,7 +898,7 @@ static ssize_t rx_streaming_interval_read(struct file *file, static const struct file_operations rx_streaming_interval_ops = { .read = rx_streaming_interval_read, .write = rx_streaming_interval_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -959,7 +953,7 @@ static ssize_t rx_streaming_always_read(struct file *file, static const struct file_operations rx_streaming_always_ops = { .read = rx_streaming_always_read, .write = rx_streaming_always_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; @@ -1003,7 +997,7 @@ out: static const struct file_operations beacon_filtering_ops = { .write = beacon_filtering_write, - .open = wl1271_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 663b32c2e931..0ebbb1906c30 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1965,7 +1965,7 @@ static int __init netif_init(void) if (xen_initial_domain()) return 0; - if (!xen_platform_pci_unplug) + if (xen_hvm_domain() && !xen_platform_pci_unplug) return -ENODEV; printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n"); diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c index bba81216b4db..bf984b6dc477 100644 --- a/drivers/of/gpio.c +++ b/drivers/of/gpio.c @@ -140,7 +140,7 @@ int of_gpio_simple_xlate(struct gpio_chip *gc, if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) return -EINVAL; - if (gpiospec->args[0] > gc->ngpio) + if (gpiospec->args[0] >= gc->ngpio) return -EINVAL; if (flags) diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c index ee8fd037bb53..849357c1045c 100644 --- a/drivers/oprofile/oprofilefs.c +++ b/drivers/oprofile/oprofilefs.c @@ -117,25 +117,17 @@ static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_ } -static int default_open(struct inode *inode, struct file *filp) -{ - if (inode->i_private) - filp->private_data = inode->i_private; - return 0; -} - - static const struct file_operations ulong_fops = { .read = ulong_read_file, .write = ulong_write_file, - .open = default_open, + .open = simple_open, .llseek = default_llseek, }; static const struct file_operations ulong_ro_fops = { .read = ulong_read_file, - .open = default_open, + .open = simple_open, .llseek = default_llseek, }; @@ -187,7 +179,7 @@ static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t coun static const struct file_operations atomic_ro_fops = { .read = atomic_read_file, - .open = default_open, + .open = simple_open, .llseek = default_llseek, }; diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 8644d5372e7f..42cfcd9eb9aa 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -44,6 +44,7 @@ #include <asm/ropes.h> #include <asm/mckinley.h> /* for proc_mckinley_root */ #include <asm/runway.h> /* for proc_runway_root */ +#include <asm/page.h> /* for PAGE0 */ #include <asm/pdc.h> /* for PDC_MODEL_* */ #include <asm/pdcpat.h> /* for is_pdc_pat() */ #include <asm/parisc-device.h> diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 083a49fee56a..165274c064bc 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_UNICORE32) += setup-bus.o setup-irq.o obj-$(CONFIG_PARISC) += setup-bus.o obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o obj-$(CONFIG_PPC) += setup-bus.o +obj-$(CONFIG_FRV) += setup-bus.o obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o obj-$(CONFIG_X86_VISWS) += setup-irq.o obj-$(CONFIG_MN10300) += setup-bus.o diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 0f150f271c2a..1929c0c63b75 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -200,7 +200,7 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) return PCI_D1; case ACPI_STATE_D2: return PCI_D2; - case ACPI_STATE_D3: + case ACPI_STATE_D3_HOT: return PCI_D3hot; case ACPI_STATE_D3_COLD: return PCI_D3cold; @@ -223,7 +223,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) [PCI_D0] = ACPI_STATE_D0, [PCI_D1] = ACPI_STATE_D1, [PCI_D2] = ACPI_STATE_D2, - [PCI_D3hot] = ACPI_STATE_D3, + [PCI_D3hot] = ACPI_STATE_D3_HOT, [PCI_D3cold] = ACPI_STATE_D3 }; int error = -EINVAL; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 815674415267..111569ccab43 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -967,16 +967,59 @@ pci_save_state(struct pci_dev *dev) return 0; } +static void pci_restore_config_dword(struct pci_dev *pdev, int offset, + u32 saved_val, int retry) +{ + u32 val; + + pci_read_config_dword(pdev, offset, &val); + if (val == saved_val) + return; + + for (;;) { + dev_dbg(&pdev->dev, "restoring config space at offset " + "%#x (was %#x, writing %#x)\n", offset, val, saved_val); + pci_write_config_dword(pdev, offset, saved_val); + if (retry-- <= 0) + return; + + pci_read_config_dword(pdev, offset, &val); + if (val == saved_val) + return; + + mdelay(1); + } +} + +static void pci_restore_config_space_range(struct pci_dev *pdev, + int start, int end, int retry) +{ + int index; + + for (index = end; index >= start; index--) + pci_restore_config_dword(pdev, 4 * index, + pdev->saved_config_space[index], + retry); +} + +static void pci_restore_config_space(struct pci_dev *pdev) +{ + if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) { + pci_restore_config_space_range(pdev, 10, 15, 0); + /* Restore BARs before the command register. */ + pci_restore_config_space_range(pdev, 4, 9, 10); + pci_restore_config_space_range(pdev, 0, 3, 0); + } else { + pci_restore_config_space_range(pdev, 0, 15, 0); + } +} + /** * pci_restore_state - Restore the saved state of a PCI device * @dev: - PCI device that we're dealing with */ void pci_restore_state(struct pci_dev *dev) { - int i; - u32 val; - int tries; - if (!dev->state_saved) return; @@ -984,24 +1027,8 @@ void pci_restore_state(struct pci_dev *dev) pci_restore_pcie_state(dev); pci_restore_ats_state(dev); - /* - * The Base Address register should be programmed before the command - * register(s) - */ - for (i = 15; i >= 0; i--) { - pci_read_config_dword(dev, i * 4, &val); - tries = 10; - while (tries && val != dev->saved_config_space[i]) { - dev_dbg(&dev->dev, "restoring config " - "space at offset %#x (was %#x, writing %#x)\n", - i, val, (int)dev->saved_config_space[i]); - pci_write_config_dword(dev,i * 4, - dev->saved_config_space[i]); - pci_read_config_dword(dev, i * 4, &val); - mdelay(10); - tries--; - } - } + pci_restore_config_space(dev); + pci_restore_pcix_state(dev); pci_restore_msi_state(dev); pci_restore_iov_state(dev); diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index fd00ff02ab4d..d6cc62cb4cf7 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -290,6 +290,7 @@ static int pci_frontend_enable_msix(struct pci_dev *dev, } else { printk(KERN_DEBUG "enable msix get value %x\n", op.value); + err = op.value; } } else { dev_err(&dev->dev, "enable msix get err %x\n", err); diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index ec3b8cc188af..df6296c5f47b 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -908,10 +908,6 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) const struct pinctrl_ops *ops = pctldev->desc->pctlops; unsigned selector = 0; - /* No grouping */ - if (!ops) - return 0; - mutex_lock(&pinctrl_mutex); seq_puts(s, "registered pin groups:\n"); @@ -1225,6 +1221,19 @@ static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) #endif +static int pinctrl_check_ops(struct pinctrl_dev *pctldev) +{ + const struct pinctrl_ops *ops = pctldev->desc->pctlops; + + if (!ops || + !ops->list_groups || + !ops->get_group_name || + !ops->get_group_pins) + return -EINVAL; + + return 0; +} + /** * pinctrl_register() - register a pin controller device * @pctldesc: descriptor for this pin controller @@ -1256,6 +1265,14 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, INIT_LIST_HEAD(&pctldev->gpio_ranges); pctldev->dev = dev; + /* check core ops for sanity */ + ret = pinctrl_check_ops(pctldev); + if (ret) { + pr_err("%s pinctrl ops lacks necessary functions\n", + pctldesc->name); + goto out_err; + } + /* If we're implementing pinmuxing, check the ops for sanity */ if (pctldesc->pmxops) { ret = pinmux_check_ops(pctldev); diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index bc8384c6f3eb..639db4d0aa76 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -50,7 +50,7 @@ */ #undef START_IN_KERNEL_MODE -#define DRV_VER "0.5.24" +#define DRV_VER "0.5.26" /* * According to the Atom N270 datasheet, @@ -83,8 +83,8 @@ static int kernelmode; #endif static unsigned int interval = 10; -static unsigned int fanon = 63000; -static unsigned int fanoff = 58000; +static unsigned int fanon = 60000; +static unsigned int fanoff = 53000; static unsigned int verbose; static unsigned int fanstate = ACERHDF_FAN_AUTO; static char force_bios[16]; @@ -150,6 +150,8 @@ static const struct bios_settings_t bios_tbl[] = { {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} }, {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} }, {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} }, + /* LT1005u */ + {"Acer", "LT-10Q", "v0.3310", 0x55, 0x58, {0x20, 0x00} }, /* Acer 1410 */ {"Acer", "Aspire 1410", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, {"Acer", "Aspire 1410", "v0.3113", 0x55, 0x58, {0x9e, 0x00} }, @@ -161,6 +163,7 @@ static const struct bios_settings_t bios_tbl[] = { {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x00} }, {"Acer", "Aspire 1410", "v1.3308", 0x55, 0x58, {0x9e, 0x00} }, {"Acer", "Aspire 1410", "v1.3310", 0x55, 0x58, {0x9e, 0x00} }, + {"Acer", "Aspire 1410", "v1.3314", 0x55, 0x58, {0x9e, 0x00} }, /* Acer 1810xx */ {"Acer", "Aspire 1810TZ", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, {"Acer", "Aspire 1810T", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, @@ -183,29 +186,44 @@ static const struct bios_settings_t bios_tbl[] = { {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00} }, {"Acer", "Aspire 1810T", "v1.3310", 0x55, 0x58, {0x9e, 0x00} }, {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00} }, + {"Acer", "Aspire 1810T", "v1.3314", 0x55, 0x58, {0x9e, 0x00} }, /* Acer 531 */ + {"Acer", "AO531h", "v0.3104", 0x55, 0x58, {0x20, 0x00} }, {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AO531h", "v0.3304", 0x55, 0x58, {0x20, 0x00} }, + /* Acer 751 */ + {"Acer", "AO751h", "V0.3212", 0x55, 0x58, {0x21, 0x00} }, + /* Acer 1825 */ + {"Acer", "Aspire 1825PTZ", "V1.3118", 0x55, 0x58, {0x9e, 0x00} }, + {"Acer", "Aspire 1825PTZ", "V1.3127", 0x55, 0x58, {0x9e, 0x00} }, + /* Acer TravelMate 7730 */ + {"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00} }, /* Gateway */ - {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, - {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} }, - {"Gateway", "LT31", "v1.3103", 0x55, 0x58, {0x9e, 0x00} }, - {"Gateway", "LT31", "v1.3201", 0x55, 0x58, {0x9e, 0x00} }, - {"Gateway", "LT31", "v1.3302", 0x55, 0x58, {0x9e, 0x00} }, + {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, + {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} }, + {"Gateway", "LT31", "v1.3103", 0x55, 0x58, {0x9e, 0x00} }, + {"Gateway", "LT31", "v1.3201", 0x55, 0x58, {0x9e, 0x00} }, + {"Gateway", "LT31", "v1.3302", 0x55, 0x58, {0x9e, 0x00} }, + {"Gateway", "LT31", "v1.3303t", 0x55, 0x58, {0x9e, 0x00} }, /* Packard Bell */ - {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} }, - {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, - {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, - {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, - {"Packard Bell", "DOTMU", "v1.3303", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMU", "v0.3120", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMU", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMU", "v0.3113", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMU", "v0.3115", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMU", "v0.3117", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMU", "v0.3119", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMU", "v1.3204", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMA", "v1.3201", 0x55, 0x58, {0x9e, 0x00} }, - {"Packard Bell", "DOTMA", "v1.3302", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} }, + {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, + {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, + {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, + {"Packard Bell", "ENBFT", "V1.3118", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "ENBFT", "V1.3127", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMU", "v1.3303", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMU", "v0.3120", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMU", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMU", "v0.3113", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMU", "v0.3115", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMU", "v0.3117", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMU", "v0.3119", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMU", "v1.3204", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMA", "v1.3201", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMA", "v1.3302", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTMA", "v1.3303t", 0x55, 0x58, {0x9e, 0x00} }, + {"Packard Bell", "DOTVR46", "v1.3308", 0x55, 0x58, {0x9e, 0x00} }, /* pewpew-terminator */ {"", "", "", 0, 0, {0, 0} } }; @@ -701,15 +719,20 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Peter Feuerer"); MODULE_DESCRIPTION("Aspire One temperature and fan driver"); MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:"); +MODULE_ALIAS("dmi:*:*Acer*:pnAO751h*:"); MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1410*:"); MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1810*:"); +MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1825PTZ:"); MODULE_ALIAS("dmi:*:*Acer*:pnAO531*:"); +MODULE_ALIAS("dmi:*:*Acer*:TravelMate*7730G:"); MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:"); MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:"); MODULE_ALIAS("dmi:*:*Packard*Bell*:pnAOA*:"); MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOA*:"); MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:"); +MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:"); MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:"); +MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:"); module_init(acerhdf_init); module_exit(acerhdf_exit); diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index a05fc9c955d8..e6c08ee8d46c 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -212,6 +212,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { }, .driver_data = &quirk_dell_vostro_v130, }, + { } }; static struct calling_interface_buffer *buffer; diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index f7ba316e0ed6..0ffdb3cde2bb 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1565,7 +1565,7 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) ips->poll_turbo_status = true; if (!ips_get_i915_syms(ips)) { - dev_err(&dev->dev, "failed to get i915 symbols, graphics turbo disabled\n"); + dev_info(&dev->dev, "failed to get i915 symbols, graphics turbo disabled until i915 loads\n"); ips->gpu_turbo_enabled = false; } else { dev_dbg(&dev->dev, "graphics turbo enabled\n"); diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 0a3594c7e912..bcbad8452a6f 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -78,7 +78,7 @@ static int __devinit mfld_pb_probe(struct platform_device *pdev) input_set_capability(input, EV_KEY, KEY_POWER); - error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0, + error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_NO_SUSPEND, DRIVER_NAME, input); if (error) { dev_err(&pdev->dev, "Unable to request irq %d for mfld power" diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 17499a55113d..81fd606e47bc 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -138,9 +138,10 @@ static int __devinit anatop_regulator_probe(struct platform_device *pdev) rdesc->type = REGULATOR_VOLTAGE; rdesc->owner = THIS_MODULE; sreg->mfd = anatopmfd; - ret = of_property_read_u32(np, "reg", &sreg->control_reg); + ret = of_property_read_u32(np, "anatop-reg-offset", + &sreg->control_reg); if (ret) { - dev_err(dev, "no reg property set\n"); + dev_err(dev, "no anatop-reg-offset property set\n"); goto anatop_probe_end; } ret = of_property_read_u32(np, "anatop-vol-bit-width", @@ -213,7 +214,7 @@ static struct of_device_id __devinitdata of_anatop_regulator_match_tbl[] = { { /* end */ } }; -static struct platform_driver anatop_regulator = { +static struct platform_driver anatop_regulator_driver = { .driver = { .name = "anatop_regulator", .owner = THIS_MODULE, @@ -225,13 +226,13 @@ static struct platform_driver anatop_regulator = { static int __init anatop_regulator_init(void) { - return platform_driver_register(&anatop_regulator); + return platform_driver_register(&anatop_regulator_driver); } postcore_initcall(anatop_regulator_init); static void __exit anatop_regulator_exit(void) { - platform_driver_unregister(&anatop_regulator); + platform_driver_unregister(&anatop_regulator_driver); } module_exit(anatop_regulator_exit); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c056abd7562a..046fb1bd8619 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1431,7 +1431,10 @@ void devm_regulator_put(struct regulator *regulator) rc = devres_destroy(regulator->dev, devm_regulator_release, devm_regulator_match, regulator); - WARN_ON(rc); + if (rc == 0) + regulator_put(regulator); + else + WARN_ON(rc); } EXPORT_SYMBOL_GPL(devm_regulator_put); @@ -2992,14 +2995,14 @@ void regulator_unregister(struct regulator_dev *rdev) if (rdev == NULL) return; + if (rdev->supply) + regulator_put(rdev->supply); mutex_lock(®ulator_list_mutex); debugfs_remove_recursive(rdev->debugfs); flush_work_sync(&rdev->disable_work.work); WARN_ON(rdev->open_count); unset_regulator_supplies(rdev); list_del(&rdev->list); - if (rdev->supply) - regulator_put(rdev->supply); kfree(rdev->constraints); device_unregister(&rdev->dev); mutex_unlock(®ulator_list_mutex); diff --git a/drivers/regulator/fixed-helper.c b/drivers/regulator/fixed-helper.c index 30d0a15b8949..cacd33c9d042 100644 --- a/drivers/regulator/fixed-helper.c +++ b/drivers/regulator/fixed-helper.c @@ -18,7 +18,6 @@ static void regulator_fixed_release(struct device *dev) /** * regulator_register_fixed - register a no-op fixed regulator - * @name: supply name * @id: platform device id * @supplies: consumers for this regulator * @num_supplies: number of consumers @@ -32,7 +31,7 @@ struct platform_device *regulator_register_fixed(int id, if (!data) return NULL; - data->cfg.supply_name = "dummy"; + data->cfg.supply_name = "fixed-dummy"; data->cfg.microvolts = 0; data->cfg.gpio = -EINVAL; data->cfg.enabled_at_boot = 1; diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 96579296f04d..17a58c56eebf 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -684,7 +684,7 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev, } new_val++; - } while (desc->min + desc->step + new_val <= desc->max); + } while (desc->min + desc->step * new_val <= desc->max); new_idx = tmp_idx; new_val = tmp_val; diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index e8cfc99dd8f0..845aa2263b8a 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -552,7 +552,7 @@ static int __devinit mc13892_regulator_probe(struct platform_device *pdev) mc13xxx_lock(mc13892); ret = mc13xxx_reg_read(mc13892, MC13892_REVISION, &val); if (ret) - goto err_free; + goto err_unlock; /* enable switch auto mode */ if ((val & 0x0000FFFF) == 0x45d0) { @@ -562,7 +562,7 @@ static int __devinit mc13892_regulator_probe(struct platform_device *pdev) MC13892_SWITCHERS4_SW1MODE_AUTO | MC13892_SWITCHERS4_SW2MODE_AUTO); if (ret) - goto err_free; + goto err_unlock; ret = mc13xxx_reg_rmw(mc13892, MC13892_SWITCHERS5, MC13892_SWITCHERS5_SW3MODE_M | @@ -570,7 +570,7 @@ static int __devinit mc13892_regulator_probe(struct platform_device *pdev) MC13892_SWITCHERS5_SW3MODE_AUTO | MC13892_SWITCHERS5_SW4MODE_AUTO); if (ret) - goto err_free; + goto err_unlock; } mc13xxx_unlock(mc13892); @@ -612,10 +612,10 @@ static int __devinit mc13892_regulator_probe(struct platform_device *pdev) err: while (--i >= 0) regulator_unregister(priv->regulators[i]); + return ret; -err_free: +err_unlock: mc13xxx_unlock(mc13892); - return ret; } diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 58447db15de1..4ca2db059004 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -311,8 +311,7 @@ static int s5m8767_set_voltage(struct regulator_dev *rdev, struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); const struct s5m_voltage_desc *desc; int reg_id = rdev_get_id(rdev); - int reg, mask, ret; - int i; + int sel, reg, mask, ret; u8 val; switch (reg_id) { @@ -333,19 +332,20 @@ static int s5m8767_set_voltage(struct regulator_dev *rdev, desc = reg_voltage_map[reg_id]; - i = s5m8767_convert_voltage_to_sel(desc, min_uV, max_uV); - if (i < 0) - return i; + sel = s5m8767_convert_voltage_to_sel(desc, min_uV, max_uV); + if (sel < 0) + return sel; ret = s5m8767_get_voltage_register(rdev, ®); if (ret) return ret; s5m_reg_read(s5m8767->iodev, reg, &val); - val = val & mask; + val &= ~mask; + val |= sel; ret = s5m_reg_write(s5m8767->iodev, reg, val); - *selector = i; + *selector = sel; return ret; } diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 29b615ce3aff..cfc1f16f7771 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -79,6 +79,11 @@ static int tps6586x_ldo_list_voltage(struct regulator_dev *rdev, unsigned selector) { struct tps6586x_regulator *info = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + + /* LDO0 has minimal voltage 1.2V rather than 1.25V */ + if ((rid == TPS6586X_ID_LDO_0) && (selector == 0)) + return (info->voltages[0] - 50) * 1000; return info->voltages[selector] * 1000; } diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 4904a40b0d46..ff810e787eac 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -380,13 +380,15 @@ static int wm831x_buckv_set_current_limit(struct regulator_dev *rdev, int i; for (i = 0; i < ARRAY_SIZE(wm831x_dcdc_ilim); i++) { - if (max_uA <= wm831x_dcdc_ilim[i]) + if ((min_uA <= wm831x_dcdc_ilim[i]) && + (wm831x_dcdc_ilim[i] <= max_uA)) break; } if (i == ARRAY_SIZE(wm831x_dcdc_ilim)) return -EINVAL; - return wm831x_set_bits(wm831x, reg, WM831X_DC1_HC_THR_MASK, i); + return wm831x_set_bits(wm831x, reg, WM831X_DC1_HC_THR_MASK, + i << WM831X_DC1_HC_THR_SHIFT); } static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev) @@ -400,7 +402,8 @@ static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev) if (val < 0) return val; - return wm831x_dcdc_ilim[val & WM831X_DC1_HC_THR_MASK]; + val = (val & WM831X_DC1_HC_THR_MASK) >> WM831X_DC1_HC_THR_SHIFT; + return wm831x_dcdc_ilim[val]; } static struct regulator_ops wm831x_buckv_ops = { diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 634aac3f2d5f..b414e09c5620 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -101,7 +101,7 @@ static int wm831x_isink_set_current(struct regulator_dev *rdev, for (i = 0; i < ARRAY_SIZE(wm831x_isinkv_values); i++) { int val = wm831x_isinkv_values[i]; - if (min_uA >= val && val <= max_uA) { + if (min_uA <= val && val <= max_uA) { ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ISEL_MASK, i); return ret; diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index f1e4ab0f9fda..641e9f6499d1 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -506,22 +506,19 @@ static int wm831x_aldo_set_mode(struct regulator_dev *rdev, { struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); struct wm831x *wm831x = ldo->wm831x; - int ctrl_reg = ldo->base + WM831X_LDO_CONTROL; int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; int ret; switch (mode) { case REGULATOR_MODE_NORMAL: - ret = wm831x_set_bits(wm831x, on_reg, - WM831X_LDO7_ON_MODE, 0); + ret = wm831x_set_bits(wm831x, on_reg, WM831X_LDO7_ON_MODE, 0); if (ret < 0) return ret; break; case REGULATOR_MODE_IDLE: - ret = wm831x_set_bits(wm831x, ctrl_reg, - WM831X_LDO7_ON_MODE, + ret = wm831x_set_bits(wm831x, on_reg, WM831X_LDO7_ON_MODE, WM831X_LDO7_ON_MODE); if (ret < 0) return ret; diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index ab1e183a74b5..05ecfb872319 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -99,7 +99,7 @@ static int get_isink_val(int min_uA, int max_uA, u16 *setting) { int i; - for (i = ARRAY_SIZE(isink_cur) - 1; i >= 0; i--) { + for (i = 0; i < ARRAY_SIZE(isink_cur); i++) { if (min_uA <= isink_cur[i] && max_uA >= isink_cur[i]) { *setting = i; return 0; @@ -186,7 +186,7 @@ static int wm8350_isink_get_current(struct regulator_dev *rdev) return 0; } - return DIV_ROUND_CLOSEST(isink_cur[val], 100); + return isink_cur[val]; } /* turn on ISINK followed by DCDC */ @@ -495,25 +495,25 @@ static int wm8350_dcdc_set_suspend_enable(struct regulator_dev *rdev) val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER) & ~WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, - wm8350->pmic.dcdc1_hib_mode); + val | wm8350->pmic.dcdc1_hib_mode); break; case WM8350_DCDC_3: val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER) & ~WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, - wm8350->pmic.dcdc3_hib_mode); + val | wm8350->pmic.dcdc3_hib_mode); break; case WM8350_DCDC_4: val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER) & ~WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, - wm8350->pmic.dcdc4_hib_mode); + val | wm8350->pmic.dcdc4_hib_mode); break; case WM8350_DCDC_6: val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER) & ~WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, - wm8350->pmic.dcdc6_hib_mode); + val | wm8350->pmic.dcdc6_hib_mode); break; case WM8350_DCDC_2: case WM8350_DCDC_5: @@ -535,25 +535,25 @@ static int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev) val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, - WM8350_DCDC_HIB_MODE_DIS); + val | WM8350_DCDC_HIB_MODE_DIS); break; case WM8350_DCDC_3: val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, - WM8350_DCDC_HIB_MODE_DIS); + val | WM8350_DCDC_HIB_MODE_DIS); break; case WM8350_DCDC_4: val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, - WM8350_DCDC_HIB_MODE_DIS); + val | WM8350_DCDC_HIB_MODE_DIS); break; case WM8350_DCDC_6: val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, - WM8350_DCDC_HIB_MODE_DIS); + val | WM8350_DCDC_HIB_MODE_DIS); break; case WM8350_DCDC_2: case WM8350_DCDC_5: @@ -575,13 +575,13 @@ static int wm8350_dcdc25_set_suspend_enable(struct regulator_dev *rdev) val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) & ~WM8350_DC2_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | - WM8350_DC2_HIB_MODE_ACTIVE); + (WM8350_DC2_HIB_MODE_ACTIVE << WM8350_DC2_HIB_MODE_SHIFT)); break; case WM8350_DCDC_5: val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) - & ~WM8350_DC2_HIB_MODE_MASK; + & ~WM8350_DC5_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | - WM8350_DC5_HIB_MODE_ACTIVE); + (WM8350_DC5_HIB_MODE_ACTIVE << WM8350_DC5_HIB_MODE_SHIFT)); break; default: return -EINVAL; @@ -600,13 +600,13 @@ static int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev) val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) & ~WM8350_DC2_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | - WM8350_DC2_HIB_MODE_DISABLE); + (WM8350_DC2_HIB_MODE_DISABLE << WM8350_DC2_HIB_MODE_SHIFT)); break; case WM8350_DCDC_5: val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) - & ~WM8350_DC2_HIB_MODE_MASK; + & ~WM8350_DC5_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | - WM8350_DC2_HIB_MODE_DISABLE); + (WM8350_DC5_HIB_MODE_DISABLE << WM8350_DC5_HIB_MODE_SHIFT)); break; default: return -EINVAL; @@ -749,7 +749,7 @@ static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev) /* all LDOs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; - wm8350_reg_write(wm8350, volt_reg, WM8350_LDO1_HIB_MODE_DIS); + wm8350_reg_write(wm8350, volt_reg, val | WM8350_LDO1_HIB_MODE_DIS); return 0; } diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 70277a530133..85d31a69e117 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -50,16 +50,9 @@ static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf, return simple_read_from_buffer(userbuf, count, ppos, trace->va, len); } -static int rproc_open_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - - return 0; -} - static const struct file_operations trace_rproc_ops = { .read = rproc_trace_read, - .open = rproc_open_generic, + .open = simple_open, .llseek = generic_file_llseek, }; @@ -94,7 +87,7 @@ static ssize_t rproc_state_read(struct file *filp, char __user *userbuf, static const struct file_operations rproc_state_ops = { .read = rproc_state_read, - .open = rproc_open_generic, + .open = simple_open, .llseek = generic_file_llseek, }; @@ -114,7 +107,7 @@ static ssize_t rproc_name_read(struct file *filp, char __user *userbuf, static const struct file_operations rproc_name_ops = { .read = rproc_name_read, - .open = rproc_open_generic, + .open = simple_open, .llseek = generic_file_llseek, }; diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index afee0e8ae714..feddefc42109 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -72,9 +72,9 @@ static int pm860x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) struct pm860x_rtc_info *info = dev_get_drvdata(dev); if (enabled) - pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM, ALARM); + pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, ALARM_EN); else - pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM, 0); + pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, 0); return 0; } diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index cd188ab72f79..c293d0cdb104 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -902,6 +902,7 @@ read_rtc: } ds1307->nvram->attr.name = "nvram"; ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR; + sysfs_bin_attr_init(ds1307->nvram); ds1307->nvram->read = ds1307_nvram_read, ds1307->nvram->write = ds1307_nvram_write, ds1307->nvram->size = chip->nvram_size; diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index 550292304b0f..c9f890b088da 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -213,7 +213,6 @@ static struct platform_driver efi_rtc_driver = { .name = "rtc-efi", .owner = THIS_MODULE, }, - .probe = efi_rtc_probe, .remove = __exit_p(efi_rtc_remove), }; diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index 42f5f829b3ee..029e421baaed 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -360,12 +360,11 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op) &mpc5200_rtc_ops, THIS_MODULE); } - rtc->rtc->uie_unsupported = 1; - if (IS_ERR(rtc->rtc)) { err = PTR_ERR(rtc->rtc); goto out_free_irq; } + rtc->rtc->uie_unsupported = 1; return 0; diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 692de7360e94..684ef4bbfce4 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -339,8 +339,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) dev_dbg(&adev->dev, "revision = 0x%01x\n", ldata->hw_revision); /* Enable the clockwatch on ST Variants */ - if ((ldata->hw_designer == AMBA_VENDOR_ST) && - (ldata->hw_revision > 1)) + if (ldata->hw_designer == AMBA_VENDOR_ST) writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN, ldata->base + RTC_CR); diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c index 7f8e6c247935..33b6ba0afa0d 100644 --- a/drivers/rtc/rtc-r9701.c +++ b/drivers/rtc/rtc-r9701.c @@ -122,6 +122,7 @@ static const struct rtc_class_ops r9701_rtc_ops = { static int __devinit r9701_probe(struct spi_device *spi) { struct rtc_device *rtc; + struct rtc_time dt; unsigned char tmp; int res; @@ -132,6 +133,27 @@ static int __devinit r9701_probe(struct spi_device *spi) return -ENODEV; } + /* + * The device seems to be present. Now check if the registers + * contain invalid values. If so, try to write a default date: + * 2000/1/1 00:00:00 + */ + r9701_get_datetime(&spi->dev, &dt); + if (rtc_valid_tm(&dt)) { + dev_info(&spi->dev, "trying to repair invalid date/time\n"); + dt.tm_sec = 0; + dt.tm_min = 0; + dt.tm_hour = 0; + dt.tm_mday = 1; + dt.tm_mon = 0; + dt.tm_year = 100; + + if (r9701_set_datetime(&spi->dev, &dt)) { + dev_err(&spi->dev, "cannot repair RTC register\n"); + return -ENODEV; + } + } + rtc = rtc_device_register("r9701", &spi->dev, &r9701_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 9ccea134a996..3f3a29752369 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -40,6 +40,10 @@ enum s3c_cpu_type { TYPE_S3C64XX, }; +struct s3c_rtc_drv_data { + int cpu_type; +}; + /* I have yet to find an S3C implementation with more than one * of these rtc blocks in */ @@ -446,10 +450,12 @@ static const struct of_device_id s3c_rtc_dt_match[]; static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) { #ifdef CONFIG_OF + struct s3c_rtc_drv_data *data; if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); - return match->data; + data = (struct s3c_rtc_drv_data *) match->data; + return data->cpu_type; } #endif return platform_get_device_id(pdev)->driver_data; @@ -664,20 +670,27 @@ static int s3c_rtc_resume(struct platform_device *pdev) #define s3c_rtc_resume NULL #endif +static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = { + [TYPE_S3C2410] = { TYPE_S3C2410 }, + [TYPE_S3C2416] = { TYPE_S3C2416 }, + [TYPE_S3C2443] = { TYPE_S3C2443 }, + [TYPE_S3C64XX] = { TYPE_S3C64XX }, +}; + #ifdef CONFIG_OF static const struct of_device_id s3c_rtc_dt_match[] = { { - .compatible = "samsung,s3c2410-rtc" - .data = TYPE_S3C2410, + .compatible = "samsung,s3c2410-rtc", + .data = &s3c_rtc_drv_data_array[TYPE_S3C2410], }, { - .compatible = "samsung,s3c2416-rtc" - .data = TYPE_S3C2416, + .compatible = "samsung,s3c2416-rtc", + .data = &s3c_rtc_drv_data_array[TYPE_S3C2416], }, { - .compatible = "samsung,s3c2443-rtc" - .data = TYPE_S3C2443, + .compatible = "samsung,s3c2443-rtc", + .data = &s3c_rtc_drv_data_array[TYPE_S3C2443], }, { - .compatible = "samsung,s3c6410-rtc" - .data = TYPE_S3C64XX, + .compatible = "samsung,s3c6410-rtc", + .data = &s3c_rtc_drv_data_array[TYPE_S3C64XX], }, {}, }; diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 4c2c6df2a9ef..258abeabf624 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -112,6 +112,7 @@ static const u8 twl6030_rtc_reg_map[] = { #define BIT_RTC_CTRL_REG_TEST_MODE_M 0x10 #define BIT_RTC_CTRL_REG_SET_32_COUNTER_M 0x20 #define BIT_RTC_CTRL_REG_GET_TIME_M 0x40 +#define BIT_RTC_CTRL_REG_RTC_V_OPT 0x80 /* RTC_STATUS_REG bitfields */ #define BIT_RTC_STATUS_REG_RUN_M 0x02 @@ -235,25 +236,57 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) unsigned char rtc_data[ALL_TIME_REGS + 1]; int ret; u8 save_control; + u8 rtc_control; ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "%s: reading CTRL_REG, error %d\n", __func__, ret); return ret; + } + /* for twl6030/32 make sure BIT_RTC_CTRL_REG_GET_TIME_M is clear */ + if (twl_class_is_6030()) { + if (save_control & BIT_RTC_CTRL_REG_GET_TIME_M) { + save_control &= ~BIT_RTC_CTRL_REG_GET_TIME_M; + ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "%s clr GET_TIME, error %d\n", + __func__, ret); + return ret; + } + } + } - save_control |= BIT_RTC_CTRL_REG_GET_TIME_M; + /* Copy RTC counting registers to static registers or latches */ + rtc_control = save_control | BIT_RTC_CTRL_REG_GET_TIME_M; - ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); - if (ret < 0) + /* for twl6030/32 enable read access to static shadowed registers */ + if (twl_class_is_6030()) + rtc_control |= BIT_RTC_CTRL_REG_RTC_V_OPT; + + ret = twl_rtc_write_u8(rtc_control, REG_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "%s: writing CTRL_REG, error %d\n", __func__, ret); return ret; + } ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); if (ret < 0) { - dev_err(dev, "rtc_read_time error %d\n", ret); + dev_err(dev, "%s: reading data, error %d\n", __func__, ret); return ret; } + /* for twl6030 restore original state of rtc control register */ + if (twl_class_is_6030()) { + ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "%s: restore CTRL_REG, error %d\n", + __func__, ret); + return ret; + } + } + tm->tm_sec = bcd2bin(rtc_data[0]); tm->tm_min = bcd2bin(rtc_data[1]); tm->tm_hour = bcd2bin(rtc_data[2]); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index c21871a4e73d..bc2e8a7c265b 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2844,6 +2844,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( sector_t recid, trkid; unsigned int offs; unsigned int count, count_to_trk_end; + int ret; basedev = block->base; if (rq_data_dir(req) == READ) { @@ -2884,8 +2885,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0); if (IS_ERR(itcw)) { - dasd_sfree_request(cqr, startdev); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto out_error; } cqr->cpaddr = itcw_get_tcw(itcw); if (prepare_itcw(itcw, first_trk, last_trk, @@ -2897,8 +2898,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( /* Clock not in sync and XRC is enabled. * Try again later. */ - dasd_sfree_request(cqr, startdev); - return ERR_PTR(-EAGAIN); + ret = -EAGAIN; + goto out_error; } len_to_track_end = 0; /* @@ -2937,8 +2938,10 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( tidaw_flags = 0; last_tidaw = itcw_add_tidaw(itcw, tidaw_flags, dst, part_len); - if (IS_ERR(last_tidaw)) - return ERR_PTR(-EINVAL); + if (IS_ERR(last_tidaw)) { + ret = -EINVAL; + goto out_error; + } dst += part_len; } } @@ -2947,8 +2950,10 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( dst = page_address(bv->bv_page) + bv->bv_offset; last_tidaw = itcw_add_tidaw(itcw, 0x00, dst, bv->bv_len); - if (IS_ERR(last_tidaw)) - return ERR_PTR(-EINVAL); + if (IS_ERR(last_tidaw)) { + ret = -EINVAL; + goto out_error; + } } } last_tidaw->flags |= TIDAW_FLAGS_LAST; @@ -2968,6 +2973,9 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; return cqr; +out_error: + dasd_sfree_request(cqr, startdev); + return ERR_PTR(ret); } static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 85f4a9a5d12e..73bef0bd394c 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -903,7 +903,7 @@ static int ur_set_online(struct ccw_device *cdev) goto fail_urdev_put; } - cdev_init(urd->char_device, &ur_fops); + urd->char_device->ops = &ur_fops; urd->char_device->dev = MKDEV(major, minor); urd->char_device->owner = ur_fops.owner; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 120955c66410..8334dadc681d 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1672,7 +1672,8 @@ static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd) { QETH_DBF_TEXT(SETUP, 2, "cfgblkt"); - if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && prcd[76] == 0xF5) { + if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && + (prcd[76] == 0xF5 || prcd[76] == 0xF6)) { card->info.blkt.time_total = 250; card->info.blkt.inter_packet = 5; card->info.blkt.inter_packet_jumbo = 15; @@ -4540,7 +4541,8 @@ static void qeth_determine_capabilities(struct qeth_card *card) goto out_offline; } qeth_configure_unitaddr(card, prcd); - qeth_configure_blkt_default(card, prcd); + if (ddev_offline) + qeth_configure_blkt_default(card, prcd); kfree(prcd); rc = qdio_get_ssqd_desc(ddev, &card->ssqd); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 351dc0b86fab..a3a056a9db67 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -218,6 +218,9 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, if (!shost->shost_gendev.parent) shost->shost_gendev.parent = dev ? dev : &platform_bus; + if (!dma_dev) + dma_dev = shost->shost_gendev.parent; + shost->dma_dev = dma_dev; error = device_add(&shost->shost_gendev); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e002cd466e9a..467dc38246f9 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4549,8 +4549,12 @@ static int ipr_ata_slave_alloc(struct scsi_device *sdev) ENTER; if (sdev->sdev_target) sata_port = sdev->sdev_target->hostdata; - if (sata_port) + if (sata_port) { rc = ata_sas_port_init(sata_port->ap); + if (rc == 0) + rc = ata_sas_sync_probe(sata_port->ap); + } + if (rc) ipr_slave_destroy(sdev); diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index ef9560dff295..cc83b66d45b7 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -1742,17 +1742,19 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK; - if (mfs >= FC_SP_MIN_MAX_PAYLOAD && - mfs <= lport->mfs) { - lport->mfs = mfs; - fc_host_maxframe_size(lport->host) = mfs; - } else { + + if (mfs < FC_SP_MIN_MAX_PAYLOAD || mfs > FC_SP_MAX_MAX_PAYLOAD) { FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, " "lport->mfs:%hu\n", mfs, lport->mfs); fc_lport_error(lport, fp); goto err; } + if (mfs <= lport->mfs) { + lport->mfs = mfs; + fc_host_maxframe_size(lport->host) = mfs; + } + csp_flags = ntohs(flp->fl_csp.sp_features); r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index bc0cecc6ad62..441d88ad99a7 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -546,11 +546,12 @@ static struct ata_port_info sata_port_info = { .port_ops = &sas_sata_ops }; -int sas_ata_init_host_and_port(struct domain_device *found_dev) +int sas_ata_init(struct domain_device *found_dev) { struct sas_ha_struct *ha = found_dev->port->ha; struct Scsi_Host *shost = ha->core.shost; struct ata_port *ap; + int rc; ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, @@ -567,8 +568,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev) ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; - /* publish initialized ata port */ - smp_wmb(); + rc = ata_sas_port_init(ap); + if (rc) { + ata_sas_port_destroy(ap); + return rc; + } found_dev->sata_dev.ap = ap; return 0; @@ -648,18 +652,13 @@ static void sas_get_ata_command_set(struct domain_device *dev) void sas_probe_sata(struct asd_sas_port *port) { struct domain_device *dev, *n; - int err; mutex_lock(&port->ha->disco_mutex); - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + list_for_each_entry(dev, &port->disco_list, disco_list_node) { if (!dev_is_sata(dev)) continue; - err = sas_ata_init_host_and_port(dev); - if (err) - sas_fail_probe(dev, __func__, err); - else - ata_sas_async_port_init(dev->sata_dev.ap); + ata_sas_async_probe(dev->sata_dev.ap); } mutex_unlock(&port->ha->disco_mutex); @@ -718,18 +717,6 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) sas_put_device(dev); } -static bool sas_ata_dev_eh_valid(struct domain_device *dev) -{ - struct ata_port *ap; - - if (!dev_is_sata(dev)) - return false; - ap = dev->sata_dev.ap; - /* consume fully initialized ata ports */ - smp_rmb(); - return !!ap; -} - void sas_ata_strategy_handler(struct Scsi_Host *shost) { struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); @@ -753,7 +740,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) spin_lock(&port->dev_list_lock); list_for_each_entry(dev, &port->dev_list, dev_list_node) { - if (!sas_ata_dev_eh_valid(dev)) + if (!dev_is_sata(dev)) continue; async_schedule_domain(async_sas_ata_eh, dev, &async); } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 364679675602..629a0865b130 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -72,6 +72,7 @@ static int sas_get_port_device(struct asd_sas_port *port) struct asd_sas_phy *phy; struct sas_rphy *rphy; struct domain_device *dev; + int rc = -ENODEV; dev = sas_alloc_device(); if (!dev) @@ -110,9 +111,16 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_init_dev(dev); + dev->port = port; switch (dev->dev_type) { - case SAS_END_DEV: case SATA_DEV: + rc = sas_ata_init(dev); + if (rc) { + rphy = NULL; + break; + } + /* fall through */ + case SAS_END_DEV: rphy = sas_end_device_alloc(port->port); break; case EDGE_DEV: @@ -131,19 +139,14 @@ static int sas_get_port_device(struct asd_sas_port *port) if (!rphy) { sas_put_device(dev); - return -ENODEV; + return rc; } - spin_lock_irq(&port->phy_list_lock); - list_for_each_entry(phy, &port->phy_list, port_phy_el) - sas_phy_set_target(phy, dev); - spin_unlock_irq(&port->phy_list_lock); rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE); sas_fill_in_rphy(dev, rphy); sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); port->port_dev = dev; - dev->port = port; dev->linkrate = port->linkrate; dev->min_linkrate = port->linkrate; dev->max_linkrate = port->linkrate; @@ -155,6 +158,7 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_device_set_phy(dev, port->port); dev->rphy = rphy; + get_device(&dev->rphy->dev); if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEV) list_add_tail(&dev->disco_list_node, &port->disco_list); @@ -164,6 +168,11 @@ static int sas_get_port_device(struct asd_sas_port *port) spin_unlock_irq(&port->dev_list_lock); } + spin_lock_irq(&port->phy_list_lock); + list_for_each_entry(phy, &port->phy_list, port_phy_el) + sas_phy_set_target(phy, dev); + spin_unlock_irq(&port->phy_list_lock); + return 0; } @@ -205,8 +214,7 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) static void sas_probe_devices(struct work_struct *work) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_PROBE, &port->disc.pending); @@ -255,6 +263,9 @@ void sas_free_device(struct kref *kref) { struct domain_device *dev = container_of(kref, typeof(*dev), kref); + put_device(&dev->rphy->dev); + dev->rphy = NULL; + if (dev->parent) sas_put_device(dev->parent); @@ -291,8 +302,7 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d static void sas_destruct_devices(struct work_struct *work) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_DESTRUCT, &port->disc.pending); @@ -302,7 +312,6 @@ static void sas_destruct_devices(struct work_struct *work) sas_remove_children(&dev->rphy->dev); sas_rphy_delete(dev->rphy); - dev->rphy = NULL; sas_unregister_common_dev(port, dev); } } @@ -314,11 +323,11 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) /* this rphy never saw sas_rphy_add */ list_del_init(&dev->disco_list_node); sas_rphy_free(dev->rphy); - dev->rphy = NULL; sas_unregister_common_dev(port, dev); + return; } - if (dev->rphy && !test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { + if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { sas_rphy_unlink(dev->rphy); list_move_tail(&dev->disco_list_node, &port->destroy_list); sas_discover_event(dev->port, DISCE_DESTRUCT); @@ -377,8 +386,7 @@ static void sas_discover_domain(struct work_struct *work) { struct domain_device *dev; int error = 0; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending); @@ -419,8 +427,6 @@ static void sas_discover_domain(struct work_struct *work) if (error) { sas_rphy_free(dev->rphy); - dev->rphy = NULL; - list_del_init(&dev->disco_list_node); spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); @@ -437,8 +443,7 @@ static void sas_discover_domain(struct work_struct *work) static void sas_revalidate_domain(struct work_struct *work) { int res = 0; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; struct sas_ha_struct *ha = port->ha; @@ -466,21 +471,25 @@ static void sas_revalidate_domain(struct work_struct *work) /* ---------- Events ---------- */ -static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work) +static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw) { - /* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */ - scsi_queue_work(ha->core.shost, work); + /* chained work is not subject to SA_HA_DRAINING or + * SAS_HA_REGISTERED, because it is either submitted in the + * workqueue, or known to be submitted from a context that is + * not racing against draining + */ + scsi_queue_work(ha->core.shost, &sw->work); } static void sas_chain_event(int event, unsigned long *pending, - struct work_struct *work, + struct sas_work *sw, struct sas_ha_struct *ha) { if (!test_and_set_bit(event, pending)) { unsigned long flags; spin_lock_irqsave(&ha->state_lock, flags); - sas_chain_work(ha, work); + sas_chain_work(ha, sw); spin_unlock_irqrestore(&ha->state_lock, flags); } } @@ -519,7 +528,7 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) disc->pending = 0; for (i = 0; i < DISC_NUM_EVENTS; i++) { - INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]); + INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]); disc->disc_work[i].port = port; } } diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 16639bbae629..4e4292d210c1 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -27,19 +27,21 @@ #include "sas_internal.h" #include "sas_dump.h" -void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) +void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) { if (!test_bit(SAS_HA_REGISTERED, &ha->state)) return; - if (test_bit(SAS_HA_DRAINING, &ha->state)) - list_add(&work->entry, &ha->defer_q); - else - scsi_queue_work(ha->core.shost, work); + if (test_bit(SAS_HA_DRAINING, &ha->state)) { + /* add it to the defer list, if not already pending */ + if (list_empty(&sw->drain_node)) + list_add(&sw->drain_node, &ha->defer_q); + } else + scsi_queue_work(ha->core.shost, &sw->work); } static void sas_queue_event(int event, unsigned long *pending, - struct work_struct *work, + struct sas_work *work, struct sas_ha_struct *ha) { if (!test_and_set_bit(event, pending)) { @@ -55,7 +57,7 @@ static void sas_queue_event(int event, unsigned long *pending, void __sas_drain_work(struct sas_ha_struct *ha) { struct workqueue_struct *wq = ha->core.shost->work_q; - struct work_struct *w, *_w; + struct sas_work *sw, *_sw; set_bit(SAS_HA_DRAINING, &ha->state); /* flush submitters */ @@ -66,9 +68,9 @@ void __sas_drain_work(struct sas_ha_struct *ha) spin_lock_irq(&ha->state_lock); clear_bit(SAS_HA_DRAINING, &ha->state); - list_for_each_entry_safe(w, _w, &ha->defer_q, entry) { - list_del_init(&w->entry); - sas_queue_work(ha, w); + list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { + list_del_init(&sw->drain_node); + sas_queue_work(ha, sw); } spin_unlock_irq(&ha->state_lock); } @@ -151,7 +153,7 @@ int sas_init_events(struct sas_ha_struct *sas_ha) int i; for (i = 0; i < HA_NUM_EVENTS; i++) { - INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); + INIT_SAS_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); sas_ha->ha_events[i].ha = sas_ha; } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 05acd9e35fc4..caa0525d2523 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -202,6 +202,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) u8 sas_addr[SAS_ADDR_SIZE]; struct smp_resp *resp = rsp; struct discover_resp *dr = &resp->disc; + struct sas_ha_struct *ha = dev->port->ha; struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; struct sas_rphy *rphy = dev->rphy; @@ -209,6 +210,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) char *type; if (new_phy) { + if (WARN_ON_ONCE(test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))) + return; phy->phy = sas_phy_alloc(&rphy->dev, phy_id); /* FIXME: error_handling */ @@ -233,6 +236,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); phy->attached_dev_type = to_dev_type(dr); + if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) + goto out; phy->phy_id = phy_id; phy->linkrate = dr->linkrate; phy->attached_sata_host = dr->attached_sata_host; @@ -240,7 +245,14 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) phy->attached_sata_ps = dr->attached_sata_ps; phy->attached_iproto = dr->iproto << 1; phy->attached_tproto = dr->tproto << 1; - memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); + /* help some expanders that fail to zero sas_address in the 'no + * device' case + */ + if (phy->attached_dev_type == NO_DEVICE || + phy->linkrate < SAS_LINK_RATE_1_5_GBPS) + memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + else + memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); phy->attached_phy_id = dr->attached_phy_id; phy->phy_change_count = dr->change_count; phy->routing_attr = dr->routing_attr; @@ -266,6 +278,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) return; } + out: switch (phy->attached_dev_type) { case SATA_PENDING: type = "stp pending"; @@ -304,7 +317,15 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) else return; - SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", + /* if the attached device type changed and ata_eh is active, + * make sure we run revalidation when eh completes (see: + * sas_enable_revalidation) + */ + if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) + set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending); + + SAS_DPRINTK("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", + test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "", SAS_ADDR(dev->sas_addr), phy->phy_id, sas_route_char(dev, phy), phy->linkrate, SAS_ADDR(phy->attached_sas_addr), type); @@ -776,13 +797,16 @@ static struct domain_device *sas_ex_discover_end_dev( if (res) goto out_free; + sas_init_dev(child); + res = sas_ata_init(child); + if (res) + goto out_free; rphy = sas_end_device_alloc(phy->port); - if (unlikely(!rphy)) + if (!rphy) goto out_free; - sas_init_dev(child); - child->rphy = rphy; + get_device(&rphy->dev); list_add_tail(&child->disco_list_node, &parent->port->disco_list); @@ -806,6 +830,7 @@ static struct domain_device *sas_ex_discover_end_dev( sas_init_dev(child); child->rphy = rphy; + get_device(&rphy->dev); sas_fill_in_rphy(child, rphy); list_add_tail(&child->disco_list_node, &parent->port->disco_list); @@ -830,8 +855,6 @@ static struct domain_device *sas_ex_discover_end_dev( out_list_del: sas_rphy_free(child->rphy); - child->rphy = NULL; - list_del(&child->disco_list_node); spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); @@ -911,6 +934,7 @@ static struct domain_device *sas_ex_discover_expander( } port = parent->port; child->rphy = rphy; + get_device(&rphy->dev); edev = rphy_to_expander_device(rphy); child->dev_type = phy->attached_dev_type; kref_get(&parent->kref); @@ -934,6 +958,7 @@ static struct domain_device *sas_ex_discover_expander( res = sas_discover_expander(child); if (res) { + sas_rphy_delete(rphy); spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); spin_unlock_irq(&parent->port->dev_list_lock); @@ -1718,9 +1743,17 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, int phy_change_count = 0; res = sas_get_phy_change_count(dev, i, &phy_change_count); - if (res) - goto out; - else if (phy_change_count != ex->ex_phy[i].phy_change_count) { + switch (res) { + case SMP_RESP_PHY_VACANT: + case SMP_RESP_NO_PHY: + continue; + case SMP_RESP_FUNC_ACC: + break; + default: + return res; + } + + if (phy_change_count != ex->ex_phy[i].phy_change_count) { if (update) ex->ex_phy[i].phy_change_count = phy_change_count; @@ -1728,8 +1761,7 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, return 0; } } -out: - return res; + return 0; } static int sas_get_ex_change_count(struct domain_device *dev, int *ecc) diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 120bff64be30..10cb5ae30977 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -94,8 +94,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) void sas_hae_reset(struct work_struct *work) { - struct sas_ha_event *ev = - container_of(work, struct sas_ha_event, work); + struct sas_ha_event *ev = to_sas_ha_event(work); struct sas_ha_struct *ha = ev->ha; clear_bit(HAE_RESET, &ha->pending); @@ -369,14 +368,14 @@ static void sas_phy_release(struct sas_phy *phy) static void phy_reset_work(struct work_struct *work) { - struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); + struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work); d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); } static void phy_enable_work(struct work_struct *work) { - struct sas_phy_data *d = container_of(work, typeof(*d), enable_work); + struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work); d->enable_result = sas_phy_enable(d->phy, d->enable); } @@ -389,8 +388,8 @@ static int sas_phy_setup(struct sas_phy *phy) return -ENOMEM; mutex_init(&d->event_lock); - INIT_WORK(&d->reset_work, phy_reset_work); - INIT_WORK(&d->enable_work, phy_enable_work); + INIT_SAS_WORK(&d->reset_work, phy_reset_work); + INIT_SAS_WORK(&d->enable_work, phy_enable_work); d->phy = phy; phy->hostdata = d; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index f05c63879949..507e4cf12e56 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -45,10 +45,10 @@ struct sas_phy_data { struct mutex event_lock; int hard_reset; int reset_result; - struct work_struct reset_work; + struct sas_work reset_work; int enable; int enable_result; - struct work_struct enable_work; + struct sas_work enable_work; }; void sas_scsi_recover_host(struct Scsi_Host *shost); @@ -80,7 +80,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work); void sas_porte_link_reset_err(struct work_struct *work); void sas_porte_timer_event(struct work_struct *work); void sas_porte_hard_reset(struct work_struct *work); -void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work); +void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw); int sas_notify_lldd_dev_found(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *); diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index dcfd4a9105c5..521422e857ab 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -32,8 +32,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending); @@ -43,8 +42,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) static void sas_phye_oob_done(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending); @@ -53,8 +51,7 @@ static void sas_phye_oob_done(struct work_struct *work) static void sas_phye_oob_error(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; @@ -85,8 +82,7 @@ static void sas_phye_oob_error(struct work_struct *work) static void sas_phye_spinup_hold(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = @@ -127,14 +123,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) phy->error = 0; INIT_LIST_HEAD(&phy->port_phy_el); for (k = 0; k < PORT_NUM_EVENTS; k++) { - INIT_WORK(&phy->port_events[k].work, - sas_port_event_fns[k]); + INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]); phy->port_events[k].phy = phy; } for (k = 0; k < PHY_NUM_EVENTS; k++) { - INIT_WORK(&phy->phy_events[k].work, - sas_phy_event_fns[k]); + INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]); phy->phy_events[k].phy = phy; } @@ -144,8 +138,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) spin_lock_init(&phy->sas_prim_lock); phy->frame_rcvd_size = 0; - phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, - i); + phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i); if (!phy->phy) return -ENOMEM; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index eb19c016d500..e884a8c58a0c 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -123,7 +123,7 @@ static void sas_form_port(struct asd_sas_phy *phy) spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags); if (!port->port) { - port->port = sas_port_alloc(phy->phy->dev.parent, phy->id); + port->port = sas_port_alloc(phy->phy->dev.parent, port->id); BUG_ON(!port->port); sas_port_add(port->port); } @@ -208,8 +208,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) void sas_porte_bytes_dmaed(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending); @@ -219,8 +218,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work) void sas_porte_broadcast_rcvd(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; unsigned long flags; u32 prim; @@ -237,8 +235,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) void sas_porte_link_reset_err(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); @@ -248,8 +245,7 @@ void sas_porte_link_reset_err(struct work_struct *work) void sas_porte_timer_event(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); @@ -259,8 +255,7 @@ void sas_porte_timer_event(struct work_struct *work) void sas_porte_hard_reset(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 5bdf2eecb178..af04b0d6688d 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -997,13 +997,6 @@ lpfc_debugfs_dumpDataDif_write(struct file *file, const char __user *buf, return nbytes; } -static int -lpfc_debugfs_dif_err_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t lpfc_debugfs_dif_err_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) @@ -3541,7 +3534,7 @@ static const struct file_operations lpfc_debugfs_op_dumpDif = { #undef lpfc_debugfs_op_dif_err static const struct file_operations lpfc_debugfs_op_dif_err = { .owner = THIS_MODULE, - .open = lpfc_debugfs_dif_err_open, + .open = simple_open, .llseek = lpfc_debugfs_lseek, .read = lpfc_debugfs_dif_err_read, .write = lpfc_debugfs_dif_err_write, diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index f74cc0602f3b..bc3cc6d91117 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -1367,6 +1367,9 @@ qla2x00_read_optrom(struct fc_bsg_job *bsg_job) struct qla_hw_data *ha = vha->hw; int rval = 0; + if (ha->flags.isp82xx_reset_hdlr_active) + return -EBUSY; + rval = qla2x00_optrom_setup(bsg_job, vha, 0); if (rval) return rval; diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 897731b93df2..62324a1d5573 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -15,7 +15,7 @@ * | Mailbox commands | 0x113e | 0x112c-0x112e | * | | | 0x113a | * | Device Discovery | 0x2086 | 0x2020-0x2022 | - * | Queue Command and IO tracing | 0x302f | 0x3006,0x3008 | + * | Queue Command and IO tracing | 0x3030 | 0x3006,0x3008 | * | | | 0x302d-0x302e | * | DPC Thread | 0x401c | | * | Async Events | 0x505d | 0x502b-0x502f | diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index f79844ce7122..ce42288049b5 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1715,13 +1715,24 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) res = DID_ERROR << 16; break; } - } else { + } else if (lscsi_status != SAM_STAT_TASK_SET_FULL && + lscsi_status != SAM_STAT_BUSY) { + /* + * scsi status of task set and busy are considered to be + * task not completed. + */ + ql_dbg(ql_dbg_io, fcport->vha, 0x301f, "Dropped frame(s) detected (0x%x " - "of 0x%x bytes).\n", resid, scsi_bufflen(cp)); + "of 0x%x bytes).\n", resid, + scsi_bufflen(cp)); res = DID_ERROR << 16 | lscsi_status; goto check_scsi_status; + } else { + ql_dbg(ql_dbg_io, fcport->vha, 0x3030, + "scsi_status: 0x%x, lscsi_status: 0x%x\n", + scsi_status, lscsi_status); } res = DID_OK << 16 | lscsi_status; diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index f0528539bbbc..de722a933438 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -3125,6 +3125,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) ql_log(ql_log_info, vha, 0x00b7, "HW State: COLD/RE-INIT.\n"); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); + qla82xx_set_rst_ready(ha); if (ql2xmdenable) { if (qla82xx_md_collect(vha)) ql_log(ql_log_warn, vha, 0xb02c, diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index a2f999273a5f..7db803377c64 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3577,9 +3577,25 @@ void qla2x00_relogin(struct scsi_qla_host *vha) continue; /* Attempt a retry. */ status = 1; - } else + } else { status = qla2x00_fabric_login(vha, fcport, &next_loopid); + if (status == QLA_SUCCESS) { + int status2; + uint8_t opts; + + opts = 0; + if (fcport->flags & + FCF_FCP2_DEVICE) + opts |= BIT_1; + status2 = + qla2x00_get_port_database( + vha, fcport, + opts); + if (status2 != QLA_SUCCESS) + status = 1; + } + } } else status = qla2x00_local_device_login(vha, fcport); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 3c13c0a6be63..a683e766d1ae 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -1017,6 +1017,9 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) return; + if (ha->flags.isp82xx_reset_hdlr_active) + return; + ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header)); if (hdr.version == __constant_cpu_to_le16(0xffff)) diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 29d780c38040..f5fdb16bec9b 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,9 +7,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.03.07.13-k" +#define QLA2XXX_VERSION "8.04.00.03-k" #define QLA_DRIVER_MAJOR_VER 8 -#define QLA_DRIVER_MINOR_VER 3 -#define QLA_DRIVER_PATCH_VER 7 +#define QLA_DRIVER_MINOR_VER 4 +#define QLA_DRIVER_PATCH_VER 0 #define QLA_DRIVER_BETA_VER 3 diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 2cfcbffa41fd..386f0c53bea7 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -835,7 +835,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, scsi_eh_restore_cmnd(scmd, &ses); - if (sdrv->eh_action) + if (sdrv && sdrv->eh_action) rtn = sdrv->eh_action(scmd, cmnd, cmnd_size, rtn); return rtn; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index ead6405f3e51..5dfd7495d1a1 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1638,7 +1638,7 @@ struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, request_fn_proc *request_fn) { struct request_queue *q; - struct device *dev = shost->shost_gendev.parent; + struct device *dev = shost->dma_dev; q = blk_init_queue(request_fn, NULL); if (!q) diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index efccd72c4a3e..1b3843117268 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -175,7 +175,8 @@ static void virtscsi_complete_free(void *buf) if (cmd->comp) complete_all(cmd->comp); - mempool_free(cmd, virtscsi_cmd_pool); + else + mempool_free(cmd, virtscsi_cmd_pool); } static void virtscsi_ctrl_done(struct virtqueue *vq) @@ -311,21 +312,22 @@ out: static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd) { DECLARE_COMPLETION_ONSTACK(comp); - int ret; + int ret = FAILED; cmd->comp = ∁ - ret = virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd, - sizeof cmd->req.tmf, sizeof cmd->resp.tmf, - GFP_NOIO); - if (ret < 0) - return FAILED; + if (virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd, + sizeof cmd->req.tmf, sizeof cmd->resp.tmf, + GFP_NOIO) < 0) + goto out; wait_for_completion(&comp); - if (cmd->resp.tmf.response != VIRTIO_SCSI_S_OK && - cmd->resp.tmf.response != VIRTIO_SCSI_S_FUNCTION_SUCCEEDED) - return FAILED; + if (cmd->resp.tmf.response == VIRTIO_SCSI_S_OK || + cmd->resp.tmf.response == VIRTIO_SCSI_S_FUNCTION_SUCCEEDED) + ret = SUCCESS; - return SUCCESS; +out: + mempool_free(cmd, virtscsi_cmd_pool); + return ret; } static int virtscsi_device_reset(struct scsi_cmnd *sc) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3ed748355b98..00c024039c97 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -74,7 +74,7 @@ config SPI_ATMEL This selects a driver for the Atmel SPI Controller, present on many AT32 (AVR32) and AT91 (ARM) chips. -config SPI_BFIN +config SPI_BFIN5XX tristate "SPI controller driver for ADI Blackfin5xx" depends on BLACKFIN help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index a1d48e0ba3dc..9d75d2198ff5 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATH79) += spi-ath79.o obj-$(CONFIG_SPI_AU1550) += spi-au1550.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o -obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o +obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index f01b2648452e..7491971139a6 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -1,7 +1,7 @@ /* * Broadcom BCM63xx SPI controller support * - * Copyright (C) 2009-2011 Florian Fainelli <florian@openwrt.org> + * Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org> * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> * * This program is free software; you can redistribute it and/or @@ -30,6 +30,8 @@ #include <linux/spi/spi.h> #include <linux/completion.h> #include <linux/err.h> +#include <linux/workqueue.h> +#include <linux/pm_runtime.h> #include <bcm63xx_dev_spi.h> @@ -37,8 +39,6 @@ #define DRV_VER "0.1.2" struct bcm63xx_spi { - spinlock_t lock; - int stopping; struct completion done; void __iomem *regs; @@ -96,17 +96,12 @@ static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { { 391000, SPI_CLK_0_391MHZ } }; -static int bcm63xx_spi_setup_transfer(struct spi_device *spi, - struct spi_transfer *t) +static int bcm63xx_spi_check_transfer(struct spi_device *spi, + struct spi_transfer *t) { - struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); u8 bits_per_word; - u8 clk_cfg, reg; - u32 hz; - int i; bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; - hz = (t) ? t->speed_hz : spi->max_speed_hz; if (bits_per_word != 8) { dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", __func__, bits_per_word); @@ -119,6 +114,19 @@ static int bcm63xx_spi_setup_transfer(struct spi_device *spi, return -EINVAL; } + return 0; +} + +static void bcm63xx_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); + u32 hz; + u8 clk_cfg, reg; + int i; + + hz = (t) ? t->speed_hz : spi->max_speed_hz; + /* Find the closest clock configuration */ for (i = 0; i < SPI_CLK_MASK; i++) { if (hz <= bcm63xx_spi_freq_table[i][0]) { @@ -139,8 +147,6 @@ static int bcm63xx_spi_setup_transfer(struct spi_device *spi, bcm_spi_writeb(bs, reg, SPI_CLK_CFG); dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", clk_cfg, hz); - - return 0; } /* the spi->mode bits understood by this driver: */ @@ -153,9 +159,6 @@ static int bcm63xx_spi_setup(struct spi_device *spi) bs = spi_master_get_devdata(spi->master); - if (bs->stopping) - return -ESHUTDOWN; - if (!spi->bits_per_word) spi->bits_per_word = 8; @@ -165,7 +168,7 @@ static int bcm63xx_spi_setup(struct spi_device *spi) return -EINVAL; } - ret = bcm63xx_spi_setup_transfer(spi, NULL); + ret = bcm63xx_spi_check_transfer(spi, NULL); if (ret < 0) { dev_err(&spi->dev, "setup: unsupported mode bits %x\n", spi->mode & ~MODEBITS); @@ -190,28 +193,29 @@ static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) bs->remaining_bytes -= size; } -static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, + struct spi_transfer *t) { struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); u16 msg_ctl; u16 cmd; + /* Disable the CMD_DONE interrupt */ + bcm_spi_writeb(bs, 0, SPI_INT_MASK); + dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf, t->len); /* Transmitter is inhibited */ bs->tx_ptr = t->tx_buf; bs->rx_ptr = t->rx_buf; - init_completion(&bs->done); if (t->tx_buf) { bs->remaining_bytes = t->len; bcm63xx_spi_fill_tx_fifo(bs); } - /* Enable the command done interrupt which - * we use to determine completion of a command */ - bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); + init_completion(&bs->done); /* Fill in the Message control register */ msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); @@ -230,33 +234,76 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); bcm_spi_writew(bs, cmd, SPI_CMD); - wait_for_completion(&bs->done); - /* Disable the CMD_DONE interrupt */ - bcm_spi_writeb(bs, 0, SPI_INT_MASK); + /* Enable the CMD_DONE interrupt */ + bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); return t->len - bs->remaining_bytes; } -static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m) +static int bcm63xx_spi_prepare_transfer(struct spi_master *master) { - struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); - struct spi_transfer *t; - int ret = 0; + struct bcm63xx_spi *bs = spi_master_get_devdata(master); - if (unlikely(list_empty(&m->transfers))) - return -EINVAL; + pm_runtime_get_sync(&bs->pdev->dev); - if (bs->stopping) - return -ESHUTDOWN; + return 0; +} + +static int bcm63xx_spi_unprepare_transfer(struct spi_master *master) +{ + struct bcm63xx_spi *bs = spi_master_get_devdata(master); + + pm_runtime_put(&bs->pdev->dev); + + return 0; +} + +static int bcm63xx_spi_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct bcm63xx_spi *bs = spi_master_get_devdata(master); + struct spi_transfer *t; + struct spi_device *spi = m->spi; + int status = 0; + unsigned int timeout = 0; list_for_each_entry(t, &m->transfers, transfer_list) { - ret += bcm63xx_txrx_bufs(spi, t); - } + unsigned int len = t->len; + u8 rx_tail; - m->complete(m->context); + status = bcm63xx_spi_check_transfer(spi, t); + if (status < 0) + goto exit; - return ret; + /* configure adapter for a new transfer */ + bcm63xx_spi_setup_transfer(spi, t); + + while (len) { + /* send the data */ + len -= bcm63xx_txrx_bufs(spi, t); + + timeout = wait_for_completion_timeout(&bs->done, HZ); + if (!timeout) { + status = -ETIMEDOUT; + goto exit; + } + + /* read out all data */ + rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); + + /* Read out all the data */ + if (rx_tail) + memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); + } + + m->actual_length += t->len; + } +exit: + m->status = status; + spi_finalize_current_message(master); + + return 0; } /* This driver supports single master mode only. Hence @@ -267,39 +314,15 @@ static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) struct spi_master *master = (struct spi_master *)dev_id; struct bcm63xx_spi *bs = spi_master_get_devdata(master); u8 intr; - u16 cmd; /* Read interupts and clear them immediately */ intr = bcm_spi_readb(bs, SPI_INT_STATUS); bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); bcm_spi_writeb(bs, 0, SPI_INT_MASK); - /* A tansfer completed */ - if (intr & SPI_INTR_CMD_DONE) { - u8 rx_tail; - - rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); - - /* Read out all the data */ - if (rx_tail) - memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); - - /* See if there is more data to send */ - if (bs->remaining_bytes > 0) { - bcm63xx_spi_fill_tx_fifo(bs); - - /* Start the transfer */ - bcm_spi_writew(bs, SPI_HD_W << SPI_MSG_TYPE_SHIFT, - SPI_MSG_CTL); - cmd = bcm_spi_readw(bs, SPI_CMD); - cmd |= SPI_CMD_START_IMMEDIATE; - cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); - bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); - bcm_spi_writew(bs, cmd, SPI_CMD); - } else { - complete(&bs->done); - } - } + /* A transfer completed */ + if (intr & SPI_INTR_CMD_DONE) + complete(&bs->done); return IRQ_HANDLED; } @@ -345,7 +368,6 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) } bs = spi_master_get_devdata(master); - init_completion(&bs->done); platform_set_drvdata(pdev, master); bs->pdev = pdev; @@ -379,12 +401,13 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) master->bus_num = pdata->bus_num; master->num_chipselect = pdata->num_chipselect; master->setup = bcm63xx_spi_setup; - master->transfer = bcm63xx_transfer; + master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer; + master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; + master->transfer_one_message = bcm63xx_spi_transfer_one; + master->mode_bits = MODEBITS; bs->speed_hz = pdata->speed_hz; - bs->stopping = 0; bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); - spin_lock_init(&bs->lock); /* Initialize hardware */ clk_enable(bs->clk); @@ -418,18 +441,16 @@ static int __devexit bcm63xx_spi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct bcm63xx_spi *bs = spi_master_get_devdata(master); + spi_unregister_master(master); + /* reset spi block */ bcm_spi_writeb(bs, 0, SPI_INT_MASK); - spin_lock(&bs->lock); - bs->stopping = 1; /* HW shutdown */ clk_disable(bs->clk); clk_put(bs->clk); - spin_unlock(&bs->lock); platform_set_drvdata(pdev, 0); - spi_unregister_master(master); return 0; } diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 248a2cc671a9..1fe51198a622 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -252,19 +252,15 @@ static void bfin_sport_spi_restore_state(struct bfin_sport_spi_master_data *drv_data) { struct bfin_sport_spi_slave_data *chip = drv_data->cur_chip; - unsigned int bits = (drv_data->ops == &bfin_sport_transfer_ops_u8 ? 7 : 15); bfin_sport_spi_disable(drv_data); dev_dbg(drv_data->dev, "restoring spi ctl state\n"); bfin_write(&drv_data->regs->tcr1, chip->ctl_reg); - bfin_write(&drv_data->regs->tcr2, bits); bfin_write(&drv_data->regs->tclkdiv, chip->baud); - bfin_write(&drv_data->regs->tfsdiv, bits); SSYNC(); bfin_write(&drv_data->regs->rcr1, chip->ctl_reg & ~(ITCLK | ITFS)); - bfin_write(&drv_data->regs->rcr2, bits); SSYNC(); bfin_sport_spi_cs_active(chip); @@ -420,11 +416,15 @@ bfin_sport_spi_pump_transfers(unsigned long data) drv_data->cs_change = transfer->cs_change; /* Bits per word setup */ - bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word; - if (bits_per_word == 8) - drv_data->ops = &bfin_sport_transfer_ops_u8; - else + bits_per_word = transfer->bits_per_word ? : + message->spi->bits_per_word ? : 8; + if (bits_per_word % 16 == 0) drv_data->ops = &bfin_sport_transfer_ops_u16; + else + drv_data->ops = &bfin_sport_transfer_ops_u8; + bfin_write(&drv_data->regs->tcr2, bits_per_word - 1); + bfin_write(&drv_data->regs->tfsdiv, bits_per_word - 1); + bfin_write(&drv_data->regs->rcr2, bits_per_word - 1); drv_data->state = RUNNING_STATE; @@ -598,11 +598,12 @@ bfin_sport_spi_setup(struct spi_device *spi) } chip->cs_chg_udelay = chip_info->cs_chg_udelay; chip->idle_tx_val = chip_info->idle_tx_val; - spi->bits_per_word = chip_info->bits_per_word; } } - if (spi->bits_per_word != 8 && spi->bits_per_word != 16) { + if (spi->bits_per_word % 8) { + dev_err(&spi->dev, "%d bits_per_word is not supported\n", + spi->bits_per_word); ret = -EINVAL; goto error; } diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 3b83ff8b1e2b..9bb4d4af8547 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -396,7 +396,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) /* last read */ if (drv_data->rx) { dev_dbg(&drv_data->pdev->dev, "last read\n"); - if (n_bytes % 2) { + if (!(n_bytes % 2)) { u16 *buf = (u16 *)drv_data->rx; for (loop = 0; loop < n_bytes / 2; loop++) *buf++ = bfin_read(&drv_data->regs->rdbr); @@ -424,7 +424,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) if (drv_data->rx && drv_data->tx) { /* duplex */ dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n"); - if (n_bytes % 2) { + if (!(n_bytes % 2)) { u16 *buf = (u16 *)drv_data->rx; u16 *buf2 = (u16 *)drv_data->tx; for (loop = 0; loop < n_bytes / 2; loop++) { @@ -442,7 +442,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) } else if (drv_data->rx) { /* read */ dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n"); - if (n_bytes % 2) { + if (!(n_bytes % 2)) { u16 *buf = (u16 *)drv_data->rx; for (loop = 0; loop < n_bytes / 2; loop++) { *buf++ = bfin_read(&drv_data->regs->rdbr); @@ -458,7 +458,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) } else if (drv_data->tx) { /* write */ dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n"); - if (n_bytes % 2) { + if (!(n_bytes % 2)) { u16 *buf = (u16 *)drv_data->tx; for (loop = 0; loop < n_bytes / 2; loop++) { bfin_read(&drv_data->regs->rdbr); @@ -587,6 +587,7 @@ static void bfin_spi_pump_transfers(unsigned long data) if (message->state == DONE_STATE) { dev_dbg(&drv_data->pdev->dev, "transfer: all done!\n"); message->status = 0; + bfin_spi_flush(drv_data); bfin_spi_giveback(drv_data); return; } @@ -870,8 +871,10 @@ static void bfin_spi_pump_transfers(unsigned long data) message->actual_length += drv_data->len_in_bytes; /* Move to next transfer of this msg */ message->state = bfin_spi_next_transfer(drv_data); - if (drv_data->cs_change) + if (drv_data->cs_change && message->state != DONE_STATE) { + bfin_spi_flush(drv_data); bfin_spi_cs_deactive(drv_data, chip); + } } /* Schedule next transfer tasklet */ @@ -1026,7 +1029,6 @@ static int bfin_spi_setup(struct spi_device *spi) chip->cs_chg_udelay = chip_info->cs_chg_udelay; chip->idle_tx_val = chip_info->idle_tx_val; chip->pio_interrupt = chip_info->pio_interrupt; - spi->bits_per_word = chip_info->bits_per_word; } else { /* force a default base state */ chip->ctl_reg &= bfin_ctl_reg; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 31bfba805cf4..9b2901feaf78 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -653,7 +653,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", rx_buf_count); if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, t->len, + dma_unmap_single(&spi->dev, t->tx_dma, t->len, DMA_TO_DEVICE); return -ENOMEM; } @@ -692,10 +692,10 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) if (spicfg->io_type == SPI_IO_TYPE_DMA) { if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, t->len, + dma_unmap_single(&spi->dev, t->tx_dma, t->len, DMA_TO_DEVICE); - dma_unmap_single(NULL, t->rx_dma, rx_buf_count, + dma_unmap_single(&spi->dev, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); clear_io_bits(dspi->base + SPIINT, SPIINT_DMA_REQ_EN); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 082458d73ce9..d1a495f64e2d 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -63,12 +63,6 @@ struct chip_data { }; #ifdef CONFIG_DEBUG_FS -static int spi_show_regs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - #define SPI_REGS_BUFSIZE 1024 static ssize_t spi_show_regs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -128,7 +122,7 @@ static ssize_t spi_show_regs(struct file *file, char __user *user_buf, static const struct file_operations mrst_spi_regs_ops = { .owner = THIS_MODULE, - .open = spi_show_regs_open, + .open = simple_open, .read = spi_show_regs, .llseek = default_llseek, }; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 6db2887852d6..e8055073e84d 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -545,13 +545,12 @@ static void ep93xx_spi_pio_transfer(struct ep93xx_spi *espi) * in case of failure. */ static struct dma_async_tx_descriptor * -ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) +ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir) { struct spi_transfer *t = espi->current_msg->state; struct dma_async_tx_descriptor *txd; enum dma_slave_buswidth buswidth; struct dma_slave_config conf; - enum dma_transfer_direction slave_dirn; struct scatterlist *sg; struct sg_table *sgt; struct dma_chan *chan; @@ -567,14 +566,13 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) memset(&conf, 0, sizeof(conf)); conf.direction = dir; - if (dir == DMA_FROM_DEVICE) { + if (dir == DMA_DEV_TO_MEM) { chan = espi->dma_rx; buf = t->rx_buf; sgt = &espi->rx_sgt; conf.src_addr = espi->sspdr_phys; conf.src_addr_width = buswidth; - slave_dirn = DMA_DEV_TO_MEM; } else { chan = espi->dma_tx; buf = t->tx_buf; @@ -582,7 +580,6 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) conf.dst_addr = espi->sspdr_phys; conf.dst_addr_width = buswidth; - slave_dirn = DMA_MEM_TO_DEV; } ret = dmaengine_slave_config(chan, &conf); @@ -633,8 +630,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) if (!nents) return ERR_PTR(-ENOMEM); - txd = dmaengine_prep_slave_sg(chan, sgt->sgl, nents, - slave_dirn, DMA_CTRL_ACK); + txd = dmaengine_prep_slave_sg(chan, sgt->sgl, nents, dir, DMA_CTRL_ACK); if (!txd) { dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir); return ERR_PTR(-ENOMEM); @@ -651,12 +647,12 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) * unmapped. */ static void ep93xx_spi_dma_finish(struct ep93xx_spi *espi, - enum dma_data_direction dir) + enum dma_transfer_direction dir) { struct dma_chan *chan; struct sg_table *sgt; - if (dir == DMA_FROM_DEVICE) { + if (dir == DMA_DEV_TO_MEM) { chan = espi->dma_rx; sgt = &espi->rx_sgt; } else { @@ -677,16 +673,16 @@ static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi) struct spi_message *msg = espi->current_msg; struct dma_async_tx_descriptor *rxd, *txd; - rxd = ep93xx_spi_dma_prepare(espi, DMA_FROM_DEVICE); + rxd = ep93xx_spi_dma_prepare(espi, DMA_DEV_TO_MEM); if (IS_ERR(rxd)) { dev_err(&espi->pdev->dev, "DMA RX failed: %ld\n", PTR_ERR(rxd)); msg->status = PTR_ERR(rxd); return; } - txd = ep93xx_spi_dma_prepare(espi, DMA_TO_DEVICE); + txd = ep93xx_spi_dma_prepare(espi, DMA_MEM_TO_DEV); if (IS_ERR(txd)) { - ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE); + ep93xx_spi_dma_finish(espi, DMA_DEV_TO_MEM); dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(rxd)); msg->status = PTR_ERR(txd); return; @@ -705,8 +701,8 @@ static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi) wait_for_completion(&espi->wait); - ep93xx_spi_dma_finish(espi, DMA_TO_DEVICE); - ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE); + ep93xx_spi_dma_finish(espi, DMA_MEM_TO_DEV); + ep93xx_spi_dma_finish(espi, DMA_DEV_TO_MEM); } /** diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 24cacff57786..5f748c0d96bd 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -139,10 +139,12 @@ static void fsl_spi_change_mode(struct spi_device *spi) static void fsl_spi_chipselect(struct spi_device *spi, int value) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data; + struct fsl_spi_platform_data *pdata; bool pol = spi->mode & SPI_CS_HIGH; struct spi_mpc8xxx_cs *cs = spi->controller_state; + pdata = spi->dev.parent->parent->platform_data; + if (value == BITBANG_CS_INACTIVE) { if (pdata->cs_control) pdata->cs_control(spi, !pol); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 31054e3de4c1..570f22053be8 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -83,7 +83,7 @@ struct spi_imx_data { struct spi_bitbang bitbang; struct completion xfer_done; - void *base; + void __iomem *base; int irq; struct clk *clk; unsigned long spi_clk; @@ -766,8 +766,12 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs); - if (ret < 0) - num_cs = mxc_platform_info->num_chipselect; + if (ret < 0) { + if (mxc_platform_info) + num_cs = mxc_platform_info->num_chipselect; + else + return ret; + } master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data) + sizeof(int) * num_cs); @@ -784,7 +788,7 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) for (i = 0; i < master->num_chipselect; i++) { int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); - if (cs_gpio < 0) + if (cs_gpio < 0 && mxc_platform_info) cs_gpio = mxc_platform_info->chipselect[i]; spi_imx->chipselect[i] = cs_gpio; diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 96f0da66b185..400ae2121a2a 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1667,9 +1667,15 @@ static int calculate_effective_freq(struct pl022 *pl022, int freq, struct /* cpsdvsr = 254 & scr = 255 */ min_tclk = spi_rate(rate, CPSDVR_MAX, SCR_MAX); - if (!((freq <= max_tclk) && (freq >= min_tclk))) { + if (freq > max_tclk) + dev_warn(&pl022->adev->dev, + "Max speed that can be programmed is %d Hz, you requested %d\n", + max_tclk, freq); + + if (freq < min_tclk) { dev_err(&pl022->adev->dev, - "controller data is incorrect: out of range frequency"); + "Requested frequency: %d Hz is less than minimum possible %d Hz\n", + freq, min_tclk); return -EINVAL; } @@ -1681,26 +1687,37 @@ static int calculate_effective_freq(struct pl022 *pl022, int freq, struct while (scr <= SCR_MAX) { tmp = spi_rate(rate, cpsdvsr, scr); - if (tmp > freq) + if (tmp > freq) { + /* we need lower freq */ scr++; + continue; + } + /* - * If found exact value, update and break. - * If found more closer value, update and continue. + * If found exact value, mark found and break. + * If found more closer value, update and break. */ - else if ((tmp == freq) || (tmp > best_freq)) { + if (tmp > best_freq) { best_freq = tmp; best_cpsdvsr = cpsdvsr; best_scr = scr; if (tmp == freq) - break; + found = 1; } - scr++; + /* + * increased scr will give lower rates, which are not + * required + */ + break; } cpsdvsr += 2; scr = SCR_MIN; } + WARN(!best_freq, "pl022: Matching cpsdvsr and scr not found for %d Hz rate \n", + freq); + clk_freq->cpsdvsr = (u8) (best_cpsdvsr & 0xFF); clk_freq->scr = (u8) (best_scr & 0xFF); dev_dbg(&pl022->adev->dev, @@ -1823,9 +1840,12 @@ static int pl022_setup(struct spi_device *spi) } else chip->cs_control = chip_info->cs_control; - if (bits <= 3) { - /* PL022 doesn't support less than 4-bits */ + /* Check bits per word with vendor specific range */ + if ((bits <= 3) || (bits > pl022->vendor->max_bpw)) { status = -ENOTSUPP; + dev_err(&spi->dev, "illegal data size for this controller!\n"); + dev_err(&spi->dev, "This controller can only handle 4 <= n <= %d bit words\n", + pl022->vendor->max_bpw); goto err_config_params; } else if (bits <= 8) { dev_dbg(&spi->dev, "4 <= n <=8 bits per word\n"); @@ -1838,20 +1858,10 @@ static int pl022_setup(struct spi_device *spi) chip->read = READING_U16; chip->write = WRITING_U16; } else { - if (pl022->vendor->max_bpw >= 32) { - dev_dbg(&spi->dev, "17 <= n <= 32 bits per word\n"); - chip->n_bytes = 4; - chip->read = READING_U32; - chip->write = WRITING_U32; - } else { - dev_err(&spi->dev, - "illegal data size for this controller!\n"); - dev_err(&spi->dev, - "a standard pl022 can only handle " - "1 <= n <= 16 bit words\n"); - status = -ENOTSUPP; - goto err_config_params; - } + dev_dbg(&spi->dev, "17 <= n <= 32 bits per word\n"); + chip->n_bytes = 4; + chip->read = READING_U32; + chip->write = WRITING_U32; } /* Now Initialize all register settings required for this chip */ @@ -2195,7 +2205,6 @@ static int pl022_runtime_suspend(struct device *dev) struct pl022 *pl022 = dev_get_drvdata(dev); clk_disable(pl022->clk); - amba_vcore_disable(pl022->adev); return 0; } @@ -2204,7 +2213,6 @@ static int pl022_runtime_resume(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - amba_vcore_enable(pl022->adev); clk_enable(pl022->clk); return 0; diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 08a3b1133d29..eb1dee26bda3 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -27,13 +27,14 @@ config ANDROID_LOGGER config ANDROID_PERSISTENT_RAM bool + depends on HAVE_MEMBLOCK select REED_SOLOMON select REED_SOLOMON_ENC8 select REED_SOLOMON_DEC8 config ANDROID_RAM_CONSOLE bool "Android RAM buffer console" - depends on !S390 && !UML + depends on !S390 && !UML && HAVE_MEMBLOCK select ANDROID_PERSISTENT_RAM default n diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 052b43e4e505..b91e4bc332a7 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -55,7 +55,6 @@ static int lowmem_minfree[6] = { }; static int lowmem_minfree_size = 4; -static struct task_struct *lowmem_deathpending; static unsigned long lowmem_deathpending_timeout; #define lowmem_print(level, x...) \ @@ -64,24 +63,6 @@ static unsigned long lowmem_deathpending_timeout; printk(x); \ } while (0) -static int -task_notify_func(struct notifier_block *self, unsigned long val, void *data); - -static struct notifier_block task_nb = { - .notifier_call = task_notify_func, -}; - -static int -task_notify_func(struct notifier_block *self, unsigned long val, void *data) -{ - struct task_struct *task = data; - - if (task == lowmem_deathpending) - lowmem_deathpending = NULL; - - return NOTIFY_OK; -} - static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) { struct task_struct *tsk; @@ -97,19 +78,6 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) int other_file = global_page_state(NR_FILE_PAGES) - global_page_state(NR_SHMEM); - /* - * If we already have a death outstanding, then - * bail out right away; indicating to vmscan - * that we have nothing further to offer on - * this pass. - * - * Note: Currently you need CONFIG_PROFILING - * for this to work correctly. - */ - if (lowmem_deathpending && - time_before_eq(jiffies, lowmem_deathpending_timeout)) - return 0; - if (lowmem_adj_size < array_size) array_size = lowmem_adj_size; if (lowmem_minfree_size < array_size) @@ -148,6 +116,12 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) if (!p) continue; + if (test_tsk_thread_flag(p, TIF_MEMDIE) && + time_before_eq(jiffies, lowmem_deathpending_timeout)) { + task_unlock(p); + rcu_read_unlock(); + return 0; + } oom_score_adj = p->signal->oom_score_adj; if (oom_score_adj < min_score_adj) { task_unlock(p); @@ -174,15 +148,9 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", selected->pid, selected->comm, selected_oom_score_adj, selected_tasksize); - /* - * If CONFIG_PROFILING is off, then we don't want to stall - * the killer by setting lowmem_deathpending. - */ -#ifdef CONFIG_PROFILING - lowmem_deathpending = selected; lowmem_deathpending_timeout = jiffies + HZ; -#endif send_sig(SIGKILL, selected, 0); + set_tsk_thread_flag(selected, TIF_MEMDIE); rem -= selected_tasksize; } lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n", @@ -198,7 +166,6 @@ static struct shrinker lowmem_shrinker = { static int __init lowmem_init(void) { - task_handoff_register(&task_nb); register_shrinker(&lowmem_shrinker); return 0; } @@ -206,7 +173,6 @@ static int __init lowmem_init(void) static void __exit lowmem_exit(void) { unregister_shrinker(&lowmem_shrinker); - task_handoff_unregister(&task_nb); } module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); diff --git a/drivers/staging/android/persistent_ram.c b/drivers/staging/android/persistent_ram.c index e08f2574e30a..8d8c1e33e0ff 100644 --- a/drivers/staging/android/persistent_ram.c +++ b/drivers/staging/android/persistent_ram.c @@ -399,12 +399,12 @@ static __init struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) { struct persistent_ram_zone *prz; - int ret; + int ret = -ENOMEM; prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL); if (!prz) { pr_err("persistent_ram: failed to allocate persistent ram zone\n"); - return ERR_PTR(-ENOMEM); + goto err; } INIT_LIST_HEAD(&prz->node); @@ -412,13 +412,13 @@ struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) ret = persistent_ram_buffer_init(dev_name(dev), prz); if (ret) { pr_err("persistent_ram: failed to initialize buffer\n"); - return ERR_PTR(ret); + goto err; } prz->ecc = ecc; ret = persistent_ram_init_ecc(prz, prz->buffer_size); if (ret) - return ERR_PTR(ret); + goto err; if (prz->buffer->sig == PERSISTENT_RAM_SIG) { if (buffer_size(prz) > prz->buffer_size || @@ -442,6 +442,9 @@ struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) atomic_set(&prz->buffer->size, 0); return prz; +err: + kfree(prz); + return ERR_PTR(ret); } struct persistent_ram_zone * __init diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c index bc723eff11af..45c522cbe784 100644 --- a/drivers/staging/android/timed_gpio.c +++ b/drivers/staging/android/timed_gpio.c @@ -85,7 +85,7 @@ static int timed_gpio_probe(struct platform_device *pdev) struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; struct timed_gpio *cur_gpio; struct timed_gpio_data *gpio_data, *gpio_dat; - int i, j, ret = 0; + int i, ret; if (!pdata) return -EBUSY; @@ -108,18 +108,12 @@ static int timed_gpio_probe(struct platform_device *pdev) gpio_dat->dev.get_time = gpio_get_time; gpio_dat->dev.enable = gpio_enable; ret = gpio_request(cur_gpio->gpio, cur_gpio->name); - if (ret >= 0) { - ret = timed_output_dev_register(&gpio_dat->dev); - if (ret < 0) - gpio_free(cur_gpio->gpio); - } + if (ret < 0) + goto err_out; + ret = timed_output_dev_register(&gpio_dat->dev); if (ret < 0) { - for (j = 0; j < i; j++) { - timed_output_dev_unregister(&gpio_data[i].dev); - gpio_free(gpio_data[i].gpio); - } - kfree(gpio_data); - return ret; + gpio_free(cur_gpio->gpio); + goto err_out; } gpio_dat->gpio = cur_gpio->gpio; @@ -131,6 +125,15 @@ static int timed_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio_data); return 0; + +err_out: + while (--i >= 0) { + timed_output_dev_unregister(&gpio_data[i].dev); + gpio_free(gpio_data[i].gpio); + } + kfree(gpio_data); + + return ret; } static int timed_gpio_remove(struct platform_device *pdev) diff --git a/drivers/staging/iio/inkern.c b/drivers/staging/iio/inkern.c index de2c8ea64965..ef07a02bf542 100644 --- a/drivers/staging/iio/inkern.c +++ b/drivers/staging/iio/inkern.c @@ -82,6 +82,7 @@ int iio_map_array_unregister(struct iio_dev *indio_dev, ret = -ENODEV; goto error_ret; } + i++; } error_ret: mutex_unlock(&iio_map_list_lock); diff --git a/drivers/staging/iio/magnetometer/ak8975.c b/drivers/staging/iio/magnetometer/ak8975.c index d5ddac3d8831..ebc2d0840caf 100644 --- a/drivers/staging/iio/magnetometer/ak8975.c +++ b/drivers/staging/iio/magnetometer/ak8975.c @@ -108,7 +108,8 @@ static const int ak8975_index_to_reg[] = { static int ak8975_write_data(struct i2c_client *client, u8 reg, u8 val, u8 mask, u8 shift) { - struct ak8975_data *data = i2c_get_clientdata(client); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ak8975_data *data = iio_priv(indio_dev); u8 regval; int ret; @@ -159,7 +160,8 @@ static int ak8975_read_data(struct i2c_client *client, */ static int ak8975_setup(struct i2c_client *client) { - struct ak8975_data *data = i2c_get_clientdata(client); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ak8975_data *data = iio_priv(indio_dev); u8 device_id; int ret; @@ -509,6 +511,7 @@ static int ak8975_probe(struct i2c_client *client, goto exit_gpio; } data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); /* Perform some basic start-of-day setup of the device. */ err = ak8975_setup(client); if (err < 0) { @@ -516,7 +519,6 @@ static int ak8975_probe(struct i2c_client *client, goto exit_free_iio; } - i2c_set_clientdata(client, indio_dev); data->client = client; mutex_init(&data->lock); data->eoc_irq = client->irq; diff --git a/drivers/staging/iio/magnetometer/hmc5843.c b/drivers/staging/iio/magnetometer/hmc5843.c index 91dd3da70cb4..e00b416c4d33 100644 --- a/drivers/staging/iio/magnetometer/hmc5843.c +++ b/drivers/staging/iio/magnetometer/hmc5843.c @@ -521,7 +521,9 @@ static int hmc5843_detect(struct i2c_client *client, /* Called when we have found a new HMC5843. */ static void hmc5843_init_client(struct i2c_client *client) { - struct hmc5843_data *data = i2c_get_clientdata(client); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct hmc5843_data *data = iio_priv(indio_dev); + hmc5843_set_meas_conf(client, data->meas_conf); hmc5843_set_rate(client, data->rate); hmc5843_configure(client, data->operating_mode); diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c index 43ebc43e6b9a..1075fb1df0d9 100644 --- a/drivers/staging/media/as102/as102_fw.c +++ b/drivers/staging/media/as102/as102_fw.c @@ -165,7 +165,7 @@ error: int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) { int errno = -EFAULT; - const struct firmware *firmware; + const struct firmware *firmware = NULL; unsigned char *cmd_buf = NULL; char *fw1, *fw2; struct usb_device *dev = bus_adap->usb_dev; diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index 400df8cbee53..d91751f9ffe8 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -36,6 +36,7 @@ #include <linux/prefetch.h> #include <linux/ratelimit.h> #include <linux/smp.h> +#include <linux/interrupt.h> #include <net/dst.h> #ifdef CONFIG_XFRM #include <linux/xfrm.h> diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index 56d74dc2fbd5..91a97b3e45c6 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -32,6 +32,7 @@ #include <linux/ip.h> #include <linux/ratelimit.h> #include <linux/string.h> +#include <linux/interrupt.h> #include <net/dst.h> #ifdef CONFIG_XFRM #include <linux/xfrm.h> diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 9112cd882154..60cba8194de3 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -31,6 +31,7 @@ #include <linux/etherdevice.h> #include <linux/phy.h> #include <linux/slab.h> +#include <linux/interrupt.h> #include <net/dst.h> diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index 3df5b4c58ecd..620b8d54223d 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -803,9 +803,6 @@ static void pdev_shutdown(struct platform_device *device) static int pdev_probe(struct platform_device *device) { DBG("%s", device->name); - if (platform_driver_register(&omap_dmm_driver)) - dev_err(&device->dev, "DMM registration failed\n"); - return drm_platform_init(&omap_drm_driver, device); } @@ -833,6 +830,10 @@ struct platform_driver pdev = { static int __init omap_drm_init(void) { DBG("init"); + if (platform_driver_register(&omap_dmm_driver)) { + /* we can continue on without DMM.. so not fatal */ + dev_err(NULL, "DMM registration failed\n"); + } return platform_driver_register(&pdev); } diff --git a/drivers/staging/ozwpan/TODO b/drivers/staging/ozwpan/TODO index f7a9c122f596..c2d30a7112f3 100644 --- a/drivers/staging/ozwpan/TODO +++ b/drivers/staging/ozwpan/TODO @@ -8,5 +8,7 @@ TODO: - code review by USB developer community. - testing with as many devices as possible. -Please send any patches for this driver to Chris Kelly <ckelly@ozmodevices.com> +Please send any patches for this driver to +Rupesh Gujare <rgujare@ozmodevices.com> +Chris Kelly <ckelly@ozmodevices.com> and Greg Kroah-Hartman <gregkh@linuxfoundation.org>. diff --git a/drivers/staging/ozwpan/ozpd.c b/drivers/staging/ozwpan/ozpd.c index 2b45d3d1800c..04cd57f2a6da 100644 --- a/drivers/staging/ozwpan/ozpd.c +++ b/drivers/staging/ozwpan/ozpd.c @@ -383,8 +383,6 @@ static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f) pd->tx_pool = &f->link; pd->tx_pool_count++; f = 0; - } else { - kfree(f); } spin_unlock_bh(&pd->tx_frame_lock); if (f) diff --git a/drivers/staging/ramster/Kconfig b/drivers/staging/ramster/Kconfig index 8b57b87edda4..4af1f8d4b953 100644 --- a/drivers/staging/ramster/Kconfig +++ b/drivers/staging/ramster/Kconfig @@ -1,10 +1,6 @@ -# Dependency on CONFIG_BROKEN is because there is a commit dependency -# on a cleancache naming change to be submitted by Konrad Wilk -# a39c00ded70339603ffe1b0ffdf3ade85bcf009a "Merge branch 'stable/cleancache.v13' -# into linux-next. Once this commit is present, BROKEN can be removed config RAMSTER bool "Cross-machine RAM capacity sharing, aka peer-to-peer tmem" - depends on (CLEANCACHE || FRONTSWAP) && CONFIGFS_FS=y && !ZCACHE && !XVMALLOC && !HIGHMEM && BROKEN + depends on (CLEANCACHE || FRONTSWAP) && CONFIGFS_FS=y && !ZCACHE && !XVMALLOC && !HIGHMEM select LZO_COMPRESS select LZO_DECOMPRESS default n diff --git a/drivers/staging/rts_pstor/ms.c b/drivers/staging/rts_pstor/ms.c index 66341dff8c99..f9a4498984cc 100644 --- a/drivers/staging/rts_pstor/ms.c +++ b/drivers/staging/rts_pstor/ms.c @@ -3498,7 +3498,8 @@ static int ms_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 log_blk++; - for (seg_no = 0; seg_no < sizeof(ms_start_idx)/2; seg_no++) { + for (seg_no = 0; seg_no < ARRAY_SIZE(ms_start_idx) - 1; + seg_no++) { if (log_blk < ms_start_idx[seg_no+1]) break; } diff --git a/drivers/staging/rts_pstor/rtsx.c b/drivers/staging/rts_pstor/rtsx.c index a7feb3e328a0..1dccd933a7e4 100644 --- a/drivers/staging/rts_pstor/rtsx.c +++ b/drivers/staging/rts_pstor/rtsx.c @@ -1000,6 +1000,11 @@ static int __devinit rtsx_probe(struct pci_dev *pci, rtsx_init_chip(dev->chip); + /* set the supported max_lun and max_id for the scsi host + * NOTE: the minimal value of max_id is 1 */ + host->max_id = 1; + host->max_lun = dev->chip->max_lun; + /* Start up our control thread */ th = kthread_run(rtsx_control_thread, dev, CR_DRIVER_NAME); if (IS_ERR(th)) { diff --git a/drivers/staging/rts_pstor/rtsx_transport.c b/drivers/staging/rts_pstor/rtsx_transport.c index 4e3d2c106af0..9b2e5c99870f 100644 --- a/drivers/staging/rts_pstor/rtsx_transport.c +++ b/drivers/staging/rts_pstor/rtsx_transport.c @@ -335,6 +335,7 @@ static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card, int sg_cnt, i, resid; int err = 0; long timeleft; + struct scatterlist *sg_ptr; u32 val = TRIG_DMA; if ((sg == NULL) || (num_sg <= 0) || !offset || !index) @@ -371,7 +372,7 @@ static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card, sg_cnt = dma_map_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir); resid = size; - + sg_ptr = sg; chip->sgi = 0; /* Usually the next entry will be @sg@ + 1, but if this sg element * is part of a chained scatterlist, it could jump to the start of @@ -379,14 +380,14 @@ static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card, * the proper sg */ for (i = 0; i < *index; i++) - sg = sg_next(sg); + sg_ptr = sg_next(sg_ptr); for (i = *index; i < sg_cnt; i++) { dma_addr_t addr; unsigned int len; u8 option; - addr = sg_dma_address(sg); - len = sg_dma_len(sg); + addr = sg_dma_address(sg_ptr); + len = sg_dma_len(sg_ptr); RTSX_DEBUGP("DMA addr: 0x%x, Len: 0x%x\n", (unsigned int)addr, len); @@ -415,7 +416,7 @@ static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card, if (!resid) break; - sg = sg_next(sg); + sg_ptr = sg_next(sg_ptr); } RTSX_DEBUGP("SG table count = %d\n", chip->sgi); diff --git a/drivers/staging/sep/sep_main.c b/drivers/staging/sep/sep_main.c index ad54c2e5c932..f1701bc6e312 100644 --- a/drivers/staging/sep/sep_main.c +++ b/drivers/staging/sep/sep_main.c @@ -3114,7 +3114,7 @@ static long sep_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) current->pid); if (1 == test_bit(SEP_LEGACY_SENDMSG_DONE_OFFSET, &call_status->status)) { - dev_warn(&sep->pdev->dev, + dev_dbg(&sep->pdev->dev, "[PID%d] dcb prep needed before send msg\n", current->pid); error = -EPROTO; @@ -3122,9 +3122,9 @@ static long sep_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } if (!arg) { - dev_warn(&sep->pdev->dev, + dev_dbg(&sep->pdev->dev, "[PID%d] dcb null arg\n", current->pid); - error = EINVAL; + error = -EINVAL; goto end_function; } diff --git a/drivers/staging/tidspbridge/core/tiomap3430.c b/drivers/staging/tidspbridge/core/tiomap3430.c index 7862513cc295..9cf29fcea11e 100644 --- a/drivers/staging/tidspbridge/core/tiomap3430.c +++ b/drivers/staging/tidspbridge/core/tiomap3430.c @@ -79,10 +79,6 @@ #define OMAP343X_CONTROL_IVA2_BOOTADDR (OMAP2_CONTROL_GENERAL + 0x0190) #define OMAP343X_CONTROL_IVA2_BOOTMOD (OMAP2_CONTROL_GENERAL + 0x0194) -#define OMAP343X_CTRL_REGADDR(reg) \ - OMAP2_L4_IO_ADDRESS(OMAP343X_CTRL_BASE + (reg)) - - /* Forward Declarations: */ static int bridge_brd_monitor(struct bridge_dev_context *dev_ctxt); static int bridge_brd_read(struct bridge_dev_context *dev_ctxt, @@ -418,19 +414,27 @@ static int bridge_brd_start(struct bridge_dev_context *dev_ctxt, /* Assert RST1 i.e only the RST only for DSP megacell */ if (!status) { + /* + * XXX: ioremapping MUST be removed once ctrl + * function is made available. + */ + void __iomem *ctrl = ioremap(OMAP343X_CTRL_BASE, SZ_4K); + if (!ctrl) + return -ENOMEM; + (*pdata->dsp_prm_rmw_bits)(OMAP3430_RST1_IVA2_MASK, OMAP3430_RST1_IVA2_MASK, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); /* Mask address with 1K for compatibility */ __raw_writel(dsp_addr & OMAP3_IVA2_BOOTADDR_MASK, - OMAP343X_CTRL_REGADDR( - OMAP343X_CONTROL_IVA2_BOOTADDR)); + ctrl + OMAP343X_CONTROL_IVA2_BOOTADDR); /* * Set bootmode to self loop if dsp_debug flag is true */ __raw_writel((dsp_debug) ? OMAP3_IVA2_BOOTMOD_IDLE : 0, - OMAP343X_CTRL_REGADDR( - OMAP343X_CONTROL_IVA2_BOOTMOD)); + ctrl + OMAP343X_CONTROL_IVA2_BOOTMOD); + + iounmap(ctrl); } } if (!status) { diff --git a/drivers/staging/tidspbridge/core/wdt.c b/drivers/staging/tidspbridge/core/wdt.c index 70055c8111ed..870f934f4f3b 100644 --- a/drivers/staging/tidspbridge/core/wdt.c +++ b/drivers/staging/tidspbridge/core/wdt.c @@ -53,7 +53,10 @@ int dsp_wdt_init(void) int ret = 0; dsp_wdt.sm_wdt = NULL; - dsp_wdt.reg_base = OMAP2_L4_IO_ADDRESS(OMAP34XX_WDT3_BASE); + dsp_wdt.reg_base = ioremap(OMAP34XX_WDT3_BASE, SZ_4K); + if (!dsp_wdt.reg_base) + return -ENOMEM; + tasklet_init(&dsp_wdt.wdt3_tasklet, dsp_wdt_dpc, 0); dsp_wdt.fclk = clk_get(NULL, "wdt3_fck"); @@ -99,6 +102,9 @@ void dsp_wdt_exit(void) dsp_wdt.fclk = NULL; dsp_wdt.iclk = NULL; dsp_wdt.sm_wdt = NULL; + + if (dsp_wdt.reg_base) + iounmap(dsp_wdt.reg_base); dsp_wdt.reg_base = NULL; } diff --git a/drivers/staging/vme/devices/vme_pio2_core.c b/drivers/staging/vme/devices/vme_pio2_core.c index 9fedc442a779..573c80003f0c 100644 --- a/drivers/staging/vme/devices/vme_pio2_core.c +++ b/drivers/staging/vme/devices/vme_pio2_core.c @@ -35,10 +35,10 @@ static int vector[PIO2_CARDS_MAX]; static int vector_num; static int level[PIO2_CARDS_MAX]; static int level_num; -static const char *variant[PIO2_CARDS_MAX]; +static char *variant[PIO2_CARDS_MAX]; static int variant_num; -static int loopback; +static bool loopback; static int pio2_match(struct vme_dev *); static int __devinit pio2_probe(struct vme_dev *); diff --git a/drivers/staging/vt6655/key.c b/drivers/staging/vt6655/key.c index 0ff8d7bbf2a7..774b0d4a7e06 100644 --- a/drivers/staging/vt6655/key.c +++ b/drivers/staging/vt6655/key.c @@ -655,6 +655,9 @@ bool KeybSetDefaultKey ( return (false); } + if (uKeyLength > MAX_KEY_LEN) + return false; + pTable->KeyTable[MAX_KEY_TABLE-1].bInUse = true; for(ii=0;ii<ETH_ALEN;ii++) pTable->KeyTable[MAX_KEY_TABLE-1].abyBSSID[ii] = 0xFF; diff --git a/drivers/staging/vt6656/ioctl.c b/drivers/staging/vt6656/ioctl.c index 1463d76895f0..d59456c29df1 100644 --- a/drivers/staging/vt6656/ioctl.c +++ b/drivers/staging/vt6656/ioctl.c @@ -565,7 +565,7 @@ int private_ioctl(PSDevice pDevice, struct ifreq *rq) result = -ENOMEM; break; } - pNodeList = (PSNodeList)kmalloc(sizeof(SNodeList) + (sNodeList.uItem * sizeof(SNodeItem)), (int)GFP_ATOMIC); + pNodeList = kmalloc(sizeof(SNodeList) + (sNodeList.uItem * sizeof(SNodeItem)), (int)GFP_ATOMIC); if (pNodeList == NULL) { result = -ENOMEM; break; @@ -601,6 +601,7 @@ int private_ioctl(PSDevice pDevice, struct ifreq *rq) } } if (copy_to_user(pReq->data, pNodeList, sizeof(SNodeList) + (sNodeList.uItem * sizeof(SNodeItem)))) { + kfree(pNodeList); result = -EFAULT; break; } diff --git a/drivers/staging/vt6656/key.c b/drivers/staging/vt6656/key.c index 27bb523c8a97..ee62a06a75f4 100644 --- a/drivers/staging/vt6656/key.c +++ b/drivers/staging/vt6656/key.c @@ -684,6 +684,9 @@ BOOL KeybSetDefaultKey( return (FALSE); } + if (uKeyLength > MAX_KEY_LEN) + return false; + pTable->KeyTable[MAX_KEY_TABLE-1].bInUse = TRUE; for (ii = 0; ii < ETH_ALEN; ii++) pTable->KeyTable[MAX_KEY_TABLE-1].abyBSSID[ii] = 0xFF; diff --git a/drivers/staging/xgifb/vb_init.c b/drivers/staging/xgifb/vb_init.c index 94d5c35e22fb..3650bbff7686 100644 --- a/drivers/staging/xgifb/vb_init.c +++ b/drivers/staging/xgifb/vb_init.c @@ -61,7 +61,7 @@ XGINew_GetXG20DRAMType(struct xgi_hw_device_info *HwDeviceExtension, } temp = xgifb_reg_get(pVBInfo->P3c4, 0x3B); /* SR3B[7][3]MAA15 MAA11 (Power on Trapping) */ - if ((temp & 0x88) == 0x80) + if (((temp & 0x88) == 0x80) || ((temp & 0x88) == 0x08)) data = 0; /* DDR */ else data = 1; /* DDRII */ diff --git a/drivers/staging/xgifb/vb_setmode.c b/drivers/staging/xgifb/vb_setmode.c index 2919924213c4..60d4adf99923 100644 --- a/drivers/staging/xgifb/vb_setmode.c +++ b/drivers/staging/xgifb/vb_setmode.c @@ -152,6 +152,7 @@ void InitTo330Pointer(unsigned char ChipType, struct vb_device_info *pVBInfo) pVBInfo->pXGINew_CR97 = &XG20_CR97; if (ChipType == XG27) { + unsigned char temp; pVBInfo->MCLKData = (struct SiS_MCLKData *) XGI27New_MCLKData; pVBInfo->CR40 = XGI27_cr41; @@ -162,7 +163,13 @@ void InitTo330Pointer(unsigned char ChipType, struct vb_device_info *pVBInfo) pVBInfo->pCRDE = XG27_CRDE; pVBInfo->pSR40 = &XG27_SR40; pVBInfo->pSR41 = &XG27_SR41; + pVBInfo->SR15 = XG27_SR13; + /*Z11m DDR*/ + temp = xgifb_reg_get(pVBInfo->P3c4, 0x3B); + /* SR3B[7][3]MAA15 MAA11 (Power on Trapping) */ + if (((temp & 0x88) == 0x80) || ((temp & 0x88) == 0x08)) + pVBInfo->pXGINew_CR97 = &Z11m_CR97; } if (ChipType >= XG20) { diff --git a/drivers/staging/xgifb/vb_table.h b/drivers/staging/xgifb/vb_table.h index dddf261ed53d..e8d6f674b274 100644 --- a/drivers/staging/xgifb/vb_table.h +++ b/drivers/staging/xgifb/vb_table.h @@ -33,6 +33,13 @@ static struct XGI_ECLKDataStruct XGI340_ECLKData[] = { {0x5c, 0x23, 0x01, 166} }; +static unsigned char XG27_SR13[4][8] = { + {0x35, 0x45, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00}, /* SR13 */ + {0x41, 0x51, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00}, /* SR14 */ + {0x32, 0x32, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00}, /* SR18 */ + {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00} /* SR1B */ +}; + static unsigned char XGI340_SR13[4][8] = { {0x35, 0x45, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00}, /* SR13 */ {0x41, 0x51, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00}, /* SR14 */ @@ -71,7 +78,7 @@ static unsigned char XGI27_cr41[24][8] = { {0x20, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00}, /* 0 CR41 */ {0xC4, 0x40, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, /* 1 CR8A */ {0xC4, 0x40, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, /* 2 CR8B */ - {0xB5, 0x13, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00}, /* 3 CR40[7], + {0xB3, 0x13, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00}, /* 3 CR40[7], CR99[2:0], CR45[3:0]*/ {0xf0, 0xf5, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00}, /* 4 CR59 */ @@ -2803,6 +2810,8 @@ static unsigned char XG27_CRDE[2]; static unsigned char XG27_SR40 = 0x04 ; static unsigned char XG27_SR41 = 0x00 ; +static unsigned char Z11m_CR97 = 0x80 ; + static struct XGI330_VCLKDataStruct XGI_VCLKData[] = { /* SR2B,SR2C,SR2D */ {0x1B, 0xE1, 25}, /* 00 (25.175MHz) */ diff --git a/drivers/staging/zcache/Kconfig b/drivers/staging/zcache/Kconfig index 3ed2c8f656a5..7048e01f0817 100644 --- a/drivers/staging/zcache/Kconfig +++ b/drivers/staging/zcache/Kconfig @@ -2,7 +2,7 @@ config ZCACHE bool "Dynamic compression of swap pages and clean pagecache pages" # X86 dependency is because zsmalloc uses non-portable pte/tlb # functions - depends on (CLEANCACHE || FRONTSWAP) && CRYPTO && X86 + depends on (CLEANCACHE || FRONTSWAP) && CRYPTO=y && X86 select ZSMALLOC select CRYPTO_LZO default n diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 09caa4f2687e..917461c66014 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -267,33 +267,39 @@ static unsigned long obj_idx_to_offset(struct page *page, return off + obj_idx * class_size; } +static void reset_page(struct page *page) +{ + clear_bit(PG_private, &page->flags); + clear_bit(PG_private_2, &page->flags); + set_page_private(page, 0); + page->mapping = NULL; + page->freelist = NULL; + reset_page_mapcount(page); +} + static void free_zspage(struct page *first_page) { - struct page *nextp, *tmp; + struct page *nextp, *tmp, *head_extra; BUG_ON(!is_first_page(first_page)); BUG_ON(first_page->inuse); - nextp = (struct page *)page_private(first_page); + head_extra = (struct page *)page_private(first_page); - clear_bit(PG_private, &first_page->flags); - clear_bit(PG_private_2, &first_page->flags); - set_page_private(first_page, 0); - first_page->mapping = NULL; - first_page->freelist = NULL; - reset_page_mapcount(first_page); + reset_page(first_page); __free_page(first_page); /* zspage with only 1 system page */ - if (!nextp) + if (!head_extra) return; - list_for_each_entry_safe(nextp, tmp, &nextp->lru, lru) { + list_for_each_entry_safe(nextp, tmp, &head_extra->lru, lru) { list_del(&nextp->lru); - clear_bit(PG_private_2, &nextp->flags); - nextp->index = 0; + reset_page(nextp); __free_page(nextp); } + reset_page(head_extra); + __free_page(head_extra); } /* Initialize a newly allocated zspage */ diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 70c3ffb981e7..e320ec24aa1b 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -60,7 +60,6 @@ static void core_clear_initiator_node_from_tpg( int i; struct se_dev_entry *deve; struct se_lun *lun; - struct se_lun_acl *acl, *acl_tmp; spin_lock_irq(&nacl->device_list_lock); for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) { @@ -81,28 +80,7 @@ static void core_clear_initiator_node_from_tpg( core_update_device_list_for_node(lun, NULL, deve->mapped_lun, TRANSPORT_LUNFLAGS_NO_ACCESS, nacl, tpg, 0); - spin_lock(&lun->lun_acl_lock); - list_for_each_entry_safe(acl, acl_tmp, - &lun->lun_acl_list, lacl_list) { - if (!strcmp(acl->initiatorname, nacl->initiatorname) && - (acl->mapped_lun == deve->mapped_lun)) - break; - } - - if (!acl) { - pr_err("Unable to locate struct se_lun_acl for %s," - " mapped_lun: %u\n", nacl->initiatorname, - deve->mapped_lun); - spin_unlock(&lun->lun_acl_lock); - spin_lock_irq(&nacl->device_list_lock); - continue; - } - - list_del(&acl->lacl_list); - spin_unlock(&lun->lun_acl_lock); - spin_lock_irq(&nacl->device_list_lock); - kfree(acl); } spin_unlock_irq(&nacl->device_list_lock); } diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h index 830657908db8..c5eb3c33c3db 100644 --- a/drivers/target/tcm_fc/tcm_fc.h +++ b/drivers/target/tcm_fc/tcm_fc.h @@ -122,6 +122,7 @@ struct ft_cmd { /* Local sense buffer */ unsigned char ft_sense_buffer[TRANSPORT_SENSE_BUFFER]; u32 was_ddp_setup:1; /* Set only if ddp is setup */ + u32 aborted:1; /* Set if aborted by reset or timeout */ struct scatterlist *sg; /* Set only if DDP is setup */ u32 sg_cnt; /* No. of item in scatterlist */ }; diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 62dec9715ce5..a375f257aabc 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -121,6 +121,8 @@ int ft_queue_status(struct se_cmd *se_cmd) struct fc_exch *ep; size_t len; + if (cmd->aborted) + return 0; ft_dump_cmd(cmd, __func__); ep = fc_seq_exch(cmd->seq); lport = ep->lp; @@ -187,6 +189,8 @@ int ft_write_pending(struct se_cmd *se_cmd) ft_dump_cmd(cmd, __func__); + if (cmd->aborted) + return 0; ep = fc_seq_exch(cmd->seq); lport = ep->lp; fp = fc_frame_alloc(lport, sizeof(*txrdy)); @@ -252,10 +256,10 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg) struct ft_cmd *cmd = arg; struct fc_frame_header *fh; - if (IS_ERR(fp)) { + if (unlikely(IS_ERR(fp))) { /* XXX need to find cmd if queued */ cmd->seq = NULL; - transport_generic_free_cmd(&cmd->se_cmd, 0); + cmd->aborted = true; return; } @@ -399,6 +403,8 @@ int ft_queue_tm_resp(struct se_cmd *se_cmd) struct se_tmr_req *tmr = se_cmd->se_tmr_req; enum fcp_resp_rsp_codes code; + if (cmd->aborted) + return 0; switch (tmr->response) { case TMR_FUNCTION_COMPLETE: code = FCP_TMF_CMPL; diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index f357039349ba..2948dc944619 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -300,6 +300,7 @@ static struct se_portal_group *ft_add_tpg( { struct ft_lport_acl *lacl; struct ft_tpg *tpg; + struct workqueue_struct *wq; unsigned long index; int ret; @@ -321,18 +322,20 @@ static struct se_portal_group *ft_add_tpg( tpg->lport_acl = lacl; INIT_LIST_HEAD(&tpg->lun_list); - ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg, - tpg, TRANSPORT_TPG_TYPE_NORMAL); - if (ret < 0) { + wq = alloc_workqueue("tcm_fc", 0, 1); + if (!wq) { kfree(tpg); return NULL; } - tpg->workqueue = alloc_workqueue("tcm_fc", 0, 1); - if (!tpg->workqueue) { + ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg, + tpg, TRANSPORT_TPG_TYPE_NORMAL); + if (ret < 0) { + destroy_workqueue(wq); kfree(tpg); return NULL; } + tpg->workqueue = wq; mutex_lock(&ft_lport_lock); list_add_tail(&tpg->list, &lacl->tpg_list); diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index 2b693eefac55..dc7c0db26e20 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -81,6 +81,8 @@ int ft_queue_data_in(struct se_cmd *se_cmd) void *from; void *to = NULL; + if (cmd->aborted) + return 0; ep = fc_seq_exch(cmd->seq); lport = ep->lp; cmd->seq = lport->tt.seq_start_next(cmd->seq); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 24145c30c9b0..6cc4358f68c1 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1073,8 +1073,10 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, (new_serial.close_delay != port->close_delay) || (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) + (port->flags & ~ASYNC_USR_MASK))) { + tty_unlock(); return -EPERM; + } port->flags = ((port->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); state->custom_divisor = new_serial.custom_divisor; diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 5b149b466ec8..5c27f7e6c9f1 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -1572,13 +1572,11 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) do { struct uart_8250_port *up; struct uart_port *port; - bool skip; up = list_entry(l, struct uart_8250_port, list); port = &up->port; - skip = pass_counter && up->port.flags & UPF_IIR_ONCE; - if (!skip && port->handle_irq(port)) { + if (port->handle_irq(port)) { handled = 1; end = NULL; } else if (end == NULL) @@ -2037,10 +2035,12 @@ static int serial8250_startup(struct uart_port *port) spin_unlock_irqrestore(&port->lock, flags); /* - * If the interrupt is not reasserted, setup a timer to - * kick the UART on a regular basis. + * If the interrupt is not reasserted, or we otherwise + * don't trust the iir, setup a timer to kick the UART + * on a regular basis. */ - if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) { + if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) || + up->port.flags & UPF_BUG_THRE) { up->bugs |= UART_BUG_THRE; pr_debug("ttyS%d - using backup timer\n", serial_index(port)); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index da2b0b0a183f..858dca865d6a 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1096,7 +1096,7 @@ static int kt_serial_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_port *port, int idx) { - port->flags |= UPF_IIR_ONCE; + port->flags |= UPF_BUG_THRE; return skip_tx_en_setup(priv, board, port, idx); } @@ -1118,18 +1118,6 @@ pci_xr17c154_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } -static int try_enable_msi(struct pci_dev *dev) -{ - /* use msi if available, but fallback to legacy otherwise */ - pci_enable_msi(dev); - return 0; -} - -static void disable_msi(struct pci_dev *dev) -{ - pci_disable_msi(dev); -} - #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B #define PCI_DEVICE_ID_OCTPRO 0x0001 @@ -1249,9 +1237,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .device = PCI_DEVICE_ID_INTEL_PATSBURG_KT, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .init = try_enable_msi, .setup = kt_serial_setup, - .exit = disable_msi, }, /* * ITE diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 665beb68f670..070b442c1f81 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1041,7 +1041,7 @@ config SERIAL_OMAP config SERIAL_OMAP_CONSOLE bool "Console on OMAP serial port" - depends on SERIAL_OMAP + depends on SERIAL_OMAP=y select SERIAL_CORE_CONSOLE help Select this option if you would like to use omap serial port as diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index e7903751e058..1f0330915d5a 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -556,7 +556,7 @@ static int __devinit altera_uart_probe(struct platform_device *pdev) res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res_mem) port->mapbase = res_mem->start; - else if (platp->mapbase) + else if (platp) port->mapbase = platp->mapbase; else return -EINVAL; @@ -564,7 +564,7 @@ static int __devinit altera_uart_probe(struct platform_device *pdev) res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res_irq) port->irq = res_irq->start; - else if (platp->irq) + else if (platp) port->irq = platp->irq; /* Check platform data first so we can override device node data */ diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 0c65c9e66986..3d569cd68f58 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1946,10 +1946,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto unmap; } - /* Ensure interrupts from this UART are masked and cleared */ - writew(0, uap->port.membase + UART011_IMSC); - writew(0xffff, uap->port.membase + UART011_ICR); - uap->vendor = vendor; uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; @@ -1967,6 +1963,10 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->port.line = i; pl011_dma_probe(uap); + /* Ensure interrupts from this UART are masked and cleared */ + writew(0, uap->port.membase + UART011_IMSC); + writew(0xffff, uap->port.membase + UART011_ICR); + snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); amba_ports[i] = uap; diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index f9a6be7a9bed..3d7e1ee2fa57 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -389,6 +389,8 @@ static void atmel_start_rx(struct uart_port *port) { UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */ + UART_PUT_CR(port, ATMEL_US_RXEN); + if (atmel_use_dma_rx(port)) { /* enable PDC controller */ UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | @@ -404,6 +406,8 @@ static void atmel_start_rx(struct uart_port *port) */ static void atmel_stop_rx(struct uart_port *port) { + UART_PUT_CR(port, ATMEL_US_RXDIS); + if (atmel_use_dma_rx(port)) { /* disable PDC receive */ UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index e6c3dbd781d6..836fe2731234 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -154,10 +154,9 @@ static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) port->x_char = 0; return IRQ_HANDLED; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - clps711xuart_stop_tx(port); - return IRQ_HANDLED; - } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + goto disable_tx_irq; count = port->fifosize >> 1; do { @@ -171,8 +170,11 @@ static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) - clps711xuart_stop_tx(port); + if (uart_circ_empty(xmit)) { + disable_tx_irq: + disable_irq_nosync(TX_IRQ(port)); + tx_enabled(port) = 0; + } return IRQ_HANDLED; } diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index a9234ba8f8d5..c4b50af46c44 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -127,11 +127,6 @@ static inline void serial_out(struct uart_hsu_port *up, int offset, int value) #define HSU_REGS_BUFSIZE 1024 -static int hsu_show_regs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} static ssize_t port_show_regs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -231,14 +226,14 @@ static ssize_t dma_show_regs(struct file *file, char __user *user_buf, static const struct file_operations port_regs_ops = { .owner = THIS_MODULE, - .open = hsu_show_regs_open, + .open = simple_open, .read = port_show_regs, .llseek = default_llseek, }; static const struct file_operations dma_regs_ops = { .owner = THIS_MODULE, - .open = hsu_show_regs_open, + .open = simple_open, .read = dma_show_regs, .llseek = default_llseek, }; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 0121486ac4fa..d00b38eb268e 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1381,29 +1381,24 @@ static int serial_omap_probe(struct platform_device *pdev) return -ENODEV; } - if (!request_mem_region(mem->start, resource_size(mem), + if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), pdev->dev.driver->name)) { dev_err(&pdev->dev, "memory region already claimed\n"); return -EBUSY; } dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); - if (!dma_rx) { - ret = -EINVAL; - goto err; - } + if (!dma_rx) + return -ENXIO; dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); - if (!dma_tx) { - ret = -EINVAL; - goto err; - } + if (!dma_tx) + return -ENXIO; + + up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL); + if (!up) + return -ENOMEM; - up = kzalloc(sizeof(*up), GFP_KERNEL); - if (up == NULL) { - ret = -ENOMEM; - goto do_release_region; - } up->pdev = pdev; up->port.dev = &pdev->dev; up->port.type = PORT_OMAP; @@ -1423,16 +1418,17 @@ static int serial_omap_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", up->port.line); ret = -ENODEV; - goto err; + goto err_port_line; } sprintf(up->name, "OMAP UART%d", up->port.line); up->port.mapbase = mem->start; - up->port.membase = ioremap(mem->start, resource_size(mem)); + up->port.membase = devm_ioremap(&pdev->dev, mem->start, + resource_size(mem)); if (!up->port.membase) { dev_err(&pdev->dev, "can't ioremap UART\n"); ret = -ENOMEM; - goto err; + goto err_ioremap; } up->port.flags = omap_up_info->flags; @@ -1478,16 +1474,19 @@ static int serial_omap_probe(struct platform_device *pdev) ret = uart_add_one_port(&serial_omap_reg, &up->port); if (ret != 0) - goto do_release_region; + goto err_add_port; pm_runtime_put(&pdev->dev); platform_set_drvdata(pdev, up); return 0; -err: + +err_add_port: + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); +err_ioremap: +err_port_line: dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", pdev->id, __func__, ret); -do_release_region: - release_mem_region(mem->start, resource_size(mem)); return ret; } @@ -1499,8 +1498,6 @@ static int serial_omap_remove(struct platform_device *dev) pm_runtime_disable(&up->pdev->dev); uart_remove_one_port(&serial_omap_reg, &up->port); pm_qos_remove_request(&up->pm_qos_request); - - kfree(up); } platform_set_drvdata(dev, NULL); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index e825460478be..c2816f494807 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -210,6 +210,7 @@ enum { #define CMITC_UARTCLK 192000000 /* 192.0000 MHz */ #define FRI2_64_UARTCLK 64000000 /* 64.0000 MHz */ #define FRI2_48_UARTCLK 48000000 /* 48.0000 MHz */ +#define NTC1_UARTCLK 64000000 /* 64.0000 MHz */ struct pch_uart_buffer { unsigned char *buf; @@ -304,11 +305,7 @@ static const int trigger_level_1[4] = { 1, 1, 1, 1 }; #ifdef CONFIG_DEBUG_FS #define PCH_REGS_BUFSIZE 1024 -static int pch_show_regs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} + static ssize_t port_show_regs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -362,7 +359,7 @@ static ssize_t port_show_regs(struct file *file, char __user *user_buf, static const struct file_operations port_regs_ops = { .owner = THIS_MODULE, - .open = pch_show_regs_open, + .open = simple_open, .read = port_show_regs, .llseek = default_llseek, }; @@ -388,6 +385,12 @@ static int pch_uart_get_uartclk(void) if (cmp && strstr(cmp, "Fish River Island II")) return FRI2_48_UARTCLK; + /* Kontron COMe-mTT10 (nanoETXexpress-TT) */ + cmp = dmi_get_system_info(DMI_BOARD_NAME); + if (cmp && (strstr(cmp, "COMe-mTT") || + strstr(cmp, "nanoETXexpress-TT"))) + return NTC1_UARTCLK; + return DEFAULT_UARTCLK; } @@ -1444,9 +1447,11 @@ static int pch_uart_verify_port(struct uart_port *port, __func__); return -EOPNOTSUPP; #endif - priv->use_dma = 1; priv->use_dma_flag = 1; dev_info(priv->port.dev, "PCH UART : Use DMA Mode\n"); + if (!priv->use_dma) + pch_request_dma(port); + priv->use_dma = 1; } return 0; @@ -1655,6 +1660,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, } pci_enable_msi(pdev); + pci_set_master(pdev); iobase = pci_resource_start(pdev, 0); mapbase = pci_resource_start(pdev, 1); diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 08ebe901bb59..654755a990df 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -469,7 +469,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) tty = NULL; if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { if (!ZS_IS_OPEN(uap_a)) { - pmz_debug("ChanA interrupt while open !\n"); + pmz_debug("ChanA interrupt while not open !\n"); goto skip_a; } write_zsreg(uap_a, R0, RES_H_IUS); @@ -493,8 +493,8 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) spin_lock(&uap_b->port.lock); tty = NULL; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { - if (!ZS_IS_OPEN(uap_a)) { - pmz_debug("ChanB interrupt while open !\n"); + if (!ZS_IS_OPEN(uap_b)) { + pmz_debug("ChanB interrupt while not open !\n"); goto skip_b; } write_zsreg(uap_b, R0, RES_H_IUS); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index de249d265bec..d8b0aee35632 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -982,6 +982,7 @@ static void s3c24xx_serial_resetport(struct uart_port *port, ucon &= ucon_mask; wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); /* reset both fifos */ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index bf461cf99616..3158e17b665c 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -355,9 +355,6 @@ static void sci_serial_out(struct uart_port *p, int offset, int value) WARN(1, "Invalid register access\n"); } -#define sci_in(up, offset) (up->serial_in(up, offset)) -#define sci_out(up, offset, value) (up->serial_out(up, offset, value)) - static int sci_probe_regmap(struct plat_sci_port *cfg) { switch (cfg->type) { @@ -422,9 +419,9 @@ static int sci_poll_get_char(struct uart_port *port) int c; do { - status = sci_in(port, SCxSR); + status = serial_port_in(port, SCxSR); if (status & SCxSR_ERRORS(port)) { - sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); continue; } break; @@ -433,11 +430,11 @@ static int sci_poll_get_char(struct uart_port *port) if (!(status & SCxSR_RDxF(port))) return NO_POLL_CHAR; - c = sci_in(port, SCxRDR); + c = serial_port_in(port, SCxRDR); /* Dummy read */ - sci_in(port, SCxSR); - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + serial_port_in(port, SCxSR); + serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); return c; } @@ -448,11 +445,11 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) unsigned short status; do { - status = sci_in(port, SCxSR); + status = serial_port_in(port, SCxSR); } while (!(status & SCxSR_TDxE(port))); - sci_out(port, SCxTDR, c); - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); + serial_port_out(port, SCxTDR, c); + serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ @@ -480,10 +477,10 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) ((!(cflag & CRTSCTS)))) { unsigned short status; - status = sci_in(port, SCSPTR); + status = serial_port_in(port, SCSPTR); status &= ~SCSPTR_CTSIO; status |= SCSPTR_RTSIO; - sci_out(port, SCSPTR, status); /* Set RTS = 1 */ + serial_port_out(port, SCSPTR, status); /* Set RTS = 1 */ } } @@ -493,13 +490,13 @@ static int sci_txfill(struct uart_port *port) reg = sci_getreg(port, SCTFDR); if (reg->size) - return sci_in(port, SCTFDR) & 0xff; + return serial_port_in(port, SCTFDR) & 0xff; reg = sci_getreg(port, SCFDR); if (reg->size) - return sci_in(port, SCFDR) >> 8; + return serial_port_in(port, SCFDR) >> 8; - return !(sci_in(port, SCxSR) & SCI_TDRE); + return !(serial_port_in(port, SCxSR) & SCI_TDRE); } static int sci_txroom(struct uart_port *port) @@ -513,13 +510,13 @@ static int sci_rxfill(struct uart_port *port) reg = sci_getreg(port, SCRFDR); if (reg->size) - return sci_in(port, SCRFDR) & 0xff; + return serial_port_in(port, SCRFDR) & 0xff; reg = sci_getreg(port, SCFDR); if (reg->size) - return sci_in(port, SCFDR) & ((port->fifosize << 1) - 1); + return serial_port_in(port, SCFDR) & ((port->fifosize << 1) - 1); - return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; + return (serial_port_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } /* @@ -547,14 +544,14 @@ static void sci_transmit_chars(struct uart_port *port) unsigned short ctrl; int count; - status = sci_in(port, SCxSR); + status = serial_port_in(port, SCxSR); if (!(status & SCxSR_TDxE(port))) { - ctrl = sci_in(port, SCSCR); + ctrl = serial_port_in(port, SCSCR); if (uart_circ_empty(xmit)) ctrl &= ~SCSCR_TIE; else ctrl |= SCSCR_TIE; - sci_out(port, SCSCR, ctrl); + serial_port_out(port, SCSCR, ctrl); return; } @@ -573,27 +570,27 @@ static void sci_transmit_chars(struct uart_port *port) break; } - sci_out(port, SCxTDR, c); + serial_port_out(port, SCxTDR, c); port->icount.tx++; } while (--count > 0); - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); if (uart_circ_empty(xmit)) { sci_stop_tx(port); } else { - ctrl = sci_in(port, SCSCR); + ctrl = serial_port_in(port, SCSCR); if (port->type != PORT_SCI) { - sci_in(port, SCxSR); /* Dummy read */ - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + serial_port_in(port, SCxSR); /* Dummy read */ + serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); } ctrl |= SCSCR_TIE; - sci_out(port, SCSCR, ctrl); + serial_port_out(port, SCSCR, ctrl); } } @@ -608,7 +605,7 @@ static void sci_receive_chars(struct uart_port *port) unsigned short status; unsigned char flag; - status = sci_in(port, SCxSR); + status = serial_port_in(port, SCxSR); if (!(status & SCxSR_RDxF(port))) return; @@ -621,7 +618,7 @@ static void sci_receive_chars(struct uart_port *port) break; if (port->type == PORT_SCI) { - char c = sci_in(port, SCxRDR); + char c = serial_port_in(port, SCxRDR); if (uart_handle_sysrq_char(port, c) || sci_port->break_flag) count = 0; @@ -629,9 +626,9 @@ static void sci_receive_chars(struct uart_port *port) tty_insert_flip_char(tty, c, TTY_NORMAL); } else { for (i = 0; i < count; i++) { - char c = sci_in(port, SCxRDR); + char c = serial_port_in(port, SCxRDR); - status = sci_in(port, SCxSR); + status = serial_port_in(port, SCxSR); #if defined(CONFIG_CPU_SH3) /* Skip "chars" during break */ if (sci_port->break_flag) { @@ -672,8 +669,8 @@ static void sci_receive_chars(struct uart_port *port) } } - sci_in(port, SCxSR); /* dummy read */ - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + serial_port_in(port, SCxSR); /* dummy read */ + serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); copied += count; port->icount.rx += count; @@ -683,8 +680,8 @@ static void sci_receive_chars(struct uart_port *port) /* Tell the rest of the system the news. New characters! */ tty_flip_buffer_push(tty); } else { - sci_in(port, SCxSR); /* dummy read */ - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + serial_port_in(port, SCxSR); /* dummy read */ + serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); } } @@ -726,7 +723,7 @@ static void sci_break_timer(unsigned long data) static int sci_handle_errors(struct uart_port *port) { int copied = 0; - unsigned short status = sci_in(port, SCxSR); + unsigned short status = serial_port_in(port, SCxSR); struct tty_struct *tty = port->state->port.tty; struct sci_port *s = to_sci_port(port); @@ -804,8 +801,8 @@ static int sci_handle_fifo_overrun(struct uart_port *port) if (!reg->size) return 0; - if ((sci_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) { - sci_out(port, SCLSR, 0); + if ((serial_port_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) { + serial_port_out(port, SCLSR, 0); port->icount.overrun++; @@ -822,7 +819,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port) static int sci_handle_breaks(struct uart_port *port) { int copied = 0; - unsigned short status = sci_in(port, SCxSR); + unsigned short status = serial_port_in(port, SCxSR); struct tty_struct *tty = port->state->port.tty; struct sci_port *s = to_sci_port(port); @@ -859,8 +856,8 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) struct sci_port *s = to_sci_port(port); if (s->chan_rx) { - u16 scr = sci_in(port, SCSCR); - u16 ssr = sci_in(port, SCxSR); + u16 scr = serial_port_in(port, SCSCR); + u16 ssr = serial_port_in(port, SCxSR); /* Disable future Rx interrupts */ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { @@ -869,9 +866,9 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) } else { scr &= ~SCSCR_RIE; } - sci_out(port, SCSCR, scr); + serial_port_out(port, SCSCR, scr); /* Clear current interrupt */ - sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); + serial_port_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", jiffies, s->rx_timeout); mod_timer(&s->rx_timer, jiffies + s->rx_timeout); @@ -909,15 +906,15 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) if (port->type == PORT_SCI) { if (sci_handle_errors(port)) { /* discard character in rx buffer */ - sci_in(port, SCxSR); - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + serial_port_in(port, SCxSR); + serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); } } else { sci_handle_fifo_overrun(port); sci_rx_interrupt(irq, ptr); } - sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); /* Kick the transmission */ sci_tx_interrupt(irq, ptr); @@ -931,7 +928,7 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr) /* Handle BREAKs */ sci_handle_breaks(port); - sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); + serial_port_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); return IRQ_HANDLED; } @@ -955,8 +952,8 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) struct sci_port *s = to_sci_port(port); irqreturn_t ret = IRQ_NONE; - ssr_status = sci_in(port, SCxSR); - scr_status = sci_in(port, SCSCR); + ssr_status = serial_port_in(port, SCxSR); + scr_status = serial_port_in(port, SCSCR); err_enabled = scr_status & port_rx_irq_mask(port); /* Tx Interrupt */ @@ -1170,7 +1167,7 @@ static void sci_free_gpios(struct sci_port *port) static unsigned int sci_tx_empty(struct uart_port *port) { - unsigned short status = sci_in(port, SCxSR); + unsigned short status = serial_port_in(port, SCxSR); unsigned short in_tx_fifo = sci_txfill(port); return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; @@ -1198,7 +1195,7 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) */ reg = sci_getreg(port, SCFCR); if (reg->size) - sci_out(port, SCFCR, sci_in(port, SCFCR) | 1); + serial_port_out(port, SCFCR, serial_port_in(port, SCFCR) | 1); } } @@ -1240,8 +1237,8 @@ static void sci_dma_tx_complete(void *arg) } else { s->cookie_tx = -EINVAL; if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 ctrl = sci_in(port, SCSCR); - sci_out(port, SCSCR, ctrl & ~SCSCR_TIE); + u16 ctrl = serial_port_in(port, SCSCR); + serial_port_out(port, SCSCR, ctrl & ~SCSCR_TIE); } } @@ -1494,13 +1491,13 @@ static void sci_start_tx(struct uart_port *port) #ifdef CONFIG_SERIAL_SH_SCI_DMA if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 new, scr = sci_in(port, SCSCR); + u16 new, scr = serial_port_in(port, SCSCR); if (s->chan_tx) new = scr | 0x8000; else new = scr & ~0x8000; if (new != scr) - sci_out(port, SCSCR, new); + serial_port_out(port, SCSCR, new); } if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && @@ -1512,8 +1509,8 @@ static void sci_start_tx(struct uart_port *port) if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - sci_out(port, SCSCR, ctrl | SCSCR_TIE); + ctrl = serial_port_in(port, SCSCR); + serial_port_out(port, SCSCR, ctrl | SCSCR_TIE); } } @@ -1522,40 +1519,40 @@ static void sci_stop_tx(struct uart_port *port) unsigned short ctrl; /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); + ctrl = serial_port_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~0x8000; ctrl &= ~SCSCR_TIE; - sci_out(port, SCSCR, ctrl); + serial_port_out(port, SCSCR, ctrl); } static void sci_start_rx(struct uart_port *port) { unsigned short ctrl; - ctrl = sci_in(port, SCSCR) | port_rx_irq_mask(port); + ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~0x4000; - sci_out(port, SCSCR, ctrl); + serial_port_out(port, SCSCR, ctrl); } static void sci_stop_rx(struct uart_port *port) { unsigned short ctrl; - ctrl = sci_in(port, SCSCR); + ctrl = serial_port_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~0x4000; ctrl &= ~port_rx_irq_mask(port); - sci_out(port, SCSCR, ctrl); + serial_port_out(port, SCSCR, ctrl); } static void sci_enable_ms(struct uart_port *port) @@ -1589,13 +1586,13 @@ static void rx_timer_fn(unsigned long arg) { struct sci_port *s = (struct sci_port *)arg; struct uart_port *port = &s->port; - u16 scr = sci_in(port, SCSCR); + u16 scr = serial_port_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { scr &= ~0x4000; enable_irq(s->cfg->irqs[1]); } - sci_out(port, SCSCR, scr | SCSCR_RIE); + serial_port_out(port, SCSCR, scr | SCSCR_RIE); dev_dbg(port->dev, "DMA Rx timed out\n"); schedule_work(&s->work_rx); } @@ -1776,14 +1773,14 @@ static void sci_reset(struct uart_port *port) unsigned int status; do { - status = sci_in(port, SCxSR); + status = serial_port_in(port, SCxSR); } while (!(status & SCxSR_TEND(port))); - sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ + serial_port_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ reg = sci_getreg(port, SCFCR); if (reg->size) - sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); + serial_port_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); } static void sci_set_termios(struct uart_port *port, struct ktermios *termios, @@ -1812,7 +1809,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_reset(port); - smr_val = sci_in(port, SCSMR) & 3; + smr_val = serial_port_in(port, SCSMR) & 3; if ((termios->c_cflag & CSIZE) == CS7) smr_val |= 0x40; @@ -1825,19 +1822,19 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); - sci_out(port, SCSMR, smr_val); + serial_port_out(port, SCSMR, smr_val); dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, s->cfg->scscr); if (t > 0) { if (t >= 256) { - sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); + serial_port_out(port, SCSMR, (serial_port_in(port, SCSMR) & ~3) | 1); t >>= 2; } else - sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); + serial_port_out(port, SCSMR, serial_port_in(port, SCSMR) & ~3); - sci_out(port, SCBRR, t); + serial_port_out(port, SCBRR, t); udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ } @@ -1845,7 +1842,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, reg = sci_getreg(port, SCFCR); if (reg->size) { - unsigned short ctrl = sci_in(port, SCFCR); + unsigned short ctrl = serial_port_in(port, SCFCR); if (s->cfg->capabilities & SCIx_HAVE_RTSCTS) { if (termios->c_cflag & CRTSCTS) @@ -1861,10 +1858,10 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, */ ctrl &= ~(SCFCR_RFRST | SCFCR_TFRST); - sci_out(port, SCFCR, ctrl); + serial_port_out(port, SCFCR, ctrl); } - sci_out(port, SCSCR, s->cfg->scscr); + serial_port_out(port, SCSCR, s->cfg->scscr); #ifdef CONFIG_SERIAL_SH_SCI_DMA /* @@ -2166,7 +2163,7 @@ static void serial_console_write(struct console *co, const char *s, /* wait until fifo is empty and last bit has been transmitted */ bits = SCxSR_TDxE(port) | SCxSR_TEND(port); - while ((sci_in(port, SCxSR) & bits) != bits) + while ((serial_port_in(port, SCxSR) & bits) != bits) cpu_relax(); sci_port_disable(sci_port); @@ -2260,12 +2257,12 @@ static int sci_runtime_suspend(struct device *dev) if (uart_console(port)) { struct plat_sci_reg *reg; - sci_port->saved_smr = sci_in(port, SCSMR); - sci_port->saved_brr = sci_in(port, SCBRR); + sci_port->saved_smr = serial_port_in(port, SCSMR); + sci_port->saved_brr = serial_port_in(port, SCBRR); reg = sci_getreg(port, SCFCR); if (reg->size) - sci_port->saved_fcr = sci_in(port, SCFCR); + sci_port->saved_fcr = serial_port_in(port, SCFCR); else sci_port->saved_fcr = 0; } @@ -2279,13 +2276,13 @@ static int sci_runtime_resume(struct device *dev) if (uart_console(port)) { sci_reset(port); - sci_out(port, SCSMR, sci_port->saved_smr); - sci_out(port, SCBRR, sci_port->saved_brr); + serial_port_out(port, SCSMR, sci_port->saved_smr); + serial_port_out(port, SCBRR, sci_port->saved_brr); if (sci_port->saved_fcr) - sci_out(port, SCFCR, sci_port->saved_fcr); + serial_port_out(port, SCFCR, sci_port->saved_fcr); - sci_out(port, SCSCR, sci_port->cfg->scscr); + serial_port_out(port, SCSCR, sci_port->cfg->scscr); } return 0; } diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index a1a2d364f92b..4c22a1529aac 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -20,10 +20,10 @@ defined(CONFIG_ARCH_SH7372) || \ defined(CONFIG_ARCH_R8A7740) -# define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc) -# define SCxSR_ERROR_CLEAR(port) (sci_in(port, SCxSR) & 0xfd73) -# define SCxSR_TDxE_CLEAR(port) (sci_in(port, SCxSR) & 0xffdf) -# define SCxSR_BREAK_CLEAR(port) (sci_in(port, SCxSR) & 0xffe3) +# define SCxSR_RDxF_CLEAR(port) (serial_port_in(port, SCxSR) & 0xfffc) +# define SCxSR_ERROR_CLEAR(port) (serial_port_in(port, SCxSR) & 0xfd73) +# define SCxSR_TDxE_CLEAR(port) (serial_port_in(port, SCxSR) & 0xffdf) +# define SCxSR_BREAK_CLEAR(port) (serial_port_in(port, SCxSR) & 0xffe3) #else # define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc) # define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073) diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index b3b70b0bf85b..babd9470982b 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1581,7 +1581,7 @@ static int __init sunzilog_init(void) if (err) goto out_unregister_uart; - if (!zilog_irq) { + if (zilog_irq) { struct uart_sunzilog_port *up = sunzilog_irq_chain; err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED, "zs", sunzilog_irq_chain); @@ -1622,7 +1622,7 @@ static void __exit sunzilog_exit(void) { platform_driver_unregister(&zs_driver); - if (!zilog_irq) { + if (zilog_irq) { struct uart_sunzilog_port *up = sunzilog_irq_chain; /* Disable Interrupts */ diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 136e86faa1e1..05728894a88c 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -327,7 +327,7 @@ static void send_sig_all(int sig) if (is_global_init(p)) continue; - force_sig(sig, p); + do_send_sig_info(sig, SEND_SIG_FORCED, p, true); } read_unlock(&tasklist_lock); } diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 86dd1e302bb3..29ca20dbd335 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1085,15 +1085,21 @@ void vt_set_led_state(int console, int leds) * * Handle console start. This is a wrapper for the VT layer * so that we can keep kbd knowledge internal + * + * FIXME: We eventually need to hold the kbd lock here to protect + * the LED updating. We can't do it yet because fn_hold calls stop_tty + * and start_tty under the kbd_event_lock, while normal tty paths + * don't hold the lock. We probably need to split out an LED lock + * but not during an -rc release! */ void vt_kbd_con_start(int console) { struct kbd_struct * kbd = kbd_table + console; - unsigned long flags; - spin_lock_irqsave(&kbd_event_lock, flags); +/* unsigned long flags; */ +/* spin_lock_irqsave(&kbd_event_lock, flags); */ clr_vc_kbd_led(kbd, VC_SCROLLOCK); set_leds(); - spin_unlock_irqrestore(&kbd_event_lock, flags); +/* spin_unlock_irqrestore(&kbd_event_lock, flags); */ } /** @@ -1102,22 +1108,28 @@ void vt_kbd_con_start(int console) * * Handle console stop. This is a wrapper for the VT layer * so that we can keep kbd knowledge internal + * + * FIXME: We eventually need to hold the kbd lock here to protect + * the LED updating. We can't do it yet because fn_hold calls stop_tty + * and start_tty under the kbd_event_lock, while normal tty paths + * don't hold the lock. We probably need to split out an LED lock + * but not during an -rc release! */ void vt_kbd_con_stop(int console) { struct kbd_struct * kbd = kbd_table + console; - unsigned long flags; - spin_lock_irqsave(&kbd_event_lock, flags); +/* unsigned long flags; */ +/* spin_lock_irqsave(&kbd_event_lock, flags); */ set_vc_kbd_led(kbd, VC_SCROLLOCK); set_leds(); - spin_unlock_irqrestore(&kbd_event_lock, flags); +/* spin_unlock_irqrestore(&kbd_event_lock, flags); */ } /* * This is the tasklet that updates LED state on all keyboards * attached to the box. The reason we use tasklet is that we * need to handle the scenario when keyboard handler is not - * registered yet but we already getting updates form VT to + * registered yet but we already getting updates from the VT to * update led state. */ static void kbd_bh(unsigned long dummy) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 3bdd4b19dd06..2156188db4a6 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -2932,11 +2932,10 @@ static int __init con_init(void) gotoxy(vc, vc->vc_x, vc->vc_y); csi_J(vc, 0); update_screen(vc); - pr_info("Console: %s %s %dx%d", + pr_info("Console: %s %s %dx%d\n", vc->vc_can_do_color ? "colour" : "mono", display_desc, vc->vc_cols, vc->vc_rows); printable = 1; - printk("\n"); console_unlock(); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index cbd8f5f80596..76316a33061b 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -2,14 +2,6 @@ # USB device configuration # -menuconfig USB_SUPPORT - bool "USB support" - depends on HAS_IOMEM - default y - ---help--- - This option adds core support for Universal Serial Bus (USB). - You will also need drivers from the following menu to make use of it. - # many non-PCI SOC chips embed OHCI config USB_ARCH_HAS_OHCI boolean @@ -63,6 +55,14 @@ config USB_ARCH_HAS_XHCI boolean default PCI +menuconfig USB_SUPPORT + bool "USB support" + depends on HAS_IOMEM + default y + ---help--- + This option adds core support for Universal Serial Bus (USB). + You will also need drivers from the following menu to make use of it. + if USB_SUPPORT config USB_COMMON diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index c6f6560d436c..0bb2b3248dad 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -157,8 +157,9 @@ static void wdm_out_callback(struct urb *urb) spin_lock(&desc->iuspin); desc->werr = urb->status; spin_unlock(&desc->iuspin); - clear_bit(WDM_IN_USE, &desc->flags); kfree(desc->outbuf); + desc->outbuf = NULL; + clear_bit(WDM_IN_USE, &desc->flags); wake_up(&desc->wait); } @@ -338,7 +339,7 @@ static ssize_t wdm_write if (we < 0) return -EIO; - desc->outbuf = buf = kmalloc(count, GFP_KERNEL); + buf = kmalloc(count, GFP_KERNEL); if (!buf) { rv = -ENOMEM; goto outnl; @@ -406,10 +407,12 @@ static ssize_t wdm_write req->wIndex = desc->inum; req->wLength = cpu_to_le16(count); set_bit(WDM_IN_USE, &desc->flags); + desc->outbuf = buf; rv = usb_submit_urb(desc->command, GFP_KERNEL); if (rv < 0) { kfree(buf); + desc->outbuf = NULL; clear_bit(WDM_IN_USE, &desc->flags); dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); } else { diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f8e2d6d52e5c..9a56635dc19c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1189,8 +1189,13 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) if (status == 0) { status = usb_suspend_device(udev, msg); - /* Again, ignore errors during system sleep transitions */ - if (!PMSG_IS_AUTO(msg)) + /* + * Ignore errors from non-root-hub devices during + * system sleep transitions. For the most part, + * these devices should go to low power anyway when + * the entire bus is suspended. + */ + if (udev->parent && !PMSG_IS_AUTO(msg)) status = 0; } diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 622b4a48e732..57ed9e400c06 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -493,6 +493,15 @@ static int hcd_pci_suspend_noirq(struct device *dev) pci_save_state(pci_dev); + /* + * Some systems crash if an EHCI controller is in D3 during + * a sleep transition. We have to leave such controllers in D0. + */ + if (hcd->broken_pci_sleep) { + dev_dbg(dev, "Staying in PCI D0\n"); + return retval; + } + /* If the root hub is dead rather than suspended, disallow remote * wakeup. usb_hc_died() should ensure that both hosts are marked as * dying, so we only need to check the primary roothub. diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9d7fc9a39933..140d3e11f212 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1978,6 +1978,18 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) if (status == 0) { usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; + + /* Did we race with a root-hub wakeup event? */ + if (rhdev->do_remote_wakeup) { + char buffer[6]; + + status = hcd->driver->hub_status_data(hcd, buffer); + if (status != 0) { + dev_dbg(&rhdev->dev, "suspend raced with wakeup event\n"); + hcd_bus_resume(rhdev, PMSG_AUTO_RESUME); + status = -EBUSY; + } + } } else { spin_lock_irq(&hcd_root_hub_lock); if (!HCD_DEAD(hcd)) { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 28664eb7f555..ec6c97dadbe4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1667,7 +1667,6 @@ void usb_disconnect(struct usb_device **pdev) { struct usb_device *udev = *pdev; int i; - struct usb_hcd *hcd = bus_to_hcd(udev->bus); /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. @@ -1690,9 +1689,7 @@ void usb_disconnect(struct usb_device **pdev) * so that the hardware is now fully quiesced. */ dev_dbg (&udev->dev, "unregistering device\n"); - mutex_lock(hcd->bandwidth_mutex); usb_disable_device(udev, 0); - mutex_unlock(hcd->bandwidth_mutex); usb_hcd_synchronize_unlinks(udev); usb_remove_ep_devs(&udev->ep0); @@ -3163,6 +3160,22 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (retval) goto fail; + /* + * Some superspeed devices have finished the link training process + * and attached to a superspeed hub port, but the device descriptor + * got from those devices show they aren't superspeed devices. Warm + * reset the port attached by the devices can fix them. + */ + if ((udev->speed == USB_SPEED_SUPER) && + (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) { + dev_err(&udev->dev, "got a wrong device descriptor, " + "warm reset device\n"); + hub_port_reset(hub, port1, udev, + HUB_BH_RESET_TIME, true); + retval = -EINVAL; + goto fail; + } + if (udev->descriptor.bMaxPacketSize0 == 0xff || udev->speed == USB_SPEED_SUPER) i = 512; diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index cefa0c8b5b6a..d2b9af59cba9 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -428,18 +428,10 @@ static loff_t default_file_lseek (struct file *file, loff_t offset, int orig) return retval; } -static int default_open (struct inode *inode, struct file *file) -{ - if (inode->i_private) - file->private_data = inode->i_private; - - return 0; -} - static const struct file_operations default_file_operations = { .read = default_read_file, .write = default_write_file, - .open = default_open, + .open = simple_open, .llseek = default_file_lseek, }; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b3bdfede45e6..ca717da3be95 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -308,7 +308,8 @@ static void sg_complete(struct urb *urb) retval = usb_unlink_urb(io->urbs [i]); if (retval != -EINPROGRESS && retval != -ENODEV && - retval != -EBUSY) + retval != -EBUSY && + retval != -EIDRM) dev_err(&io->dev->dev, "%s, unlink --> %d\n", __func__, retval); @@ -317,7 +318,6 @@ static void sg_complete(struct urb *urb) } spin_lock(&io->lock); } - urb->dev = NULL; /* on the last completion, signal usb_sg_wait() */ io->bytes += urb->actual_length; @@ -524,7 +524,6 @@ void usb_sg_wait(struct usb_sg_request *io) case -ENXIO: /* hc didn't queue this one */ case -EAGAIN: case -ENOMEM: - io->urbs[i]->dev = NULL; retval = 0; yield(); break; @@ -542,7 +541,6 @@ void usb_sg_wait(struct usb_sg_request *io) /* fail any uncompleted urbs */ default: - io->urbs[i]->dev = NULL; io->urbs[i]->status = retval; dev_dbg(&io->dev->dev, "%s, submit --> %d\n", __func__, retval); @@ -593,7 +591,10 @@ void usb_sg_cancel(struct usb_sg_request *io) if (!io->urbs [i]->dev) continue; retval = usb_unlink_urb(io->urbs [i]); - if (retval != -EINPROGRESS && retval != -EBUSY) + if (retval != -EINPROGRESS + && retval != -ENODEV + && retval != -EBUSY + && retval != -EIDRM) dev_warn(&io->dev->dev, "%s, unlink --> %d\n", __func__, retval); } @@ -1135,8 +1136,6 @@ void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf, * Deallocates hcd/hardware state for the endpoints (nuking all or most * pending urbs) and usbcore state for the interfaces, so that usbcore * must usb_set_configuration() before any interfaces could be used. - * - * Must be called with hcd->bandwidth_mutex held. */ void usb_disable_device(struct usb_device *dev, int skip_ep0) { @@ -1189,7 +1188,9 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) usb_disable_endpoint(dev, i + USB_DIR_IN, false); } /* Remove endpoints from the host controller internal state */ + mutex_lock(hcd->bandwidth_mutex); usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); + mutex_unlock(hcd->bandwidth_mutex); /* Second pass: remove endpoint pointers */ } for (i = skip_ep0; i < 16; ++i) { @@ -1749,7 +1750,6 @@ free_interfaces: /* if it's already configured, clear out old state first. * getting rid of old interfaces means unbinding their drivers. */ - mutex_lock(hcd->bandwidth_mutex); if (dev->state != USB_STATE_ADDRESS) usb_disable_device(dev, 1); /* Skip ep0 */ @@ -1762,6 +1762,7 @@ free_interfaces: * host controller will not allow submissions to dropped endpoints. If * this call fails, the device state is unchanged. */ + mutex_lock(hcd->bandwidth_mutex); ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); if (ret < 0) { mutex_unlock(hcd->bandwidth_mutex); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 7239a73c1b8c..cd9b3a2cd8a7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -539,6 +539,10 @@ EXPORT_SYMBOL_GPL(usb_submit_urb); * never submitted, or it was unlinked before, or the hardware is already * finished with it), even if the completion handler has not yet run. * + * The URB must not be deallocated while this routine is running. In + * particular, when a driver calls this routine, it must insure that the + * completion handler cannot deallocate the URB. + * * Unlinking and Endpoint Queues: * * [The behaviors and guarantees described below do not apply to virtual @@ -603,6 +607,10 @@ EXPORT_SYMBOL_GPL(usb_unlink_urb); * with error -EPERM. Thus even if the URB's completion handler always * tries to resubmit, it will not succeed and the URB will become idle. * + * The URB must not be deallocated while this routine is running. In + * particular, when a driver calls this routine, it must insure that the + * completion handler cannot deallocate the URB. + * * This routine may not be used in an interrupt context (such as a bottom * half or a completion handler), or when holding a spinlock, or in other * situations where the caller can't schedule(). @@ -640,6 +648,10 @@ EXPORT_SYMBOL_GPL(usb_kill_urb); * with error -EPERM. Thus even if the URB's completion handler always * tries to resubmit, it will not succeed and the URB will become idle. * + * The URB must not be deallocated while this routine is running. In + * particular, when a driver calls this routine, it must insure that the + * completion handler cannot deallocate the URB. + * * This routine may not be used in an interrupt context (such as a bottom * half or a completion handler), or when holding a spinlock, or in other * situations where the caller can't schedule(). diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 7bd815a507e8..99b58d84553a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -206,11 +206,11 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) for (i = 0; i < dwc->num_event_buffers; i++) { evt = dwc->ev_buffs[i]; - if (evt) { + if (evt) dwc3_free_one_event_buffer(dwc, evt); - dwc->ev_buffs[i] = NULL; - } } + + kfree(dwc->ev_buffs); } /** diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 25910e251c04..3584a169886f 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -353,6 +353,9 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, dwc->test_mode_nr = wIndex >> 8; dwc->test_mode = true; + break; + default: + return -EINVAL; } break; @@ -559,15 +562,20 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, length = trb->size & DWC3_TRB_SIZE_MASK; if (dwc->ep0_bounced) { + unsigned transfer_size = ur->length; + unsigned maxp = ep0->endpoint.maxpacket; + + transfer_size += (maxp - (transfer_size % maxp)); transferred = min_t(u32, ur->length, - ep0->endpoint.maxpacket - length); + transfer_size - length); memcpy(ur->buf, dwc->ep0_bounce, transferred); dwc->ep0_bounced = false; } else { transferred = ur->length - length; - ur->actual += transferred; } + ur->actual += transferred; + if ((epnum & 1) && ur->actual < ur->length) { /* for some reason we did not get everything out */ diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 0c935d7c65bd..9d7bcd910074 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1863,8 +1863,8 @@ static int __devinit at91udc_probe(struct platform_device *pdev) mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT); } else { - if (request_irq(udc->board.vbus_pin, at91_vbus_irq, - 0, driver_name, udc)) { + if (request_irq(gpio_to_irq(udc->board.vbus_pin), + at91_vbus_irq, 0, driver_name, udc)) { DBG("request vbus irq %d failed\n", udc->board.vbus_pin); retval = -EBUSY; @@ -1886,7 +1886,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev) return 0; fail4: if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled) - free_irq(udc->board.vbus_pin, udc); + free_irq(gpio_to_irq(udc->board.vbus_pin), udc); fail3: if (gpio_is_valid(udc->board.vbus_pin)) gpio_free(udc->board.vbus_pin); @@ -1924,7 +1924,7 @@ static int __exit at91udc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); remove_debug_file(udc); if (gpio_is_valid(udc->board.vbus_pin)) { - free_irq(udc->board.vbus_pin, udc); + free_irq(gpio_to_irq(udc->board.vbus_pin), udc); gpio_free(udc->board.vbus_pin); } free_irq(udc->udp_irq, udc); diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index a6dfd2164166..170cbe89d9f8 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -927,7 +927,6 @@ static int dummy_udc_stop(struct usb_gadget *g, dum->driver = NULL; - dummy_pullup(&dum->gadget, 0); return 0; } diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 1cbba70836bc..f52cb1ae45d9 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -712,7 +712,7 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value) if (code == FUNCTIONFS_INTERFACE_REVMAP) { struct ffs_function *func = ffs->func; ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV; - } else if (gadget->ops->ioctl) { + } else if (gadget && gadget->ops->ioctl) { ret = gadget->ops->ioctl(gadget, code, value); } else { ret = -ENOTTY; @@ -1382,6 +1382,7 @@ static void functionfs_unbind(struct ffs_data *ffs) ffs->ep0req = NULL; ffs->gadget = NULL; ffs_data_put(ffs); + clear_bit(FFS_FL_BOUND, &ffs->flags); } } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index a371e966425f..cb8c162cae5a 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2189,7 +2189,7 @@ unknown_cmnd: common->data_size_from_cmnd = 0; sprintf(unknown, "Unknown x%02x", common->cmnd[0]); reply = check_command(common, common->cmnd_size, - DATA_DIR_UNKNOWN, 0xff, 0, unknown); + DATA_DIR_UNKNOWN, ~0, 0, unknown); if (reply == 0) { common->curlun->sense_data = SS_INVALID_COMMAND; reply = -EINVAL; diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 85a5cebe96b3..965a6293206a 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -345,7 +345,7 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) } skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, - skb->len <= 1, req->actual, req->actual); + skb->len <= 1, req->actual, PAGE_SIZE); page = NULL; if (req->actual < req->length) { /* Last fragment */ diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 7b1cf18df5e3..52343654f5df 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -500,6 +500,7 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_response_complete; + req->context = rndis; rndis_free_response(rndis->config, buf); value = n; } diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 4fac56927741..a896d73f7a93 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -2579,7 +2579,7 @@ static int do_scsi_command(struct fsg_dev *fsg) fsg->data_size_from_cmnd = 0; sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); if ((reply = check_command(fsg, fsg->cmnd_size, - DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) { + DATA_DIR_UNKNOWN, ~0, 0, unknown)) == 0) { fsg->curlun->sense_data = SS_INVALID_COMMAND; reply = -EINVAL; } diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 5f94e79cd6b9..55abfb6bd612 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -730,7 +730,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) : (1 << (ep_index(ep))); /* check if the pipe is empty */ - if (!(list_empty(&ep->queue))) { + if (!(list_empty(&ep->queue)) && !(ep_index(ep) == 0)) { /* Add td to the end */ struct fsl_req *lastreq; lastreq = list_entry(ep->queue.prev, struct fsl_req, queue); @@ -918,10 +918,6 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return -ENOMEM; } - /* Update ep0 state */ - if ((ep_index(ep) == 0)) - udc->ep0_state = DATA_STATE_XMIT; - /* irq handler advances the queue */ if (req != NULL) list_add_tail(&req->queue, &ep->queue); @@ -1279,7 +1275,8 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) udc->ep0_dir = USB_DIR_OUT; ep = &udc->eps[0]; - udc->ep0_state = WAIT_FOR_OUT_STATUS; + if (udc->ep0_state != DATA_STATE_XMIT) + udc->ep0_state = WAIT_FOR_OUT_STATUS; req->ep = ep; req->req.length = 0; @@ -1384,6 +1381,9 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, list_add_tail(&req->queue, &ep->queue); udc->ep0_state = DATA_STATE_XMIT; + if (ep0_prime_status(udc, EP_DIR_OUT)) + ep0stall(udc); + return; stall: ep0stall(udc); @@ -1492,6 +1492,14 @@ static void setup_received_irq(struct fsl_udc *udc, spin_lock(&udc->lock); udc->ep0_state = (setup->bRequestType & USB_DIR_IN) ? DATA_STATE_XMIT : DATA_STATE_RECV; + /* + * If the data stage is IN, send status prime immediately. + * See 2.0 Spec chapter 8.5.3.3 for detail. + */ + if (udc->ep0_state == DATA_STATE_XMIT) + if (ep0_prime_status(udc, EP_DIR_OUT)) + ep0stall(udc); + } else { /* No data phase, IN status from gadget */ udc->ep0_dir = USB_DIR_IN; @@ -1520,9 +1528,8 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, switch (udc->ep0_state) { case DATA_STATE_XMIT: - /* receive status phase */ - if (ep0_prime_status(udc, EP_DIR_OUT)) - ep0stall(udc); + /* already primed at setup_received_irq */ + udc->ep0_state = WAIT_FOR_OUT_STATUS; break; case DATA_STATE_RECV: /* send status phase */ diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 331cd6729d3c..a85eaf40b948 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -161,7 +161,7 @@ static struct usb_composite_driver gfs_driver = { static struct ffs_data *gfs_ffs_data; static unsigned long gfs_registered; -static int gfs_init(void) +static int __init gfs_init(void) { ENTER(); @@ -169,7 +169,7 @@ static int gfs_init(void) } module_init(gfs_init); -static void gfs_exit(void) +static void __exit gfs_exit(void) { ENTER(); diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 8793f32bab11..e58b16442971 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1574,7 +1574,6 @@ static void destroy_ep_files (struct dev_data *dev) DBG (dev, "%s %d\n", __func__, dev->state); /* dev->state must prevent interference */ -restart: spin_lock_irq (&dev->lock); while (!list_empty(&dev->epfiles)) { struct ep_data *ep; diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 69295ba9d99a..105b206cd844 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -340,7 +340,7 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg) /* currently we allocate TX FIFOs for all possible endpoints, * and assume that they are all the same size. */ - for (ep = 0; ep <= 15; ep++) { + for (ep = 1; ep <= 15; ep++) { val = addr; val |= size << S3C_DPTXFSIZn_DPTxFSize_SHIFT; addr += size; @@ -741,7 +741,7 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, /* write size / packets */ writel(epsize, hsotg->regs + epsize_reg); - if (using_dma(hsotg)) { + if (using_dma(hsotg) && !continuing) { unsigned int dma_reg; /* write DMA address to control register, buffer already @@ -1696,10 +1696,12 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, reg |= mpsval; writel(reg, regs + S3C_DIEPCTL(ep)); - reg = readl(regs + S3C_DOEPCTL(ep)); - reg &= ~S3C_DxEPCTL_MPS_MASK; - reg |= mpsval; - writel(reg, regs + S3C_DOEPCTL(ep)); + if (ep) { + reg = readl(regs + S3C_DOEPCTL(ep)); + reg &= ~S3C_DxEPCTL_MPS_MASK; + reg |= mpsval; + writel(reg, regs + S3C_DOEPCTL(ep)); + } return; @@ -1919,7 +1921,8 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, ints & S3C_DIEPMSK_TxFIFOEmpty) { dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", __func__, idx); - s3c_hsotg_trytx(hsotg, hs_ep); + if (!using_dma(hsotg)) + s3c_hsotg_trytx(hsotg, hs_ep); } } } diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 56da49f31d6c..e5e44f8cde9a 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -263,9 +263,9 @@ static void usb_gadget_remove_driver(struct usb_udc *udc) if (udc_is_newstyle(udc)) { udc->driver->disconnect(udc->gadget); + usb_gadget_disconnect(udc->gadget); udc->driver->unbind(udc->gadget); usb_gadget_udc_stop(udc->gadget, udc->driver); - usb_gadget_disconnect(udc->gadget); } else { usb_gadget_stop(udc->gadget, udc->driver); } @@ -411,9 +411,13 @@ static ssize_t usb_udc_softconn_store(struct device *dev, struct usb_udc *udc = container_of(dev, struct usb_udc, dev); if (sysfs_streq(buf, "connect")) { + if (udc_is_newstyle(udc)) + usb_gadget_udc_start(udc->gadget, udc->driver); usb_gadget_connect(udc->gadget); } else if (sysfs_streq(buf, "disconnect")) { usb_gadget_disconnect(udc->gadget); + if (udc_is_newstyle(udc)) + usb_gadget_udc_stop(udc->gadget, udc->driver); } else { dev_err(dev, "unsupported command '%s'\n", buf); return -EINVAL; diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index bc78c606c12b..ca4e03a1c73a 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -28,7 +28,7 @@ struct uvc_request_data { - unsigned int length; + __s32 length; __u8 data[60]; }; diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index d776adb2da67..0cdf89d32a15 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -543,11 +543,11 @@ done: return ret; } +/* called with queue->irqlock held.. */ static struct uvc_buffer * uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) { struct uvc_buffer *nextbuf; - unsigned long flags; if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && buf->buf.length != buf->buf.bytesused) { @@ -556,14 +556,12 @@ uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) return buf; } - spin_lock_irqsave(&queue->irqlock, flags); list_del(&buf->queue); if (!list_empty(&queue->irqqueue)) nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, queue); else nextbuf = NULL; - spin_unlock_irqrestore(&queue->irqlock, flags); buf->buf.sequence = queue->sequence++; do_gettimeofday(&buf->buf.timestamp); diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index f6e083b50191..54d7ca559cb2 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -39,7 +39,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) if (data->length < 0) return usb_ep_set_halt(cdev->gadget->ep0); - req->length = min(uvc->event_length, data->length); + req->length = min_t(unsigned int, uvc->event_length, data->length); req->zero = data->length < uvc->event_length; req->dma = DMA_ADDR_INVALID; diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 19f318ababa2..cf14c95a6700 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -13,6 +13,7 @@ #include <linux/clk.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/of_platform.h> /* interface and function clocks */ diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index fd9109d7eb0e..680e1a31fb87 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -352,7 +352,6 @@ static int debug_async_open(struct inode *, struct file *); static int debug_periodic_open(struct inode *, struct file *); static int debug_registers_open(struct inode *, struct file *); static int debug_async_open(struct inode *, struct file *); -static int debug_lpm_open(struct inode *, struct file *); static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos); static ssize_t debug_lpm_write(struct file *file, const char __user *buffer, @@ -385,7 +384,7 @@ static const struct file_operations debug_registers_fops = { }; static const struct file_operations debug_lpm_fops = { .owner = THIS_MODULE, - .open = debug_lpm_open, + .open = simple_open, .read = debug_lpm_read, .write = debug_lpm_write, .release = debug_lpm_close, @@ -970,12 +969,6 @@ static int debug_registers_open(struct inode *inode, struct file *file) return file->private_data ? 0 : -ENOMEM; } -static int debug_lpm_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static int debug_lpm_close(struct inode *inode, struct file *file) { return 0; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 3e7345172e03..d0a84bd3f3eb 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -218,6 +218,9 @@ static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, u32 portsc; struct usb_hcd *hcd = ehci_to_hcd(ehci); void __iomem *non_ehci = hcd->regs; + struct fsl_usb2_platform_data *pdata; + + pdata = hcd->self.controller->platform_data; portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]); portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW); @@ -234,7 +237,9 @@ static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, /* fall through */ case FSL_USB2_PHY_UTMI: /* enable UTMI PHY */ - setbits32(non_ehci + FSL_SOC_USB_CTRL, CTRL_UTMI_PHY_EN); + if (pdata->have_sysif_regs) + setbits32(non_ehci + FSL_SOC_USB_CTRL, + CTRL_UTMI_PHY_EN); portsc |= PORT_PTS_UTMI; break; case FSL_USB2_PHY_NONE: diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 057cdda7a489..4a3bc5b7a06f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -347,6 +347,8 @@ static int ehci_reset (struct ehci_hcd *ehci) if (ehci->debug) dbgp_external_startup(); + ehci->port_c_suspend = ehci->suspended_ports = + ehci->resuming_ports = 0; return retval; } @@ -856,8 +858,13 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) goto dead; } + /* + * We don't use STS_FLR, but some controllers don't like it to + * remain on, so mask it out along with the other status bits. + */ + masked_status = status & (INTR_MASK | STS_FLR); + /* Shared IRQ? */ - masked_status = status & INTR_MASK; if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) { spin_unlock(&ehci->lock); return IRQ_NONE; @@ -908,7 +915,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) pcd_status = status; /* resume root hub? */ - if (!(cmd & CMD_RUN)) + if (ehci->rh_state == EHCI_RH_SUSPENDED) usb_hcd_resume_root_hub(hcd); /* get per-port change detect bits */ @@ -939,6 +946,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) * like usb_port_resume() does. */ ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); + set_bit(i, &ehci->resuming_ports); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); mod_timer(&hcd->rh_timer, ehci->reset_done[i]); } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 256fbd42e48c..38fe07623152 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -223,15 +223,10 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) * remote wakeup, we must fail the suspend. */ if (hcd->self.root_hub->do_remote_wakeup) { - port = HCS_N_PORTS(ehci->hcs_params); - while (port--) { - if (ehci->reset_done[port] != 0) { - spin_unlock_irq(&ehci->lock); - ehci_dbg(ehci, "suspend failed because " - "port %d is resuming\n", - port + 1); - return -EBUSY; - } + if (ehci->resuming_ports) { + spin_unlock_irq(&ehci->lock); + ehci_dbg(ehci, "suspend failed because a port is resuming\n"); + return -EBUSY; } } @@ -554,16 +549,12 @@ static int ehci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 temp, status = 0; + u32 temp, status; u32 mask; int ports, i, retval = 1; unsigned long flags; u32 ppcd = 0; - /* if !USB_SUSPEND, root hub timers won't get shut down ... */ - if (ehci->rh_state != EHCI_RH_RUNNING) - return 0; - /* init status to no-changes */ buf [0] = 0; ports = HCS_N_PORTS (ehci->hcs_params); @@ -572,6 +563,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) retval++; } + /* Inform the core about resumes-in-progress by returning + * a non-zero value even if there are no status changes. + */ + status = ehci->resuming_ports; + /* Some boards (mostly VIA?) report bogus overcurrent indications, * causing massive log spam unless we completely ignore them. It * may be relevant that VIA VT8235 controllers, where PORT_POWER is @@ -846,6 +842,7 @@ static int ehci_hub_control ( ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESUME), status_reg); + clear_bit(wIndex, &ehci->resuming_ports); retval = handshake(ehci, status_reg, PORT_RESUME, 0, 2000 /* 2msec */); if (retval != 0) { @@ -864,6 +861,7 @@ static int ehci_hub_control ( ehci->reset_done[wIndex])) { status |= USB_PORT_STAT_C_RESET << 16; ehci->reset_done [wIndex] = 0; + clear_bit(wIndex, &ehci->resuming_ports); /* force reset to complete */ ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), @@ -884,8 +882,10 @@ static int ehci_hub_control ( ehci_readl(ehci, status_reg)); } - if (!(temp & (PORT_RESUME|PORT_RESET))) + if (!(temp & (PORT_RESUME|PORT_RESET))) { ehci->reset_done[wIndex] = 0; + clear_bit(wIndex, &ehci->resuming_ports); + } /* transfer dedicated ports to the companion hc */ if ((temp & PORT_CONNECT) && @@ -920,6 +920,7 @@ static int ehci_hub_control ( status |= USB_PORT_STAT_SUSPEND; } else if (test_bit(wIndex, &ehci->suspended_ports)) { clear_bit(wIndex, &ehci->suspended_ports); + clear_bit(wIndex, &ehci->resuming_ports); ehci->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &ehci->port_c_suspend); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index bba9850f32f0..5c78f9e71466 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -42,6 +42,7 @@ #include <plat/usb.h> #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> +#include <linux/gpio.h> /* EHCI Register Set */ #define EHCI_INSNREG04 (0xA0) @@ -191,6 +192,19 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) } } + if (pdata->phy_reset) { + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_request_one(pdata->reset_gpio_port[0], + GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); + + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_request_one(pdata->reset_gpio_port[1], + GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); + + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(10); + } + pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -237,6 +251,19 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* root ports should always stay powered */ ehci_port_power(omap_ehci, 1); + if (pdata->phy_reset) { + /* Hold the PHY in RESET for enough time till + * PHY is settled and ready + */ + udelay(10); + + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_set_value(pdata->reset_gpio_port[0], 1); + + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_set_value(pdata->reset_gpio_port[1], 1); + } + return 0; err_add_hcd: @@ -259,8 +286,9 @@ err_io: */ static int ehci_hcd_omap_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct usb_hcd *hcd = dev_get_drvdata(dev); + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ehci_hcd_omap_platform_data *pdata = dev->platform_data; usb_remove_hcd(hcd); disable_put_regulator(dev->platform_data); @@ -269,6 +297,13 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); pm_runtime_disable(dev); + if (pdata->phy_reset) { + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_free(pdata->reset_gpio_port[0]); + + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_free(pdata->reset_gpio_port[1]); + } return 0; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 01bb7241d6ef..fe8dc069164e 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -144,6 +144,14 @@ static int ehci_pci_setup(struct usb_hcd *hcd) hcd->has_tt = 1; tdi_reset(ehci); } + if (pdev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK) { + /* EHCI #1 or #2 on 6 Series/C200 Series chipset */ + if (pdev->device == 0x1c26 || pdev->device == 0x1c2d) { + ehci_info(ehci, "broken D3 during system sleep on ASUS\n"); + hcd->broken_pci_sleep = 1; + device_set_wakeup_capable(&pdev->dev, false); + } + } break; case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 3de48a2d7955..f214a80cdee2 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -24,6 +24,7 @@ #include <linux/gpio.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/pm_runtime.h> #include <mach/usb_phy.h> #include <mach/iomap.h> @@ -37,9 +38,7 @@ struct tegra_ehci_hcd { struct clk *emc_clk; struct usb_phy *transceiver; int host_resumed; - int bus_suspended; int port_resuming; - int power_down_on_bus_suspend; enum tegra_usb_phy_port_speed port_speed; }; @@ -224,6 +223,7 @@ static int tegra_ehci_hub_control( temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); /* start resume signalling */ ehci_writel(ehci, temp | PORT_RESUME, status_reg); + set_bit(wIndex-1, &ehci->resuming_ports); spin_unlock_irqrestore(&ehci->lock, flags); msleep(20); @@ -236,6 +236,7 @@ static int tegra_ehci_hub_control( pr_err("%s: timeout waiting for SUSPEND\n", __func__); ehci->reset_done[wIndex-1] = 0; + clear_bit(wIndex-1, &ehci->resuming_ports); tegra->port_resuming = 1; goto done; @@ -271,120 +272,6 @@ static void tegra_ehci_restart(struct usb_hcd *hcd) up_write(&ehci_cf_port_reset_rwsem); } -static int tegra_usb_suspend(struct usb_hcd *hcd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct ehci_regs __iomem *hw = tegra->ehci->regs; - unsigned long flags; - - spin_lock_irqsave(&tegra->ehci->lock, flags); - - tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; - ehci_halt(tegra->ehci); - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - - tegra_ehci_power_down(hcd); - return 0; -} - -static int tegra_usb_resume(struct usb_hcd *hcd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct ehci_regs __iomem *hw = ehci->regs; - unsigned long val; - - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - tegra_ehci_power_up(hcd); - - if (tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) { - /* Wait for the phy to detect new devices - * before we restart the controller */ - msleep(10); - goto restart; - } - - /* Force the phy to keep data lines in suspend state */ - tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); - - /* Enable host mode */ - tdi_reset(ehci); - - /* Enable Port Power */ - val = readl(&hw->port_status[0]); - val |= PORT_POWER; - writel(val, &hw->port_status[0]); - udelay(10); - - /* Check if the phy resume from LP0. When the phy resume from LP0 - * USB register will be reset. */ - if (!readl(&hw->async_next)) { - /* Program the field PTC based on the saved speed mode */ - val = readl(&hw->port_status[0]); - val &= ~PORT_TEST(~0); - if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH) - val |= PORT_TEST_FORCE; - else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) - val |= PORT_TEST(6); - else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= PORT_TEST(7); - writel(val, &hw->port_status[0]); - udelay(10); - - /* Disable test mode by setting PTC field to NORMAL_OP */ - val = readl(&hw->port_status[0]); - val &= ~PORT_TEST(~0); - writel(val, &hw->port_status[0]); - udelay(10); - } - - /* Poll until CCS is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_CONNECT, - PORT_CONNECT, 2000)) { - pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); - goto restart; - } - - /* Poll until PE is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_PE, - PORT_PE, 2000)) { - pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); - goto restart; - } - - /* Clear the PCI status, to avoid an interrupt taken upon resume */ - val = readl(&hw->status); - val |= STS_PCD; - writel(val, &hw->status); - - /* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */ - val = readl(&hw->port_status[0]); - if ((val & PORT_POWER) && (val & PORT_PE)) { - val |= PORT_SUSPEND; - writel(val, &hw->port_status[0]); - - /* Wait until port suspend completes */ - if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND, - PORT_SUSPEND, 1000)) { - pr_err("%s: timeout waiting for PORT_SUSPEND\n", - __func__); - goto restart; - } - } - - tegra_ehci_phy_restore_end(tegra->phy); - return 0; - -restart: - if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) - tegra_ehci_phy_restore_end(tegra->phy); - - tegra_ehci_restart(hcd); - return 0; -} - static void tegra_ehci_shutdown(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); @@ -432,36 +319,6 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) return retval; } -#ifdef CONFIG_PM -static int tegra_ehci_bus_suspend(struct usb_hcd *hcd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - int error_status = 0; - - error_status = ehci_bus_suspend(hcd); - if (!error_status && tegra->power_down_on_bus_suspend) { - tegra_usb_suspend(hcd); - tegra->bus_suspended = 1; - } - - return error_status; -} - -static int tegra_ehci_bus_resume(struct usb_hcd *hcd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - - if (tegra->bus_suspended && tegra->power_down_on_bus_suspend) { - tegra_usb_resume(hcd); - tegra->bus_suspended = 0; - } - - tegra_usb_phy_preresume(tegra->phy); - tegra->port_resuming = 1; - return ehci_bus_resume(hcd); -} -#endif - struct temp_buffer { void *kmalloc_ptr; void *old_xfer_buffer; @@ -572,8 +429,8 @@ static const struct hc_driver tegra_ehci_hc_driver = { .hub_control = tegra_ehci_hub_control, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, #ifdef CONFIG_PM - .bus_suspend = tegra_ehci_bus_suspend, - .bus_resume = tegra_ehci_bus_resume, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, @@ -601,11 +458,187 @@ static int setup_vbus_gpio(struct platform_device *pdev) dev_err(&pdev->dev, "can't enable vbus\n"); return err; } - gpio_set_value(gpio, 1); return err; } +#ifdef CONFIG_PM + +static int controller_suspend(struct device *dev) +{ + struct tegra_ehci_hcd *tegra = + platform_get_drvdata(to_platform_device(dev)); + struct ehci_hcd *ehci = tegra->ehci; + struct usb_hcd *hcd = ehci_to_hcd(ehci); + struct ehci_regs __iomem *hw = ehci->regs; + unsigned long flags; + + if (time_before(jiffies, ehci->next_statechange)) + msleep(10); + + spin_lock_irqsave(&ehci->lock, flags); + + tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; + ehci_halt(ehci); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + spin_unlock_irqrestore(&ehci->lock, flags); + + tegra_ehci_power_down(hcd); + return 0; +} + +static int controller_resume(struct device *dev) +{ + struct tegra_ehci_hcd *tegra = + platform_get_drvdata(to_platform_device(dev)); + struct ehci_hcd *ehci = tegra->ehci; + struct usb_hcd *hcd = ehci_to_hcd(ehci); + struct ehci_regs __iomem *hw = ehci->regs; + unsigned long val; + + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + tegra_ehci_power_up(hcd); + + if (tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) { + /* Wait for the phy to detect new devices + * before we restart the controller */ + msleep(10); + goto restart; + } + + /* Force the phy to keep data lines in suspend state */ + tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); + + /* Enable host mode */ + tdi_reset(ehci); + + /* Enable Port Power */ + val = readl(&hw->port_status[0]); + val |= PORT_POWER; + writel(val, &hw->port_status[0]); + udelay(10); + + /* Check if the phy resume from LP0. When the phy resume from LP0 + * USB register will be reset. */ + if (!readl(&hw->async_next)) { + /* Program the field PTC based on the saved speed mode */ + val = readl(&hw->port_status[0]); + val &= ~PORT_TEST(~0); + if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH) + val |= PORT_TEST_FORCE; + else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) + val |= PORT_TEST(6); + else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) + val |= PORT_TEST(7); + writel(val, &hw->port_status[0]); + udelay(10); + + /* Disable test mode by setting PTC field to NORMAL_OP */ + val = readl(&hw->port_status[0]); + val &= ~PORT_TEST(~0); + writel(val, &hw->port_status[0]); + udelay(10); + } + + /* Poll until CCS is enabled */ + if (handshake(ehci, &hw->port_status[0], PORT_CONNECT, + PORT_CONNECT, 2000)) { + pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); + goto restart; + } + + /* Poll until PE is enabled */ + if (handshake(ehci, &hw->port_status[0], PORT_PE, + PORT_PE, 2000)) { + pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); + goto restart; + } + + /* Clear the PCI status, to avoid an interrupt taken upon resume */ + val = readl(&hw->status); + val |= STS_PCD; + writel(val, &hw->status); + + /* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */ + val = readl(&hw->port_status[0]); + if ((val & PORT_POWER) && (val & PORT_PE)) { + val |= PORT_SUSPEND; + writel(val, &hw->port_status[0]); + + /* Wait until port suspend completes */ + if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND, + PORT_SUSPEND, 1000)) { + pr_err("%s: timeout waiting for PORT_SUSPEND\n", + __func__); + goto restart; + } + } + + tegra_ehci_phy_restore_end(tegra->phy); + goto done; + + restart: + if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) + tegra_ehci_phy_restore_end(tegra->phy); + + tegra_ehci_restart(hcd); + + done: + tegra_usb_phy_preresume(tegra->phy); + tegra->port_resuming = 1; + return 0; +} + +static int tegra_ehci_suspend(struct device *dev) +{ + struct tegra_ehci_hcd *tegra = + platform_get_drvdata(to_platform_device(dev)); + struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); + int rc = 0; + + /* + * When system sleep is supported and USB controller wakeup is + * implemented: If the controller is runtime-suspended and the + * wakeup setting needs to be changed, call pm_runtime_resume(). + */ + if (HCD_HW_ACCESSIBLE(hcd)) + rc = controller_suspend(dev); + return rc; +} + +static int tegra_ehci_resume(struct device *dev) +{ + int rc; + + rc = controller_resume(dev); + if (rc == 0) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + return rc; +} + +static int tegra_ehci_runtime_suspend(struct device *dev) +{ + return controller_suspend(dev); +} + +static int tegra_ehci_runtime_resume(struct device *dev) +{ + return controller_resume(dev); +} + +static const struct dev_pm_ops tegra_ehci_pm_ops = { + .suspend = tegra_ehci_suspend, + .resume = tegra_ehci_resume, + .runtime_suspend = tegra_ehci_runtime_suspend, + .runtime_resume = tegra_ehci_runtime_resume, +}; + +#endif + static u64 tegra_ehci_dma_mask = DMA_BIT_MASK(32); static int tegra_ehci_probe(struct platform_device *pdev) @@ -720,7 +753,6 @@ static int tegra_ehci_probe(struct platform_device *pdev) } tegra->host_resumed = 1; - tegra->power_down_on_bus_suspend = pdata->power_down_on_bus_suspend; tegra->ehci = hcd_to_ehci(hcd); irq = platform_get_irq(pdev, 0); @@ -729,7 +761,6 @@ static int tegra_ehci_probe(struct platform_device *pdev) err = -ENODEV; goto fail; } - set_irq_flags(irq, IRQF_VALID); #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { @@ -745,6 +776,14 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail; } + pm_runtime_set_active(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + + /* Don't skip the pm_runtime_forbid call if wakeup isn't working */ + /* if (!pdata->power_down_on_bus_suspend) */ + pm_runtime_forbid(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); return err; fail: @@ -771,33 +810,6 @@ fail_hcd: return err; } -#ifdef CONFIG_PM -static int tegra_ehci_resume(struct platform_device *pdev) -{ - struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); - - if (tegra->bus_suspended) - return 0; - - return tegra_usb_resume(hcd); -} - -static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); - - if (tegra->bus_suspended) - return 0; - - if (time_before(jiffies, tegra->ehci->next_statechange)) - msleep(10); - - return tegra_usb_suspend(hcd); -} -#endif - static int tegra_ehci_remove(struct platform_device *pdev) { struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); @@ -806,6 +818,10 @@ static int tegra_ehci_remove(struct platform_device *pdev) if (tegra == NULL || hcd == NULL) return -EINVAL; + pm_runtime_get_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); @@ -846,13 +862,12 @@ static struct of_device_id tegra_ehci_of_match[] __devinitdata = { static struct platform_driver tegra_ehci_driver = { .probe = tegra_ehci_probe, .remove = tegra_ehci_remove, -#ifdef CONFIG_PM - .suspend = tegra_ehci_suspend, - .resume = tegra_ehci_resume, -#endif .shutdown = tegra_ehci_hcd_shutdown, .driver = { .name = "tegra-ehci", .of_match_table = tegra_ehci_of_match, +#ifdef CONFIG_PM + .pm = &tegra_ehci_pm_ops, +#endif } }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 8f9acbc96fde..2694ed6558d2 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -117,6 +117,8 @@ struct ehci_hcd { /* one per controller */ the change-suspend feature turned on */ unsigned long suspended_ports; /* which ports are suspended */ + unsigned long resuming_ports; /* which ports have + started to resume */ /* per-HC memory pools (could be per-bus, but ...) */ struct dma_pool *qh_pool; /* qh per active urb */ diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index db8963f5fbce..13ebeca8e73e 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -27,6 +27,10 @@ #error "CONFIG_ARCH_AT91 must be defined." #endif +#define valid_port(index) ((index) >= 0 && (index) < AT91_MAX_USBH_PORTS) +#define at91_for_each_port(index) \ + for ((index) = 0; (index) < AT91_MAX_USBH_PORTS; (index)++) + /* interface and function clocks; sometimes also an AHB clock */ static struct clk *iclk, *fclk, *hclk; static int clocked; @@ -90,7 +94,7 @@ static void at91_stop_hc(struct platform_device *pdev) /*-------------------------------------------------------------------------*/ -static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); +static void __devexit usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ @@ -104,7 +108,7 @@ static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. */ -static int usb_hcd_at91_probe(const struct hc_driver *driver, +static int __devinit usb_hcd_at91_probe(const struct hc_driver *driver, struct platform_device *pdev) { int retval; @@ -199,7 +203,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, * context, "rmmod" or something similar. * */ -static void usb_hcd_at91_remove(struct usb_hcd *hcd, +static void __devexit usb_hcd_at91_remove(struct usb_hcd *hcd, struct platform_device *pdev) { usb_remove_hcd(hcd); @@ -240,26 +244,26 @@ ohci_at91_start (struct usb_hcd *hcd) static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable) { - if (port < 0 || port >= 2) + if (!valid_port(port)) return; if (!gpio_is_valid(pdata->vbus_pin[port])) return; gpio_set_value(pdata->vbus_pin[port], - !pdata->vbus_pin_active_low[port] ^ enable); + pdata->vbus_pin_active_low[port] ^ enable); } static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) { - if (port < 0 || port >= 2) + if (!valid_port(port)) return -EINVAL; if (!gpio_is_valid(pdata->vbus_pin[port])) return -EINVAL; return gpio_get_value(pdata->vbus_pin[port]) ^ - !pdata->vbus_pin_active_low[port]; + pdata->vbus_pin_active_low[port]; } /* @@ -271,9 +275,9 @@ static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) int length = ohci_hub_status_data(hcd, buf); int port; - for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { + at91_for_each_port(port) { if (pdata->overcurrent_changed[port]) { - if (! length) + if (!length) length = 1; buf[0] |= 1 << (port + 1); } @@ -297,11 +301,17 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, "ohci_at91_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n", hcd, typeReq, wValue, wIndex, buf, wLength); + wIndex--; + switch (typeReq) { case SetPortFeature: if (wValue == USB_PORT_FEAT_POWER) { dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n"); - ohci_at91_usb_set_power(pdata, wIndex - 1, 1); + if (valid_port(wIndex)) { + ohci_at91_usb_set_power(pdata, wIndex, 1); + ret = 0; + } + goto out; } break; @@ -312,9 +322,9 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, dev_dbg(hcd->self.controller, "ClearPortFeature: C_OVER_CURRENT\n"); - if (wIndex == 1 || wIndex == 2) { - pdata->overcurrent_changed[wIndex-1] = 0; - pdata->overcurrent_status[wIndex-1] = 0; + if (valid_port(wIndex)) { + pdata->overcurrent_changed[wIndex] = 0; + pdata->overcurrent_status[wIndex] = 0; } goto out; @@ -323,9 +333,8 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, dev_dbg(hcd->self.controller, "ClearPortFeature: OVER_CURRENT\n"); - if (wIndex == 1 || wIndex == 2) { - pdata->overcurrent_status[wIndex-1] = 0; - } + if (valid_port(wIndex)) + pdata->overcurrent_status[wIndex] = 0; goto out; @@ -333,15 +342,15 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, dev_dbg(hcd->self.controller, "ClearPortFeature: POWER\n"); - if (wIndex == 1 || wIndex == 2) { - ohci_at91_usb_set_power(pdata, wIndex - 1, 0); + if (valid_port(wIndex)) { + ohci_at91_usb_set_power(pdata, wIndex, 0); return 0; } } break; } - ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); + ret = ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, buf, wLength); if (ret) goto out; @@ -377,18 +386,15 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex); - if (wIndex == 1 || wIndex == 2) { - if (! ohci_at91_usb_get_power(pdata, wIndex-1)) { + if (valid_port(wIndex)) { + if (!ohci_at91_usb_get_power(pdata, wIndex)) *data &= ~cpu_to_le32(RH_PS_PPS); - } - if (pdata->overcurrent_changed[wIndex-1]) { + if (pdata->overcurrent_changed[wIndex]) *data |= cpu_to_le32(RH_PS_OCIC); - } - if (pdata->overcurrent_status[wIndex-1]) { + if (pdata->overcurrent_status[wIndex]) *data |= cpu_to_le32(RH_PS_POCI); - } } } @@ -450,14 +456,14 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) /* From the GPIO notifying the over-current situation, find * out the corresponding port */ - for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { + at91_for_each_port(port) { if (gpio_to_irq(pdata->overcurrent_pin[port]) == irq) { gpio = pdata->overcurrent_pin[port]; break; } } - if (port == ARRAY_SIZE(pdata->overcurrent_pin)) { + if (port == AT91_MAX_USBH_PORTS) { dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n"); return IRQ_HANDLED; } @@ -467,7 +473,7 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) /* When notified of an over-current situation, disable power on the corresponding port, and mark this port in over-current. */ - if (! val) { + if (!val) { ohci_at91_usb_set_power(pdata, port, 0); pdata->overcurrent_status[port] = 1; pdata->overcurrent_changed[port] = 1; @@ -492,7 +498,7 @@ static u64 at91_ohci_dma_mask = DMA_BIT_MASK(32); static int __devinit ohci_at91_of_init(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - int i, ret, gpio; + int i, gpio; enum of_gpio_flags flags; struct at91_usbh_data *pdata; u32 ports; @@ -514,48 +520,17 @@ static int __devinit ohci_at91_of_init(struct platform_device *pdev) if (!of_property_read_u32(np, "num-ports", &ports)) pdata->ports = ports; - for (i = 0; i < 2; i++) { + at91_for_each_port(i) { gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i, &flags); pdata->vbus_pin[i] = gpio; if (!gpio_is_valid(gpio)) continue; pdata->vbus_pin_active_low[i] = flags & OF_GPIO_ACTIVE_LOW; - ret = gpio_request(gpio, "ohci_vbus"); - if (ret) { - dev_warn(&pdev->dev, "can't request vbus gpio %d", gpio); - continue; - } - ret = gpio_direction_output(gpio, !(flags & OF_GPIO_ACTIVE_LOW) ^ 1); - if (ret) - dev_warn(&pdev->dev, "can't put vbus gpio %d as output %d", - !(flags & OF_GPIO_ACTIVE_LOW) ^ 1, gpio); } - for (i = 0; i < 2; i++) { - gpio = of_get_named_gpio_flags(np, "atmel,oc-gpio", i, &flags); - pdata->overcurrent_pin[i] = gpio; - if (!gpio_is_valid(gpio)) - continue; - ret = gpio_request(gpio, "ohci_overcurrent"); - if (ret) { - dev_err(&pdev->dev, "can't request overcurrent gpio %d", gpio); - continue; - } - - ret = gpio_direction_input(gpio); - if (ret) { - dev_err(&pdev->dev, "can't configure overcurrent gpio %d as input", gpio); - continue; - } - - ret = request_irq(gpio_to_irq(gpio), - ohci_hcd_at91_overcurrent_irq, - IRQF_SHARED, "ohci_overcurrent", pdev); - if (ret) { - gpio_free(gpio); - dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n"); - } - } + at91_for_each_port(i) + pdata->overcurrent_pin[i] = + of_get_named_gpio_flags(np, "atmel,oc-gpio", i, &flags); pdev->dev.platform_data = pdata; @@ -570,39 +545,73 @@ static int __devinit ohci_at91_of_init(struct platform_device *pdev) /*-------------------------------------------------------------------------*/ -static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) +static int __devinit ohci_hcd_at91_drv_probe(struct platform_device *pdev) { struct at91_usbh_data *pdata; int i; + int gpio; + int ret; - i = ohci_at91_of_init(pdev); - - if (i) - return i; + ret = ohci_at91_of_init(pdev); + if (ret) + return ret; pdata = pdev->dev.platform_data; if (pdata) { - for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { + at91_for_each_port(i) { if (!gpio_is_valid(pdata->vbus_pin[i])) continue; - gpio_request(pdata->vbus_pin[i], "ohci_vbus"); + gpio = pdata->vbus_pin[i]; + + ret = gpio_request(gpio, "ohci_vbus"); + if (ret) { + dev_err(&pdev->dev, + "can't request vbus gpio %d\n", gpio); + continue; + } + ret = gpio_direction_output(gpio, + !pdata->vbus_pin_active_low[i]); + if (ret) { + dev_err(&pdev->dev, + "can't put vbus gpio %d as output %d\n", + gpio, !pdata->vbus_pin_active_low[i]); + gpio_free(gpio); + continue; + } + ohci_at91_usb_set_power(pdata, i, 1); } - for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { - int ret; - + at91_for_each_port(i) { if (!gpio_is_valid(pdata->overcurrent_pin[i])) continue; - gpio_request(pdata->overcurrent_pin[i], "ohci_overcurrent"); + gpio = pdata->overcurrent_pin[i]; + + ret = gpio_request(gpio, "ohci_overcurrent"); + if (ret) { + dev_err(&pdev->dev, + "can't request overcurrent gpio %d\n", + gpio); + continue; + } + + ret = gpio_direction_input(gpio); + if (ret) { + dev_err(&pdev->dev, + "can't configure overcurrent gpio %d as input\n", + gpio); + gpio_free(gpio); + continue; + } - ret = request_irq(gpio_to_irq(pdata->overcurrent_pin[i]), + ret = request_irq(gpio_to_irq(gpio), ohci_hcd_at91_overcurrent_irq, IRQF_SHARED, "ohci_overcurrent", pdev); if (ret) { - gpio_free(pdata->overcurrent_pin[i]); - dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n"); + gpio_free(gpio); + dev_err(&pdev->dev, + "can't get gpio IRQ for overcurrent\n"); } } } @@ -611,20 +620,20 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev); } -static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) +static int __devexit ohci_hcd_at91_drv_remove(struct platform_device *pdev) { struct at91_usbh_data *pdata = pdev->dev.platform_data; int i; if (pdata) { - for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { + at91_for_each_port(i) { if (!gpio_is_valid(pdata->vbus_pin[i])) continue; ohci_at91_usb_set_power(pdata, i, 0); gpio_free(pdata->vbus_pin[i]); } - for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { + at91_for_each_port(i) { if (!gpio_is_valid(pdata->overcurrent_pin[i])) continue; free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev); @@ -687,7 +696,7 @@ MODULE_ALIAS("platform:at91_ohci"); static struct platform_driver ohci_hcd_at91_driver = { .probe = ohci_hcd_at91_drv_probe, - .remove = ohci_hcd_at91_drv_remove, + .remove = __devexit_p(ohci_hcd_at91_drv_remove), .shutdown = usb_hcd_platform_shutdown, .suspend = ohci_hcd_at91_drv_suspend, .resume = ohci_hcd_at91_drv_resume, diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 11de5f1be981..32dada8c8b4f 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -825,9 +825,13 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev) } } - /* Disable any BIOS SMIs */ - writel(XHCI_LEGACY_DISABLE_SMI, - base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET); + val = readl(base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET); + /* Mask off (turn off) any enabled SMIs */ + val &= XHCI_LEGACY_DISABLE_SMI; + /* Mask all SMI events bits, RW1C */ + val |= XHCI_LEGACY_SMI_EVENTS; + /* Disable any BIOS SMIs and clear all SMI events*/ + writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET); if (usb_is_intel_switchable_xhci(pdev)) usb_enable_xhci_ports(pdev); diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 045cde4cbc3d..768d54295a20 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -196,11 +196,12 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) status = get_hub_status_data(uhci, buf); switch (uhci->rh_state) { - case UHCI_RH_SUSPENDING: case UHCI_RH_SUSPENDED: /* if port change, ask to be resumed */ - if (status || uhci->resuming_ports) + if (status || uhci->resuming_ports) { + status = 1; usb_hcd_resume_root_hub(hcd); + } break; case UHCI_RH_AUTO_STOPPED: diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index e9b0f043455d..4b436f5a4171 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -119,7 +119,7 @@ static void xhci_print_command_reg(struct xhci_hcd *xhci) xhci_dbg(xhci, " Event Interrupts %s\n", (temp & CMD_EIE) ? "enabled " : "disabled"); xhci_dbg(xhci, " Host System Error Interrupts %s\n", - (temp & CMD_EIE) ? "enabled " : "disabled"); + (temp & CMD_HSEIE) ? "enabled " : "disabled"); xhci_dbg(xhci, " HC has %sfinished light reset\n", (temp & CMD_LRESET) ? "not " : ""); } diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index c7f33123d4c0..377f4242dabb 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -62,8 +62,9 @@ /* USB Legacy Support Control and Status Register - section 7.1.2 */ /* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ #define XHCI_LEGACY_CONTROL_OFFSET (0x04) -/* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ -#define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) +/* bits 1:3, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ +#define XHCI_LEGACY_DISABLE_SMI ((0x7 << 1) + (0xff << 5) + (0x7 << 17)) +#define XHCI_LEGACY_SMI_EVENTS (0x7 << 29) /* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ #define XHCI_L1C (1 << 16) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index cae4c6f2845a..68eaa908ac8e 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1796,11 +1796,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) int i; /* Free the Event Ring Segment Table and the actual Event Ring */ - if (xhci->ir_set) { - xhci_writel(xhci, 0, &xhci->ir_set->erst_size); - xhci_write_64(xhci, 0, &xhci->ir_set->erst_base); - xhci_write_64(xhci, 0, &xhci->ir_set->erst_dequeue); - } size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) dma_free_coherent(&pdev->dev, size, @@ -1812,7 +1807,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->event_ring = NULL; xhci_dbg(xhci, "Freed event ring\n"); - xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); xhci->cmd_ring = NULL; @@ -1841,7 +1835,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->medium_streams_pool = NULL; xhci_dbg(xhci, "Freed medium stream array pool\n"); - xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr); if (xhci->dcbaa) dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa), xhci->dcbaa, xhci->dcbaa->dma); @@ -2459,6 +2452,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) fail: xhci_warn(xhci, "Couldn't initialize memory\n"); + xhci_halt(xhci); + xhci_reset(xhci); xhci_mem_cleanup(xhci); return -ENOMEM; } diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index ef98b38626fb..7a856a767e77 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -95,6 +95,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_RESET_ON_RESUME; xhci_dbg(xhci, "QUIRK: Resetting on resume\n"); } + if (pdev->vendor == PCI_VENDOR_ID_VIA) + xhci->quirks |= XHCI_RESET_ON_RESUME; } /* called during probe() after chip reset completes */ @@ -326,7 +328,7 @@ int __init xhci_register_pci(void) return pci_register_driver(&xhci_pci_driver); } -void __exit xhci_unregister_pci(void) +void xhci_unregister_pci(void) { pci_unregister_driver(&xhci_pci_driver); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6bd9d53062eb..3d9422f16a20 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2417,7 +2417,7 @@ hw_died: u32 irq_pending; /* Acknowledge the PCI interrupt */ irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); - irq_pending |= 0x3; + irq_pending |= IMAN_IP; xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); } @@ -2734,7 +2734,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, urb->dev->speed == USB_SPEED_FULL) urb->interval /= 8; } - return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); + return xhci_queue_bulk_tx(xhci, mem_flags, urb, slot_id, ep_index); } /* @@ -3514,7 +3514,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, } ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free; - return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); + return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); } /**** Command Ring Operations ****/ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index e1963d4a430f..36641a7f2371 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -106,6 +106,9 @@ int xhci_halt(struct xhci_hcd *xhci) STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); if (!ret) xhci->xhc_state |= XHCI_STATE_HALTED; + else + xhci_warn(xhci, "Host not halted after %u microseconds.\n", + XHCI_MAX_HALT_USEC); return ret; } @@ -664,11 +667,11 @@ static void xhci_save_registers(struct xhci_hcd *xhci) xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg); - xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); - xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control); xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size); xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); + xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control); } static void xhci_restore_registers(struct xhci_hcd *xhci) @@ -677,10 +680,11 @@ static void xhci_restore_registers(struct xhci_hcd *xhci) xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg); - xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending); - xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control); xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size); xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); + xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue); + xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending); + xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control); } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 91074fdab3eb..3d69c4b2b542 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -205,6 +205,10 @@ struct xhci_op_regs { #define CMD_PM_INDEX (1 << 11) /* bits 12:31 are reserved (and should be preserved on writes). */ +/* IMAN - Interrupt Management Register */ +#define IMAN_IP (1 << 1) +#define IMAN_IE (1 << 0) + /* USBSTS - USB status - status bitmasks */ /* HC not running - set to 1 when run/stop bit is cleared. */ #define STS_HALT XHCI_STS_HALT diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 959145baf3cf..9dcb68f04f03 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -423,7 +423,7 @@ alloc_sglist(int nents, int max, int vary) unsigned i; unsigned size = max; - sg = kmalloc(nents * sizeof *sg, GFP_KERNEL); + sg = kmalloc_array(nents, sizeof *sg, GFP_KERNEL); if (!sg) return NULL; sg_init_table(sg, nents); @@ -904,6 +904,9 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param) struct ctrl_ctx context; int i; + if (param->sglen == 0 || param->iterations > UINT_MAX / param->sglen) + return -EOPNOTSUPP; + spin_lock_init(&context.lock); context.dev = dev; init_completion(&context.complete); @@ -1981,8 +1984,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) /* queued control messaging */ case 10: - if (param->sglen == 0) - break; retval = 0; dev_info(&intf->dev, "TEST 10: queue %d control calls, %d times\n", @@ -2276,6 +2277,8 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) if (status < 0) { WARNING(dev, "couldn't get endpoints, %d\n", status); + kfree(dev->buf); + kfree(dev); return status; } /* may find bulk or ISO pipes */ diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 897edda42270..70201462e19c 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -99,9 +99,7 @@ static void yurex_delete(struct kref *kref) usb_put_dev(dev->udev); if (dev->cntl_urb) { usb_kill_urb(dev->cntl_urb); - if (dev->cntl_req) - usb_free_coherent(dev->udev, YUREX_BUF_SIZE, - dev->cntl_req, dev->cntl_urb->setup_dma); + kfree(dev->cntl_req); if (dev->cntl_buffer) usb_free_coherent(dev->udev, YUREX_BUF_SIZE, dev->cntl_buffer, dev->cntl_urb->transfer_dma); @@ -234,9 +232,7 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_ } /* allocate buffer for control req */ - dev->cntl_req = usb_alloc_coherent(dev->udev, YUREX_BUF_SIZE, - GFP_KERNEL, - &dev->cntl_urb->setup_dma); + dev->cntl_req = kmalloc(YUREX_BUF_SIZE, GFP_KERNEL); if (!dev->cntl_req) { err("Could not allocate cntl_req"); goto error; @@ -286,7 +282,7 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_ usb_rcvintpipe(dev->udev, dev->int_in_endpointAddr), dev->int_buffer, YUREX_BUF_SIZE, yurex_interrupt, dev, 1); - dev->cntl_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + dev->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; if (usb_submit_urb(dev->urb, GFP_KERNEL)) { retval = -EIO; err("Could not submitting URB"); diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 97ab975fa442..768b4b55c816 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -386,7 +386,7 @@ static int davinci_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_transceiver(); if (!musb->xceiv) - return -ENODEV; + goto unregister; musb->mregs += DAVINCI_BASE_OFFSET; @@ -444,6 +444,7 @@ static int davinci_musb_init(struct musb *musb) fail: usb_put_transceiver(musb->xceiv); +unregister: usb_nop_xceiv_unregister(); return -ENODEV; } diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 0f8b82918a40..66aaccf04490 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -137,6 +137,9 @@ static int musb_ulpi_read(struct usb_phy *phy, u32 offset) int i = 0; u8 r; u8 power; + int ret; + + pm_runtime_get_sync(phy->io_dev); /* Make sure the transceiver is not in low power mode */ power = musb_readb(addr, MUSB_POWER); @@ -154,15 +157,22 @@ static int musb_ulpi_read(struct usb_phy *phy, u32 offset) while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) & MUSB_ULPI_REG_CMPLT)) { i++; - if (i == 10000) - return -ETIMEDOUT; + if (i == 10000) { + ret = -ETIMEDOUT; + goto out; + } } r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); r &= ~MUSB_ULPI_REG_CMPLT; musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); - return musb_readb(addr, MUSB_ULPI_REG_DATA); + ret = musb_readb(addr, MUSB_ULPI_REG_DATA); + +out: + pm_runtime_put(phy->io_dev); + + return ret; } static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data) @@ -171,6 +181,9 @@ static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data) int i = 0; u8 r = 0; u8 power; + int ret = 0; + + pm_runtime_get_sync(phy->io_dev); /* Make sure the transceiver is not in low power mode */ power = musb_readb(addr, MUSB_POWER); @@ -184,15 +197,20 @@ static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data) while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) & MUSB_ULPI_REG_CMPLT)) { i++; - if (i == 10000) - return -ETIMEDOUT; + if (i == 10000) { + ret = -ETIMEDOUT; + goto out; + } } r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); r &= ~MUSB_ULPI_REG_CMPLT; musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); - return 0; +out: + pm_runtime_put(phy->io_dev); + + return ret; } #else #define musb_ulpi_read NULL @@ -1904,14 +1922,17 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (!musb->isr) { status = -ENODEV; - goto fail3; + goto fail2; } if (!musb->xceiv->io_ops) { + musb->xceiv->io_dev = musb->controller; musb->xceiv->io_priv = musb->mregs; musb->xceiv->io_ops = &musb_ulpi_access; } + pm_runtime_get_sync(musb->controller); + #ifndef CONFIG_MUSB_PIO_ONLY if (use_dma && dev->dma_mask) { struct dma_controller *c; @@ -2023,6 +2044,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) goto fail5; #endif + pm_runtime_put(musb->controller); + dev_info(dev, "USB %s mode controller at %p using %s, IRQ %d\n", ({char *s; switch (musb->board_mode) { @@ -2047,6 +2070,9 @@ fail4: musb_gadget_cleanup(musb); fail3: + pm_runtime_put_sync(musb->controller); + +fail2: if (musb->irq_wake) device_init_wakeup(dev, 0); musb_platform_exit(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 93de517a32a0..f4a40f001c88 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -449,7 +449,7 @@ struct musb { * We added this flag to forcefully disable double * buffering until we get it working. */ - unsigned double_buffer_not_ok:1 __deprecated; + unsigned double_buffer_not_ok:1; struct musb_hdrc_config *config; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 79cb0af779fa..ef8d744800ac 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2098,7 +2098,7 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) } /* turn off DMA requests, discard state, stop polling ... */ - if (is_in) { + if (ep->epnum && is_in) { /* giveback saves bulk toggle */ csr = musb_h_flush_rxfifo(ep, 0); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 2ae0bb309994..c7785e81254c 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -282,7 +282,8 @@ static void musb_otg_notifier_work(struct work_struct *data_notifier_work) static int omap2430_musb_init(struct musb *musb) { - u32 l, status = 0; + u32 l; + int status = 0; struct device *dev = musb->controller; struct musb_hdrc_platform_data *plat = dev->platform_data; struct omap_musb_board_data *data = plat->board_data; @@ -301,7 +302,7 @@ static int omap2430_musb_init(struct musb *musb) status = pm_runtime_get_sync(dev); if (status < 0) { - dev_err(dev, "pm_runtime_get_sync FAILED"); + dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status); goto err1; } @@ -333,6 +334,7 @@ static int omap2430_musb_init(struct musb *musb) setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); + pm_runtime_put_noidle(musb->controller); return 0; err1: @@ -452,14 +454,14 @@ static int __devinit omap2430_probe(struct platform_device *pdev) goto err2; } + pm_runtime_enable(&pdev->dev); + ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); goto err2; } - pm_runtime_enable(&pdev->dev); - return 0; err2: @@ -478,7 +480,6 @@ static int __devexit omap2430_remove(struct platform_device *pdev) platform_device_del(glue->musb); platform_device_put(glue->musb); - pm_runtime_put(&pdev->dev); kfree(glue); return 0; @@ -491,11 +492,13 @@ static int omap2430_runtime_suspend(struct device *dev) struct omap2430_glue *glue = dev_get_drvdata(dev); struct musb *musb = glue_to_musb(glue); - musb->context.otg_interfsel = musb_readl(musb->mregs, - OTG_INTERFSEL); + if (musb) { + musb->context.otg_interfsel = musb_readl(musb->mregs, + OTG_INTERFSEL); - omap2430_low_level_exit(musb); - usb_phy_set_suspend(musb->xceiv, 1); + omap2430_low_level_exit(musb); + usb_phy_set_suspend(musb->xceiv, 1); + } return 0; } @@ -505,11 +508,13 @@ static int omap2430_runtime_resume(struct device *dev) struct omap2430_glue *glue = dev_get_drvdata(dev); struct musb *musb = glue_to_musb(glue); - omap2430_low_level_init(musb); - musb_writel(musb->mregs, OTG_INTERFSEL, - musb->context.otg_interfsel); + if (musb) { + omap2430_low_level_init(musb); + musb_writel(musb->mregs, OTG_INTERFSEL, + musb->context.otg_interfsel); - usb_phy_set_suspend(musb->xceiv, 0); + usb_phy_set_suspend(musb->xceiv, 0); + } return 0; } diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index 3ece43a2e4c1..a0a2178974fe 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -96,7 +96,7 @@ static void gpio_vbus_work(struct work_struct *work) struct gpio_vbus_data *gpio_vbus = container_of(work, struct gpio_vbus_data, work); struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data; - int gpio; + int gpio, status; if (!gpio_vbus->phy.otg->gadget) return; @@ -108,7 +108,9 @@ static void gpio_vbus_work(struct work_struct *work) */ gpio = pdata->gpio_pullup; if (is_vbus_powered(pdata)) { + status = USB_EVENT_VBUS; gpio_vbus->phy.state = OTG_STATE_B_PERIPHERAL; + gpio_vbus->phy.last_event = status; usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget); /* drawing a "unit load" is *always* OK, except for OTG */ @@ -117,6 +119,9 @@ static void gpio_vbus_work(struct work_struct *work) /* optionally enable D+ pullup */ if (gpio_is_valid(gpio)) gpio_set_value(gpio, !pdata->gpio_pullup_inverted); + + atomic_notifier_call_chain(&gpio_vbus->phy.notifier, + status, gpio_vbus->phy.otg->gadget); } else { /* optionally disable D+ pullup */ if (gpio_is_valid(gpio)) @@ -125,7 +130,12 @@ static void gpio_vbus_work(struct work_struct *work) set_vbus_draw(gpio_vbus, 0); usb_gadget_vbus_disconnect(gpio_vbus->phy.otg->gadget); + status = USB_EVENT_NONE; gpio_vbus->phy.state = OTG_STATE_B_IDLE; + gpio_vbus->phy.last_event = status; + + atomic_notifier_call_chain(&gpio_vbus->phy.notifier, + status, gpio_vbus->phy.otg->gadget); } } @@ -287,6 +297,9 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) irq, err); goto err_irq; } + + ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier); + INIT_WORK(&gpio_vbus->work, gpio_vbus_work); gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 7f547dc3a590..ed8adb052ca7 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -60,8 +60,6 @@ static int usb_serial_device_probe(struct device *dev) retval = -ENODEV; goto exit; } - if (port->dev_state != PORT_REGISTERING) - goto exit; driver = port->serial->type; if (driver->port_probe) { @@ -98,9 +96,6 @@ static int usb_serial_device_remove(struct device *dev) if (!port) return -ENODEV; - if (port->dev_state != PORT_UNREGISTERING) - return retval; - device_remove_file(&port->dev, &dev_attr_port_number); driver = port->serial->type; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 0310e2df59f5..ec30f95ef399 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -287,7 +287,8 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request, /* Issue the request, attempting to read 'size' bytes */ result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), request, REQTYPE_DEVICE_TO_HOST, 0x0000, - port_priv->bInterfaceNumber, buf, size, 300); + port_priv->bInterfaceNumber, buf, size, + USB_CTRL_GET_TIMEOUT); /* Convert data into an array of integers */ for (i = 0; i < length; i++) @@ -340,12 +341,14 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request, result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, REQTYPE_HOST_TO_DEVICE, 0x0000, - port_priv->bInterfaceNumber, buf, size, 300); + port_priv->bInterfaceNumber, buf, size, + USB_CTRL_SET_TIMEOUT); } else { result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, REQTYPE_HOST_TO_DEVICE, data[0], - port_priv->bInterfaceNumber, NULL, 0, 300); + port_priv->bInterfaceNumber, NULL, 0, + USB_CTRL_SET_TIMEOUT); } kfree(buf); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ff8605b4b4be..02e7f2d32d52 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -75,7 +75,8 @@ struct ftdi_private { unsigned long last_dtr_rts; /* saved modem control outputs */ struct async_icount icount; wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ - char prev_status, diff_status; /* Used for TIOCMIWAIT */ + char prev_status; /* Used for TIOCMIWAIT */ + bool dev_gone; /* Used to abort TIOCMIWAIT */ char transmit_empty; /* If transmitter is empty or not */ struct usb_serial_port *port; __u16 interface; /* FT2232C, FT2232H or FT4232H port interface @@ -1681,6 +1682,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) init_waitqueue_head(&priv->delta_msr_wait); priv->flags = ASYNC_LOW_LATENCY; + priv->dev_gone = false; if (quirk && quirk->port_probe) quirk->port_probe(priv); @@ -1839,6 +1841,9 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) dbg("%s", __func__); + priv->dev_gone = true; + wake_up_interruptible_all(&priv->delta_msr_wait); + remove_sysfs_attrs(port); kref_put(&priv->kref, ftdi_sio_priv_release); @@ -1982,17 +1987,19 @@ static int ftdi_process_packet(struct tty_struct *tty, N.B. packet may be processed more than once, but differences are only processed once. */ status = packet[0] & FTDI_STATUS_B0_MASK; - if (status & FTDI_RS0_CTS) - priv->icount.cts++; - if (status & FTDI_RS0_DSR) - priv->icount.dsr++; - if (status & FTDI_RS0_RI) - priv->icount.rng++; - if (status & FTDI_RS0_RLSD) - priv->icount.dcd++; if (status != priv->prev_status) { - priv->diff_status |= status ^ priv->prev_status; - wake_up_interruptible(&priv->delta_msr_wait); + char diff_status = status ^ priv->prev_status; + + if (diff_status & FTDI_RS0_CTS) + priv->icount.cts++; + if (diff_status & FTDI_RS0_DSR) + priv->icount.dsr++; + if (diff_status & FTDI_RS0_RI) + priv->icount.rng++; + if (diff_status & FTDI_RS0_RLSD) + priv->icount.dcd++; + + wake_up_interruptible_all(&priv->delta_msr_wait); priv->prev_status = status; } @@ -2395,15 +2402,12 @@ static int ftdi_ioctl(struct tty_struct *tty, */ case TIOCMIWAIT: cprev = priv->icount; - while (1) { + while (!priv->dev_gone) { interruptible_sleep_on(&priv->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; cnow = priv->icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || @@ -2412,7 +2416,7 @@ static int ftdi_ioctl(struct tty_struct *tty, } cprev = cnow; } - /* not reached */ + return -EIO; break; case TIOCSERGETLSR: return get_lsr_info(port, (struct serial_struct __user *)arg); diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 6e1622f2a297..08d16e8c002d 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -27,8 +27,8 @@ /* Product information. */ #define FOCUS_VENDOR_ID 0x0C2E -#define FOCUS_PRODUCT_ID 0x0720 -#define FOCUS_PRODUCT_ID_UNI 0x0710 +#define FOCUS_PRODUCT_ID_BI 0x0720 +#define FOCUS_PRODUCT_ID_UNI 0x0700 #define METROUSB_SET_REQUEST_TYPE 0x40 #define METROUSB_SET_MODEM_CTRL_REQUEST 10 @@ -47,7 +47,7 @@ struct metrousb_private { /* Device table list. */ static struct usb_device_id id_table[] = { - { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID) }, + { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) }, { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) }, { }, /* Terminating entry. */ }; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 836cfa9a515f..f4465ccddc35 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -708,6 +708,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED) }, + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED3) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED4) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED5) }, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ff4a174fa5de..a1a9062954c4 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -420,7 +420,7 @@ static void pl2303_set_termios(struct tty_struct *tty, control = priv->line_control; if ((cflag & CBAUD) == B0) priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); - else + else if ((old_termios->c_cflag & CBAUD) == B0) priv->line_control |= (CONTROL_DTR | CONTROL_RTS); if (control != priv->line_control) { control = priv->line_control; diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index f14465a83dd1..8c8bf806f6fa 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -221,7 +221,7 @@ static const struct sierra_iface_info typeB_interface_list = { }; /* 'blacklist' of interfaces not served by this driver */ -static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 }; +static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11, 19, 20 }; static const struct sierra_iface_info direct_ip_interface_blacklist = { .infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces), .ifaceinfo = direct_ip_non_serial_ifaces, @@ -298,6 +298,9 @@ static const struct usb_device_id id_table[] = { /* Sierra Wireless HSPA Non-Composite Device */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)}, { USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */ + { USB_DEVICE(0x1199, 0x68A2), /* Sierra Wireless MC77xx in QMI mode */ + .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist + }, { USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */ .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist }, diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 69230f01056a..97355a15bbea 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1059,6 +1059,12 @@ int usb_serial_probe(struct usb_interface *interface, serial->attached = 1; } + /* Avoid race with tty_open and serial_install by setting the + * disconnected flag and not clearing it until all ports have been + * registered. + */ + serial->disconnected = 1; + if (get_free_serial(serial, num_ports, &minor) == NULL) { dev_err(&interface->dev, "No more free serial devices\n"); goto probe_error; @@ -1070,19 +1076,16 @@ int usb_serial_probe(struct usb_interface *interface, port = serial->port[i]; dev_set_name(&port->dev, "ttyUSB%d", port->number); dbg ("%s - registering %s", __func__, dev_name(&port->dev)); - port->dev_state = PORT_REGISTERING; device_enable_async_suspend(&port->dev); retval = device_add(&port->dev); - if (retval) { + if (retval) dev_err(&port->dev, "Error registering port device, " "continuing\n"); - port->dev_state = PORT_UNREGISTERED; - } else { - port->dev_state = PORT_REGISTERED; - } } + serial->disconnected = 0; + usb_serial_console_init(debug, minor); exit: @@ -1124,22 +1127,8 @@ void usb_serial_disconnect(struct usb_interface *interface) } kill_traffic(port); cancel_work_sync(&port->work); - if (port->dev_state == PORT_REGISTERED) { - - /* Make sure the port is bound so that the - * driver's port_remove method is called. - */ - if (!port->dev.driver) { - int rc; - - port->dev.driver = - &serial->type->driver; - rc = device_bind_driver(&port->dev); - } - port->dev_state = PORT_UNREGISTERING; + if (device_is_registered(&port->dev)) device_del(&port->dev); - port->dev_state = PORT_UNREGISTERED; - } } } serial->type->disconnect(serial); diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index c18538e4a6db..2653e73db623 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -132,6 +132,35 @@ static struct us_unusual_dev for_dynamic_ids = #undef COMPLIANT_DEV #undef USUAL_DEV +#ifdef CONFIG_LOCKDEP + +static struct lock_class_key us_interface_key[USB_MAXINTERFACES]; + +static void us_set_lock_class(struct mutex *mutex, + struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_config *config = udev->actconfig; + int i; + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + if (config->interface[i] == intf) + break; + } + + BUG_ON(i == config->desc.bNumInterfaces); + + lockdep_set_class(mutex, &us_interface_key[i]); +} + +#else + +static void us_set_lock_class(struct mutex *mutex, + struct usb_interface *intf) +{ +} + +#endif #ifdef CONFIG_PM /* Minimal support for suspend and resume */ @@ -895,6 +924,7 @@ int usb_stor_probe1(struct us_data **pus, *pus = us = host_to_us(host); memset(us, 0, sizeof(struct us_data)); mutex_init(&(us->dev_mutex)); + us_set_lock_class(&us->dev_mutex, intf); init_completion(&us->cmnd_ready); init_completion(&(us->notify)); init_waitqueue_head(&us->delay_wait); diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c index 66797e9c5010..810c90ae2c55 100644 --- a/drivers/uwb/hwa-rc.c +++ b/drivers/uwb/hwa-rc.c @@ -645,7 +645,8 @@ void hwarc_neep_cb(struct urb *urb) dev_err(dev, "NEEP: URB error %d\n", urb->status); } result = usb_submit_urb(urb, GFP_ATOMIC); - if (result < 0) { + if (result < 0 && result != -ENODEV && result != -EPERM) { + /* ignoring unrecoverable errors */ dev_err(dev, "NEEP: Can't resubmit URB (%d) resetting device\n", result); goto error; diff --git a/drivers/uwb/neh.c b/drivers/uwb/neh.c index a269937be1b8..8cb71bb333c2 100644 --- a/drivers/uwb/neh.c +++ b/drivers/uwb/neh.c @@ -107,6 +107,7 @@ struct uwb_rc_neh { u8 evt_type; __le16 evt; u8 context; + u8 completed; uwb_rc_cmd_cb_f cb; void *arg; @@ -409,6 +410,7 @@ static void uwb_rc_neh_grok_event(struct uwb_rc *rc, struct uwb_rceb *rceb, size struct device *dev = &rc->uwb_dev.dev; struct uwb_rc_neh *neh; struct uwb_rceb *notif; + unsigned long flags; if (rceb->bEventContext == 0) { notif = kmalloc(size, GFP_ATOMIC); @@ -422,7 +424,11 @@ static void uwb_rc_neh_grok_event(struct uwb_rc *rc, struct uwb_rceb *rceb, size } else { neh = uwb_rc_neh_lookup(rc, rceb); if (neh) { - del_timer_sync(&neh->timer); + spin_lock_irqsave(&rc->neh_lock, flags); + /* to guard against a timeout */ + neh->completed = 1; + del_timer(&neh->timer); + spin_unlock_irqrestore(&rc->neh_lock, flags); uwb_rc_neh_cb(neh, rceb, size); } else dev_warn(dev, "event 0x%02x/%04x/%02x (%zu bytes): nobody cared\n", @@ -568,6 +574,10 @@ static void uwb_rc_neh_timer(unsigned long arg) unsigned long flags; spin_lock_irqsave(&rc->neh_lock, flags); + if (neh->completed) { + spin_unlock_irqrestore(&rc->neh_lock, flags); + return; + } if (neh->context) __uwb_rc_neh_rm(rc, neh); else diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index 2eecec0c13c9..6ec45beb7af5 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -159,13 +159,6 @@ static int cmd_ie_rm(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_rm) return uwb_rc_ie_rm(rc, ie_to_rm->data[0]); } -static int command_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - - return 0; -} - static ssize_t command_write(struct file *file, const char __user *buf, size_t len, loff_t *off) { @@ -206,7 +199,7 @@ static ssize_t command_write(struct file *file, const char __user *buf, } static const struct file_operations command_fops = { - .open = command_open, + .open = simple_open, .write = command_write, .read = NULL, .llseek = no_llseek, diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index f0da2c32fbde..5c170100de9c 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -24,6 +24,7 @@ #include <linux/if_arp.h> #include <linux/if_tun.h> #include <linux/if_macvlan.h> +#include <linux/if_vlan.h> #include <net/sock.h> @@ -238,7 +239,7 @@ static void handle_tx(struct vhost_net *net) vq->heads[vq->upend_idx].len = len; ubuf->callback = vhost_zerocopy_callback; - ubuf->arg = vq->ubufs; + ubuf->ctx = vq->ubufs; ubuf->desc = vq->upend_idx; msg.msg_control = ubuf; msg.msg_controllen = sizeof(ubuf); @@ -283,8 +284,12 @@ static int peek_head_len(struct sock *sk) spin_lock_irqsave(&sk->sk_receive_queue.lock, flags); head = skb_peek(&sk->sk_receive_queue); - if (likely(head)) + if (likely(head)) { len = head->len; + if (vlan_tx_tag_present(head)) + len += VLAN_HLEN; + } + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, flags); return len; } diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index fc9a1d75281f..3de00d9fae2e 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -155,7 +155,7 @@ static int vhost_test_release(struct inode *inode, struct file *f) vhost_test_stop(n, &private); vhost_test_flush(n); - vhost_dev_cleanup(&n->dev); + vhost_dev_cleanup(&n->dev, false); /* We do an extra flush before freeing memory, * since jobs can re-queue themselves. */ vhost_test_flush(n); diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 947f00d8e091..51e4c1eeec4f 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1598,10 +1598,9 @@ void vhost_ubuf_put_and_wait(struct vhost_ubuf_ref *ubufs) kfree(ubufs); } -void vhost_zerocopy_callback(void *arg) +void vhost_zerocopy_callback(struct ubuf_info *ubuf) { - struct ubuf_info *ubuf = arg; - struct vhost_ubuf_ref *ubufs = ubuf->arg; + struct vhost_ubuf_ref *ubufs = ubuf->ctx; struct vhost_virtqueue *vq = ubufs->vq; /* set len = 1 to mark this desc buffers done DMA */ diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 8dcf4cca6bf2..8de1fd5b8efb 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -188,7 +188,7 @@ bool vhost_enable_notify(struct vhost_dev *, struct vhost_virtqueue *); int vhost_log_write(struct vhost_virtqueue *vq, struct vhost_log *log, unsigned int log_num, u64 len); -void vhost_zerocopy_callback(void *arg); +void vhost_zerocopy_callback(struct ubuf_info *); int vhost_zerocopy_signal_used(struct vhost_virtqueue *vq); #define vq_err(vq, fmt, ...) do { \ diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c index befcbd8ef019..ffbce4525468 100644 --- a/drivers/video/au1100fb.c +++ b/drivers/video/au1100fb.c @@ -499,7 +499,8 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) au1100fb_fix.mmio_start = regs_res->start; au1100fb_fix.mmio_len = resource_size(regs_res); - if (!devm_request_mem_region(au1100fb_fix.mmio_start, + if (!devm_request_mem_region(&dev->dev, + au1100fb_fix.mmio_start, au1100fb_fix.mmio_len, DRIVER_NAME)) { print_err("fail to lock memory region at 0x%08lx", @@ -516,7 +517,7 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres * (fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS; - fbdev->fb_mem = dmam_alloc_coherent(&dev->dev, &dev->dev, + fbdev->fb_mem = dmam_alloc_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c index 3e9a773db09f..7ca79f02056e 100644 --- a/drivers/video/au1200fb.c +++ b/drivers/video/au1200fb.c @@ -1724,7 +1724,7 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev) /* Allocate the framebuffer to the maximum screen size */ fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8; - fbdev->fb_mem = dmam_alloc_noncoherent(&dev->dev, &dev->dev, + fbdev->fb_mem = dmam_alloc_noncoherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 7ed9991fa747..af16884491ed 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -245,6 +245,12 @@ config BACKLIGHT_DA903X If you have a LCD backlight connected to the WLED output of DA9030 or DA9034 WLED output, say Y here to enable this driver. +config BACKLIGHT_DA9052 + tristate "Dialog DA9052/DA9053 WLED" + depends on PMIC_DA9052 + help + Enable the Backlight Driver for DA9052-BC and DA9053-AA/Bx PMICs. + config BACKLIGHT_MAX8925 tristate "Backlight driver for MAX8925" depends on MFD_MAX8925 diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 8071eb656147..36855ae887d6 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o +obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o diff --git a/drivers/video/backlight/da9052_bl.c b/drivers/video/backlight/da9052_bl.c new file mode 100644 index 000000000000..b628d68f5162 --- /dev/null +++ b/drivers/video/backlight/da9052_bl.c @@ -0,0 +1,187 @@ +/* + * Backlight Driver for Dialog DA9052 PMICs + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen <dchen@diasemi.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/backlight.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> + +#define DA9052_MAX_BRIGHTNESS 0xFF + +enum { + DA9052_WLEDS_OFF, + DA9052_WLEDS_ON, +}; + +enum { + DA9052_TYPE_WLED1, + DA9052_TYPE_WLED2, + DA9052_TYPE_WLED3, +}; + +static unsigned char wled_bank[] = { + DA9052_LED1_CONF_REG, + DA9052_LED2_CONF_REG, + DA9052_LED3_CONF_REG, +}; + +struct da9052_bl { + struct da9052 *da9052; + uint brightness; + uint state; + uint led_reg; +}; + +static int da9052_adjust_wled_brightness(struct da9052_bl *wleds) +{ + unsigned char boost_en; + unsigned char i_sink; + int ret; + + boost_en = 0x3F; + i_sink = 0xFF; + if (wleds->state == DA9052_WLEDS_OFF) { + boost_en = 0x00; + i_sink = 0x00; + } + + ret = da9052_reg_write(wleds->da9052, DA9052_BOOST_REG, boost_en); + if (ret < 0) + return ret; + + ret = da9052_reg_write(wleds->da9052, DA9052_LED_CONT_REG, i_sink); + if (ret < 0) + return ret; + + ret = da9052_reg_write(wleds->da9052, wled_bank[wleds->led_reg], 0x0); + if (ret < 0) + return ret; + + msleep(10); + + if (wleds->brightness) { + ret = da9052_reg_write(wleds->da9052, wled_bank[wleds->led_reg], + wleds->brightness); + if (ret < 0) + return ret; + } + + return 0; +} + +static int da9052_backlight_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + struct da9052_bl *wleds = bl_get_data(bl); + + wleds->brightness = brightness; + wleds->state = DA9052_WLEDS_ON; + + return da9052_adjust_wled_brightness(wleds); +} + +static int da9052_backlight_get_brightness(struct backlight_device *bl) +{ + struct da9052_bl *wleds = bl_get_data(bl); + + return wleds->brightness; +} + +static const struct backlight_ops da9052_backlight_ops = { + .update_status = da9052_backlight_update_status, + .get_brightness = da9052_backlight_get_brightness, +}; + +static int da9052_backlight_probe(struct platform_device *pdev) +{ + struct backlight_device *bl; + struct backlight_properties props; + struct da9052_bl *wleds; + + wleds = devm_kzalloc(&pdev->dev, sizeof(struct da9052_bl), GFP_KERNEL); + if (!wleds) + return -ENOMEM; + + wleds->da9052 = dev_get_drvdata(pdev->dev.parent); + wleds->brightness = 0; + wleds->led_reg = platform_get_device_id(pdev)->driver_data; + wleds->state = DA9052_WLEDS_OFF; + + props.type = BACKLIGHT_RAW; + props.max_brightness = DA9052_MAX_BRIGHTNESS; + + bl = backlight_device_register(pdev->name, wleds->da9052->dev, wleds, + &da9052_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "Failed to register backlight\n"); + devm_kfree(&pdev->dev, wleds); + return PTR_ERR(bl); + } + + bl->props.max_brightness = DA9052_MAX_BRIGHTNESS; + bl->props.brightness = 0; + platform_set_drvdata(pdev, bl); + + return da9052_adjust_wled_brightness(wleds); +} + +static int da9052_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct da9052_bl *wleds = bl_get_data(bl); + + wleds->brightness = 0; + wleds->state = DA9052_WLEDS_OFF; + da9052_adjust_wled_brightness(wleds); + backlight_device_unregister(bl); + devm_kfree(&pdev->dev, wleds); + + return 0; +} + +static struct platform_device_id da9052_wled_ids[] = { + { + .name = "da9052-wled1", + .driver_data = DA9052_TYPE_WLED1, + }, + { + .name = "da9052-wled2", + .driver_data = DA9052_TYPE_WLED2, + }, + { + .name = "da9052-wled3", + .driver_data = DA9052_TYPE_WLED3, + }, +}; + +static struct platform_driver da9052_wled_driver = { + .probe = da9052_backlight_probe, + .remove = da9052_backlight_remove, + .id_table = da9052_wled_ids, + .driver = { + .name = "da9052-wled", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(da9052_wled_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("Backlight driver for DA9052 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-backlight"); diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c index be20b5cbe26c..3a6d5419e3e3 100644 --- a/drivers/video/backlight/locomolcd.c +++ b/drivers/video/backlight/locomolcd.c @@ -229,14 +229,7 @@ static struct locomo_driver poodle_lcd_driver = { static int __init locomolcd_init(void) { - int ret = locomo_driver_register(&poodle_lcd_driver); - if (ret) - return ret; - -#ifdef CONFIG_SA1100_COLLIE - sa1100fb_lcd_power = locomolcd_power; -#endif - return 0; + return locomo_driver_register(&poodle_lcd_driver); } static void __exit locomolcd_exit(void) diff --git a/drivers/video/bfin-lq035q1-fb.c b/drivers/video/bfin-lq035q1-fb.c index 86922ac84412..353c02fe8a95 100644 --- a/drivers/video/bfin-lq035q1-fb.c +++ b/drivers/video/bfin-lq035q1-fb.c @@ -13,6 +13,7 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/fb.h> +#include <linux/gpio.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/types.h> diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c index 6468a297e341..39571f9e0162 100644 --- a/drivers/video/console/sticore.c +++ b/drivers/video/console/sticore.c @@ -22,7 +22,9 @@ #include <linux/font.h> #include <asm/hardware.h> +#include <asm/page.h> #include <asm/parisc-device.h> +#include <asm/pdc.h> #include <asm/cacheflush.h> #include <asm/grfioctl.h> diff --git a/drivers/video/kyro/STG4000Reg.h b/drivers/video/kyro/STG4000Reg.h index 5d6269882589..50f4670e9252 100644 --- a/drivers/video/kyro/STG4000Reg.h +++ b/drivers/video/kyro/STG4000Reg.h @@ -73,210 +73,210 @@ typedef enum _OVRL_PIX_FORMAT { /* Register Table */ typedef struct { /* 0h */ - volatile unsigned long Thread0Enable; /* 0x0000 */ - volatile unsigned long Thread1Enable; /* 0x0004 */ - volatile unsigned long Thread0Recover; /* 0x0008 */ - volatile unsigned long Thread1Recover; /* 0x000C */ - volatile unsigned long Thread0Step; /* 0x0010 */ - volatile unsigned long Thread1Step; /* 0x0014 */ - volatile unsigned long VideoInStatus; /* 0x0018 */ - volatile unsigned long Core2InSignStart; /* 0x001C */ - volatile unsigned long Core1ResetVector; /* 0x0020 */ - volatile unsigned long Core1ROMOffset; /* 0x0024 */ - volatile unsigned long Core1ArbiterPriority; /* 0x0028 */ - volatile unsigned long VideoInControl; /* 0x002C */ - volatile unsigned long VideoInReg0CtrlA; /* 0x0030 */ - volatile unsigned long VideoInReg0CtrlB; /* 0x0034 */ - volatile unsigned long VideoInReg1CtrlA; /* 0x0038 */ - volatile unsigned long VideoInReg1CtrlB; /* 0x003C */ - volatile unsigned long Thread0Kicker; /* 0x0040 */ - volatile unsigned long Core2InputSign; /* 0x0044 */ - volatile unsigned long Thread0ProgCtr; /* 0x0048 */ - volatile unsigned long Thread1ProgCtr; /* 0x004C */ - volatile unsigned long Thread1Kicker; /* 0x0050 */ - volatile unsigned long GPRegister1; /* 0x0054 */ - volatile unsigned long GPRegister2; /* 0x0058 */ - volatile unsigned long GPRegister3; /* 0x005C */ - volatile unsigned long GPRegister4; /* 0x0060 */ - volatile unsigned long SerialIntA; /* 0x0064 */ - - volatile unsigned long Fill0[6]; /* GAP 0x0068 - 0x007C */ - - volatile unsigned long SoftwareReset; /* 0x0080 */ - volatile unsigned long SerialIntB; /* 0x0084 */ - - volatile unsigned long Fill1[37]; /* GAP 0x0088 - 0x011C */ - - volatile unsigned long ROMELQV; /* 0x011C */ - volatile unsigned long WLWH; /* 0x0120 */ - volatile unsigned long ROMELWL; /* 0x0124 */ - - volatile unsigned long dwFill_1; /* GAP 0x0128 */ - - volatile unsigned long IntStatus; /* 0x012C */ - volatile unsigned long IntMask; /* 0x0130 */ - volatile unsigned long IntClear; /* 0x0134 */ - - volatile unsigned long Fill2[6]; /* GAP 0x0138 - 0x014C */ - - volatile unsigned long ROMGPIOA; /* 0x0150 */ - volatile unsigned long ROMGPIOB; /* 0x0154 */ - volatile unsigned long ROMGPIOC; /* 0x0158 */ - volatile unsigned long ROMGPIOD; /* 0x015C */ - - volatile unsigned long Fill3[2]; /* GAP 0x0160 - 0x0168 */ - - volatile unsigned long AGPIntID; /* 0x0168 */ - volatile unsigned long AGPIntClassCode; /* 0x016C */ - volatile unsigned long AGPIntBIST; /* 0x0170 */ - volatile unsigned long AGPIntSSID; /* 0x0174 */ - volatile unsigned long AGPIntPMCSR; /* 0x0178 */ - volatile unsigned long VGAFrameBufBase; /* 0x017C */ - volatile unsigned long VGANotify; /* 0x0180 */ - volatile unsigned long DACPLLMode; /* 0x0184 */ - volatile unsigned long Core1VideoClockDiv; /* 0x0188 */ - volatile unsigned long AGPIntStat; /* 0x018C */ + volatile u32 Thread0Enable; /* 0x0000 */ + volatile u32 Thread1Enable; /* 0x0004 */ + volatile u32 Thread0Recover; /* 0x0008 */ + volatile u32 Thread1Recover; /* 0x000C */ + volatile u32 Thread0Step; /* 0x0010 */ + volatile u32 Thread1Step; /* 0x0014 */ + volatile u32 VideoInStatus; /* 0x0018 */ + volatile u32 Core2InSignStart; /* 0x001C */ + volatile u32 Core1ResetVector; /* 0x0020 */ + volatile u32 Core1ROMOffset; /* 0x0024 */ + volatile u32 Core1ArbiterPriority; /* 0x0028 */ + volatile u32 VideoInControl; /* 0x002C */ + volatile u32 VideoInReg0CtrlA; /* 0x0030 */ + volatile u32 VideoInReg0CtrlB; /* 0x0034 */ + volatile u32 VideoInReg1CtrlA; /* 0x0038 */ + volatile u32 VideoInReg1CtrlB; /* 0x003C */ + volatile u32 Thread0Kicker; /* 0x0040 */ + volatile u32 Core2InputSign; /* 0x0044 */ + volatile u32 Thread0ProgCtr; /* 0x0048 */ + volatile u32 Thread1ProgCtr; /* 0x004C */ + volatile u32 Thread1Kicker; /* 0x0050 */ + volatile u32 GPRegister1; /* 0x0054 */ + volatile u32 GPRegister2; /* 0x0058 */ + volatile u32 GPRegister3; /* 0x005C */ + volatile u32 GPRegister4; /* 0x0060 */ + volatile u32 SerialIntA; /* 0x0064 */ + + volatile u32 Fill0[6]; /* GAP 0x0068 - 0x007C */ + + volatile u32 SoftwareReset; /* 0x0080 */ + volatile u32 SerialIntB; /* 0x0084 */ + + volatile u32 Fill1[37]; /* GAP 0x0088 - 0x011C */ + + volatile u32 ROMELQV; /* 0x011C */ + volatile u32 WLWH; /* 0x0120 */ + volatile u32 ROMELWL; /* 0x0124 */ + + volatile u32 dwFill_1; /* GAP 0x0128 */ + + volatile u32 IntStatus; /* 0x012C */ + volatile u32 IntMask; /* 0x0130 */ + volatile u32 IntClear; /* 0x0134 */ + + volatile u32 Fill2[6]; /* GAP 0x0138 - 0x014C */ + + volatile u32 ROMGPIOA; /* 0x0150 */ + volatile u32 ROMGPIOB; /* 0x0154 */ + volatile u32 ROMGPIOC; /* 0x0158 */ + volatile u32 ROMGPIOD; /* 0x015C */ + + volatile u32 Fill3[2]; /* GAP 0x0160 - 0x0168 */ + + volatile u32 AGPIntID; /* 0x0168 */ + volatile u32 AGPIntClassCode; /* 0x016C */ + volatile u32 AGPIntBIST; /* 0x0170 */ + volatile u32 AGPIntSSID; /* 0x0174 */ + volatile u32 AGPIntPMCSR; /* 0x0178 */ + volatile u32 VGAFrameBufBase; /* 0x017C */ + volatile u32 VGANotify; /* 0x0180 */ + volatile u32 DACPLLMode; /* 0x0184 */ + volatile u32 Core1VideoClockDiv; /* 0x0188 */ + volatile u32 AGPIntStat; /* 0x018C */ /* - volatile unsigned long Fill4[0x0400/4 - 0x0190/4]; //GAP 0x0190 - 0x0400 - volatile unsigned long Fill5[0x05FC/4 - 0x0400/4]; //GAP 0x0400 - 0x05FC Fog Table - volatile unsigned long Fill6[0x0604/4 - 0x0600/4]; //GAP 0x0600 - 0x0604 - volatile unsigned long Fill7[0x0680/4 - 0x0608/4]; //GAP 0x0608 - 0x0680 - volatile unsigned long Fill8[0x07FC/4 - 0x0684/4]; //GAP 0x0684 - 0x07FC + volatile u32 Fill4[0x0400/4 - 0x0190/4]; //GAP 0x0190 - 0x0400 + volatile u32 Fill5[0x05FC/4 - 0x0400/4]; //GAP 0x0400 - 0x05FC Fog Table + volatile u32 Fill6[0x0604/4 - 0x0600/4]; //GAP 0x0600 - 0x0604 + volatile u32 Fill7[0x0680/4 - 0x0608/4]; //GAP 0x0608 - 0x0680 + volatile u32 Fill8[0x07FC/4 - 0x0684/4]; //GAP 0x0684 - 0x07FC */ - volatile unsigned long Fill4[412]; /* 0x0190 - 0x07FC */ - - volatile unsigned long TACtrlStreamBase; /* 0x0800 */ - volatile unsigned long TAObjDataBase; /* 0x0804 */ - volatile unsigned long TAPtrDataBase; /* 0x0808 */ - volatile unsigned long TARegionDataBase; /* 0x080C */ - volatile unsigned long TATailPtrBase; /* 0x0810 */ - volatile unsigned long TAPtrRegionSize; /* 0x0814 */ - volatile unsigned long TAConfiguration; /* 0x0818 */ - volatile unsigned long TAObjDataStartAddr; /* 0x081C */ - volatile unsigned long TAObjDataEndAddr; /* 0x0820 */ - volatile unsigned long TAXScreenClip; /* 0x0824 */ - volatile unsigned long TAYScreenClip; /* 0x0828 */ - volatile unsigned long TARHWClamp; /* 0x082C */ - volatile unsigned long TARHWCompare; /* 0x0830 */ - volatile unsigned long TAStart; /* 0x0834 */ - volatile unsigned long TAObjReStart; /* 0x0838 */ - volatile unsigned long TAPtrReStart; /* 0x083C */ - volatile unsigned long TAStatus1; /* 0x0840 */ - volatile unsigned long TAStatus2; /* 0x0844 */ - volatile unsigned long TAIntStatus; /* 0x0848 */ - volatile unsigned long TAIntMask; /* 0x084C */ - - volatile unsigned long Fill5[235]; /* GAP 0x0850 - 0x0BF8 */ - - volatile unsigned long TextureAddrThresh; /* 0x0BFC */ - volatile unsigned long Core1Translation; /* 0x0C00 */ - volatile unsigned long TextureAddrReMap; /* 0x0C04 */ - volatile unsigned long RenderOutAGPRemap; /* 0x0C08 */ - volatile unsigned long _3DRegionReadTrans; /* 0x0C0C */ - volatile unsigned long _3DPtrReadTrans; /* 0x0C10 */ - volatile unsigned long _3DParamReadTrans; /* 0x0C14 */ - volatile unsigned long _3DRegionReadThresh; /* 0x0C18 */ - volatile unsigned long _3DPtrReadThresh; /* 0x0C1C */ - volatile unsigned long _3DParamReadThresh; /* 0x0C20 */ - volatile unsigned long _3DRegionReadAGPRemap; /* 0x0C24 */ - volatile unsigned long _3DPtrReadAGPRemap; /* 0x0C28 */ - volatile unsigned long _3DParamReadAGPRemap; /* 0x0C2C */ - volatile unsigned long ZBufferAGPRemap; /* 0x0C30 */ - volatile unsigned long TAIndexAGPRemap; /* 0x0C34 */ - volatile unsigned long TAVertexAGPRemap; /* 0x0C38 */ - volatile unsigned long TAUVAddrTrans; /* 0x0C3C */ - volatile unsigned long TATailPtrCacheTrans; /* 0x0C40 */ - volatile unsigned long TAParamWriteTrans; /* 0x0C44 */ - volatile unsigned long TAPtrWriteTrans; /* 0x0C48 */ - volatile unsigned long TAParamWriteThresh; /* 0x0C4C */ - volatile unsigned long TAPtrWriteThresh; /* 0x0C50 */ - volatile unsigned long TATailPtrCacheAGPRe; /* 0x0C54 */ - volatile unsigned long TAParamWriteAGPRe; /* 0x0C58 */ - volatile unsigned long TAPtrWriteAGPRe; /* 0x0C5C */ - volatile unsigned long SDRAMArbiterConf; /* 0x0C60 */ - volatile unsigned long SDRAMConf0; /* 0x0C64 */ - volatile unsigned long SDRAMConf1; /* 0x0C68 */ - volatile unsigned long SDRAMConf2; /* 0x0C6C */ - volatile unsigned long SDRAMRefresh; /* 0x0C70 */ - volatile unsigned long SDRAMPowerStat; /* 0x0C74 */ - - volatile unsigned long Fill6[2]; /* GAP 0x0C78 - 0x0C7C */ - - volatile unsigned long RAMBistData; /* 0x0C80 */ - volatile unsigned long RAMBistCtrl; /* 0x0C84 */ - volatile unsigned long FIFOBistKey; /* 0x0C88 */ - volatile unsigned long RAMBistResult; /* 0x0C8C */ - volatile unsigned long FIFOBistResult; /* 0x0C90 */ + volatile u32 Fill4[412]; /* 0x0190 - 0x07FC */ + + volatile u32 TACtrlStreamBase; /* 0x0800 */ + volatile u32 TAObjDataBase; /* 0x0804 */ + volatile u32 TAPtrDataBase; /* 0x0808 */ + volatile u32 TARegionDataBase; /* 0x080C */ + volatile u32 TATailPtrBase; /* 0x0810 */ + volatile u32 TAPtrRegionSize; /* 0x0814 */ + volatile u32 TAConfiguration; /* 0x0818 */ + volatile u32 TAObjDataStartAddr; /* 0x081C */ + volatile u32 TAObjDataEndAddr; /* 0x0820 */ + volatile u32 TAXScreenClip; /* 0x0824 */ + volatile u32 TAYScreenClip; /* 0x0828 */ + volatile u32 TARHWClamp; /* 0x082C */ + volatile u32 TARHWCompare; /* 0x0830 */ + volatile u32 TAStart; /* 0x0834 */ + volatile u32 TAObjReStart; /* 0x0838 */ + volatile u32 TAPtrReStart; /* 0x083C */ + volatile u32 TAStatus1; /* 0x0840 */ + volatile u32 TAStatus2; /* 0x0844 */ + volatile u32 TAIntStatus; /* 0x0848 */ + volatile u32 TAIntMask; /* 0x084C */ + + volatile u32 Fill5[235]; /* GAP 0x0850 - 0x0BF8 */ + + volatile u32 TextureAddrThresh; /* 0x0BFC */ + volatile u32 Core1Translation; /* 0x0C00 */ + volatile u32 TextureAddrReMap; /* 0x0C04 */ + volatile u32 RenderOutAGPRemap; /* 0x0C08 */ + volatile u32 _3DRegionReadTrans; /* 0x0C0C */ + volatile u32 _3DPtrReadTrans; /* 0x0C10 */ + volatile u32 _3DParamReadTrans; /* 0x0C14 */ + volatile u32 _3DRegionReadThresh; /* 0x0C18 */ + volatile u32 _3DPtrReadThresh; /* 0x0C1C */ + volatile u32 _3DParamReadThresh; /* 0x0C20 */ + volatile u32 _3DRegionReadAGPRemap; /* 0x0C24 */ + volatile u32 _3DPtrReadAGPRemap; /* 0x0C28 */ + volatile u32 _3DParamReadAGPRemap; /* 0x0C2C */ + volatile u32 ZBufferAGPRemap; /* 0x0C30 */ + volatile u32 TAIndexAGPRemap; /* 0x0C34 */ + volatile u32 TAVertexAGPRemap; /* 0x0C38 */ + volatile u32 TAUVAddrTrans; /* 0x0C3C */ + volatile u32 TATailPtrCacheTrans; /* 0x0C40 */ + volatile u32 TAParamWriteTrans; /* 0x0C44 */ + volatile u32 TAPtrWriteTrans; /* 0x0C48 */ + volatile u32 TAParamWriteThresh; /* 0x0C4C */ + volatile u32 TAPtrWriteThresh; /* 0x0C50 */ + volatile u32 TATailPtrCacheAGPRe; /* 0x0C54 */ + volatile u32 TAParamWriteAGPRe; /* 0x0C58 */ + volatile u32 TAPtrWriteAGPRe; /* 0x0C5C */ + volatile u32 SDRAMArbiterConf; /* 0x0C60 */ + volatile u32 SDRAMConf0; /* 0x0C64 */ + volatile u32 SDRAMConf1; /* 0x0C68 */ + volatile u32 SDRAMConf2; /* 0x0C6C */ + volatile u32 SDRAMRefresh; /* 0x0C70 */ + volatile u32 SDRAMPowerStat; /* 0x0C74 */ + + volatile u32 Fill6[2]; /* GAP 0x0C78 - 0x0C7C */ + + volatile u32 RAMBistData; /* 0x0C80 */ + volatile u32 RAMBistCtrl; /* 0x0C84 */ + volatile u32 FIFOBistKey; /* 0x0C88 */ + volatile u32 RAMBistResult; /* 0x0C8C */ + volatile u32 FIFOBistResult; /* 0x0C90 */ /* - volatile unsigned long Fill11[0x0CBC/4 - 0x0C94/4]; //GAP 0x0C94 - 0x0CBC - volatile unsigned long Fill12[0x0CD0/4 - 0x0CC0/4]; //GAP 0x0CC0 - 0x0CD0 3DRegisters + volatile u32 Fill11[0x0CBC/4 - 0x0C94/4]; //GAP 0x0C94 - 0x0CBC + volatile u32 Fill12[0x0CD0/4 - 0x0CC0/4]; //GAP 0x0CC0 - 0x0CD0 3DRegisters */ - volatile unsigned long Fill7[16]; /* 0x0c94 - 0x0cd0 */ + volatile u32 Fill7[16]; /* 0x0c94 - 0x0cd0 */ - volatile unsigned long SDRAMAddrSign; /* 0x0CD4 */ - volatile unsigned long SDRAMDataSign; /* 0x0CD8 */ - volatile unsigned long SDRAMSignConf; /* 0x0CDC */ + volatile u32 SDRAMAddrSign; /* 0x0CD4 */ + volatile u32 SDRAMDataSign; /* 0x0CD8 */ + volatile u32 SDRAMSignConf; /* 0x0CDC */ /* DWFILL; //GAP 0x0CE0 */ - volatile unsigned long dwFill_2; - - volatile unsigned long ISPSignature; /* 0x0CE4 */ - - volatile unsigned long Fill8[454]; /*GAP 0x0CE8 - 0x13FC */ - - volatile unsigned long DACPrimAddress; /* 0x1400 */ - volatile unsigned long DACPrimSize; /* 0x1404 */ - volatile unsigned long DACCursorAddr; /* 0x1408 */ - volatile unsigned long DACCursorCtrl; /* 0x140C */ - volatile unsigned long DACOverlayAddr; /* 0x1410 */ - volatile unsigned long DACOverlayUAddr; /* 0x1414 */ - volatile unsigned long DACOverlayVAddr; /* 0x1418 */ - volatile unsigned long DACOverlaySize; /* 0x141C */ - volatile unsigned long DACOverlayVtDec; /* 0x1420 */ - - volatile unsigned long Fill9[9]; /* GAP 0x1424 - 0x1444 */ - - volatile unsigned long DACVerticalScal; /* 0x1448 */ - volatile unsigned long DACPixelFormat; /* 0x144C */ - volatile unsigned long DACHorizontalScal; /* 0x1450 */ - volatile unsigned long DACVidWinStart; /* 0x1454 */ - volatile unsigned long DACVidWinEnd; /* 0x1458 */ - volatile unsigned long DACBlendCtrl; /* 0x145C */ - volatile unsigned long DACHorTim1; /* 0x1460 */ - volatile unsigned long DACHorTim2; /* 0x1464 */ - volatile unsigned long DACHorTim3; /* 0x1468 */ - volatile unsigned long DACVerTim1; /* 0x146C */ - volatile unsigned long DACVerTim2; /* 0x1470 */ - volatile unsigned long DACVerTim3; /* 0x1474 */ - volatile unsigned long DACBorderColor; /* 0x1478 */ - volatile unsigned long DACSyncCtrl; /* 0x147C */ - volatile unsigned long DACStreamCtrl; /* 0x1480 */ - volatile unsigned long DACLUTAddress; /* 0x1484 */ - volatile unsigned long DACLUTData; /* 0x1488 */ - volatile unsigned long DACBurstCtrl; /* 0x148C */ - volatile unsigned long DACCrcTrigger; /* 0x1490 */ - volatile unsigned long DACCrcDone; /* 0x1494 */ - volatile unsigned long DACCrcResult1; /* 0x1498 */ - volatile unsigned long DACCrcResult2; /* 0x149C */ - volatile unsigned long DACLinecount; /* 0x14A0 */ - - volatile unsigned long Fill10[151]; /*GAP 0x14A4 - 0x16FC */ - - volatile unsigned long DigVidPortCtrl; /* 0x1700 */ - volatile unsigned long DigVidPortStat; /* 0x1704 */ + volatile u32 dwFill_2; + + volatile u32 ISPSignature; /* 0x0CE4 */ + + volatile u32 Fill8[454]; /*GAP 0x0CE8 - 0x13FC */ + + volatile u32 DACPrimAddress; /* 0x1400 */ + volatile u32 DACPrimSize; /* 0x1404 */ + volatile u32 DACCursorAddr; /* 0x1408 */ + volatile u32 DACCursorCtrl; /* 0x140C */ + volatile u32 DACOverlayAddr; /* 0x1410 */ + volatile u32 DACOverlayUAddr; /* 0x1414 */ + volatile u32 DACOverlayVAddr; /* 0x1418 */ + volatile u32 DACOverlaySize; /* 0x141C */ + volatile u32 DACOverlayVtDec; /* 0x1420 */ + + volatile u32 Fill9[9]; /* GAP 0x1424 - 0x1444 */ + + volatile u32 DACVerticalScal; /* 0x1448 */ + volatile u32 DACPixelFormat; /* 0x144C */ + volatile u32 DACHorizontalScal; /* 0x1450 */ + volatile u32 DACVidWinStart; /* 0x1454 */ + volatile u32 DACVidWinEnd; /* 0x1458 */ + volatile u32 DACBlendCtrl; /* 0x145C */ + volatile u32 DACHorTim1; /* 0x1460 */ + volatile u32 DACHorTim2; /* 0x1464 */ + volatile u32 DACHorTim3; /* 0x1468 */ + volatile u32 DACVerTim1; /* 0x146C */ + volatile u32 DACVerTim2; /* 0x1470 */ + volatile u32 DACVerTim3; /* 0x1474 */ + volatile u32 DACBorderColor; /* 0x1478 */ + volatile u32 DACSyncCtrl; /* 0x147C */ + volatile u32 DACStreamCtrl; /* 0x1480 */ + volatile u32 DACLUTAddress; /* 0x1484 */ + volatile u32 DACLUTData; /* 0x1488 */ + volatile u32 DACBurstCtrl; /* 0x148C */ + volatile u32 DACCrcTrigger; /* 0x1490 */ + volatile u32 DACCrcDone; /* 0x1494 */ + volatile u32 DACCrcResult1; /* 0x1498 */ + volatile u32 DACCrcResult2; /* 0x149C */ + volatile u32 DACLinecount; /* 0x14A0 */ + + volatile u32 Fill10[151]; /*GAP 0x14A4 - 0x16FC */ + + volatile u32 DigVidPortCtrl; /* 0x1700 */ + volatile u32 DigVidPortStat; /* 0x1704 */ /* - volatile unsigned long Fill11[0x1FFC/4 - 0x1708/4]; //GAP 0x1708 - 0x1FFC - volatile unsigned long Fill17[0x3000/4 - 0x2FFC/4]; //GAP 0x2000 - 0x2FFC ALUT + volatile u32 Fill11[0x1FFC/4 - 0x1708/4]; //GAP 0x1708 - 0x1FFC + volatile u32 Fill17[0x3000/4 - 0x2FFC/4]; //GAP 0x2000 - 0x2FFC ALUT */ - volatile unsigned long Fill11[1598]; + volatile u32 Fill11[1598]; /* DWFILL; //GAP 0x3000 ALUT 256MB offset */ - volatile unsigned long Fill_3; + volatile u32 Fill_3; } STG4000REG; diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 4527cbf0a4ec..b061d709bc44 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -420,7 +420,7 @@ static void mddi_resume(struct msm_mddi_client_data *cdata) mddi_set_auto_hibernate(&mddi->client_data, 1); } -static int __init mddi_get_client_caps(struct mddi_info *mddi) +static int __devinit mddi_get_client_caps(struct mddi_info *mddi) { int i, j; @@ -622,9 +622,9 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) static struct mddi_info mddi_info[2]; -static int __init mddi_clk_setup(struct platform_device *pdev, - struct mddi_info *mddi, - unsigned long clk_rate) +static int __devinit mddi_clk_setup(struct platform_device *pdev, + struct mddi_info *mddi, + unsigned long clk_rate) { int ret; diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 260cca7ddb41..b0e2a4261afe 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -73,7 +73,7 @@ static void uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *ns struct uvesafb_task *utask; struct uvesafb_ktask *task; - if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN)) return; if (msg->seq >= UVESAFB_TASKS_MAX) @@ -815,8 +815,15 @@ static int __devinit uvesafb_vbe_init(struct fb_info *info) par->pmi_setpal = pmi_setpal; par->ypan = ypan; - if (par->pmi_setpal || par->ypan) - uvesafb_vbe_getpmi(task, par); + if (par->pmi_setpal || par->ypan) { + if (__supported_pte_mask & _PAGE_NX) { + par->pmi_setpal = par->ypan = 0; + printk(KERN_WARNING "uvesafb: NX protection is actively." + "We have better not to use the PMI.\n"); + } else { + uvesafb_vbe_getpmi(task, par); + } + } #else /* The protected mode interface is not available on non-x86. */ par->pmi_setpal = par->ypan = 0; diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index cb4529c40d74..b7f5173ff9e9 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -365,7 +365,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, struct fb_info *fb_info; int fb_size; int val; - int ret; + int ret = 0; info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { @@ -458,26 +458,31 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, xenfb_init_shared_page(info, fb_info); ret = xenfb_connect_backend(dev, info); - if (ret < 0) - goto error; + if (ret < 0) { + xenbus_dev_fatal(dev, ret, "xenfb_connect_backend"); + goto error_fb; + } ret = register_framebuffer(fb_info); if (ret) { - fb_deferred_io_cleanup(fb_info); - fb_dealloc_cmap(&fb_info->cmap); - framebuffer_release(fb_info); xenbus_dev_fatal(dev, ret, "register_framebuffer"); - goto error; + goto error_fb; } info->fb_info = fb_info; xenfb_make_preferred_console(); return 0; - error_nomem: - ret = -ENOMEM; - xenbus_dev_fatal(dev, ret, "allocating device memory"); - error: +error_fb: + fb_deferred_io_cleanup(fb_info); + fb_dealloc_cmap(&fb_info->cmap); + framebuffer_release(fb_info); +error_nomem: + if (!ret) { + ret = -ENOMEM; + xenbus_dev_fatal(dev, ret, "allocating device memory"); + } +error: xenfb_remove(dev); return ret; } diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 05f0a80818a2..c2d05a8279fd 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -28,6 +28,13 @@ #include <linux/slab.h> #include <linux/module.h> +/* + * Balloon device works in 4K page units. So each page is pointed to by + * multiple balloon pages. All memory counters in this driver are in balloon + * page units. + */ +#define VIRTIO_BALLOON_PAGES_PER_PAGE (PAGE_SIZE >> VIRTIO_BALLOON_PFN_SHIFT) + struct virtio_balloon { struct virtio_device *vdev; @@ -42,8 +49,13 @@ struct virtio_balloon /* Waiting for host to ack the pages we released. */ struct completion acked; - /* The pages we've told the Host we're not using. */ + /* Number of balloon pages we've told the Host we're not using. */ unsigned int num_pages; + /* + * The pages we've told the Host we're not using. + * Each page on this list adds VIRTIO_BALLOON_PAGES_PER_PAGE + * to num_pages above. + */ struct list_head pages; /* The array of pfns we tell the Host about. */ @@ -66,7 +78,13 @@ static u32 page_to_balloon_pfn(struct page *page) BUILD_BUG_ON(PAGE_SHIFT < VIRTIO_BALLOON_PFN_SHIFT); /* Convert pfn from Linux page size to balloon page size. */ - return pfn >> (PAGE_SHIFT - VIRTIO_BALLOON_PFN_SHIFT); + return pfn * VIRTIO_BALLOON_PAGES_PER_PAGE; +} + +static struct page *balloon_pfn_to_page(u32 pfn) +{ + BUG_ON(pfn % VIRTIO_BALLOON_PAGES_PER_PAGE); + return pfn_to_page(pfn / VIRTIO_BALLOON_PAGES_PER_PAGE); } static void balloon_ack(struct virtqueue *vq) @@ -96,12 +114,23 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq) wait_for_completion(&vb->acked); } +static void set_page_pfns(u32 pfns[], struct page *page) +{ + unsigned int i; + + /* Set balloon pfns pointing at this page. + * Note that the first pfn points at start of the page. */ + for (i = 0; i < VIRTIO_BALLOON_PAGES_PER_PAGE; i++) + pfns[i] = page_to_balloon_pfn(page) + i; +} + static void fill_balloon(struct virtio_balloon *vb, size_t num) { /* We can only do one array worth at a time. */ num = min(num, ARRAY_SIZE(vb->pfns)); - for (vb->num_pfns = 0; vb->num_pfns < num; vb->num_pfns++) { + for (vb->num_pfns = 0; vb->num_pfns < num; + vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) { struct page *page = alloc_page(GFP_HIGHUSER | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); if (!page) { @@ -113,9 +142,9 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num) msleep(200); break; } - vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page); + set_page_pfns(vb->pfns + vb->num_pfns, page); + vb->num_pages += VIRTIO_BALLOON_PAGES_PER_PAGE; totalram_pages--; - vb->num_pages++; list_add(&page->lru, &vb->pages); } @@ -130,8 +159,9 @@ static void release_pages_by_pfn(const u32 pfns[], unsigned int num) { unsigned int i; - for (i = 0; i < num; i++) { - __free_page(pfn_to_page(pfns[i])); + /* Find pfns pointing at start of each page, get pages and free them. */ + for (i = 0; i < num; i += VIRTIO_BALLOON_PAGES_PER_PAGE) { + __free_page(balloon_pfn_to_page(pfns[i])); totalram_pages++; } } @@ -143,11 +173,12 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) /* We can only do one array worth at a time. */ num = min(num, ARRAY_SIZE(vb->pfns)); - for (vb->num_pfns = 0; vb->num_pfns < num; vb->num_pfns++) { + for (vb->num_pfns = 0; vb->num_pfns < num; + vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) { page = list_first_entry(&vb->pages, struct page, lru); list_del(&page->lru); - vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page); - vb->num_pages--; + set_page_pfns(vb->pfns + vb->num_pfns, page); + vb->num_pages -= VIRTIO_BALLOON_PAGES_PER_PAGE; } /* @@ -234,11 +265,14 @@ static void virtballoon_changed(struct virtio_device *vdev) static inline s64 towards_target(struct virtio_balloon *vb) { - u32 v; + __le32 v; + s64 target; + vb->vdev->config->get(vb->vdev, offsetof(struct virtio_balloon_config, num_pages), &v, sizeof(v)); - return (s64)v - vb->num_pages; + target = le32_to_cpu(v); + return target - vb->num_pages; } static void update_balloon_size(struct virtio_balloon *vb) diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index cbc7ceef2786..9f13b897fd64 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -435,16 +435,16 @@ static void hpwdt_start(void) { reload = SECS_TO_TICKS(soft_margin); iowrite16(reload, hpwdt_timer_reg); - iowrite16(0x85, hpwdt_timer_con); + iowrite8(0x85, hpwdt_timer_con); } static void hpwdt_stop(void) { unsigned long data; - data = ioread16(hpwdt_timer_con); + data = ioread8(hpwdt_timer_con); data &= 0xFE; - iowrite16(data, hpwdt_timer_con); + iowrite8(data, hpwdt_timer_con); } static void hpwdt_ping(void) diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 94243136f6bf..ea20c51d24c7 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -183,15 +183,17 @@ config XEN_ACPI_PROCESSOR depends on XEN && X86 && ACPI_PROCESSOR && CPU_FREQ default m help - This ACPI processor uploads Power Management information to the Xen hypervisor. - - To do that the driver parses the Power Management data and uploads said - information to the Xen hypervisor. Then the Xen hypervisor can select the - proper Cx and Pxx states. It also registers itslef as the SMM so that - other drivers (such as ACPI cpufreq scaling driver) will not load. - - To compile this driver as a module, choose M here: the - module will be called xen_acpi_processor If you do not know what to choose, - select M here. If the CPUFREQ drivers are built in, select Y here. + This ACPI processor uploads Power Management information to the Xen + hypervisor. + + To do that the driver parses the Power Management data and uploads + said information to the Xen hypervisor. Then the Xen hypervisor can + select the proper Cx and Pxx states. It also registers itslef as the + SMM so that other drivers (such as ACPI cpufreq scaling driver) will + not load. + + To compile this driver as a module, choose M here: the module will be + called xen_acpi_processor If you do not know what to choose, select + M here. If the CPUFREQ drivers are built in, select Y here. endmenu diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 4b33acd8ed4e..0a8a17cd80be 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -274,7 +274,7 @@ static unsigned int cpu_from_evtchn(unsigned int evtchn) static bool pirq_check_eoi_map(unsigned irq) { - return test_bit(irq, pirq_eoi_map); + return test_bit(pirq_from_irq(irq), pirq_eoi_map); } static bool pirq_needs_eoi_flag(unsigned irq) diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 99d8151c824a..1ffd03bf8e10 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -722,7 +722,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) vma->vm_flags |= VM_RESERVED|VM_DONTEXPAND; if (use_ptemod) - vma->vm_flags |= VM_DONTCOPY|VM_PFNMAP; + vma->vm_flags |= VM_DONTCOPY; vma->vm_private_data = map; diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index b4d4eac761db..f100ce20b16b 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -1029,6 +1029,7 @@ int gnttab_init(void) int i; unsigned int max_nr_glist_frames, nr_glist_frames; unsigned int nr_init_grefs; + int ret; nr_grant_frames = 1; boot_max_nr_grant_frames = __max_nr_grant_frames(); @@ -1047,12 +1048,16 @@ int gnttab_init(void) nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; for (i = 0; i < nr_glist_frames; i++) { gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL); - if (gnttab_list[i] == NULL) + if (gnttab_list[i] == NULL) { + ret = -ENOMEM; goto ini_nomem; + } } - if (gnttab_resume() < 0) - return -ENODEV; + if (gnttab_resume() < 0) { + ret = -ENODEV; + goto ini_nomem; + } nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME; @@ -1070,7 +1075,7 @@ int gnttab_init(void) for (i--; i >= 0; i--) free_page((unsigned long)gnttab_list[i]); kfree(gnttab_list); - return -ENOMEM; + return ret; } EXPORT_SYMBOL_GPL(gnttab_init); diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 9e14ae6cd49c..412b96cc5305 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -132,6 +132,7 @@ static void do_suspend(void) err = dpm_suspend_end(PMSG_FREEZE); if (err) { printk(KERN_ERR "dpm_suspend_end failed: %d\n", err); + si.cancelled = 0; goto out_resume; } diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 19e6a2041371..1afb4fba11b4 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -204,7 +204,8 @@ error: void * xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, - dma_addr_t *dma_handle, gfp_t flags) + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs) { void *ret; int order = get_order(size); @@ -253,7 +254,7 @@ EXPORT_SYMBOL_GPL(xen_swiotlb_alloc_coherent); void xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, - dma_addr_t dev_addr) + dma_addr_t dev_addr, struct dma_attrs *attrs) { int order = get_order(size); phys_addr_t phys; diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 174b5653cd8a..0b48579a9cd6 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -128,7 +128,10 @@ static int push_cxx_to_hypervisor(struct acpi_processor *_pr) pr_debug(" C%d: %s %d uS\n", cx->type, cx->desc, (u32)cx->latency); } - } else + } else if (ret != -EINVAL) + /* EINVAL means the ACPI ID is incorrect - meaning the ACPI + * table is referencing a non-existing CPU - which can happen + * with broken ACPI tables. */ pr_err(DRV_NAME "(CX): Hypervisor error (%d) for ACPI CPU%u\n", ret, _pr->acpi_id); diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index 63616d7453e6..97f5d264c31e 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -234,7 +234,7 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev, if (dev_data) dev_data->ack_intr = 0; - return result; + return result > 0 ? 0 : result; } static diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index f20c5f178b40..a31b54d48839 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -135,7 +135,7 @@ static int read_backend_details(struct xenbus_device *xendev) return xenbus_read_otherend_details(xendev, "backend-id", "backend"); } -static int is_device_connecting(struct device *dev, void *data) +static int is_device_connecting(struct device *dev, void *data, bool ignore_nonessential) { struct xenbus_device *xendev = to_xenbus_device(dev); struct device_driver *drv = data; @@ -152,16 +152,41 @@ static int is_device_connecting(struct device *dev, void *data) if (drv && (dev->driver != drv)) return 0; + if (ignore_nonessential) { + /* With older QEMU, for PVonHVM guests the guest config files + * could contain: vfb = [ 'vnc=1, vnclisten=0.0.0.0'] + * which is nonsensical as there is no PV FB (there can be + * a PVKB) running as HVM guest. */ + + if ((strncmp(xendev->nodename, "device/vkbd", 11) == 0)) + return 0; + + if ((strncmp(xendev->nodename, "device/vfb", 10) == 0)) + return 0; + } xendrv = to_xenbus_driver(dev->driver); return (xendev->state < XenbusStateConnected || (xendev->state == XenbusStateConnected && xendrv->is_ready && !xendrv->is_ready(xendev))); } +static int essential_device_connecting(struct device *dev, void *data) +{ + return is_device_connecting(dev, data, true /* ignore PV[KBB+FB] */); +} +static int non_essential_device_connecting(struct device *dev, void *data) +{ + return is_device_connecting(dev, data, false); +} -static int exists_connecting_device(struct device_driver *drv) +static int exists_essential_connecting_device(struct device_driver *drv) { return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, - is_device_connecting); + essential_device_connecting); +} +static int exists_non_essential_connecting_device(struct device_driver *drv) +{ + return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, + non_essential_device_connecting); } static int print_device_status(struct device *dev, void *data) @@ -192,6 +217,23 @@ static int print_device_status(struct device *dev, void *data) /* We only wait for device setup after most initcalls have run. */ static int ready_to_wait_for_devices; +static bool wait_loop(unsigned long start, unsigned int max_delay, + unsigned int *seconds_waited) +{ + if (time_after(jiffies, start + (*seconds_waited+5)*HZ)) { + if (!*seconds_waited) + printk(KERN_WARNING "XENBUS: Waiting for " + "devices to initialise: "); + *seconds_waited += 5; + printk("%us...", max_delay - *seconds_waited); + if (*seconds_waited == max_delay) + return true; + } + + schedule_timeout_interruptible(HZ/10); + + return false; +} /* * On a 5-minute timeout, wait for all devices currently configured. We need * to do this to guarantee that the filesystems and / or network devices @@ -215,19 +257,14 @@ static void wait_for_devices(struct xenbus_driver *xendrv) if (!ready_to_wait_for_devices || !xen_domain()) return; - while (exists_connecting_device(drv)) { - if (time_after(jiffies, start + (seconds_waited+5)*HZ)) { - if (!seconds_waited) - printk(KERN_WARNING "XENBUS: Waiting for " - "devices to initialise: "); - seconds_waited += 5; - printk("%us...", 300 - seconds_waited); - if (seconds_waited == 300) - break; - } - - schedule_timeout_interruptible(HZ/10); - } + while (exists_non_essential_connecting_device(drv)) + if (wait_loop(start, 30, &seconds_waited)) + break; + + /* Skips PVKB and PVFB check.*/ + while (exists_essential_connecting_device(drv)) + if (wait_loop(start, 270, &seconds_waited)) + break; if (seconds_waited) printk("\n"); @@ -93,9 +93,8 @@ static void aio_free_ring(struct kioctx *ctx) put_page(info->ring_pages[i]); if (info->mmap_size) { - down_write(&ctx->mm->mmap_sem); - do_munmap(ctx->mm, info->mmap_base, info->mmap_size); - up_write(&ctx->mm->mmap_sem); + BUG_ON(ctx->mm != current->mm); + vm_munmap(info->mmap_base, info->mmap_size); } if (info->ring_pages && info->ring_pages != info->internal_pages) @@ -389,6 +388,17 @@ void exit_aio(struct mm_struct *mm) "exit_aio:ioctx still alive: %d %d %d\n", atomic_read(&ctx->users), ctx->dead, ctx->reqs_active); + /* + * We don't need to bother with munmap() here - + * exit_mmap(mm) is coming and it'll unmap everything. + * Since aio_free_ring() uses non-zero ->mmap_size + * as indicator that it needs to unmap the area, + * just set it to 0; aio_free_ring() is the only + * place that uses ->mmap_size, so it's safe. + * That way we get all munmap done to current->mm - + * all other callers have ctx->mm == current->mm. + */ + ctx->ring_info.mmap_size = 0; put_ioctx(ctx); } } diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index eb1cc92cd67d..908e18455413 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -110,7 +110,6 @@ struct autofs_sb_info { int sub_version; int min_proto; int max_proto; - int compat_daemon; unsigned long exp_timeout; unsigned int type; int reghost_enabled; @@ -270,6 +269,17 @@ int autofs4_fill_super(struct super_block *, void *, int); struct autofs_info *autofs4_new_ino(struct autofs_sb_info *); void autofs4_clean_ino(struct autofs_info *); +static inline int autofs_prepare_pipe(struct file *pipe) +{ + if (!pipe->f_op || !pipe->f_op->write) + return -EINVAL; + if (!S_ISFIFO(pipe->f_dentry->d_inode->i_mode)) + return -EINVAL; + /* We want a packet pipe */ + pipe->f_flags |= O_DIRECT; + return 0; +} + /* Queue management functions */ int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index 9dacb8586701..aa9103f8f01b 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -376,7 +376,7 @@ static int autofs_dev_ioctl_setpipefd(struct file *fp, err = -EBADF; goto out; } - if (!pipe->f_op || !pipe->f_op->write) { + if (autofs_prepare_pipe(pipe) < 0) { err = -EPIPE; fput(pipe); goto out; @@ -385,7 +385,6 @@ static int autofs_dev_ioctl_setpipefd(struct file *fp, sbi->pipefd = pipefd; sbi->pipe = pipe; sbi->catatonic = 0; - sbi->compat_daemon = is_compat_task(); } out: mutex_unlock(&sbi->wq_mutex); diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index d8dc002e9cc3..6e488ebe7784 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -19,7 +19,6 @@ #include <linux/parser.h> #include <linux/bitops.h> #include <linux/magic.h> -#include <linux/compat.h> #include "autofs_i.h" #include <linux/module.h> @@ -225,7 +224,6 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) set_autofs_type_indirect(&sbi->type); sbi->min_proto = 0; sbi->max_proto = 0; - sbi->compat_daemon = is_compat_task(); mutex_init(&sbi->wq_mutex); mutex_init(&sbi->pipe_mutex); spin_lock_init(&sbi->fs_lock); @@ -292,7 +290,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) printk("autofs: could not open pipe file descriptor\n"); goto fail_dput; } - if (!pipe->f_op || !pipe->f_op->write) + if (autofs_prepare_pipe(pipe) < 0) goto fail_fput; sbi->pipe = pipe; sbi->pipefd = pipefd; diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 9c098db43344..da8876d38a7b 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -91,24 +91,7 @@ static int autofs4_write(struct autofs_sb_info *sbi, return (bytes > 0); } - -/* - * The autofs_v5 packet was misdesigned. - * - * The packets are identical on x86-32 and x86-64, but have different - * alignment. Which means that 'sizeof()' will give different results. - * Fix it up for the case of running 32-bit user mode on a 64-bit kernel. - */ -static noinline size_t autofs_v5_packet_size(struct autofs_sb_info *sbi) -{ - size_t pktsz = sizeof(struct autofs_v5_packet); -#if defined(CONFIG_X86_64) && defined(CONFIG_COMPAT) - if (sbi->compat_daemon > 0) - pktsz -= 4; -#endif - return pktsz; -} - + static void autofs4_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq, int type) @@ -172,7 +155,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, { struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; - pktsz = autofs_v5_packet_size(sbi); + pktsz = sizeof(*packet); + packet->wait_queue_token = wq->wait_queue_token; packet->len = wq->name.len; memcpy(packet->name, wq->name.name, wq->name.len); diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 2eb12f13593d..d146e181d10d 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -50,9 +50,7 @@ static int set_brk(unsigned long start, unsigned long end) end = PAGE_ALIGN(end); if (end > start) { unsigned long addr; - down_write(¤t->mm->mmap_sem); - addr = do_brk(start, end - start); - up_write(¤t->mm->mmap_sem); + addr = vm_brk(start, end - start); if (BAD_ADDR(addr)) return addr; } @@ -280,9 +278,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) pos = 32; map_size = ex.a_text+ex.a_data; #endif - down_write(¤t->mm->mmap_sem); - error = do_brk(text_addr & PAGE_MASK, map_size); - up_write(¤t->mm->mmap_sem); + error = vm_brk(text_addr & PAGE_MASK, map_size); if (error != (text_addr & PAGE_MASK)) { send_sig(SIGKILL, current, 0); return error; @@ -313,9 +309,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) { loff_t pos = fd_offset; - down_write(¤t->mm->mmap_sem); - do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data); - up_write(¤t->mm->mmap_sem); + vm_brk(N_TXTADDR(ex), ex.a_text+ex.a_data); bprm->file->f_op->read(bprm->file, (char __user *)N_TXTADDR(ex), ex.a_text+ex.a_data, &pos); @@ -325,24 +319,20 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) goto beyond_if; } - down_write(¤t->mm->mmap_sem); - error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text, + error = vm_mmap(bprm->file, N_TXTADDR(ex), ex.a_text, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, fd_offset); - up_write(¤t->mm->mmap_sem); if (error != N_TXTADDR(ex)) { send_sig(SIGKILL, current, 0); return error; } - down_write(¤t->mm->mmap_sem); - error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data, + error = vm_mmap(bprm->file, N_DATADDR(ex), ex.a_data, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, fd_offset + ex.a_text); - up_write(¤t->mm->mmap_sem); if (error != N_DATADDR(ex)) { send_sig(SIGKILL, current, 0); return error; @@ -412,9 +402,7 @@ static int load_aout_library(struct file *file) "N_TXTOFF is not page aligned. Please convert library: %s\n", file->f_path.dentry->d_name.name); } - down_write(¤t->mm->mmap_sem); - do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss); - up_write(¤t->mm->mmap_sem); + vm_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss); file->f_op->read(file, (char __user *)start_addr, ex.a_text + ex.a_data, &pos); @@ -425,12 +413,10 @@ static int load_aout_library(struct file *file) goto out; } /* Now use mmap to map the library into memory. */ - down_write(¤t->mm->mmap_sem); - error = do_mmap(file, start_addr, ex.a_text + ex.a_data, + error = vm_mmap(file, start_addr, ex.a_text + ex.a_data, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, N_TXTOFF(ex)); - up_write(¤t->mm->mmap_sem); retval = error; if (error != start_addr) goto out; @@ -438,9 +424,7 @@ static int load_aout_library(struct file *file) len = PAGE_ALIGN(ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss; if (bss > len) { - down_write(¤t->mm->mmap_sem); - error = do_brk(start_addr + len, bss - len); - up_write(¤t->mm->mmap_sem); + error = vm_brk(start_addr + len, bss - len); retval = error; if (error != start_addr + len) goto out; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 48ffb3dc610a..16f735417072 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -82,9 +82,7 @@ static int set_brk(unsigned long start, unsigned long end) end = ELF_PAGEALIGN(end); if (end > start) { unsigned long addr; - down_write(¤t->mm->mmap_sem); - addr = do_brk(start, end - start); - up_write(¤t->mm->mmap_sem); + addr = vm_brk(start, end - start); if (BAD_ADDR(addr)) return addr; } @@ -514,9 +512,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1); /* Map the last of the bss segment */ - down_write(¤t->mm->mmap_sem); - error = do_brk(elf_bss, last_bss - elf_bss); - up_write(¤t->mm->mmap_sem); + error = vm_brk(elf_bss, last_bss - elf_bss); if (BAD_ADDR(error)) goto out_close; } @@ -962,10 +958,8 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) and some applications "depend" upon this behavior. Since we do not have the power to recompile these, we emulate the SVr4 behavior. Sigh. */ - down_write(¤t->mm->mmap_sem); - error = do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC, + error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, 0); - up_write(¤t->mm->mmap_sem); } #ifdef ELF_PLAT_INIT @@ -1050,8 +1044,7 @@ static int load_elf_library(struct file *file) eppnt++; /* Now use mmap to map the library into memory. */ - down_write(¤t->mm->mmap_sem); - error = do_mmap(file, + error = vm_mmap(file, ELF_PAGESTART(eppnt->p_vaddr), (eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr)), @@ -1059,7 +1052,6 @@ static int load_elf_library(struct file *file) MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, (eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr))); - up_write(¤t->mm->mmap_sem); if (error != ELF_PAGESTART(eppnt->p_vaddr)) goto out_free_ph; @@ -1072,11 +1064,8 @@ static int load_elf_library(struct file *file) len = ELF_PAGESTART(eppnt->p_filesz + eppnt->p_vaddr + ELF_MIN_ALIGN - 1); bss = eppnt->p_memsz + eppnt->p_vaddr; - if (bss > len) { - down_write(¤t->mm->mmap_sem); - do_brk(len, bss - len); - up_write(¤t->mm->mmap_sem); - } + if (bss > len) + vm_brk(len, bss - len); error = 0; out_free_ph: diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 9bd5612a8224..d390a0fffc65 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -390,21 +390,17 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, (executable_stack == EXSTACK_DEFAULT && VM_STACK_FLAGS & VM_EXEC)) stack_prot |= PROT_EXEC; - down_write(¤t->mm->mmap_sem); - current->mm->start_brk = do_mmap(NULL, 0, stack_size, stack_prot, + current->mm->start_brk = vm_mmap(NULL, 0, stack_size, stack_prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED | MAP_GROWSDOWN, 0); if (IS_ERR_VALUE(current->mm->start_brk)) { - up_write(¤t->mm->mmap_sem); retval = current->mm->start_brk; current->mm->start_brk = 0; goto error_kill; } - up_write(¤t->mm->mmap_sem); - current->mm->brk = current->mm->start_brk; current->mm->context.end_brk = current->mm->start_brk; current->mm->context.end_brk += @@ -955,10 +951,8 @@ static int elf_fdpic_map_file_constdisp_on_uclinux( if (params->flags & ELF_FDPIC_FLAG_EXECUTABLE) mflags |= MAP_EXECUTABLE; - down_write(&mm->mmap_sem); - maddr = do_mmap(NULL, load_addr, top - base, + maddr = vm_mmap(NULL, load_addr, top - base, PROT_READ | PROT_WRITE | PROT_EXEC, mflags, 0); - up_write(&mm->mmap_sem); if (IS_ERR_VALUE(maddr)) return (int) maddr; @@ -1096,10 +1090,8 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, /* create the mapping */ disp = phdr->p_vaddr & ~PAGE_MASK; - down_write(&mm->mmap_sem); - maddr = do_mmap(file, maddr, phdr->p_memsz + disp, prot, flags, + maddr = vm_mmap(file, maddr, phdr->p_memsz + disp, prot, flags, phdr->p_offset - disp); - up_write(&mm->mmap_sem); kdebug("mmap[%d] <file> sz=%lx pr=%x fl=%x of=%lx --> %08lx", loop, phdr->p_memsz + disp, prot, flags, @@ -1143,10 +1135,8 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, unsigned long xmaddr; flags |= MAP_FIXED | MAP_ANONYMOUS; - down_write(&mm->mmap_sem); - xmaddr = do_mmap(NULL, xaddr, excess - excess1, + xmaddr = vm_mmap(NULL, xaddr, excess - excess1, prot, flags, 0); - up_write(&mm->mmap_sem); kdebug("mmap[%d] <anon>" " ad=%lx sz=%lx pr=%x fl=%x of=0 --> %08lx", diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index 024d20ee3ca3..6b2daf99fab8 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -542,10 +542,8 @@ static int load_flat_file(struct linux_binprm * bprm, */ DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n"); - down_write(¤t->mm->mmap_sem); - textpos = do_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC, + textpos = vm_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_EXECUTABLE, 0); - up_write(¤t->mm->mmap_sem); if (!textpos || IS_ERR_VALUE(textpos)) { if (!textpos) textpos = (unsigned long) -ENOMEM; @@ -556,10 +554,8 @@ static int load_flat_file(struct linux_binprm * bprm, len = data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long); len = PAGE_ALIGN(len); - down_write(¤t->mm->mmap_sem); - realdatastart = do_mmap(0, 0, len, + realdatastart = vm_mmap(0, 0, len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 0); - up_write(¤t->mm->mmap_sem); if (realdatastart == 0 || IS_ERR_VALUE(realdatastart)) { if (!realdatastart) @@ -603,10 +599,8 @@ static int load_flat_file(struct linux_binprm * bprm, len = text_len + data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long); len = PAGE_ALIGN(len); - down_write(¤t->mm->mmap_sem); - textpos = do_mmap(0, 0, len, + textpos = vm_mmap(0, 0, len, PROT_READ | PROT_EXEC | PROT_WRITE, MAP_PRIVATE, 0); - up_write(¤t->mm->mmap_sem); if (!textpos || IS_ERR_VALUE(textpos)) { if (!textpos) diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c index e4fc746629a7..4517aaff61b4 100644 --- a/fs/binfmt_som.c +++ b/fs/binfmt_som.c @@ -147,10 +147,8 @@ static int map_som_binary(struct file *file, code_size = SOM_PAGEALIGN(hpuxhdr->exec_tsize); current->mm->start_code = code_start; current->mm->end_code = code_start + code_size; - down_write(¤t->mm->mmap_sem); - retval = do_mmap(file, code_start, code_size, prot, + retval = vm_mmap(file, code_start, code_size, prot, flags, SOM_PAGESTART(hpuxhdr->exec_tfile)); - up_write(¤t->mm->mmap_sem); if (retval < 0 && retval > -1024) goto out; @@ -158,20 +156,16 @@ static int map_som_binary(struct file *file, data_size = SOM_PAGEALIGN(hpuxhdr->exec_dsize); current->mm->start_data = data_start; current->mm->end_data = bss_start = data_start + data_size; - down_write(¤t->mm->mmap_sem); - retval = do_mmap(file, data_start, data_size, + retval = vm_mmap(file, data_start, data_size, prot | PROT_WRITE, flags, SOM_PAGESTART(hpuxhdr->exec_dfile)); - up_write(¤t->mm->mmap_sem); if (retval < 0 && retval > -1024) goto out; som_brk = bss_start + SOM_PAGEALIGN(hpuxhdr->exec_bsize); current->mm->start_brk = current->mm->brk = som_brk; - down_write(¤t->mm->mmap_sem); - retval = do_mmap(NULL, bss_start, som_brk - bss_start, + retval = vm_mmap(NULL, bss_start, som_brk - bss_start, prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, 0); - up_write(¤t->mm->mmap_sem); if (retval > 0 || retval < -1024) retval = 0; out: diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index f4e90748940a..bcec06750232 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -22,6 +22,7 @@ #include "ulist.h" #include "transaction.h" #include "delayed-ref.h" +#include "locking.h" /* * this structure records all encountered refs on the way up to the root @@ -893,18 +894,22 @@ static char *iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, s64 bytes_left = size - 1; struct extent_buffer *eb = eb_in; struct btrfs_key found_key; + int leave_spinning = path->leave_spinning; if (bytes_left >= 0) dest[bytes_left] = '\0'; + path->leave_spinning = 1; while (1) { len = btrfs_inode_ref_name_len(eb, iref); bytes_left -= len; if (bytes_left >= 0) read_extent_buffer(eb, dest + bytes_left, (unsigned long)(iref + 1), len); - if (eb != eb_in) + if (eb != eb_in) { + btrfs_tree_read_unlock_blocking(eb); free_extent_buffer(eb); + } ret = inode_ref_info(parent, 0, fs_root, path, &found_key); if (ret > 0) ret = -ENOENT; @@ -919,8 +924,11 @@ static char *iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, slot = path->slots[0]; eb = path->nodes[0]; /* make sure we can use eb after releasing the path */ - if (eb != eb_in) + if (eb != eb_in) { atomic_inc(&eb->refs); + btrfs_tree_read_lock(eb); + btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); + } btrfs_release_path(path); iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); @@ -931,6 +939,7 @@ static char *iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, } btrfs_release_path(path); + path->leave_spinning = leave_spinning; if (ret) return ERR_PTR(ret); @@ -1247,7 +1256,7 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, struct btrfs_path *path, iterate_irefs_t *iterate, void *ctx) { - int ret; + int ret = 0; int slot; u32 cur; u32 len; @@ -1259,7 +1268,8 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, struct btrfs_inode_ref *iref; struct btrfs_key found_key; - while (1) { + while (!ret) { + path->leave_spinning = 1; ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path, &found_key); if (ret < 0) @@ -1275,6 +1285,8 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, eb = path->nodes[0]; /* make sure we can use eb after releasing the path */ atomic_inc(&eb->refs); + btrfs_tree_read_lock(eb); + btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); btrfs_release_path(path); item = btrfs_item_nr(eb, slot); @@ -1288,13 +1300,12 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, (unsigned long long)found_key.objectid, (unsigned long long)fs_root->objectid); ret = iterate(parent, iref, eb, ctx); - if (ret) { - free_extent_buffer(eb); + if (ret) break; - } len = sizeof(*iref) + name_len; iref = (struct btrfs_inode_ref *)((char *)iref + len); } + btrfs_tree_read_unlock_blocking(eb); free_extent_buffer(eb); } @@ -1414,6 +1425,8 @@ struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root, void free_ipath(struct inode_fs_paths *ipath) { + if (!ipath) + return; kfree(ipath->fspath); kfree(ipath); } diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index d286b40a5671..86eff48dab78 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -405,6 +405,7 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start, bio_put(bio); bio = compressed_bio_alloc(bdev, first_byte, GFP_NOFS); + BUG_ON(!bio); bio->bi_private = cb; bio->bi_end_io = end_compressed_bio_write; bio_add_page(bio, page, PAGE_CACHE_SIZE, 0); @@ -687,6 +688,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, comp_bio = compressed_bio_alloc(bdev, cur_disk_byte, GFP_NOFS); + BUG_ON(!comp_bio); comp_bio->bi_private = cb; comp_bio->bi_end_io = end_compressed_bio_read; diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index e801f226d7e0..4106264fbc65 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -220,10 +220,12 @@ struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root) */ static void add_root_to_dirty_list(struct btrfs_root *root) { + spin_lock(&root->fs_info->trans_lock); if (root->track_dirty && list_empty(&root->dirty_list)) { list_add(&root->dirty_list, &root->fs_info->dirty_cowonly_roots); } + spin_unlock(&root->fs_info->trans_lock); } /* @@ -723,7 +725,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, cur = btrfs_find_tree_block(root, blocknr, blocksize); if (cur) - uptodate = btrfs_buffer_uptodate(cur, gen); + uptodate = btrfs_buffer_uptodate(cur, gen, 0); else uptodate = 0; if (!cur || !uptodate) { @@ -1358,7 +1360,12 @@ static noinline int reada_for_balance(struct btrfs_root *root, block1 = btrfs_node_blockptr(parent, slot - 1); gen = btrfs_node_ptr_generation(parent, slot - 1); eb = btrfs_find_tree_block(root, block1, blocksize); - if (eb && btrfs_buffer_uptodate(eb, gen)) + /* + * if we get -eagain from btrfs_buffer_uptodate, we + * don't want to return eagain here. That will loop + * forever + */ + if (eb && btrfs_buffer_uptodate(eb, gen, 1) != 0) block1 = 0; free_extent_buffer(eb); } @@ -1366,7 +1373,7 @@ static noinline int reada_for_balance(struct btrfs_root *root, block2 = btrfs_node_blockptr(parent, slot + 1); gen = btrfs_node_ptr_generation(parent, slot + 1); eb = btrfs_find_tree_block(root, block2, blocksize); - if (eb && btrfs_buffer_uptodate(eb, gen)) + if (eb && btrfs_buffer_uptodate(eb, gen, 1) != 0) block2 = 0; free_extent_buffer(eb); } @@ -1504,8 +1511,9 @@ read_block_for_search(struct btrfs_trans_handle *trans, tmp = btrfs_find_tree_block(root, blocknr, blocksize); if (tmp) { - if (btrfs_buffer_uptodate(tmp, 0)) { - if (btrfs_buffer_uptodate(tmp, gen)) { + /* first we do an atomic uptodate check */ + if (btrfs_buffer_uptodate(tmp, 0, 1) > 0) { + if (btrfs_buffer_uptodate(tmp, gen, 1) > 0) { /* * we found an up to date block without * sleeping, return @@ -1523,8 +1531,9 @@ read_block_for_search(struct btrfs_trans_handle *trans, free_extent_buffer(tmp); btrfs_set_path_blocking(p); + /* now we're allowed to do a blocking uptodate check */ tmp = read_tree_block(root, blocknr, blocksize, gen); - if (tmp && btrfs_buffer_uptodate(tmp, gen)) { + if (tmp && btrfs_buffer_uptodate(tmp, gen, 0) > 0) { *eb_ret = tmp; return 0; } @@ -1559,7 +1568,7 @@ read_block_for_search(struct btrfs_trans_handle *trans, * and give up so that our caller doesn't loop forever * on our EAGAINs. */ - if (!btrfs_buffer_uptodate(tmp, 0)) + if (!btrfs_buffer_uptodate(tmp, 0, 0)) ret = -EIO; free_extent_buffer(tmp); } @@ -4043,7 +4052,7 @@ again: tmp = btrfs_find_tree_block(root, blockptr, btrfs_level_size(root, level - 1)); - if (tmp && btrfs_buffer_uptodate(tmp, gen)) { + if (tmp && btrfs_buffer_uptodate(tmp, gen, 1) > 0) { free_extent_buffer(tmp); break; } @@ -4166,7 +4175,8 @@ next: struct extent_buffer *cur; cur = btrfs_find_tree_block(root, blockptr, btrfs_level_size(root, level - 1)); - if (!cur || !btrfs_buffer_uptodate(cur, gen)) { + if (!cur || + btrfs_buffer_uptodate(cur, gen, 1) <= 0) { slot++; if (cur) free_extent_buffer(cur); diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 5b8ef8eb3521..8fd72331d600 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1078,7 +1078,7 @@ struct btrfs_fs_info { * is required instead of the faster short fsync log commits */ u64 last_trans_log_full_commit; - unsigned long mount_opt:21; + unsigned long mount_opt; unsigned long compress_type:4; u64 max_inline; u64 alloc_start; @@ -2166,7 +2166,7 @@ BTRFS_SETGET_STACK_FUNCS(root_last_snapshot, struct btrfs_root_item, static inline bool btrfs_root_readonly(struct btrfs_root *root) { - return root->root_item.flags & BTRFS_ROOT_SUBVOL_RDONLY; + return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_RDONLY)) != 0; } /* struct btrfs_root_backup */ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 20196f411206..a7ffc88a7dbe 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -323,7 +323,8 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, * in the wrong place. */ static int verify_parent_transid(struct extent_io_tree *io_tree, - struct extent_buffer *eb, u64 parent_transid) + struct extent_buffer *eb, u64 parent_transid, + int atomic) { struct extent_state *cached_state = NULL; int ret; @@ -331,6 +332,9 @@ static int verify_parent_transid(struct extent_io_tree *io_tree, if (!parent_transid || btrfs_header_generation(eb) == parent_transid) return 0; + if (atomic) + return -EAGAIN; + lock_extent_bits(io_tree, eb->start, eb->start + eb->len - 1, 0, &cached_state); if (extent_buffer_uptodate(eb) && @@ -372,7 +376,8 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root, ret = read_extent_buffer_pages(io_tree, eb, start, WAIT_COMPLETE, btree_get_extent, mirror_num); - if (!ret && !verify_parent_transid(io_tree, eb, parent_transid)) + if (!ret && !verify_parent_transid(io_tree, eb, + parent_transid, 0)) break; /* @@ -383,17 +388,16 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root, if (test_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags)) break; - if (!failed_mirror) { - failed = 1; - printk(KERN_ERR "failed mirror was %d\n", eb->failed_mirror); - failed_mirror = eb->failed_mirror; - } - num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, eb->start, eb->len); if (num_copies == 1) break; + if (!failed_mirror) { + failed = 1; + failed_mirror = eb->read_mirror; + } + mirror_num++; if (mirror_num == failed_mirror) mirror_num++; @@ -564,7 +568,7 @@ struct extent_buffer *find_eb_for_page(struct extent_io_tree *tree, } static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end, - struct extent_state *state) + struct extent_state *state, int mirror) { struct extent_io_tree *tree; u64 found_start; @@ -589,6 +593,7 @@ static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end, if (!reads_done) goto err; + eb->read_mirror = mirror; if (test_bit(EXTENT_BUFFER_IOERR, &eb->bflags)) { ret = -EIO; goto err; @@ -652,7 +657,7 @@ static int btree_io_failed_hook(struct page *page, int failed_mirror) eb = (struct extent_buffer *)page->private; set_bit(EXTENT_BUFFER_IOERR, &eb->bflags); - eb->failed_mirror = failed_mirror; + eb->read_mirror = failed_mirror; if (test_and_clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags)) btree_readahead_hook(root, eb, eb->start, -EIO); return -EIO; /* we fixed nothing */ @@ -1202,7 +1207,7 @@ static int __must_check find_and_setup_root(struct btrfs_root *tree_root, root->commit_root = NULL; root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), blocksize, generation); - if (!root->node || !btrfs_buffer_uptodate(root->node, generation)) { + if (!root->node || !btrfs_buffer_uptodate(root->node, generation, 0)) { free_extent_buffer(root->node); root->node = NULL; return -EIO; @@ -2254,9 +2259,9 @@ int open_ctree(struct super_block *sb, goto fail_sb_buffer; } - if (sectorsize < PAGE_SIZE) { - printk(KERN_WARNING "btrfs: Incompatible sector size " - "found on %s\n", sb->s_id); + if (sectorsize != PAGE_SIZE) { + printk(KERN_WARNING "btrfs: Incompatible sector size(%lu) " + "found on %s\n", (unsigned long)sectorsize, sb->s_id); goto fail_sb_buffer; } @@ -3143,7 +3148,8 @@ int close_ctree(struct btrfs_root *root) return 0; } -int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid) +int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid, + int atomic) { int ret; struct inode *btree_inode = buf->pages[0]->mapping->host; @@ -3153,7 +3159,9 @@ int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid) return ret; ret = verify_parent_transid(&BTRFS_I(btree_inode)->io_tree, buf, - parent_transid); + parent_transid, atomic); + if (ret == -EAGAIN) + return ret; return !ret; } diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index a7ace1a2dd12..ab1830aaf0ed 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h @@ -66,7 +66,8 @@ void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr); void __btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr); void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root); void btrfs_mark_buffer_dirty(struct extent_buffer *buf); -int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid); +int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid, + int atomic); int btrfs_set_buffer_uptodate(struct extent_buffer *buf); int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid); u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index a84420491c11..49fd7b66d57b 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -529,9 +529,7 @@ static int cache_block_group(struct btrfs_block_group_cache *cache, * allocate blocks for the tree root we can't do the fast caching since * we likely hold important locks. */ - if (trans && (!trans->transaction->in_commit) && - (root && root != root->fs_info->tree_root) && - btrfs_test_opt(root, SPACE_CACHE)) { + if (fs_info->mount_opt & BTRFS_MOUNT_SPACE_CACHE) { ret = load_free_space_cache(fs_info, cache); spin_lock(&cache->lock); @@ -2303,6 +2301,7 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, if (ret) { printk(KERN_DEBUG "btrfs: run_delayed_extent_op returned %d\n", ret); + spin_lock(&delayed_refs->lock); return ret; } @@ -2333,6 +2332,7 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, if (ret) { printk(KERN_DEBUG "btrfs: run_one_delayed_ref returned %d\n", ret); + spin_lock(&delayed_refs->lock); return ret; } @@ -3152,15 +3152,14 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) /* * returns target flags in extended format or 0 if restripe for this * chunk_type is not in progress + * + * should be called with either volume_mutex or balance_lock held */ static u64 get_restripe_target(struct btrfs_fs_info *fs_info, u64 flags) { struct btrfs_balance_control *bctl = fs_info->balance_ctl; u64 target = 0; - BUG_ON(!mutex_is_locked(&fs_info->volume_mutex) && - !spin_is_locked(&fs_info->balance_lock)); - if (!bctl) return 0; @@ -3772,13 +3771,10 @@ again: */ if (current->journal_info) return -EAGAIN; - ret = wait_event_interruptible(space_info->wait, - !space_info->flush); - /* Must have been interrupted, return */ - if (ret) { - printk(KERN_DEBUG "btrfs: %s returning -EINTR\n", __func__); + ret = wait_event_killable(space_info->wait, !space_info->flush); + /* Must have been killed, return */ + if (ret) return -EINTR; - } spin_lock(&space_info->lock); } @@ -4205,7 +4201,7 @@ static u64 calc_global_metadata_size(struct btrfs_fs_info *fs_info) num_bytes += div64_u64(data_used + meta_used, 50); if (num_bytes * 3 > meta_used) - num_bytes = div64_u64(meta_used, 3) * 2; + num_bytes = div64_u64(meta_used, 3); return ALIGN(num_bytes, fs_info->extent_root->leafsize << 10); } @@ -4218,8 +4214,8 @@ static void update_global_block_rsv(struct btrfs_fs_info *fs_info) num_bytes = calc_global_metadata_size(fs_info); - spin_lock(&block_rsv->lock); spin_lock(&sinfo->lock); + spin_lock(&block_rsv->lock); block_rsv->size = num_bytes; @@ -4245,8 +4241,8 @@ static void update_global_block_rsv(struct btrfs_fs_info *fs_info) block_rsv->full = 1; } - spin_unlock(&sinfo->lock); spin_unlock(&block_rsv->lock); + spin_unlock(&sinfo->lock); } static void init_global_block_rsv(struct btrfs_fs_info *fs_info) @@ -6572,7 +6568,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, goto skip; } - if (!btrfs_buffer_uptodate(next, generation)) { + if (!btrfs_buffer_uptodate(next, generation, 0)) { btrfs_tree_unlock(next); free_extent_buffer(next); next = NULL; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 8d904dd7ea9f..c9018a05036e 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -402,20 +402,28 @@ static int split_state(struct extent_io_tree *tree, struct extent_state *orig, return 0; } +static struct extent_state *next_state(struct extent_state *state) +{ + struct rb_node *next = rb_next(&state->rb_node); + if (next) + return rb_entry(next, struct extent_state, rb_node); + else + return NULL; +} + /* * utility function to clear some bits in an extent state struct. - * it will optionally wake up any one waiting on this state (wake == 1), or - * forcibly remove the state from the tree (delete == 1). + * it will optionally wake up any one waiting on this state (wake == 1) * * If no bits are set on the state struct after clearing things, the * struct is freed and removed from the tree */ -static int clear_state_bit(struct extent_io_tree *tree, - struct extent_state *state, - int *bits, int wake) +static struct extent_state *clear_state_bit(struct extent_io_tree *tree, + struct extent_state *state, + int *bits, int wake) { + struct extent_state *next; int bits_to_clear = *bits & ~EXTENT_CTLBITS; - int ret = state->state & bits_to_clear; if ((bits_to_clear & EXTENT_DIRTY) && (state->state & EXTENT_DIRTY)) { u64 range = state->end - state->start + 1; @@ -427,6 +435,7 @@ static int clear_state_bit(struct extent_io_tree *tree, if (wake) wake_up(&state->wq); if (state->state == 0) { + next = next_state(state); if (state->tree) { rb_erase(&state->rb_node, &tree->state); state->tree = NULL; @@ -436,8 +445,9 @@ static int clear_state_bit(struct extent_io_tree *tree, } } else { merge_state(tree, state); + next = next_state(state); } - return ret; + return next; } static struct extent_state * @@ -476,7 +486,6 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, struct extent_state *state; struct extent_state *cached; struct extent_state *prealloc = NULL; - struct rb_node *next_node; struct rb_node *node; u64 last_end; int err; @@ -528,14 +537,11 @@ hit_next: WARN_ON(state->end < start); last_end = state->end; - if (state->end < end && !need_resched()) - next_node = rb_next(&state->rb_node); - else - next_node = NULL; - /* the state doesn't have the wanted bits, go ahead */ - if (!(state->state & bits)) + if (!(state->state & bits)) { + state = next_state(state); goto next; + } /* * | ---- desired range ---- | @@ -593,16 +599,13 @@ hit_next: goto out; } - clear_state_bit(tree, state, &bits, wake); + state = clear_state_bit(tree, state, &bits, wake); next: if (last_end == (u64)-1) goto out; start = last_end + 1; - if (start <= end && next_node) { - state = rb_entry(next_node, struct extent_state, - rb_node); + if (start <= end && state && !need_resched()) goto hit_next; - } goto search_again; out: @@ -1937,7 +1940,7 @@ int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb, struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree; u64 start = eb->start; unsigned long i, num_pages = num_extent_pages(eb->start, eb->len); - int ret; + int ret = 0; for (i = 0; i < num_pages; i++) { struct page *p = extent_buffer_page(eb, i); @@ -2180,6 +2183,10 @@ static int bio_readpage_error(struct bio *failed_bio, struct page *page, } bio = bio_alloc(GFP_NOFS, 1); + if (!bio) { + free_io_failure(inode, failrec, 0); + return -EIO; + } bio->bi_private = state; bio->bi_end_io = failed_bio->bi_end_io; bio->bi_sector = failrec->logical >> 9; @@ -2297,7 +2304,7 @@ static void end_bio_extent_readpage(struct bio *bio, int err) u64 start; u64 end; int whole_page; - int failed_mirror; + int mirror; int ret; if (err) @@ -2336,20 +2343,18 @@ static void end_bio_extent_readpage(struct bio *bio, int err) } spin_unlock(&tree->lock); + mirror = (int)(unsigned long)bio->bi_bdev; if (uptodate && tree->ops && tree->ops->readpage_end_io_hook) { ret = tree->ops->readpage_end_io_hook(page, start, end, - state); + state, mirror); if (ret) uptodate = 0; else clean_io_failure(start, page); } - if (!uptodate) - failed_mirror = (int)(unsigned long)bio->bi_bdev; - if (!uptodate && tree->ops && tree->ops->readpage_io_failed_hook) { - ret = tree->ops->readpage_io_failed_hook(page, failed_mirror); + ret = tree->ops->readpage_io_failed_hook(page, mirror); if (!ret && !err && test_bit(BIO_UPTODATE, &bio->bi_flags)) uptodate = 1; @@ -2364,8 +2369,7 @@ static void end_bio_extent_readpage(struct bio *bio, int err) * can't handle the error it will return -EIO and we * remain responsible for that page. */ - ret = bio_readpage_error(bio, page, start, end, - failed_mirror, NULL); + ret = bio_readpage_error(bio, page, start, end, mirror, NULL); if (ret == 0) { uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); @@ -4116,6 +4120,7 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, if (atomic_inc_not_zero(&exists->refs)) { spin_unlock(&mapping->private_lock); unlock_page(p); + page_cache_release(p); mark_extent_buffer_accessed(exists); goto free_eb; } @@ -4195,8 +4200,7 @@ free_eb: unlock_page(eb->pages[i]); } - if (!atomic_dec_and_test(&eb->refs)) - return exists; + WARN_ON(!atomic_dec_and_test(&eb->refs)); btrfs_release_extent_buffer(eb); return exists; } @@ -4458,7 +4462,7 @@ int read_extent_buffer_pages(struct extent_io_tree *tree, } clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags); - eb->failed_mirror = 0; + eb->read_mirror = 0; atomic_set(&eb->io_pages, num_reads); for (i = start_i; i < num_pages; i++) { page = extent_buffer_page(eb, i); diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index faf10eb57f75..b516c3b8dec6 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -79,7 +79,7 @@ struct extent_io_ops { u64 start, u64 end, struct extent_state *state); int (*readpage_end_io_hook)(struct page *page, u64 start, u64 end, - struct extent_state *state); + struct extent_state *state, int mirror); int (*writepage_end_io_hook)(struct page *page, u64 start, u64 end, struct extent_state *state, int uptodate); void (*set_bit_hook)(struct inode *inode, struct extent_state *state, @@ -135,7 +135,7 @@ struct extent_buffer { spinlock_t refs_lock; atomic_t refs; atomic_t io_pages; - int failed_mirror; + int read_mirror; struct list_head leak_list; struct rcu_head rcu_head; pid_t lock_owner; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index d83260d7498f..53bf2d764bbc 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -567,6 +567,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct inode *inode, int extent_type; int recow; int ret; + int modify_tree = -1; if (drop_cache) btrfs_drop_extent_cache(inode, start, end - 1, 0); @@ -575,10 +576,13 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct inode *inode, if (!path) return -ENOMEM; + if (start >= BTRFS_I(inode)->disk_i_size) + modify_tree = 0; + while (1) { recow = 0; ret = btrfs_lookup_file_extent(trans, root, path, ino, - search_start, -1); + search_start, modify_tree); if (ret < 0) break; if (ret > 0 && path->slots[0] > 0 && search_start == start) { @@ -634,7 +638,8 @@ next_slot: } search_start = max(key.offset, start); - if (recow) { + if (recow || !modify_tree) { + modify_tree = -1; btrfs_release_path(path); continue; } diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index e88330d3df52..202008ec367d 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -748,13 +748,6 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info, u64 used = btrfs_block_group_used(&block_group->item); /* - * If we're unmounting then just return, since this does a search on the - * normal root and not the commit root and we could deadlock. - */ - if (btrfs_fs_closing(fs_info)) - return 0; - - /* * If this block group has been marked to be cleared for one reason or * another then we can't trust the on disk cache, so just return. */ @@ -768,6 +761,8 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info, path = btrfs_alloc_path(); if (!path) return 0; + path->search_commit_root = 1; + path->skip_locking = 1; inode = lookup_free_space_inode(root, block_group, path); if (IS_ERR(inode)) { diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 115bc05e42b0..61b16c641ce0 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1947,7 +1947,7 @@ static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end, * extent_io.c will try to find good copies for us. */ static int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end, - struct extent_state *state) + struct extent_state *state, int mirror) { size_t offset = start - ((u64)page->index << PAGE_CACHE_SHIFT); struct inode *inode = page->mapping->host; @@ -4069,7 +4069,7 @@ static struct inode *new_simple_dir(struct super_block *s, BTRFS_I(inode)->dummy_inode = 1; inode->i_ino = BTRFS_EMPTY_SUBVOL_DIR_OBJECTID; - inode->i_op = &simple_dir_inode_operations; + inode->i_op = &btrfs_dir_ro_inode_operations; inode->i_fop = &simple_dir_operations; inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; @@ -4140,14 +4140,18 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) static int btrfs_dentry_delete(const struct dentry *dentry) { struct btrfs_root *root; + struct inode *inode = dentry->d_inode; - if (!dentry->d_inode && !IS_ROOT(dentry)) - dentry = dentry->d_parent; + if (!inode && !IS_ROOT(dentry)) + inode = dentry->d_parent->d_inode; - if (dentry->d_inode) { - root = BTRFS_I(dentry->d_inode)->root; + if (inode) { + root = BTRFS_I(inode)->root; if (btrfs_root_refs(&root->root_item) == 0) return 1; + + if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) + return 1; } return 0; } @@ -4188,7 +4192,6 @@ static int btrfs_real_readdir(struct file *filp, void *dirent, struct btrfs_path *path; struct list_head ins_list; struct list_head del_list; - struct qstr q; int ret; struct extent_buffer *leaf; int slot; @@ -4279,7 +4282,6 @@ static int btrfs_real_readdir(struct file *filp, void *dirent, while (di_cur < di_total) { struct btrfs_key location; - struct dentry *tmp; if (verify_dir_item(root, leaf, di)) break; @@ -4300,35 +4302,15 @@ static int btrfs_real_readdir(struct file *filp, void *dirent, d_type = btrfs_filetype_table[btrfs_dir_type(leaf, di)]; btrfs_dir_item_key_to_cpu(leaf, di, &location); - q.name = name_ptr; - q.len = name_len; - q.hash = full_name_hash(q.name, q.len); - tmp = d_lookup(filp->f_dentry, &q); - if (!tmp) { - struct btrfs_key *newkey; - - newkey = kzalloc(sizeof(struct btrfs_key), - GFP_NOFS); - if (!newkey) - goto no_dentry; - tmp = d_alloc(filp->f_dentry, &q); - if (!tmp) { - kfree(newkey); - dput(tmp); - goto no_dentry; - } - memcpy(newkey, &location, - sizeof(struct btrfs_key)); - tmp->d_fsdata = newkey; - tmp->d_flags |= DCACHE_NEED_LOOKUP; - d_rehash(tmp); - dput(tmp); - } else { - dput(tmp); - } -no_dentry: + /* is this a reference to our own snapshot? If so - * skip it + * skip it. + * + * In contrast to old kernels, we insert the snapshot's + * dir item and dir index after it has been created, so + * we won't find a reference to our own snapshot. We + * still keep the following code for backward + * compatibility. */ if (location.type == BTRFS_ROOT_ITEM_KEY && location.objectid == root->root_key.objectid) { diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 18cc23d164a8..14f8e1faa46e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2262,7 +2262,10 @@ static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg) di_args->bytes_used = dev->bytes_used; di_args->total_bytes = dev->total_bytes; memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid)); - strncpy(di_args->path, dev->name, sizeof(di_args->path)); + if (dev->name) + strncpy(di_args->path, dev->name, sizeof(di_args->path)); + else + di_args->path[0] = '\0'; out: if (ret == 0 && copy_to_user(arg, di_args, sizeof(*di_args))) diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index 4f69028a68c4..086e6bdae1c4 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h @@ -252,7 +252,7 @@ struct btrfs_data_container { struct btrfs_ioctl_ino_path_args { __u64 inum; /* in */ - __u32 size; /* in */ + __u64 size; /* in */ __u64 reserved[4]; /* struct btrfs_data_container *fspath; out */ __u64 fspath; /* out */ @@ -260,7 +260,7 @@ struct btrfs_ioctl_ino_path_args { struct btrfs_ioctl_logical_ino_args { __u64 logical; /* in */ - __u32 size; /* in */ + __u64 size; /* in */ __u64 reserved[4]; /* struct btrfs_data_container *inodes; out */ __u64 inodes; diff --git a/fs/btrfs/reada.c b/fs/btrfs/reada.c index dc5d33146fdb..ac5d01085884 100644 --- a/fs/btrfs/reada.c +++ b/fs/btrfs/reada.c @@ -250,14 +250,12 @@ static struct reada_zone *reada_find_zone(struct btrfs_fs_info *fs_info, struct btrfs_bio *bbio) { int ret; - int looped = 0; struct reada_zone *zone; struct btrfs_block_group_cache *cache = NULL; u64 start; u64 end; int i; -again: zone = NULL; spin_lock(&fs_info->reada_lock); ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone, @@ -274,9 +272,6 @@ again: spin_unlock(&fs_info->reada_lock); } - if (looped) - return NULL; - cache = btrfs_lookup_block_group(fs_info, logical); if (!cache) return NULL; @@ -307,13 +302,15 @@ again: ret = radix_tree_insert(&dev->reada_zones, (unsigned long)(zone->end >> PAGE_CACHE_SHIFT), zone); - spin_unlock(&fs_info->reada_lock); - if (ret) { + if (ret == -EEXIST) { kfree(zone); - looped = 1; - goto again; + ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone, + logical >> PAGE_CACHE_SHIFT, 1); + if (ret == 1) + kref_get(&zone->refcnt); } + spin_unlock(&fs_info->reada_lock); return zone; } @@ -323,26 +320,26 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root, struct btrfs_key *top, int level) { int ret; - int looped = 0; struct reada_extent *re = NULL; + struct reada_extent *re_exist = NULL; struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree; struct btrfs_bio *bbio = NULL; struct btrfs_device *dev; + struct btrfs_device *prev_dev; u32 blocksize; u64 length; int nzones = 0; int i; unsigned long index = logical >> PAGE_CACHE_SHIFT; -again: spin_lock(&fs_info->reada_lock); re = radix_tree_lookup(&fs_info->reada_tree, index); if (re) kref_get(&re->refcnt); spin_unlock(&fs_info->reada_lock); - if (re || looped) + if (re) return re; re = kzalloc(sizeof(*re), GFP_NOFS); @@ -398,16 +395,31 @@ again: /* insert extent in reada_tree + all per-device trees, all or nothing */ spin_lock(&fs_info->reada_lock); ret = radix_tree_insert(&fs_info->reada_tree, index, re); + if (ret == -EEXIST) { + re_exist = radix_tree_lookup(&fs_info->reada_tree, index); + BUG_ON(!re_exist); + kref_get(&re_exist->refcnt); + spin_unlock(&fs_info->reada_lock); + goto error; + } if (ret) { spin_unlock(&fs_info->reada_lock); - if (ret != -ENOMEM) { - /* someone inserted the extent in the meantime */ - looped = 1; - } goto error; } + prev_dev = NULL; for (i = 0; i < nzones; ++i) { dev = bbio->stripes[i].dev; + if (dev == prev_dev) { + /* + * in case of DUP, just add the first zone. As both + * are on the same device, there's nothing to gain + * from adding both. + * Also, it wouldn't work, as the tree is per device + * and adding would fail with EEXIST + */ + continue; + } + prev_dev = dev; ret = radix_tree_insert(&dev->reada_extents, index, re); if (ret) { while (--i >= 0) { @@ -450,9 +462,7 @@ error: } kfree(bbio); kfree(re); - if (looped) - goto again; - return NULL; + return re_exist; } static void reada_kref_dummy(struct kref *kr) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 017281dbb2a7..646ee21bb035 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1279,7 +1279,9 @@ static int __update_reloc_root(struct btrfs_root *root, int del) if (rb_node) backref_tree_panic(rb_node, -EEXIST, node->bytenr); } else { + spin_lock(&root->fs_info->trans_lock); list_del_init(&root->root_list); + spin_unlock(&root->fs_info->trans_lock); kfree(node); } return 0; @@ -3811,7 +3813,7 @@ restart: ret = btrfs_block_rsv_check(rc->extent_root, rc->block_rsv, 5); if (ret < 0) { - if (ret != -EAGAIN) { + if (ret != -ENOSPC) { err = ret; WARN_ON(1); break; diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 90acc82046c3..2f3d6f917fb3 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -998,6 +998,7 @@ static int scrub_setup_recheck_block(struct scrub_dev *sdev, page = sblock->pagev + page_index; page->logical = logical; page->physical = bbio->stripes[mirror_index].physical; + /* for missing devices, bdev is NULL */ page->bdev = bbio->stripes[mirror_index].dev->bdev; page->mirror_num = mirror_index + 1; page->page = alloc_page(GFP_NOFS); @@ -1042,8 +1043,16 @@ static int scrub_recheck_block(struct btrfs_fs_info *fs_info, struct scrub_page *page = sblock->pagev + page_num; DECLARE_COMPLETION_ONSTACK(complete); + if (page->bdev == NULL) { + page->io_error = 1; + sblock->no_io_error_seen = 0; + continue; + } + BUG_ON(!page->page); bio = bio_alloc(GFP_NOFS, 1); + if (!bio) + return -EIO; bio->bi_bdev = page->bdev; bio->bi_sector = page->physical >> 9; bio->bi_end_io = scrub_complete_bio_end_io; @@ -1171,6 +1180,8 @@ static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad, DECLARE_COMPLETION_ONSTACK(complete); bio = bio_alloc(GFP_NOFS, 1); + if (!bio) + return -EIO; bio->bi_bdev = page_bad->bdev; bio->bi_sector = page_bad->physical >> 9; bio->bi_end_io = scrub_complete_bio_end_io; @@ -1253,12 +1264,6 @@ static int scrub_checksum_data(struct scrub_block *sblock) if (memcmp(csum, on_disk_csum, sdev->csum_size)) fail = 1; - if (fail) { - spin_lock(&sdev->stat_lock); - ++sdev->stat.csum_errors; - spin_unlock(&sdev->stat_lock); - } - return fail; } @@ -1331,15 +1336,6 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock) if (memcmp(calculated_csum, on_disk_csum, sdev->csum_size)) ++crc_fail; - if (crc_fail || fail) { - spin_lock(&sdev->stat_lock); - if (crc_fail) - ++sdev->stat.csum_errors; - if (fail) - ++sdev->stat.verify_errors; - spin_unlock(&sdev->stat_lock); - } - return fail || crc_fail; } diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 8d5d380f7bdb..c5f8fca4195f 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -815,7 +815,6 @@ int btrfs_sync_fs(struct super_block *sb, int wait) return 0; } - btrfs_start_delalloc_inodes(root, 0); btrfs_wait_ordered_extents(root, 0, 0); trans = btrfs_start_transaction(root, 0); @@ -1148,13 +1147,15 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) if (ret) goto restore; } else { - if (fs_info->fs_devices->rw_devices == 0) + if (fs_info->fs_devices->rw_devices == 0) { ret = -EACCES; goto restore; + } - if (btrfs_super_log_root(fs_info->super_copy) != 0) + if (btrfs_super_log_root(fs_info->super_copy) != 0) { ret = -EINVAL; goto restore; + } ret = btrfs_cleanup_fs_roots(fs_info); if (ret) diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 8da29e8e4de1..36422254ef67 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -73,8 +73,10 @@ loop: cur_trans = root->fs_info->running_transaction; if (cur_trans) { - if (cur_trans->aborted) + if (cur_trans->aborted) { + spin_unlock(&root->fs_info->trans_lock); return cur_trans->aborted; + } atomic_inc(&cur_trans->use_count); atomic_inc(&cur_trans->num_writers); cur_trans->num_joined++; @@ -480,6 +482,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, struct btrfs_transaction *cur_trans = trans->transaction; struct btrfs_fs_info *info = root->fs_info; int count = 0; + int err = 0; if (--trans->use_count) { trans->block_rsv = trans->orig_rsv; @@ -532,18 +535,18 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, if (current->journal_info == trans) current->journal_info = NULL; - memset(trans, 0, sizeof(*trans)); - kmem_cache_free(btrfs_trans_handle_cachep, trans); if (throttle) btrfs_run_delayed_iputs(root); if (trans->aborted || root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) { - return -EIO; + err = -EIO; } - return 0; + memset(trans, 0, sizeof(*trans)); + kmem_cache_free(btrfs_trans_handle_cachep, trans); + return err; } int btrfs_end_transaction(struct btrfs_trans_handle *trans, @@ -1399,6 +1402,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ret = commit_fs_roots(trans, root); if (ret) { mutex_unlock(&root->fs_info->tree_log_mutex); + mutex_unlock(&root->fs_info->reloc_mutex); goto cleanup_transaction; } @@ -1410,6 +1414,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ret = commit_cowonly_roots(trans, root); if (ret) { mutex_unlock(&root->fs_info->tree_log_mutex); + mutex_unlock(&root->fs_info->reloc_mutex); goto cleanup_transaction; } diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index d017283ae6f5..eb1ae908582c 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -279,7 +279,7 @@ static int process_one_buffer(struct btrfs_root *log, log->fs_info->extent_root, eb->start, eb->len); - if (btrfs_buffer_uptodate(eb, gen)) { + if (btrfs_buffer_uptodate(eb, gen, 0)) { if (wc->write) btrfs_write_tree_block(eb); if (wc->wait) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index a872b48be0ae..1411b99555a4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3324,12 +3324,14 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, stripe_size = devices_info[ndevs-1].max_avail; num_stripes = ndevs * dev_stripes; - if (stripe_size * num_stripes > max_chunk_size * ncopies) { + if (stripe_size * ndevs > max_chunk_size * ncopies) { stripe_size = max_chunk_size * ncopies; - do_div(stripe_size, num_stripes); + do_div(stripe_size, ndevs); } do_div(stripe_size, dev_stripes); + + /* align to BTRFS_STRIPE_LEN */ do_div(stripe_size, BTRFS_STRIPE_LEN); stripe_size *= BTRFS_STRIPE_LEN; @@ -3805,10 +3807,11 @@ static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, else if (mirror_num) stripe_index += mirror_num - 1; else { + int old_stripe_index = stripe_index; stripe_index = find_live_mirror(map, stripe_index, map->sub_stripes, stripe_index + current->pid % map->sub_stripes); - mirror_num = stripe_index + 1; + mirror_num = stripe_index - old_stripe_index + 1; } } else { /* @@ -3833,6 +3836,7 @@ static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, int sub_stripes = 0; u64 stripes_per_dev = 0; u32 remaining_stripes = 0; + u32 last_stripe = 0; if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID10)) { @@ -3846,6 +3850,8 @@ static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, stripe_nr_orig, factor, &remaining_stripes); + div_u64_rem(stripe_nr_end - 1, factor, &last_stripe); + last_stripe *= sub_stripes; } for (i = 0; i < num_stripes; i++) { @@ -3858,16 +3864,29 @@ static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, BTRFS_BLOCK_GROUP_RAID10)) { bbio->stripes[i].length = stripes_per_dev * map->stripe_len; + if (i / sub_stripes < remaining_stripes) bbio->stripes[i].length += map->stripe_len; + + /* + * Special for the first stripe and + * the last stripe: + * + * |-------|...|-------| + * |----------| + * off end_off + */ if (i < sub_stripes) bbio->stripes[i].length -= stripe_offset; - if ((i / sub_stripes + 1) % - sub_stripes == remaining_stripes) + + if (stripe_index >= last_stripe && + stripe_index <= (last_stripe + + sub_stripes - 1)) bbio->stripes[i].length -= stripe_end_offset; + if (i == sub_stripes - 1) stripe_offset = 0; } else @@ -4334,8 +4353,10 @@ static int open_seed_devices(struct btrfs_root *root, u8 *fsid) ret = __btrfs_open_devices(fs_devices, FMODE_READ, root->fs_info->bdev_holder); - if (ret) + if (ret) { + free_fs_devices(fs_devices); goto out; + } if (!fs_devices->seeding) { __btrfs_close_devices(fs_devices); diff --git a/fs/buffer.c b/fs/buffer.c index 36d66653b931..351e18ea2e53 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -985,7 +985,6 @@ grow_dev_page(struct block_device *bdev, sector_t block, return page; failed: - BUG(); unlock_page(page); page_cache_release(page); return NULL; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index d34212822444..541ef81f6ae8 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -370,13 +370,13 @@ cifs_show_options(struct seq_file *s, struct dentry *root) (int)(srcaddr->sa_family)); } - seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); + seq_printf(s, ",uid=%u", cifs_sb->mnt_uid); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) seq_printf(s, ",forceuid"); else seq_printf(s, ",noforceuid"); - seq_printf(s, ",gid=%d", cifs_sb->mnt_gid); + seq_printf(s, ",gid=%u", cifs_sb->mnt_gid); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) seq_printf(s, ",forcegid"); else @@ -434,11 +434,15 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",noperm"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) seq_printf(s, ",strictcache"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) + seq_printf(s, ",backupuid=%u", cifs_sb->mnt_backupuid); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) + seq_printf(s, ",backupgid=%u", cifs_sb->mnt_backupgid); - seq_printf(s, ",rsize=%d", cifs_sb->rsize); - seq_printf(s, ",wsize=%d", cifs_sb->wsize); + seq_printf(s, ",rsize=%u", cifs_sb->rsize); + seq_printf(s, ",wsize=%u", cifs_sb->wsize); /* convert actimeo and display it in seconds */ - seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); + seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); return 0; } @@ -695,7 +699,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) * origin == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate * the cached file length */ - if (origin != SEEK_SET || origin != SEEK_CUR) { + if (origin != SEEK_SET && origin != SEEK_CUR) { int rc; struct inode *inode = file->f_path.dentry->d_inode; diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index d1389bb33ceb..65365358c976 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -125,5 +125,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION "1.77" +#define CIFS_VERSION "1.78" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 8fecc99be344..da2f5446fa7a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3892,13 +3892,12 @@ CIFSSMBSetCIFSACL(const int xid, struct cifs_tcon *tcon, __u16 fid, int rc = 0; int bytes_returned = 0; SET_SEC_DESC_REQ *pSMB = NULL; - NTRANSACT_RSP *pSMBr = NULL; + void *pSMBr; setCifsAclRetry: - rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, - (void **) &pSMBr); + rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr); if (rc) - return (rc); + return rc; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; @@ -3926,9 +3925,8 @@ setCifsAclRetry: pSMB->AclFlags = cpu_to_le32(aclflag); if (pntsd && acllen) { - memcpy((char *) &pSMBr->hdr.Protocol + data_offset, - (char *) pntsd, - acllen); + memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) + + data_offset, pntsd, acllen); inc_rfc1001_len(pSMB, byte_count + data_count); } else inc_rfc1001_len(pSMB, byte_count); @@ -4846,8 +4844,12 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, max_len = data_end - temp; node->node_name = cifs_strndup_from_utf16(temp, max_len, is_unicode, nls_codepage); - if (!node->node_name) + if (!node->node_name) { rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + + ref++; } parse_DFS_referrals_exit: @@ -5708,7 +5710,8 @@ CIFSSMBSetFileInfo(const int xid, struct cifs_tcon *tcon, param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; + data_offset = (char *)pSMB + + offsetof(struct smb_hdr, Protocol) + offset; count = sizeof(FILE_BASIC_INFO); pSMB->MaxParameterCount = cpu_to_le16(2); @@ -5977,7 +5980,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon, u16 fid, u32 pid_of_opener) { struct smb_com_transaction2_sfi_req *pSMB = NULL; - FILE_UNIX_BASIC_INFO *data_offset; + char *data_offset; int rc = 0; u16 params, param_offset, offset, byte_count, count; @@ -5999,8 +6002,9 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon, param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; - data_offset = (FILE_UNIX_BASIC_INFO *) - ((char *)(&pSMB->hdr.Protocol) + offset); + data_offset = (char *)pSMB + + offsetof(struct smb_hdr, Protocol) + offset; + count = sizeof(FILE_UNIX_BASIC_INFO); pSMB->MaxParameterCount = cpu_to_le16(2); @@ -6022,7 +6026,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon, inc_rfc1001_len(pSMB, byte_count); pSMB->ByteCount = cpu_to_le16(byte_count); - cifs_fill_unix_set_info(data_offset, args); + cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); if (rc) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 302a15c505a9..5dcc55197fb3 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -109,6 +109,8 @@ enum { /* Options which could be blank */ Opt_blank_pass, + Opt_blank_user, + Opt_blank_ip, Opt_err }; @@ -183,11 +185,15 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_wsize, "wsize=%s" }, { Opt_actimeo, "actimeo=%s" }, + { Opt_blank_user, "user=" }, + { Opt_blank_user, "username=" }, { Opt_user, "user=%s" }, { Opt_user, "username=%s" }, { Opt_blank_pass, "pass=" }, { Opt_pass, "pass=%s" }, { Opt_pass, "password=%s" }, + { Opt_blank_ip, "ip=" }, + { Opt_blank_ip, "addr=" }, { Opt_ip, "ip=%s" }, { Opt_ip, "addr=%s" }, { Opt_unc, "unc=%s" }, @@ -209,6 +215,8 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_ignore, "cred" }, { Opt_ignore, "credentials" }, + { Opt_ignore, "cred=%s" }, + { Opt_ignore, "credentials=%s" }, { Opt_ignore, "guest" }, { Opt_ignore, "rw" }, { Opt_ignore, "ro" }, @@ -1117,7 +1125,7 @@ static int get_option_ul(substring_t args[], unsigned long *option) string = match_strdup(args); if (string == NULL) return -ENOMEM; - rc = kstrtoul(string, 10, option); + rc = kstrtoul(string, 0, option); kfree(string); return rc; @@ -1534,15 +1542,17 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, /* String Arguments */ + case Opt_blank_user: + /* null user, ie. anonymous authentication */ + vol->nullauth = 1; + vol->username = NULL; + break; case Opt_user: string = match_strdup(args); if (string == NULL) goto out_nomem; - if (!*string) { - /* null user, ie. anonymous authentication */ - vol->nullauth = 1; - } else if (strnlen(string, MAX_USERNAME_SIZE) > + if (strnlen(string, MAX_USERNAME_SIZE) > MAX_USERNAME_SIZE) { printk(KERN_WARNING "CIFS: username too long\n"); goto cifs_parse_mount_err; @@ -1565,8 +1575,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, /* Obtain the value string */ value = strchr(data, '='); - if (value != NULL) - *value++ = '\0'; + value++; /* Set tmp_end to end of the string */ tmp_end = (char *) value + strlen(value); @@ -1612,14 +1621,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, } vol->password[j] = '\0'; break; + case Opt_blank_ip: + vol->UNCip = NULL; + break; case Opt_ip: string = match_strdup(args); if (string == NULL) goto out_nomem; - if (!*string) { - vol->UNCip = NULL; - } else if (strnlen(string, INET6_ADDRSTRLEN) > + if (strnlen(string, INET6_ADDRSTRLEN) > INET6_ADDRSTRLEN) { printk(KERN_WARNING "CIFS: ip address " "too long\n"); @@ -1637,18 +1647,19 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: invalid path to " - "network resource\n"); - goto cifs_parse_mount_err; - } - temp_len = strnlen(string, 300); if (temp_len == 300) { printk(KERN_WARNING "CIFS: UNC name too long\n"); goto cifs_parse_mount_err; } + vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); + if (vol->UNC == NULL) { + printk(KERN_WARNING "CIFS: no memory for UNC\n"); + goto cifs_parse_mount_err; + } + strcpy(vol->UNC, string); + if (strncmp(string, "//", 2) == 0) { vol->UNC[0] = '\\'; vol->UNC[1] = '\\'; @@ -1658,24 +1669,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, goto cifs_parse_mount_err; } - vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); - if (vol->UNC == NULL) { - printk(KERN_WARNING "CIFS: no memory " - "for UNC\n"); - goto cifs_parse_mount_err; - } - strcpy(vol->UNC, string); break; case Opt_domain: string = match_strdup(args); if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: invalid domain" - " name\n"); - goto cifs_parse_mount_err; - } else if (strnlen(string, 256) == 256) { + if (strnlen(string, 256) == 256) { printk(KERN_WARNING "CIFS: domain name too" " long\n"); goto cifs_parse_mount_err; @@ -1694,11 +1694,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: srcaddr value not" - " specified\n"); - goto cifs_parse_mount_err; - } else if (!cifs_convert_address( + if (!cifs_convert_address( (struct sockaddr *)&vol->srcaddr, string, strlen(string))) { printk(KERN_WARNING "CIFS: Could not parse" @@ -1711,11 +1707,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: Invalid path" - " prefix\n"); - goto cifs_parse_mount_err; - } temp_len = strnlen(string, 1024); if (string[0] != '/') temp_len++; /* missing leading slash */ @@ -1743,11 +1734,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: Invalid iocharset" - " specified\n"); - goto cifs_parse_mount_err; - } else if (strnlen(string, 1024) >= 65) { + if (strnlen(string, 1024) >= 65) { printk(KERN_WARNING "CIFS: iocharset name " "too long.\n"); goto cifs_parse_mount_err; @@ -1772,11 +1759,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: No socket option" - " specified\n"); - goto cifs_parse_mount_err; - } if (strnicmp(string, "TCP_NODELAY", 11) == 0) vol->sockopt_tcp_nodelay = 1; break; @@ -1785,12 +1767,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: Invalid (empty)" - " netbiosname\n"); - break; - } - memset(vol->source_rfc1001_name, 0x20, RFC1001_NAME_LEN); /* @@ -1818,11 +1794,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: Empty server" - " netbiosname specified\n"); - break; - } /* last byte, type, is 0x20 for servr type */ memset(vol->target_rfc1001_name, 0x20, RFC1001_NAME_LEN_WITH_NULL); @@ -1849,12 +1820,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - cERROR(1, "no protocol version specified" - " after vers= mount option"); - goto cifs_parse_mount_err; - } - if (strnicmp(string, "cifs", 4) == 0 || strnicmp(string, "1", 1) == 0) { /* This is the default */ @@ -1869,12 +1834,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (!*string) { - printk(KERN_WARNING "CIFS: no security flavor" - " specified\n"); - break; - } - if (cifs_parse_security_flavors(string, vol) != 0) goto cifs_parse_mount_err; break; @@ -2226,6 +2185,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) tcp_ses->session_estab = false; tcp_ses->sequence_number = 0; tcp_ses->lstrp = jiffies; + spin_lock_init(&tcp_ses->req_lock); INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list); INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); @@ -3271,10 +3231,6 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->mnt_uid = pvolume_info->linux_uid; cifs_sb->mnt_gid = pvolume_info->linux_gid; - if (pvolume_info->backupuid_specified) - cifs_sb->mnt_backupuid = pvolume_info->backupuid; - if (pvolume_info->backupgid_specified) - cifs_sb->mnt_backupgid = pvolume_info->backupgid; cifs_sb->mnt_file_mode = pvolume_info->file_mode; cifs_sb->mnt_dir_mode = pvolume_info->dir_mode; cFYI(1, "file mode: 0x%hx dir mode: 0x%hx", @@ -3305,10 +3261,14 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD; if (pvolume_info->cifs_acl) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; - if (pvolume_info->backupuid_specified) + if (pvolume_info->backupuid_specified) { cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID; - if (pvolume_info->backupgid_specified) + cifs_sb->mnt_backupuid = pvolume_info->backupuid; + } + if (pvolume_info->backupgid_specified) { cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID; + cifs_sb->mnt_backupgid = pvolume_info->backupgid; + } if (pvolume_info->override_uid) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; if (pvolume_info->override_gid) @@ -3657,22 +3617,6 @@ cifs_get_volume_info(char *mount_data, const char *devname) return volume_info; } -/* make sure ra_pages is a multiple of rsize */ -static inline unsigned int -cifs_ra_pages(struct cifs_sb_info *cifs_sb) -{ - unsigned int reads; - unsigned int rsize_pages = cifs_sb->rsize / PAGE_CACHE_SIZE; - - if (rsize_pages >= default_backing_dev_info.ra_pages) - return default_backing_dev_info.ra_pages; - else if (rsize_pages == 0) - return rsize_pages; - - reads = default_backing_dev_info.ra_pages / rsize_pages; - return reads * rsize_pages; -} - int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) { @@ -3760,7 +3704,7 @@ try_mount_again: cifs_sb->rsize = cifs_negotiate_rsize(tcon, volume_info); /* tune readahead according to rsize */ - cifs_sb->bdi.ra_pages = cifs_ra_pages(cifs_sb); + cifs_sb->bdi.ra_pages = cifs_sb->rsize / PAGE_CACHE_SIZE; remote_path_check: #ifdef CONFIG_CIFS_DFS_UPCALL diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index d172c8ed9017..ec4e9a2a12f8 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -668,12 +668,19 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) return 0; else { /* - * Forcibly invalidate automounting directory inodes - * (remote DFS directories) so to have them - * instantiated again for automount + * If the inode wasn't known to be a dfs entry when + * the dentry was instantiated, such as when created + * via ->readdir(), it needs to be set now since the + * attributes will have been updated by + * cifs_revalidate_dentry(). */ - if (IS_AUTOMOUNT(direntry->d_inode)) - return 0; + if (IS_AUTOMOUNT(direntry->d_inode) && + !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) { + spin_lock(&direntry->d_lock); + direntry->d_flags |= DCACHE_NEED_AUTOMOUNT; + spin_unlock(&direntry->d_lock); + } + return 1; } } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 460d87b7cda0..81725e9286e9 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -835,13 +835,21 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock) if ((flock->fl_flags & FL_POSIX) == 0) return rc; +try_again: mutex_lock(&cinode->lock_mutex); if (!cinode->can_cache_brlcks) { mutex_unlock(&cinode->lock_mutex); return rc; } - rc = posix_lock_file_wait(file, flock); + + rc = posix_lock_file(file, flock, NULL); mutex_unlock(&cinode->lock_mutex); + if (rc == FILE_LOCK_DEFERRED) { + rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next); + if (!rc) + goto try_again; + locks_delete_block(flock); + } return rc; } @@ -2170,7 +2178,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, unsigned long nr_pages, i; size_t copied, len, cur_len; ssize_t total_written = 0; - loff_t offset = *poffset; + loff_t offset; struct iov_iter it; struct cifsFileInfo *open_file; struct cifs_tcon *tcon; @@ -2192,6 +2200,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); open_file = file->private_data; tcon = tlink_tcon(open_file->tlink); + offset = *poffset; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) pid = open_file->pid; diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index dd23a321bdda..581c225f7f50 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -197,8 +197,7 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len) memcpy(scope_id, pct + 1, slen); scope_id[slen] = '\0'; - rc = strict_strtoul(scope_id, 0, - (unsigned long *)&s6->sin6_scope_id); + rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id); rc = (rc == 0) ? 1 : 0; } diff --git a/fs/dcache.c b/fs/dcache.c index b60ddc41d783..b80531c91779 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -141,18 +141,29 @@ int proc_nr_dentry(ctl_table *table, int write, void __user *buffer, * Compare 2 name strings, return 0 if they match, otherwise non-zero. * The strings are both count bytes long, and count is non-zero. */ +#ifdef CONFIG_DCACHE_WORD_ACCESS + +#include <asm/word-at-a-time.h> +/* + * NOTE! 'cs' and 'scount' come from a dentry, so it has a + * aligned allocation for this particular component. We don't + * strictly need the load_unaligned_zeropad() safety, but it + * doesn't hurt either. + * + * In contrast, 'ct' and 'tcount' can be from a pathname, and do + * need the careful unaligned handling. + */ static inline int dentry_cmp(const unsigned char *cs, size_t scount, const unsigned char *ct, size_t tcount) { -#ifdef CONFIG_DCACHE_WORD_ACCESS unsigned long a,b,mask; if (unlikely(scount != tcount)) return 1; for (;;) { - a = *(unsigned long *)cs; - b = *(unsigned long *)ct; + a = load_unaligned_zeropad(cs); + b = load_unaligned_zeropad(ct); if (tcount < sizeof(unsigned long)) break; if (unlikely(a != b)) @@ -165,7 +176,13 @@ static inline int dentry_cmp(const unsigned char *cs, size_t scount, } mask = ~(~0ul << tcount*8); return unlikely(!!((a ^ b) & mask)); +} + #else + +static inline int dentry_cmp(const unsigned char *cs, size_t scount, + const unsigned char *ct, size_t tcount) +{ if (scount != tcount) return 1; @@ -177,9 +194,10 @@ static inline int dentry_cmp(const unsigned char *cs, size_t scount, tcount--; } while (tcount); return 0; -#endif } +#endif + static void __d_free(struct rcu_head *head) { struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 21e93605161c..5dfafdd1dbd3 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -33,18 +33,10 @@ static ssize_t default_write_file(struct file *file, const char __user *buf, return count; } -static int default_open(struct inode *inode, struct file *file) -{ - if (inode->i_private) - file->private_data = inode->i_private; - - return 0; -} - const struct file_operations debugfs_file_operations = { .read = default_read_file, .write = default_write_file, - .open = default_open, + .open = simple_open, .llseek = noop_llseek, }; @@ -447,7 +439,7 @@ static ssize_t write_file_bool(struct file *file, const char __user *user_buf, static const struct file_operations fops_bool = { .read = read_file_bool, .write = write_file_bool, - .open = default_open, + .open = simple_open, .llseek = default_llseek, }; @@ -492,7 +484,7 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf, static const struct file_operations fops_blob = { .read = read_file_blob, - .open = default_open, + .open = simple_open, .llseek = default_llseek, }; diff --git a/fs/dlm/debug_fs.c b/fs/dlm/debug_fs.c index 3dca2b39e83f..1c9b08095f98 100644 --- a/fs/dlm/debug_fs.c +++ b/fs/dlm/debug_fs.c @@ -609,13 +609,6 @@ static const struct file_operations format3_fops = { /* * dump lkb's on the ls_waiters list */ - -static int waiters_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t waiters_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -644,7 +637,7 @@ static ssize_t waiters_read(struct file *file, char __user *userbuf, static const struct file_operations waiters_fops = { .owner = THIS_MODULE, - .open = waiters_open, + .open = simple_open, .read = waiters_read, .llseek = default_llseek, }; diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c index fa5c07d51dcc..4c58d4a3adc4 100644 --- a/fs/dlm/lock.c +++ b/fs/dlm/lock.c @@ -1737,6 +1737,18 @@ static int _can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now) return 1; /* + * Even if the convert is compat with all granted locks, + * QUECVT forces it behind other locks on the convert queue. + */ + + if (now && conv && (lkb->lkb_exflags & DLM_LKF_QUECVT)) { + if (list_empty(&r->res_convertqueue)) + return 1; + else + goto out; + } + + /* * The NOORDER flag is set to avoid the standard vms rules on grant * order. */ diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 739b0985b398..c0b3c70ee87a 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1663,8 +1663,10 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, if (op == EPOLL_CTL_ADD) { if (is_file_epoll(tfile)) { error = -ELOOP; - if (ep_loop_check(ep, tfile) != 0) + if (ep_loop_check(ep, tfile) != 0) { + clear_tfile_check_list(); goto error_tgt_fput; + } } else list_add(&tfile->f_tfile_llink, &tfile_check_list); } diff --git a/fs/exec.c b/fs/exec.c index 9a1d9f0a60ab..b1fd2025e59a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1371,7 +1371,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) unsigned int depth = bprm->recursion_depth; int try,retval; struct linux_binfmt *fmt; - pid_t old_pid; + pid_t old_pid, old_vpid; retval = security_bprm_check(bprm); if (retval) @@ -1382,8 +1382,9 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) return retval; /* Need to fetch pid before load_binary changes it */ + old_pid = current->pid; rcu_read_lock(); - old_pid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); + old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); rcu_read_unlock(); retval = -ENOENT; @@ -1406,7 +1407,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) if (retval >= 0) { if (depth == 0) { trace_sched_process_exec(current, old_pid, bprm); - ptrace_event(PTRACE_EVENT_EXEC, old_pid); + ptrace_event(PTRACE_EVENT_EXEC, old_vpid); } put_binfmt(fmt); allow_write_access(bprm->file); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index ab2594a30f86..0e01e90add8b 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1203,9 +1203,6 @@ struct ext4_sb_info { unsigned long s_ext_blocks; unsigned long s_ext_extents; #endif - /* ext4 extent cache stats */ - unsigned long extent_cache_hits; - unsigned long extent_cache_misses; /* for buddy allocator */ struct ext4_group_info ***s_group_info; diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 1421938e6792..abcdeab67f52 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2066,10 +2066,6 @@ static int ext4_ext_check_cache(struct inode *inode, ext4_lblk_t block, ret = 1; } errout: - if (!ret) - sbi->extent_cache_misses++; - else - sbi->extent_cache_hits++; trace_ext4_ext_in_cache(inode, block, ret); spin_unlock(&EXT4_I(inode)->i_block_reservation_lock); return ret; @@ -2882,7 +2878,7 @@ static int ext4_split_extent_at(handle_t *handle, if (err) goto fix_extent_len; /* update the extent length and mark as initialized */ - ex->ee_len = cpu_to_le32(ee_len); + ex->ee_len = cpu_to_le16(ee_len); ext4_ext_try_to_merge(inode, path, ex); err = ext4_ext_dirty(handle, inode, path + depth); goto out; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index ceebaf853beb..e1fb1d5de58e 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1305,20 +1305,20 @@ static int set_qf_name(struct super_block *sb, int qtype, substring_t *args) ext4_msg(sb, KERN_ERR, "Cannot change journaled " "quota options when quota turned on"); - return 0; + return -1; } qname = match_strdup(args); if (!qname) { ext4_msg(sb, KERN_ERR, "Not enough memory for storing quotafile name"); - return 0; + return -1; } if (sbi->s_qf_names[qtype] && strcmp(sbi->s_qf_names[qtype], qname)) { ext4_msg(sb, KERN_ERR, "%s quota file already specified", QTYPE2NAME(qtype)); kfree(qname); - return 0; + return -1; } sbi->s_qf_names[qtype] = qname; if (strchr(sbi->s_qf_names[qtype], '/')) { @@ -1326,7 +1326,7 @@ static int set_qf_name(struct super_block *sb, int qtype, substring_t *args) "quotafile must be on filesystem root"); kfree(sbi->s_qf_names[qtype]); sbi->s_qf_names[qtype] = NULL; - return 0; + return -1; } set_opt(sb, QUOTA); return 1; @@ -1341,7 +1341,7 @@ static int clear_qf_name(struct super_block *sb, int qtype) sbi->s_qf_names[qtype]) { ext4_msg(sb, KERN_ERR, "Cannot change journaled quota options" " when quota turned on"); - return 0; + return -1; } /* * The space will be released later when all options are confirmed @@ -1450,6 +1450,16 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, const struct mount_opts *m; int arg = 0; +#ifdef CONFIG_QUOTA + if (token == Opt_usrjquota) + return set_qf_name(sb, USRQUOTA, &args[0]); + else if (token == Opt_grpjquota) + return set_qf_name(sb, GRPQUOTA, &args[0]); + else if (token == Opt_offusrjquota) + return clear_qf_name(sb, USRQUOTA); + else if (token == Opt_offgrpjquota) + return clear_qf_name(sb, GRPQUOTA); +#endif if (args->from && match_int(args, &arg)) return -1; switch (token) { @@ -1549,18 +1559,6 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, sbi->s_mount_opt |= m->mount_opt; } #ifdef CONFIG_QUOTA - } else if (token == Opt_usrjquota) { - if (!set_qf_name(sb, USRQUOTA, &args[0])) - return -1; - } else if (token == Opt_grpjquota) { - if (!set_qf_name(sb, GRPQUOTA, &args[0])) - return -1; - } else if (token == Opt_offusrjquota) { - if (!clear_qf_name(sb, USRQUOTA)) - return -1; - } else if (token == Opt_offgrpjquota) { - if (!clear_qf_name(sb, GRPQUOTA)) - return -1; } else if (m->flags & MOPT_QFMT) { if (sb_any_quota_loaded(sb) && sbi->s_jquota_fmt != m->mount_opt) { @@ -1599,7 +1597,9 @@ static int parse_options(char *options, struct super_block *sb, unsigned int *journal_ioprio, int is_remount) { +#ifdef CONFIG_QUOTA struct ext4_sb_info *sbi = EXT4_SB(sb); +#endif char *p; substring_t args[MAX_OPT_ARGS]; int token; @@ -2366,18 +2366,6 @@ static ssize_t lifetime_write_kbytes_show(struct ext4_attr *a, EXT4_SB(sb)->s_sectors_written_start) >> 1))); } -static ssize_t extent_cache_hits_show(struct ext4_attr *a, - struct ext4_sb_info *sbi, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%lu\n", sbi->extent_cache_hits); -} - -static ssize_t extent_cache_misses_show(struct ext4_attr *a, - struct ext4_sb_info *sbi, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%lu\n", sbi->extent_cache_misses); -} - static ssize_t inode_readahead_blks_store(struct ext4_attr *a, struct ext4_sb_info *sbi, const char *buf, size_t count) @@ -2435,8 +2423,6 @@ static struct ext4_attr ext4_attr_##name = __ATTR(name, mode, show, store) EXT4_RO_ATTR(delayed_allocation_blocks); EXT4_RO_ATTR(session_write_kbytes); EXT4_RO_ATTR(lifetime_write_kbytes); -EXT4_RO_ATTR(extent_cache_hits); -EXT4_RO_ATTR(extent_cache_misses); EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, sbi_ui_show, inode_readahead_blks_store, s_inode_readahead_blks); EXT4_RW_ATTR_SBI_UI(inode_goal, s_inode_goal); @@ -2452,8 +2438,6 @@ static struct attribute *ext4_attrs[] = { ATTR_LIST(delayed_allocation_blocks), ATTR_LIST(session_write_kbytes), ATTR_LIST(lifetime_write_kbytes), - ATTR_LIST(extent_cache_hits), - ATTR_LIST(extent_cache_misses), ATTR_LIST(inode_readahead_blks), ATTR_LIST(inode_goal), ATTR_LIST(mb_stats), diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 206632887bb4..df5ac048dc74 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -387,9 +387,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, if (fc->no_create) return -ENOSYS; - if (flags & O_DIRECT) - return -EINVAL; - forget = fuse_alloc_forget(); if (!forget) return -ENOMEM; @@ -644,13 +641,12 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) fuse_put_request(fc, req); if (!err) { struct inode *inode = entry->d_inode; + struct fuse_inode *fi = get_fuse_inode(inode); - /* - * Set nlink to zero so the inode can be cleared, if the inode - * does have more links this will be discovered at the next - * lookup/getattr. - */ - clear_nlink(inode); + spin_lock(&fc->lock); + fi->attr_version = ++fc->attr_version; + drop_nlink(inode); + spin_unlock(&fc->lock); fuse_invalidate_attr(inode); fuse_invalidate_attr(dir); fuse_invalidate_entry_cache(entry); @@ -762,8 +758,17 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, will reflect changes in the backing inode (link count, etc.) */ - if (!err || err == -EINTR) + if (!err) { + struct fuse_inode *fi = get_fuse_inode(inode); + + spin_lock(&fc->lock); + fi->attr_version = ++fc->attr_version; + inc_nlink(inode); + spin_unlock(&fc->lock); + fuse_invalidate_attr(inode); + } else if (err == -EINTR) { fuse_invalidate_attr(inode); + } return err; } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a841868bf9ce..504e61b7fd75 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -194,10 +194,6 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir) struct fuse_conn *fc = get_fuse_conn(inode); int err; - /* VFS checks this, but only _after_ ->open() */ - if (file->f_flags & O_DIRECT) - return -EINVAL; - err = generic_file_open(inode, file); if (err) return err; @@ -932,17 +928,23 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; size_t count = 0; + size_t ocount = 0; ssize_t written = 0; + ssize_t written_buffered = 0; struct inode *inode = mapping->host; ssize_t err; struct iov_iter i; + loff_t endbyte = 0; WARN_ON(iocb->ki_pos != pos); - err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ); + ocount = 0; + err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ); if (err) return err; + count = ocount; + mutex_lock(&inode->i_mutex); vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); @@ -962,11 +964,41 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, file_update_time(file); - iov_iter_init(&i, iov, nr_segs, count, 0); - written = fuse_perform_write(file, mapping, &i, pos); - if (written >= 0) - iocb->ki_pos = pos + written; + if (file->f_flags & O_DIRECT) { + written = generic_file_direct_write(iocb, iov, &nr_segs, + pos, &iocb->ki_pos, + count, ocount); + if (written < 0 || written == count) + goto out; + + pos += written; + count -= written; + iov_iter_init(&i, iov, nr_segs, count, written); + written_buffered = fuse_perform_write(file, mapping, &i, pos); + if (written_buffered < 0) { + err = written_buffered; + goto out; + } + endbyte = pos + written_buffered - 1; + + err = filemap_write_and_wait_range(file->f_mapping, pos, + endbyte); + if (err) + goto out; + + invalidate_mapping_pages(file->f_mapping, + pos >> PAGE_CACHE_SHIFT, + endbyte >> PAGE_CACHE_SHIFT); + + written += written_buffered; + iocb->ki_pos = pos + written_buffered; + } else { + iov_iter_init(&i, iov, nr_segs, count, 0); + written = fuse_perform_write(file, mapping, &i, pos); + if (written >= 0) + iocb->ki_pos = pos + written; + } out: current->backing_dev_info = NULL; mutex_unlock(&inode->i_mutex); @@ -1101,30 +1133,41 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf, return res; } -static ssize_t fuse_direct_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t __fuse_direct_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) { struct inode *inode = file->f_path.dentry->d_inode; ssize_t res; - if (is_bad_inode(inode)) - return -EIO; - - /* Don't allow parallel writes to the same file */ - mutex_lock(&inode->i_mutex); res = generic_write_checks(file, ppos, &count, 0); if (!res) { res = fuse_direct_io(file, buf, count, ppos, 1); if (res > 0) fuse_write_update_size(inode, *ppos); } - mutex_unlock(&inode->i_mutex); fuse_invalidate_attr(inode); return res; } +static ssize_t fuse_direct_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + ssize_t res; + + if (is_bad_inode(inode)) + return -EIO; + + /* Don't allow parallel writes to the same file */ + mutex_lock(&inode->i_mutex); + res = __fuse_direct_write(file, buf, count, ppos); + mutex_unlock(&inode->i_mutex); + + return res; +} + static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req) { __free_page(req->pages[0]); @@ -2077,6 +2120,57 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc, return 0; } +static ssize_t fuse_loop_dio(struct file *filp, const struct iovec *iov, + unsigned long nr_segs, loff_t *ppos, int rw) +{ + const struct iovec *vector = iov; + ssize_t ret = 0; + + while (nr_segs > 0) { + void __user *base; + size_t len; + ssize_t nr; + + base = vector->iov_base; + len = vector->iov_len; + vector++; + nr_segs--; + + if (rw == WRITE) + nr = __fuse_direct_write(filp, base, len, ppos); + else + nr = fuse_direct_read(filp, base, len, ppos); + + if (nr < 0) { + if (!ret) + ret = nr; + break; + } + ret += nr; + if (nr != len) + break; + } + + return ret; +} + + +static ssize_t +fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +{ + ssize_t ret = 0; + struct file *file = NULL; + loff_t pos = 0; + + file = iocb->ki_filp; + pos = offset; + + ret = fuse_loop_dio(file, iov, nr_segs, &pos, rw); + + return ret; +} + static const struct file_operations fuse_file_operations = { .llseek = fuse_file_llseek, .read = do_sync_read, @@ -2120,6 +2214,7 @@ static const struct address_space_operations fuse_file_aops = { .readpages = fuse_readpages, .set_page_dirty = __set_page_dirty_nobuffers, .bmap = fuse_bmap, + .direct_IO = fuse_direct_IO, }; void fuse_init_file_inode(struct inode *inode) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 4aec5995867e..26783eb2b1fc 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -947,6 +947,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) sb->s_magic = FUSE_SUPER_MAGIC; sb->s_op = &fuse_super_operations; sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_time_gran = 1; sb->s_export_op = &fuse_export_operations; file = fget(d.fd); diff --git a/fs/gfs2/Kconfig b/fs/gfs2/Kconfig index c465ae066c62..eb08c9e43c2a 100644 --- a/fs/gfs2/Kconfig +++ b/fs/gfs2/Kconfig @@ -1,10 +1,6 @@ config GFS2_FS tristate "GFS2 file system support" depends on (64BIT || LBDAF) - select DLM if GFS2_FS_LOCKING_DLM - select CONFIGFS_FS if GFS2_FS_LOCKING_DLM - select SYSFS if GFS2_FS_LOCKING_DLM - select IP_SCTP if DLM_SCTP select FS_POSIX_ACL select CRC32 select QUOTACTL @@ -29,7 +25,8 @@ config GFS2_FS config GFS2_FS_LOCKING_DLM bool "GFS2 DLM locking" - depends on (GFS2_FS!=n) && NET && INET && (IPV6 || IPV6=n) && HOTPLUG + depends on (GFS2_FS!=n) && NET && INET && (IPV6 || IPV6=n) && \ + HOTPLUG && DLM && CONFIGFS_FS && SYSFS help Multiple node locking module for GFS2 diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 38b7a74a0f91..9b2ff0e851b1 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -807,7 +807,7 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh, if (inode == sdp->sd_rindex) { adjust_fs_space(inode); - ip->i_gh.gh_flags |= GL_NOCACHE; + sdp->sd_rindex_uptodate = 0; } brelse(dibh); @@ -873,7 +873,7 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping, if (inode == sdp->sd_rindex) { adjust_fs_space(inode); - ip->i_gh.gh_flags |= GL_NOCACHE; + sdp->sd_rindex_uptodate = 0; } brelse(dibh); diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 197c5c47e577..03c04febe26f 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -724,7 +724,11 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, int metadata; unsigned int revokes = 0; int x; - int error = 0; + int error; + + error = gfs2_rindex_update(sdp); + if (error) + return error; if (!*top) sm->sm_first = 0; diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index c35573abd371..a836056343f0 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1844,6 +1844,10 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, unsigned int x, size = len * sizeof(u64); int error; + error = gfs2_rindex_update(sdp); + if (error) + return error; + memset(&rlist, 0, sizeof(struct gfs2_rgrp_list)); ht = kzalloc(size, GFP_NOFS); diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index c98a60ee6dfd..a9ba2444e077 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1031,7 +1031,13 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) struct buffer_head *bh; struct gfs2_holder ghs[3]; struct gfs2_rgrpd *rgd; - int error = -EROFS; + int error; + + error = gfs2_rindex_update(sdp); + if (error) + return error; + + error = -EROFS; gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); @@ -1224,6 +1230,10 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, return 0; } + error = gfs2_rindex_update(sdp); + if (error) + return error; + if (odip != ndip) { error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE, 0, &r_gh); @@ -1345,7 +1355,6 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, error = alloc_required; if (error < 0) goto out_gunlock; - error = 0; if (alloc_required) { struct gfs2_qadata *qa = gfs2_qadata_get(ndip); diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index f8411bd1b805..5f5e70e047dc 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -200,10 +200,11 @@ static int make_mode(const unsigned int lmstate) return -1; } -static u32 make_flags(const u32 lkid, const unsigned int gfs_flags, +static u32 make_flags(struct gfs2_glock *gl, const unsigned int gfs_flags, const int req) { u32 lkf = DLM_LKF_VALBLK; + u32 lkid = gl->gl_lksb.sb_lkid; if (gfs_flags & LM_FLAG_TRY) lkf |= DLM_LKF_NOQUEUE; @@ -227,8 +228,11 @@ static u32 make_flags(const u32 lkid, const unsigned int gfs_flags, BUG(); } - if (lkid != 0) + if (lkid != 0) { lkf |= DLM_LKF_CONVERT; + if (test_bit(GLF_BLOCKING, &gl->gl_flags)) + lkf |= DLM_LKF_QUECVT; + } return lkf; } @@ -250,7 +254,7 @@ static int gdlm_lock(struct gfs2_glock *gl, unsigned int req_state, char strname[GDLM_STRNAME_BYTES] = ""; req = make_mode(req_state); - lkf = make_flags(gl->gl_lksb.sb_lkid, flags, req); + lkf = make_flags(gl, flags, req); gfs2_glstats_inc(gl, GFS2_LKS_DCOUNT); gfs2_sbstats_inc(gl, GFS2_LKS_DCOUNT); if (gl->gl_lksb.sb_lkid) { diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 19bde40b4864..3df65c9ab73b 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -332,9 +332,6 @@ struct gfs2_rgrpd *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, u64 blk, bool exact) struct rb_node *n, *next; struct gfs2_rgrpd *cur; - if (gfs2_rindex_update(sdp)) - return NULL; - spin_lock(&sdp->sd_rindex_spin); n = sdp->sd_rindex_tree.rb_node; while (n) { @@ -640,6 +637,7 @@ static int read_rindex_entry(struct gfs2_inode *ip, return 0; error = 0; /* someone else read in the rgrp; free it and ignore it */ + gfs2_glock_put(rgd->rd_gl); fail: kfree(rgd->rd_bits); @@ -927,6 +925,10 @@ int gfs2_fitrim(struct file *filp, void __user *argp) } else if (copy_from_user(&r, argp, sizeof(r))) return -EFAULT; + ret = gfs2_rindex_update(sdp); + if (ret) + return ret; + rgd = gfs2_blk2rgrpd(sdp, r.start, 0); rgd_end = gfs2_blk2rgrpd(sdp, r.start + r.len, 0); diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c index 2e5ba425cae7..927f4df874ae 100644 --- a/fs/gfs2/xattr.c +++ b/fs/gfs2/xattr.c @@ -238,6 +238,10 @@ static int ea_dealloc_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, unsigned int x; int error; + error = gfs2_rindex_update(sdp); + if (error) + return error; + if (GFS2_EA_IS_STUFFED(ea)) return 0; @@ -1330,6 +1334,10 @@ static int ea_dealloc_indirect(struct gfs2_inode *ip) unsigned int x; int error; + error = gfs2_rindex_update(sdp); + if (error) + return error; + memset(&rlist, 0, sizeof(struct gfs2_rgrp_list)); error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, &indbh); @@ -1439,6 +1447,10 @@ static int ea_dealloc_block(struct gfs2_inode *ip) struct gfs2_holder gh; int error; + error = gfs2_rindex_update(sdp); + if (error) + return error; + rgd = gfs2_blk2rgrpd(sdp, ip->i_eattr, 1); if (!rgd) { gfs2_consist_inode(ip); diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 4dfbfec357e8..ec2a9c23f0c9 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -366,6 +366,10 @@ int hfsplus_rename_cat(u32 cnid, err = hfs_brec_find(&src_fd); if (err) goto out; + if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) { + err = -EIO; + goto out; + } hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset, src_fd.entrylength); diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 88e155f895c6..26b53fb09f68 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -150,6 +150,11 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) filp->f_pos++; /* fall through */ case 1: + if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) { + err = -EIO; + goto out; + } + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) { @@ -181,6 +186,12 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) err = -EIO; goto out; } + + if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) { + err = -EIO; + goto out; + } + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); type = be16_to_cpu(entry.type); diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index ea251749d9d5..001ef01d2fe2 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -485,6 +485,7 @@ static struct inode *hugetlbfs_get_root(struct super_block *sb, inode->i_fop = &simple_dir_operations; /* directory inodes start off with i_nlink == 2 (for "." entry) */ inc_nlink(inode); + lockdep_annotate_inode_mutex_key(inode); } return inode; } @@ -1031,7 +1032,6 @@ static int __init init_hugetlbfs_fs(void) } error = PTR_ERR(vfsmount); - unregister_filesystem(&hugetlbfs_fs_type); out: kmem_cache_destroy(hugetlbfs_inode_cachep); diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 806525a7269c..840f70f50792 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -723,7 +723,7 @@ start_journal_io: if (commit_transaction->t_need_data_flush && (journal->j_fs_dev != journal->j_dev) && (journal->j_flags & JBD2_BARRIER)) - blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL); + blkdev_issue_flush(journal->j_fs_dev, GFP_NOFS, NULL); /* Done it all: now write the commit record asynchronously. */ if (JBD2_HAS_INCOMPAT_FEATURE(journal, @@ -859,7 +859,7 @@ wait_for_iobuf: if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT) && journal->j_flags & JBD2_BARRIER) { - blkdev_issue_flush(journal->j_dev, GFP_KERNEL, NULL); + blkdev_issue_flush(journal->j_dev, GFP_NOFS, NULL); } if (err) diff --git a/fs/libfs.c b/fs/libfs.c index 4a0d1f06da57..18d08f5db53a 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -264,6 +264,13 @@ Enomem: return ERR_PTR(-ENOMEM); } +int simple_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + return 0; +} + int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; @@ -522,6 +529,7 @@ int simple_fill_super(struct super_block *s, unsigned long magic, return 0; out: d_genocide(root); + shrink_dcache_parent(root); dput(root); return -ENOMEM; } @@ -984,6 +992,7 @@ EXPORT_SYMBOL(simple_dir_operations); EXPORT_SYMBOL(simple_empty); EXPORT_SYMBOL(simple_fill_super); EXPORT_SYMBOL(simple_getattr); +EXPORT_SYMBOL(simple_open); EXPORT_SYMBOL(simple_link); EXPORT_SYMBOL(simple_lookup); EXPORT_SYMBOL(simple_pin_fs); diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c index 3ddcbb1c0a43..13ad1539fbf2 100644 --- a/fs/lockd/clnt4xdr.c +++ b/fs/lockd/clnt4xdr.c @@ -241,7 +241,7 @@ static int decode_nlm4_stat(struct xdr_stream *xdr, __be32 *stat) p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) goto out_overflow; - if (unlikely(*p > nlm4_failed)) + if (unlikely(ntohl(*p) > ntohl(nlm4_failed))) goto out_bad_xdr; *stat = *p; return 0; diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c index 3d35e3e80c1c..d269ada7670e 100644 --- a/fs/lockd/clntxdr.c +++ b/fs/lockd/clntxdr.c @@ -236,7 +236,7 @@ static int decode_nlm_stat(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) goto out_overflow; - if (unlikely(*p > nlm_lck_denied_grace_period)) + if (unlikely(ntohl(*p) > ntohl(nlm_lck_denied_grace_period))) goto out_enum; *stat = *p; return 0; diff --git a/fs/locks.c b/fs/locks.c index 637694bf3a03..0d68f1f81799 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -510,12 +510,13 @@ static void __locks_delete_block(struct file_lock *waiter) /* */ -static void locks_delete_block(struct file_lock *waiter) +void locks_delete_block(struct file_lock *waiter) { lock_flocks(); __locks_delete_block(waiter); unlock_flocks(); } +EXPORT_SYMBOL(locks_delete_block); /* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in diff --git a/fs/namei.c b/fs/namei.c index 1898198abc3d..c42791914f82 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1407,18 +1407,9 @@ static inline int can_lookup(struct inode *inode) */ #ifdef CONFIG_DCACHE_WORD_ACCESS -#ifdef CONFIG_64BIT +#include <asm/word-at-a-time.h> -/* - * Jan Achrenius on G+: microoptimized version of - * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56" - * that works for the bytemasks without having to - * mask them first. - */ -static inline long count_masked_bytes(unsigned long mask) -{ - return mask*0x0001020304050608ul >> 56; -} +#ifdef CONFIG_64BIT static inline unsigned int fold_hash(unsigned long hash) { @@ -1428,15 +1419,6 @@ static inline unsigned int fold_hash(unsigned long hash) #else /* 32-bit case */ -/* Carl Chatfield / Jan Achrenius G+ version for 32-bit */ -static inline long count_masked_bytes(long mask) -{ - /* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */ - long a = (0x0ff0001+mask) >> 23; - /* Fix the 1 for 00 case */ - return a & mask; -} - #define fold_hash(x) (x) #endif @@ -1447,7 +1429,7 @@ unsigned int full_name_hash(const unsigned char *name, unsigned int len) unsigned long hash = 0; for (;;) { - a = *(unsigned long *)name; + a = load_unaligned_zeropad(name); if (len < sizeof(unsigned long)) break; hash += a; @@ -1464,17 +1446,6 @@ done: } EXPORT_SYMBOL(full_name_hash); -#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) -#define ONEBYTES REPEAT_BYTE(0x01) -#define SLASHBYTES REPEAT_BYTE('/') -#define HIGHBITS REPEAT_BYTE(0x80) - -/* Return the high bit set in the first byte that is a zero */ -static inline unsigned long has_zero(unsigned long a) -{ - return ((a - ONEBYTES) & ~a) & HIGHBITS; -} - /* * Calculate the length and hash of the path component, and * return the length of the component; @@ -1488,9 +1459,9 @@ static inline unsigned long hash_name(const char *name, unsigned int *hashp) do { hash = (hash + a) * 9; len += sizeof(unsigned long); - a = *(unsigned long *)(name+len); + a = load_unaligned_zeropad(name+len); /* Do we have any NUL or '/' bytes in this word? */ - mask = has_zero(a) | has_zero(a ^ SLASHBYTES); + mask = has_zero(a) | has_zero(a ^ REPEAT_BYTE('/')); } while (!mask); /* The mask *below* the first high bit set */ diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 9c94297bb70e..7f6a23f0244e 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -38,6 +38,8 @@ #include <linux/buffer_head.h> /* various write calls */ #include <linux/prefetch.h> +#include "../pnfs.h" +#include "../internal.h" #include "blocklayout.h" #define NFSDBG_FACILITY NFSDBG_PNFS_LD @@ -868,7 +870,7 @@ nfs4_blk_get_deviceinfo(struct nfs_server *server, const struct nfs_fh *fh, * GETDEVICEINFO's maxcount */ max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; - max_pages = max_resp_sz >> PAGE_SHIFT; + max_pages = nfs_page_array_len(0, max_resp_sz); dprintk("%s max_resp_sz %u max_pages %d\n", __func__, max_resp_sz, max_pages); diff --git a/fs/nfs/client.c b/fs/nfs/client.c index da7b5e4ff9ec..60f7e4ec842c 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1729,7 +1729,8 @@ error: */ struct nfs_server *nfs_clone_server(struct nfs_server *source, struct nfs_fh *fh, - struct nfs_fattr *fattr) + struct nfs_fattr *fattr, + rpc_authflavor_t flavor) { struct nfs_server *server; struct nfs_fattr *fattr_fsinfo; @@ -1758,7 +1759,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, error = nfs_init_server_rpcclient(server, source->client->cl_timeout, - source->client->cl_auth->au_flavor); + flavor); if (error < 0) goto out_free_server; if (!IS_ERR(source->client_acl)) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 4aaf0316d76a..8789210c6905 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1429,7 +1429,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry } open_flags = nd->intent.open.flags; - attr.ia_valid = 0; + attr.ia_valid = ATTR_OPEN; ctx = create_nfs_open_context(dentry, open_flags); res = ERR_CAST(ctx); @@ -1536,7 +1536,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) if (IS_ERR(ctx)) goto out; - attr.ia_valid = 0; + attr.ia_valid = ATTR_OPEN; if (openflags & O_TRUNC) { attr.ia_valid |= ATTR_SIZE; attr.ia_size = 0; diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index b7f348bb618b..ba3019f5934c 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -554,12 +554,16 @@ static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, struct nfs_client *clp; int error = 0; + if (!try_module_get(THIS_MODULE)) + return 0; + while ((clp = nfs_get_client_for_event(sb->s_fs_info, event))) { error = __rpc_pipefs_event(clp, event, sb); nfs_put_client(clp); if (error) break; } + module_put(THIS_MODULE); return error; } diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 2476dc69365f..b777bdaba4c5 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -165,7 +165,8 @@ extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *, extern void nfs_free_server(struct nfs_server *server); extern struct nfs_server *nfs_clone_server(struct nfs_server *, struct nfs_fh *, - struct nfs_fattr *); + struct nfs_fattr *, + rpc_authflavor_t); extern void nfs_mark_client_ready(struct nfs_client *clp, int state); extern int nfs4_check_client_ready(struct nfs_client *clp); extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp, @@ -186,10 +187,10 @@ static inline void nfs_fs_proc_exit(void) /* nfs4namespace.c */ #ifdef CONFIG_NFS_V4 -extern struct vfsmount *nfs_do_refmount(struct dentry *dentry); +extern struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry); #else static inline -struct vfsmount *nfs_do_refmount(struct dentry *dentry) +struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry) { return ERR_PTR(-ENOENT); } @@ -234,7 +235,6 @@ extern const u32 nfs41_maxwrite_overhead; /* nfs4proc.c */ #ifdef CONFIG_NFS_V4 extern struct rpc_procinfo nfs4_procedures[]; -void nfs_fixup_secinfo_attributes(struct nfs_fattr *, struct nfs_fh *); #endif extern int nfs4_init_ds_session(struct nfs_client *clp); diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 1807866bb3ab..d51868e5683c 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -148,66 +148,31 @@ rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors) return pseudoflavor; } -static int nfs_negotiate_security(const struct dentry *parent, - const struct dentry *dentry, - rpc_authflavor_t *flavor) +static struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir, + struct qstr *name, + struct nfs_fh *fh, + struct nfs_fattr *fattr) { - struct page *page; - struct nfs4_secinfo_flavors *flavors; - int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); - int ret = -EPERM; - - secinfo = NFS_PROTO(parent->d_inode)->secinfo; - if (secinfo != NULL) { - page = alloc_page(GFP_KERNEL); - if (!page) { - ret = -ENOMEM; - goto out; - } - flavors = page_address(page); - ret = secinfo(parent->d_inode, &dentry->d_name, flavors); - *flavor = nfs_find_best_sec(flavors); - put_page(page); - } - -out: - return ret; -} - -static int nfs_lookup_with_sec(struct nfs_server *server, struct dentry *parent, - struct dentry *dentry, struct path *path, - struct nfs_fh *fh, struct nfs_fattr *fattr, - rpc_authflavor_t *flavor) -{ - struct rpc_clnt *clone; - struct rpc_auth *auth; int err; - err = nfs_negotiate_security(parent, path->dentry, flavor); - if (err < 0) - goto out; - clone = rpc_clone_client(server->client); - auth = rpcauth_create(*flavor, clone); - if (!auth) { - err = -EIO; - goto out_shutdown; - } - err = server->nfs_client->rpc_ops->lookup(clone, parent->d_inode, - &path->dentry->d_name, - fh, fattr); -out_shutdown: - rpc_shutdown_client(clone); -out: - return err; + if (NFS_PROTO(dir)->version == 4) + return nfs4_proc_lookup_mountpoint(dir, name, fh, fattr); + + err = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, name, fh, fattr); + if (err) + return ERR_PTR(err); + return rpc_clone_client(NFS_SERVER(dir)->client); } #else /* CONFIG_NFS_V4 */ -static inline int nfs_lookup_with_sec(struct nfs_server *server, - struct dentry *parent, struct dentry *dentry, - struct path *path, struct nfs_fh *fh, - struct nfs_fattr *fattr, - rpc_authflavor_t *flavor) +static inline struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir, + struct qstr *name, + struct nfs_fh *fh, + struct nfs_fattr *fattr) { - return -EPERM; + int err = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, name, fh, fattr); + if (err) + return ERR_PTR(err); + return rpc_clone_client(NFS_SERVER(dir)->client); } #endif /* CONFIG_NFS_V4 */ @@ -226,12 +191,10 @@ static inline int nfs_lookup_with_sec(struct nfs_server *server, struct vfsmount *nfs_d_automount(struct path *path) { struct vfsmount *mnt; - struct nfs_server *server = NFS_SERVER(path->dentry->d_inode); struct dentry *parent; struct nfs_fh *fh = NULL; struct nfs_fattr *fattr = NULL; - int err; - rpc_authflavor_t flavor = RPC_AUTH_UNIX; + struct rpc_clnt *client; dprintk("--> nfs_d_automount()\n"); @@ -249,21 +212,19 @@ struct vfsmount *nfs_d_automount(struct path *path) /* Look it up again to get its attributes */ parent = dget_parent(path->dentry); - err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode, - &path->dentry->d_name, - fh, fattr); - if (err == -EPERM && NFS_PROTO(parent->d_inode)->secinfo != NULL) - err = nfs_lookup_with_sec(server, parent, path->dentry, path, fh, fattr, &flavor); + client = nfs_lookup_mountpoint(parent->d_inode, &path->dentry->d_name, fh, fattr); dput(parent); - if (err != 0) { - mnt = ERR_PTR(err); + if (IS_ERR(client)) { + mnt = ERR_CAST(client); goto out; } if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) - mnt = nfs_do_refmount(path->dentry); + mnt = nfs_do_refmount(client, path->dentry); else - mnt = nfs_do_submount(path->dentry, fh, fattr, flavor); + mnt = nfs_do_submount(path->dentry, fh, fattr, client->cl_auth->au_flavor); + rpc_shutdown_client(client); + if (IS_ERR(mnt)) goto out; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 97ecc863dd76..8d75021020b3 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -59,6 +59,7 @@ struct nfs_unique_id { #define NFS_SEQID_CONFIRMED 1 struct nfs_seqid_counter { + ktime_t create_time; int owner_id; int flags; u32 counter; @@ -204,6 +205,9 @@ struct nfs4_state_maintenance_ops { extern const struct dentry_operations nfs4_dentry_operations; extern const struct inode_operations nfs4_dir_inode_operations; +/* nfs4namespace.c */ +struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *); + /* nfs4proc.c */ extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *); @@ -212,8 +216,11 @@ extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); -extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, - struct nfs4_fs_locations *fs_locations, struct page *page); +extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struct qstr *, + struct nfs4_fs_locations *, struct page *); +extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *, + struct nfs_fh *, struct nfs_fattr *); +extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); extern int nfs4_release_lockowner(struct nfs4_lock_state *); extern const struct xattr_handler *nfs4_xattr_handlers[]; diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index a866bbd2890a..c9cff9adb2d3 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -699,7 +699,7 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_fla * GETDEVICEINFO's maxcount */ max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; - max_pages = max_resp_sz >> PAGE_SHIFT; + max_pages = nfs_page_array_len(0, max_resp_sz); dprintk("%s inode %p max_resp_sz %u max_pages %d\n", __func__, inode, max_resp_sz, max_pages); diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index 9c8eca315f43..a7f3dedc4ec7 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -52,6 +52,30 @@ Elong: } /* + * return the path component of "<server>:<path>" + * nfspath - the "<server>:<path>" string + * end - one past the last char that could contain "<server>:" + * returns NULL on failure + */ +static char *nfs_path_component(const char *nfspath, const char *end) +{ + char *p; + + if (*nfspath == '[') { + /* parse [] escaped IPv6 addrs */ + p = strchr(nfspath, ']'); + if (p != NULL && ++p < end && *p == ':') + return p + 1; + } else { + /* otherwise split on first colon */ + p = strchr(nfspath, ':'); + if (p != NULL && p < end) + return p + 1; + } + return NULL; +} + +/* * Determine the mount path as a string */ static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen) @@ -59,9 +83,9 @@ static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen) char *limit; char *path = nfs_path(&limit, dentry, buffer, buflen); if (!IS_ERR(path)) { - char *colon = strchr(path, ':'); - if (colon && colon < limit) - path = colon + 1; + char *path_component = nfs_path_component(path, limit); + if (path_component) + return path_component; } return path; } @@ -108,6 +132,58 @@ static size_t nfs_parse_server_name(char *string, size_t len, return ret; } +static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name) +{ + struct page *page; + struct nfs4_secinfo_flavors *flavors; + rpc_authflavor_t flavor; + int err; + + page = alloc_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + flavors = page_address(page); + + err = nfs4_proc_secinfo(inode, name, flavors); + if (err < 0) { + flavor = err; + goto out; + } + + flavor = nfs_find_best_sec(flavors); + +out: + put_page(page); + return flavor; +} + +/* + * Please call rpc_shutdown_client() when you are done with this client. + */ +struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode, + struct qstr *name) +{ + struct rpc_clnt *clone; + struct rpc_auth *auth; + rpc_authflavor_t flavor; + + flavor = nfs4_negotiate_security(inode, name); + if (flavor < 0) + return ERR_PTR(flavor); + + clone = rpc_clone_client(clnt); + if (IS_ERR(clone)) + return clone; + + auth = rpcauth_create(flavor, clone); + if (!auth) { + rpc_shutdown_client(clone); + clone = ERR_PTR(-EIO); + } + + return clone; +} + static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, char *page, char *page2, const struct nfs4_fs_location *location) @@ -224,7 +300,7 @@ out: * @dentry - dentry of referral * */ -struct vfsmount *nfs_do_refmount(struct dentry *dentry) +struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry) { struct vfsmount *mnt = ERR_PTR(-ENOMEM); struct dentry *parent; @@ -250,7 +326,7 @@ struct vfsmount *nfs_do_refmount(struct dentry *dentry) dprintk("%s: getting locations for %s/%s\n", __func__, parent->d_name.name, dentry->d_name.name); - err = nfs4_proc_fs_locations(parent->d_inode, &dentry->d_name, fs_locations, page); + err = nfs4_proc_fs_locations(client, parent->d_inode, &dentry->d_name, fs_locations, page); dput(parent); if (err != 0 || fs_locations->nlocations <= 0 || diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f82bde005a82..99650aaf8937 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -838,7 +838,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p->o_arg.open_flags = flags; p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE); p->o_arg.clientid = server->nfs_client->cl_clientid; - p->o_arg.id = sp->so_seqid.owner_id; + p->o_arg.id.create_time = ktime_to_ns(sp->so_seqid.create_time); + p->o_arg.id.uniquifier = sp->so_seqid.owner_id; p->o_arg.name = &dentry->d_name; p->o_arg.server = server; p->o_arg.bitmask = server->attr_bitmask; @@ -1466,8 +1467,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) goto unlock_no_action; rcu_read_unlock(); } - /* Update sequence id. */ - data->o_arg.id = sp->so_seqid.owner_id; + /* Update client id. */ data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid; if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) { task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; @@ -1954,10 +1954,19 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, }; int err; do { - err = nfs4_handle_exception(server, - _nfs4_do_setattr(inode, cred, fattr, sattr, state), - &exception); + err = _nfs4_do_setattr(inode, cred, fattr, sattr, state); + switch (err) { + case -NFS4ERR_OPENMODE: + if (state && !(state->state & FMODE_WRITE)) { + err = -EBADF; + if (sattr->ia_valid & ATTR_OPEN) + err = -EACCES; + goto out; + } + } + err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); +out: return err; } @@ -2368,8 +2377,9 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, * Note that we'll actually follow the referral later when * we detect fsid mismatch in inode revalidation */ -static int nfs4_get_referral(struct inode *dir, const struct qstr *name, - struct nfs_fattr *fattr, struct nfs_fh *fhandle) +static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir, + const struct qstr *name, struct nfs_fattr *fattr, + struct nfs_fh *fhandle) { int status = -ENOMEM; struct page *page = NULL; @@ -2382,7 +2392,7 @@ static int nfs4_get_referral(struct inode *dir, const struct qstr *name, if (locations == NULL) goto out; - status = nfs4_proc_fs_locations(dir, name, locations, page); + status = nfs4_proc_fs_locations(client, dir, name, locations, page); if (status != 0) goto out; /* Make sure server returned a different fsid for the referral */ @@ -2519,39 +2529,84 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, return status; } -void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr, struct nfs_fh *fh) +static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr) { - memset(fh, 0, sizeof(struct nfs_fh)); - fattr->fsid.major = 1; fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE | - NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_FSID | NFS_ATTR_FATTR_MOUNTPOINT; + NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_MOUNTPOINT; fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO; fattr->nlink = 2; } -static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) +static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir, + struct qstr *name, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) { struct nfs4_exception exception = { }; + struct rpc_clnt *client = *clnt; int err; do { - int status; - - status = _nfs4_proc_lookup(clnt, dir, name, fhandle, fattr); - switch (status) { + err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr); + switch (err) { case -NFS4ERR_BADNAME: - return -ENOENT; + err = -ENOENT; + goto out; case -NFS4ERR_MOVED: - return nfs4_get_referral(dir, name, fattr, fhandle); + err = nfs4_get_referral(client, dir, name, fattr, fhandle); + goto out; case -NFS4ERR_WRONGSEC: - nfs_fixup_secinfo_attributes(fattr, fhandle); + err = -EPERM; + if (client != *clnt) + goto out; + + client = nfs4_create_sec_client(client, dir, name); + if (IS_ERR(client)) + return PTR_ERR(client); + + exception.retry = 1; + break; + default: + err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception); } - err = nfs4_handle_exception(NFS_SERVER(dir), - status, &exception); } while (exception.retry); + +out: + if (err == 0) + *clnt = client; + else if (client != *clnt) + rpc_shutdown_client(client); + return err; } +static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name, + struct nfs_fh *fhandle, struct nfs_fattr *fattr) +{ + int status; + struct rpc_clnt *client = NFS_CLIENT(dir); + + status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr); + if (client != NFS_CLIENT(dir)) { + rpc_shutdown_client(client); + nfs_fixup_secinfo_attributes(fattr); + } + return status; +} + +struct rpc_clnt * +nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name, + struct nfs_fh *fhandle, struct nfs_fattr *fattr) +{ + int status; + struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir)); + + status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr); + if (status < 0) { + rpc_shutdown_client(client); + return ERR_PTR(status); + } + return client; +} + static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry) { struct nfs_server *server = NFS_SERVER(inode); @@ -3619,16 +3674,16 @@ out: return ret; } -static void nfs4_write_cached_acl(struct inode *inode, const char *buf, size_t acl_len) +static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len) { struct nfs4_cached_acl *acl; - if (buf && acl_len <= PAGE_SIZE) { + if (pages && acl_len <= PAGE_SIZE) { acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL); if (acl == NULL) goto out; acl->cached = 1; - memcpy(acl->data, buf, acl_len); + _copy_from_pages(acl->data, pages, pgbase, acl_len); } else { acl = kmalloc(sizeof(*acl), GFP_KERNEL); if (acl == NULL) @@ -3661,7 +3716,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu struct nfs_getaclres res = { .acl_len = buflen, }; - void *resp_buf; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL], .rpc_argp = &args, @@ -3675,24 +3729,27 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu if (npages == 0) npages = 1; + /* Add an extra page to handle the bitmap returned */ + npages++; + for (i = 0; i < npages; i++) { pages[i] = alloc_page(GFP_KERNEL); if (!pages[i]) goto out_free; } - if (npages > 1) { - /* for decoding across pages */ - res.acl_scratch = alloc_page(GFP_KERNEL); - if (!res.acl_scratch) - goto out_free; - } + + /* for decoding across pages */ + res.acl_scratch = alloc_page(GFP_KERNEL); + if (!res.acl_scratch) + goto out_free; + args.acl_len = npages * PAGE_SIZE; args.acl_pgbase = 0; + /* Let decode_getfacl know not to fail if the ACL data is larger than * the page we send as a guess */ if (buf == NULL) res.acl_flags |= NFS4_ACL_LEN_REQUEST; - resp_buf = page_address(pages[0]); dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", __func__, buf, buflen, npages, args.acl_len); @@ -3703,9 +3760,9 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu acl_len = res.acl_len - res.acl_data_offset; if (acl_len > args.acl_len) - nfs4_write_cached_acl(inode, NULL, acl_len); + nfs4_write_cached_acl(inode, NULL, 0, acl_len); else - nfs4_write_cached_acl(inode, resp_buf + res.acl_data_offset, + nfs4_write_cached_acl(inode, pages, res.acl_data_offset, acl_len); if (buf) { ret = -ERANGE; @@ -4558,7 +4615,9 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) { struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { }; + struct nfs4_exception exception = { + .inode = state->inode, + }; int err; do { @@ -4576,7 +4635,9 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request) { struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { }; + struct nfs4_exception exception = { + .inode = state->inode, + }; int err; err = nfs4_set_lock_state(state, request); @@ -4676,6 +4737,7 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock * { struct nfs4_exception exception = { .state = state, + .inode = state->inode, }; int err; @@ -4721,6 +4783,20 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) if (state == NULL) return -ENOLCK; + /* + * Don't rely on the VFS having checked the file open mode, + * since it won't do this for flock() locks. + */ + switch (request->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) { + case F_RDLCK: + if (!(filp->f_mode & FMODE_READ)) + return -EBADF; + break; + case F_WRLCK: + if (!(filp->f_mode & FMODE_WRITE)) + return -EBADF; + } + do { status = nfs4_proc_setlk(state, cmd, request); if ((status != -EAGAIN) || IS_SETLK(cmd)) @@ -4891,8 +4967,10 @@ static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr) fattr->nlink = 2; } -int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, - struct nfs4_fs_locations *fs_locations, struct page *page) +static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, + const struct qstr *name, + struct nfs4_fs_locations *fs_locations, + struct page *page) { struct nfs_server *server = NFS_SERVER(dir); u32 bitmask[2] = { @@ -4926,11 +5004,26 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, nfs_fattr_init(&fs_locations->fattr); fs_locations->server = server; fs_locations->nlocations = 0; - status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); + status = nfs4_call_sync(client, server, &msg, &args.seq_args, &res.seq_res, 0); dprintk("%s: returned status = %d\n", __func__, status); return status; } +int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, + const struct qstr *name, + struct nfs4_fs_locations *fs_locations, + struct page *page) +{ + struct nfs4_exception exception = { }; + int err; + do { + err = nfs4_handle_exception(NFS_SERVER(dir), + _nfs4_proc_fs_locations(client, dir, name, fs_locations, page), + &exception); + } while (exception.retry); + return err; +} + static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors) { int status; @@ -4953,8 +5046,8 @@ static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct return status; } -static int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, - struct nfs4_secinfo_flavors *flavors) +int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, + struct nfs4_secinfo_flavors *flavors) { struct nfs4_exception exception = { }; int err; @@ -5029,10 +5122,9 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) nfs4_construct_boot_verifier(clp, &verifier); args.id_len = scnprintf(args.id, sizeof(args.id), - "%s/%s.%s/%u", + "%s/%s/%u", clp->cl_ipaddr, - init_utsname()->nodename, - init_utsname()->domainname, + clp->cl_rpcclient->cl_nodename, clp->cl_rpcclient->cl_auth->au_flavor); res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0f43414eb25a..7f0fcfc1fe9d 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -393,6 +393,7 @@ nfs4_remove_state_owner_locked(struct nfs4_state_owner *sp) static void nfs4_init_seqid_counter(struct nfs_seqid_counter *sc) { + sc->create_time = ktime_get(); sc->flags = 0; sc->counter = 0; spin_lock_init(&sc->lock); @@ -434,13 +435,17 @@ nfs4_alloc_state_owner(struct nfs_server *server, static void nfs4_drop_state_owner(struct nfs4_state_owner *sp) { - if (!RB_EMPTY_NODE(&sp->so_server_node)) { + struct rb_node *rb_node = &sp->so_server_node; + + if (!RB_EMPTY_NODE(rb_node)) { struct nfs_server *server = sp->so_server; struct nfs_client *clp = server->nfs_client; spin_lock(&clp->cl_lock); - rb_erase(&sp->so_server_node, &server->state_owners); - RB_CLEAR_NODE(&sp->so_server_node); + if (!RB_EMPTY_NODE(rb_node)) { + rb_erase(rb_node, &server->state_owners); + RB_CLEAR_NODE(rb_node); + } spin_unlock(&clp->cl_lock); } } @@ -516,6 +521,14 @@ out: /** * nfs4_put_state_owner - Release a nfs4_state_owner * @sp: state owner data to release + * + * Note that we keep released state owners on an LRU + * list. + * This caches valid state owners so that they can be + * reused, to avoid the OPEN_CONFIRM on minor version 0. + * It also pins the uniquifier of dropped state owners for + * a while, to ensure that those state owner names are + * never reused. */ void nfs4_put_state_owner(struct nfs4_state_owner *sp) { @@ -525,15 +538,9 @@ void nfs4_put_state_owner(struct nfs4_state_owner *sp) if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) return; - if (!RB_EMPTY_NODE(&sp->so_server_node)) { - sp->so_expires = jiffies; - list_add_tail(&sp->so_lru, &server->state_owners_lru); - spin_unlock(&clp->cl_lock); - } else { - nfs4_remove_state_owner_locked(sp); - spin_unlock(&clp->cl_lock); - nfs4_free_state_owner(sp); - } + sp->so_expires = jiffies; + list_add_tail(&sp->so_lru, &server->state_owners_lru); + spin_unlock(&clp->cl_lock); } /** diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index c74fdb114b48..c54aae364bee 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -74,7 +74,7 @@ static int nfs4_stat_to_errno(int); /* lock,open owner id: * we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT >> 2) */ -#define open_owner_id_maxsz (1 + 1 + 4) +#define open_owner_id_maxsz (1 + 2 + 1 + 1 + 2) #define lock_owner_id_maxsz (1 + 1 + 4) #define decode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) #define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) @@ -1340,12 +1340,13 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena */ encode_nfs4_seqid(xdr, arg->seqid); encode_share_access(xdr, arg->fmode); - p = reserve_space(xdr, 32); + p = reserve_space(xdr, 36); p = xdr_encode_hyper(p, arg->clientid); - *p++ = cpu_to_be32(20); + *p++ = cpu_to_be32(24); p = xdr_encode_opaque_fixed(p, "open id:", 8); *p++ = cpu_to_be32(arg->server->s_dev); - xdr_encode_hyper(p, arg->id); + *p++ = cpu_to_be32(arg->id.uniquifier); + xdr_encode_hyper(p, arg->id.create_time); } static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg) @@ -4257,8 +4258,6 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, status = decode_attr_error(xdr, bitmap, &err); if (status < 0) goto xdr_error; - if (err == -NFS4ERR_WRONGSEC) - nfs_fixup_secinfo_attributes(fattr, fh); status = decode_attr_filehandle(xdr, bitmap, fh); if (status < 0) @@ -4901,11 +4900,19 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, bitmap[3] = {0}; struct kvec *iov = req->rq_rcv_buf.head; int status; + size_t page_len = xdr->buf->page_len; res->acl_len = 0; if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) goto out; + bm_p = xdr->p; + res->acl_data_offset = be32_to_cpup(bm_p) + 2; + res->acl_data_offset <<= 2; + /* Check if the acl data starts beyond the allocated buffer */ + if (res->acl_data_offset > page_len) + return -ERANGE; + if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) goto out; if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) @@ -4915,28 +4922,24 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, return -EIO; if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { size_t hdrlen; - u32 recvd; /* The bitmap (xdr len + bitmaps) and the attr xdr len words * are stored with the acl data to handle the problem of * variable length bitmaps.*/ xdr->p = bm_p; - res->acl_data_offset = be32_to_cpup(bm_p) + 2; - res->acl_data_offset <<= 2; /* We ignore &savep and don't do consistency checks on * the attr length. Let userspace figure it out.... */ hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; attrlen += res->acl_data_offset; - recvd = req->rq_rcv_buf.len - hdrlen; - if (attrlen > recvd) { + if (attrlen > page_len) { if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { /* getxattr interface called with a NULL buf */ res->acl_len = attrlen; goto out; } - dprintk("NFS: acl reply: attrlen %u > recvd %u\n", - attrlen, recvd); + dprintk("NFS: acl reply: attrlen %u > page_len %zu\n", + attrlen, page_len); return -EINVAL; } xdr_read_pages(xdr, attrlen); @@ -5089,16 +5092,13 @@ out_err: return -EINVAL; } -static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) +static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) { struct nfs4_secinfo_flavor *sec_flavor; int status; __be32 *p; int i, num_flavors; - status = decode_op_hdr(xdr, OP_SECINFO); - if (status) - goto out; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) goto out_overflow; @@ -5124,6 +5124,7 @@ static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) res->flavors->num_flavors++; } + status = 0; out: return status; out_overflow: @@ -5131,7 +5132,23 @@ out_overflow: return -EIO; } +static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) +{ + int status = decode_op_hdr(xdr, OP_SECINFO); + if (status) + return status; + return decode_secinfo_common(xdr, res); +} + #if defined(CONFIG_NFS_V4_1) +static int decode_secinfo_no_name(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) +{ + int status = decode_op_hdr(xdr, OP_SECINFO_NO_NAME); + if (status) + return status; + return decode_secinfo_common(xdr, res); +} + static int decode_exchange_id(struct xdr_stream *xdr, struct nfs41_exchange_id_res *res) { @@ -6816,7 +6833,7 @@ static int nfs4_xdr_dec_secinfo_no_name(struct rpc_rqst *rqstp, status = decode_putrootfh(xdr); if (status) goto out; - status = decode_secinfo(xdr, res); + status = decode_secinfo_no_name(xdr, res); out: return status; } diff --git a/fs/nfs/objlayout/objlayout.c b/fs/nfs/objlayout/objlayout.c index 8d45f1c318ce..595c5fc21a19 100644 --- a/fs/nfs/objlayout/objlayout.c +++ b/fs/nfs/objlayout/objlayout.c @@ -604,7 +604,6 @@ int objlayout_get_deviceinfo(struct pnfs_layout_hdr *pnfslay, { struct objlayout_deviceinfo *odi; struct pnfs_device pd; - struct super_block *sb; struct page *page, **pages; u32 *p; int err; @@ -623,7 +622,6 @@ int objlayout_get_deviceinfo(struct pnfs_layout_hdr *pnfslay, pd.pglen = PAGE_SIZE; pd.mincount = 0; - sb = pnfslay->plh_inode->i_sb; err = nfs4_proc_getdeviceinfo(NFS_SERVER(pnfslay->plh_inode), &pd); dprintk("%s nfs_getdeviceinfo returned %d\n", __func__, err); if (err) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index b5d451586943..38512bcd2e98 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -587,7 +587,7 @@ send_layoutget(struct pnfs_layout_hdr *lo, /* allocate pages for xdr post processing */ max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; - max_pages = max_resp_sz >> PAGE_SHIFT; + max_pages = nfs_page_array_len(0, max_resp_sz); pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags); if (!pages) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 9a0e8ef4a409..0a4be28c2ea3 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -322,7 +322,7 @@ out_bad: while (!list_empty(res)) { data = list_entry(res->next, struct nfs_read_data, list); list_del(&data->list); - nfs_readdata_free(data); + nfs_readdata_release(data); } nfs_readpage_release(req); return -ENOMEM; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 37412f706b32..4ac7fca7e4bf 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2428,7 +2428,7 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags, dprintk("--> nfs_xdev_mount()\n"); /* create a new volume representation */ - server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr); + server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor); if (IS_ERR(server)) { error = PTR_ERR(server); goto out_err_noserver; @@ -2767,11 +2767,15 @@ static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type, char *root_devname; size_t len; - len = strlen(hostname) + 3; + len = strlen(hostname) + 5; root_devname = kmalloc(len, GFP_KERNEL); if (root_devname == NULL) return ERR_PTR(-ENOMEM); - snprintf(root_devname, len, "%s:/", hostname); + /* Does hostname needs to be enclosed in brackets? */ + if (strchr(hostname, ':')) + snprintf(root_devname, len, "[%s]:/", hostname); + else + snprintf(root_devname, len, "%s:/", hostname); root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data); kfree(root_devname); return root_mnt; @@ -2951,7 +2955,7 @@ nfs4_xdev_mount(struct file_system_type *fs_type, int flags, dprintk("--> nfs4_xdev_mount()\n"); /* create a new volume representation */ - server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr); + server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor); if (IS_ERR(server)) { error = PTR_ERR(server); goto out_err_noserver; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 2c68818f68ac..c07462320f6b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -682,7 +682,8 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, req->wb_bytes = rqend - req->wb_offset; out_unlock: spin_unlock(&inode->i_lock); - nfs_clear_request_commit(req); + if (req) + nfs_clear_request_commit(req); return req; out_flushme: spin_unlock(&inode->i_lock); @@ -1018,7 +1019,7 @@ out_bad: while (!list_empty(res)) { data = list_entry(res->next, struct nfs_write_data, list); list_del(&data->list); - nfs_writedata_free(data); + nfs_writedata_release(data); } nfs_redirty_request(req); return -ENOMEM; diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 08c6e36ab2eb..43f46cd9edea 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -803,13 +803,13 @@ encode_entry_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, return p; } -static int +static __be32 compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, const char *name, int namlen) { struct svc_export *exp; struct dentry *dparent, *dchild; - int rv = 0; + __be32 rv = nfserr_noent; dparent = cd->fh.fh_dentry; exp = cd->fh.fh_export; @@ -817,26 +817,20 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, if (isdotent(name, namlen)) { if (namlen == 2) { dchild = dget_parent(dparent); - if (dchild == dparent) { - /* filesystem root - cannot return filehandle for ".." */ - dput(dchild); - return -ENOENT; - } + /* filesystem root - cannot return filehandle for ".." */ + if (dchild == dparent) + goto out; } else dchild = dget(dparent); } else dchild = lookup_one_len(name, dparent, namlen); if (IS_ERR(dchild)) - return -ENOENT; - rv = -ENOENT; + return rv; if (d_mountpoint(dchild)) goto out; - rv = fh_compose(fhp, exp, dchild, &cd->fh); - if (rv) - goto out; if (!dchild->d_inode) goto out; - rv = 0; + rv = fh_compose(fhp, exp, dchild, &cd->fh); out: dput(dchild); return rv; @@ -845,7 +839,7 @@ out: static __be32 *encode_entryplus_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, int namlen) { struct svc_fh fh; - int err; + __be32 err; fh_init(&fh, NFS3_FHSIZE); err = compose_entry_fh(cd, &fh, name, namlen); diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 2ed14dfd00a2..987e719fbae8 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -235,17 +235,17 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o */ if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0) open->op_bmval[1] = (FATTR4_WORD1_TIME_ACCESS | - FATTR4_WORD1_TIME_MODIFY); + FATTR4_WORD1_TIME_MODIFY); } else { status = nfsd_lookup(rqstp, current_fh, open->op_fname.data, open->op_fname.len, resfh); fh_unlock(current_fh); - if (status) - goto out; - status = nfsd_check_obj_isreg(resfh); } if (status) goto out; + status = nfsd_check_obj_isreg(resfh); + if (status) + goto out; if (is_create_with_attrs(open) && open->op_acl != NULL) do_set_nfs4_acl(rqstp, resfh, open->op_acl, open->op_bmval); @@ -841,6 +841,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_setattr *setattr) { __be32 status = nfs_ok; + int err; if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { nfs4_lock_state(); @@ -852,9 +853,9 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return status; } } - status = fh_want_write(&cstate->current_fh); - if (status) - return status; + err = fh_want_write(&cstate->current_fh); + if (err) + return nfserrno(err); status = nfs_ok; status = check_attr_support(rqstp, cstate, setattr->sa_bmval, diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 4767429264a2..ed3f9206a0ee 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -577,7 +577,7 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) struct cld_net *cn = nn->cld_net; if (mlen != sizeof(*cmsg)) { - dprintk("%s: got %lu bytes, expected %lu\n", __func__, mlen, + dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen, sizeof(*cmsg)); return -EINVAL; } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1841f8bf845e..7f71c69cdcdf 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4211,16 +4211,14 @@ out: * vfs_test_lock. (Arguably perhaps test_lock should be done with an * inode operation.) */ -static int nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file_lock *lock) +static __be32 nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file_lock *lock) { struct file *file; - int err; - - err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); - if (err) - return err; - err = vfs_test_lock(file, lock); - nfsd_close(file); + __be32 err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); + if (!err) { + err = nfserrno(vfs_test_lock(file, lock)); + nfsd_close(file); + } return err; } @@ -4234,7 +4232,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct inode *inode; struct file_lock file_lock; struct nfs4_lockowner *lo; - int error; __be32 status; if (locks_in_grace()) @@ -4280,12 +4277,10 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_transform_lock_offset(&file_lock); - status = nfs_ok; - error = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock); - if (error) { - status = nfserrno(error); + status = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock); + if (status) goto out; - } + if (file_lock.fl_type != F_UNLCK) { status = nfserr_denied; nfs4_set_lock_denied(&file_lock, &lockt->lt_denied); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index bcd8904ab1e3..74c00bc92b9a 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1392,7 +1392,7 @@ nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_sta for (i = 0; i < test_stateid->ts_num_ids; i++) { stateid = kmalloc(sizeof(struct nfsd4_test_stateid_id), GFP_KERNEL); if (!stateid) { - status = PTR_ERR(stateid); + status = nfserrno(-ENOMEM); goto out; } @@ -3410,7 +3410,7 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr, *p++ = htonl(test_stateid->ts_num_ids); list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) { - *p++ = htonl(stateid->ts_id_status); + *p++ = stateid->ts_id_status; } ADJUST_ARGS(); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 296d671654d6..568666156ea4 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1458,7 +1458,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, switch (createmode) { case NFS3_CREATE_UNCHECKED: if (! S_ISREG(dchild->d_inode->i_mode)) - err = nfserr_exist; + goto out; else if (truncp) { /* in nfsv4, we need to treat this case a little * differently. we don't want to truncate the diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 3165aebb43c8..31b9463fba1f 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -1134,7 +1134,7 @@ static int ocfs2_adjust_rightmost_branch(handle_t *handle, } el = path_leaf_el(path); - rec = &el->l_recs[le32_to_cpu(el->l_next_free_rec) - 1]; + rec = &el->l_recs[le16_to_cpu(el->l_next_free_rec) - 1]; ocfs2_adjust_rightmost_records(handle, et, path, rec); diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index cf7823382664..9f32d7cbb7a3 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -1036,14 +1036,14 @@ static int ocfs2_get_refcount_cpos_end(struct ocfs2_caching_info *ci, tmp_el = left_path->p_node[subtree_root].el; blkno = left_path->p_node[subtree_root+1].bh->b_blocknr; - for (i = 0; i < le32_to_cpu(tmp_el->l_next_free_rec); i++) { + for (i = 0; i < le16_to_cpu(tmp_el->l_next_free_rec); i++) { if (le64_to_cpu(tmp_el->l_recs[i].e_blkno) == blkno) { *cpos_end = le32_to_cpu(tmp_el->l_recs[i+1].e_cpos); break; } } - BUG_ON(i == le32_to_cpu(tmp_el->l_next_free_rec)); + BUG_ON(i == le16_to_cpu(tmp_el->l_next_free_rec)); out: ocfs2_free_path(left_path); @@ -1468,7 +1468,7 @@ static int ocfs2_divide_leaf_refcount_block(struct buffer_head *ref_leaf_bh, trace_ocfs2_divide_leaf_refcount_block( (unsigned long long)ref_leaf_bh->b_blocknr, - le32_to_cpu(rl->rl_count), le32_to_cpu(rl->rl_used)); + le16_to_cpu(rl->rl_count), le16_to_cpu(rl->rl_used)); /* * XXX: Improvement later. @@ -2411,7 +2411,7 @@ static int ocfs2_calc_refcount_meta_credits(struct super_block *sb, rb = (struct ocfs2_refcount_block *) prev_bh->b_data; - if (le64_to_cpu(rb->rf_records.rl_used) + + if (le16_to_cpu(rb->rf_records.rl_used) + recs_add > le16_to_cpu(rb->rf_records.rl_count)) ref_blocks++; @@ -2476,7 +2476,7 @@ static int ocfs2_calc_refcount_meta_credits(struct super_block *sb, if (prev_bh) { rb = (struct ocfs2_refcount_block *)prev_bh->b_data; - if (le64_to_cpu(rb->rf_records.rl_used) + recs_add > + if (le16_to_cpu(rb->rf_records.rl_used) + recs_add > le16_to_cpu(rb->rf_records.rl_count)) ref_blocks++; @@ -3629,7 +3629,7 @@ int ocfs2_refcounted_xattr_delete_need(struct inode *inode, * one will split a refcount rec, so totally we need * clusters * 2 new refcount rec. */ - if (le64_to_cpu(rb->rf_records.rl_used) + clusters * 2 > + if (le16_to_cpu(rb->rf_records.rl_used) + clusters * 2 > le16_to_cpu(rb->rf_records.rl_count)) ref_blocks++; diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index ba5d97e4a73e..f169da4624fd 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -600,7 +600,7 @@ static void ocfs2_bg_alloc_cleanup(handle_t *handle, ret = ocfs2_free_clusters(handle, cluster_ac->ac_inode, cluster_ac->ac_bh, le64_to_cpu(rec->e_blkno), - le32_to_cpu(rec->e_leaf_clusters)); + le16_to_cpu(rec->e_leaf_clusters)); if (ret) mlog_errno(ret); /* Try all the clusters to free */ @@ -1628,7 +1628,7 @@ static int ocfs2_bg_discontig_fix_by_rec(struct ocfs2_suballoc_result *res, { unsigned int bpc = le16_to_cpu(cl->cl_bpc); unsigned int bitoff = le32_to_cpu(rec->e_cpos) * bpc; - unsigned int bitcount = le32_to_cpu(rec->e_leaf_clusters) * bpc; + unsigned int bitcount = le16_to_cpu(rec->e_leaf_clusters) * bpc; if (res->sr_bit_offset < bitoff) return 0; diff --git a/fs/pipe.c b/fs/pipe.c index 25feaa3faac0..fec5e4ad071a 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -346,6 +346,16 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = { .get = generic_pipe_buf_get, }; +static const struct pipe_buf_operations packet_pipe_buf_ops = { + .can_merge = 0, + .map = generic_pipe_buf_map, + .unmap = generic_pipe_buf_unmap, + .confirm = generic_pipe_buf_confirm, + .release = anon_pipe_buf_release, + .steal = generic_pipe_buf_steal, + .get = generic_pipe_buf_get, +}; + static ssize_t pipe_read(struct kiocb *iocb, const struct iovec *_iov, unsigned long nr_segs, loff_t pos) @@ -407,6 +417,13 @@ redo: ret += chars; buf->offset += chars; buf->len -= chars; + + /* Was it a packet buffer? Clean up and exit */ + if (buf->flags & PIPE_BUF_FLAG_PACKET) { + total_len = chars; + buf->len = 0; + } + if (!buf->len) { buf->ops = NULL; ops->release(pipe, buf); @@ -459,6 +476,11 @@ redo: return ret; } +static inline int is_packetized(struct file *file) +{ + return (file->f_flags & O_DIRECT) != 0; +} + static ssize_t pipe_write(struct kiocb *iocb, const struct iovec *_iov, unsigned long nr_segs, loff_t ppos) @@ -593,6 +615,11 @@ redo2: buf->ops = &anon_pipe_buf_ops; buf->offset = 0; buf->len = chars; + buf->flags = 0; + if (is_packetized(filp)) { + buf->ops = &packet_pipe_buf_ops; + buf->flags = PIPE_BUF_FLAG_PACKET; + } pipe->nrbufs = ++bufs; pipe->tmp_page = NULL; @@ -1013,7 +1040,7 @@ struct file *create_write_pipe(int flags) goto err_dentry; f->f_mapping = inode->i_mapping; - f->f_flags = O_WRONLY | (flags & O_NONBLOCK); + f->f_flags = O_WRONLY | (flags & (O_NONBLOCK | O_DIRECT)); f->f_version = 0; return f; @@ -1057,7 +1084,7 @@ int do_pipe_flags(int *fd, int flags) int error; int fdw, fdr; - if (flags & ~(O_CLOEXEC | O_NONBLOCK)) + if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT)) return -EINVAL; fw = create_write_pipe(flags); diff --git a/fs/proc/root.c b/fs/proc/root.c index 46a15d8a29ca..eed44bfc85db 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -115,12 +115,13 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, if (IS_ERR(sb)) return ERR_CAST(sb); + if (!proc_parse_options(options, ns)) { + deactivate_locked_super(sb); + return ERR_PTR(-EINVAL); + } + if (!sb->s_root) { sb->s_flags = flags; - if (!proc_parse_options(options, ns)) { - deactivate_locked_super(sb); - return ERR_PTR(-EINVAL); - } err = proc_fill_super(sb); if (err) { deactivate_locked_super(sb); diff --git a/fs/proc/stat.c b/fs/proc/stat.c index 6a0c62d6e442..64c3b3172367 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c @@ -18,19 +18,39 @@ #ifndef arch_irq_stat #define arch_irq_stat() 0 #endif -#ifndef arch_idle_time -#define arch_idle_time(cpu) 0 -#endif + +#ifdef arch_idle_time + +static cputime64_t get_idle_time(int cpu) +{ + cputime64_t idle; + + idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; + if (cpu_online(cpu) && !nr_iowait_cpu(cpu)) + idle += arch_idle_time(cpu); + return idle; +} + +static cputime64_t get_iowait_time(int cpu) +{ + cputime64_t iowait; + + iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; + if (cpu_online(cpu) && nr_iowait_cpu(cpu)) + iowait += arch_idle_time(cpu); + return iowait; +} + +#else static u64 get_idle_time(int cpu) { u64 idle, idle_time = get_cpu_idle_time_us(cpu, NULL); - if (idle_time == -1ULL) { + if (idle_time == -1ULL) /* !NO_HZ so we can rely on cpustat.idle */ idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; - idle += arch_idle_time(cpu); - } else + else idle = usecs_to_cputime64(idle_time); return idle; @@ -49,6 +69,8 @@ static u64 get_iowait_time(int cpu) return iowait; } +#endif + static int show_stat(struct seq_file *p, void *v) { int i, j; diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 2b9a7607cbd5..1030a716d155 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -597,9 +597,6 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, if (!page) continue; - if (PageReserved(page)) - continue; - /* Clear accessed and referenced bits. */ ptep_test_and_clear_young(vma, addr, pte); ClearPageReferenced(page); @@ -750,6 +747,8 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, pte_t pte) else if (pte_present(pte)) *pme = make_pme(PM_PFRAME(pte_pfn(pte)) | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT); + else + *pme = make_pme(PM_NOT_PRESENT); } #ifdef CONFIG_TRANSPARENT_HUGEPAGE @@ -764,6 +763,8 @@ static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, if (pmd_present(pmd)) *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT); + else + *pme = make_pme(PM_NOT_PRESENT); } #else static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, @@ -804,8 +805,10 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, /* check to see if we've left 'vma' behind * and need a new, higher one */ - if (vma && (addr >= vma->vm_end)) + if (vma && (addr >= vma->vm_end)) { vma = find_vma(walk->mm, addr); + pme = make_pme(PM_NOT_PRESENT); + } /* check that 'vma' actually covers this address, * and that it isn't a huge page vma */ @@ -833,6 +836,8 @@ static void huge_pte_to_pagemap_entry(pagemap_entry_t *pme, if (pte_present(pte)) *pme = make_pme(PM_PFRAME(pte_pfn(pte) + offset) | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT); + else + *pme = make_pme(PM_NOT_PRESENT); } /* This function walks within one hugetlb entry in the single call */ @@ -842,7 +847,7 @@ static int pagemap_hugetlb_range(pte_t *pte, unsigned long hmask, { struct pagemapread *pm = walk->private; int err = 0; - pagemap_entry_t pme = make_pme(PM_NOT_PRESENT); + pagemap_entry_t pme; for (; addr != end; addr += PAGE_SIZE) { int offset = (addr & ~hmask) >> PAGE_SHIFT; diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 50952c9bd06c..19507889bb7f 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -52,12 +52,6 @@ struct pstore_private { char data[]; }; -static int pstore_file_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t pstore_file_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -67,7 +61,7 @@ static ssize_t pstore_file_read(struct file *file, char __user *userbuf, } static const struct file_operations pstore_file_operations = { - .open = pstore_file_open, + .open = simple_open, .read = pstore_file_read, .llseek = default_llseek, }; diff --git a/fs/splice.c b/fs/splice.c index 5f883de7ef3a..f8476841eb04 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -30,6 +30,7 @@ #include <linux/uio.h> #include <linux/security.h> #include <linux/gfp.h> +#include <linux/socket.h> /* * Attempt to steal a page from a pipe buffer. This should perhaps go into @@ -690,7 +691,9 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe, if (!likely(file->f_op && file->f_op->sendpage)) return -EINVAL; - more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; + more = (sd->flags & SPLICE_F_MORE) ? MSG_MORE : 0; + if (sd->len < sd->total_len) + more |= MSG_SENDPAGE_NOTLAST; return file->f_op->sendpage(file, buf->page, buf->offset, sd->len, &pos, more); } diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 2a7a3f5d1ca6..35a36d39fa2c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -729,6 +729,9 @@ int sysfs_create_dir(struct kobject * kobj) else parent_sd = &sysfs_root; + if (!parent_sd) + return -ENOENT; + if (sysfs_ns_type(parent_sd)) ns = kobj->ktype->namespace(kobj); type = sysfs_read_ns_type(kobj); @@ -878,7 +881,6 @@ int sysfs_rename(struct sysfs_dirent *sd, dup_name = sd->s_name; sd->s_name = new_name; - sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name); } /* Move to the appropriate place in the appropriate directories rbtree. */ @@ -886,6 +888,7 @@ int sysfs_rename(struct sysfs_dirent *sd, sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_ns = new_ns; + sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index dd1701caecc9..2df555c66d57 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -67,7 +67,11 @@ static int internal_create_group(struct kobject *kobj, int update, /* Updates may happen before the object has been instantiated */ if (unlikely(update && !kobj->sd)) return -EINVAL; - + if (!grp->attrs) { + WARN(1, "sysfs: attrs not set by subsystem for group: %s/%s\n", + kobj->name, grp->name ? "" : grp->name); + return -EINVAL; + } if (grp->name) { error = sysfs_create_subdir(kobj, grp->name, &sd); if (error) diff --git a/fs/xattr.c b/fs/xattr.c index d6dfd247bb2f..3c8c1cc333c7 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -19,8 +19,9 @@ #include <linux/export.h> #include <linux/fsnotify.h> #include <linux/audit.h> -#include <asm/uaccess.h> +#include <linux/vmalloc.h> +#include <asm/uaccess.h> /* * Check permissions for extended attribute access. This is a bit complicated @@ -320,6 +321,7 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, { int error; void *kvalue = NULL; + void *vvalue = NULL; /* If non-NULL, we used vmalloc() */ char kname[XATTR_NAME_MAX + 1]; if (flags & ~(XATTR_CREATE|XATTR_REPLACE)) @@ -334,13 +336,25 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, if (size) { if (size > XATTR_SIZE_MAX) return -E2BIG; - kvalue = memdup_user(value, size); - if (IS_ERR(kvalue)) - return PTR_ERR(kvalue); + kvalue = kmalloc(size, GFP_KERNEL | __GFP_NOWARN); + if (!kvalue) { + vvalue = vmalloc(size); + if (!vvalue) + return -ENOMEM; + kvalue = vvalue; + } + if (copy_from_user(kvalue, value, size)) { + error = -EFAULT; + goto out; + } } error = vfs_setxattr(d, kname, kvalue, size, flags); - kfree(kvalue); +out: + if (vvalue) + vfree(vvalue); + else + kfree(kvalue); return error; } @@ -492,13 +506,18 @@ listxattr(struct dentry *d, char __user *list, size_t size) { ssize_t error; char *klist = NULL; + char *vlist = NULL; /* If non-NULL, we used vmalloc() */ if (size) { if (size > XATTR_LIST_MAX) size = XATTR_LIST_MAX; - klist = kmalloc(size, GFP_KERNEL); - if (!klist) - return -ENOMEM; + klist = kmalloc(size, __GFP_NOWARN | GFP_KERNEL); + if (!klist) { + vlist = vmalloc(size); + if (!vlist) + return -ENOMEM; + klist = vlist; + } } error = vfs_listxattr(d, klist, size); @@ -510,7 +529,10 @@ listxattr(struct dentry *d, char __user *list, size_t size) than XATTR_LIST_MAX bytes. Not possible. */ error = -E2BIG; } - kfree(klist); + if (vlist) + vfree(vlist); + else + kfree(klist); return error; } diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index eba66043cf1b..e8bcc4742e0e 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -499,9 +499,10 @@ typedef u64 acpi_integer; #define ACPI_STATE_D0 (u8) 0 #define ACPI_STATE_D1 (u8) 1 #define ACPI_STATE_D2 (u8) 2 -#define ACPI_STATE_D3 (u8) 3 -#define ACPI_STATE_D3_COLD (u8) 4 -#define ACPI_D_STATES_MAX ACPI_STATE_D3_COLD +#define ACPI_STATE_D3_HOT (u8) 3 +#define ACPI_STATE_D3 (u8) 4 +#define ACPI_STATE_D3_COLD ACPI_STATE_D3 +#define ACPI_D_STATES_MAX ACPI_STATE_D3 #define ACPI_D_STATE_COUNT 5 #define ACPI_STATE_C0 (u8) 0 diff --git a/include/asm-generic/cmpxchg.h b/include/asm-generic/cmpxchg.h index 8a361834dc25..14883026015d 100644 --- a/include/asm-generic/cmpxchg.h +++ b/include/asm-generic/cmpxchg.h @@ -10,6 +10,7 @@ #error "Cannot use generic cmpxchg on SMP" #endif +#include <linux/types.h> #include <linux/irqflags.h> #ifndef xchg diff --git a/include/asm-generic/siginfo.h b/include/asm-generic/siginfo.h index 0dd4e87f6fba..5e5e3865f1ed 100644 --- a/include/asm-generic/siginfo.h +++ b/include/asm-generic/siginfo.h @@ -35,6 +35,14 @@ typedef union sigval { #define __ARCH_SI_BAND_T long #endif +#ifndef __ARCH_SI_CLOCK_T +#define __ARCH_SI_CLOCK_T __kernel_clock_t +#endif + +#ifndef __ARCH_SI_ATTRIBUTES +#define __ARCH_SI_ATTRIBUTES +#endif + #ifndef HAVE_ARCH_SIGINFO_T typedef struct siginfo { @@ -72,8 +80,8 @@ typedef struct siginfo { __kernel_pid_t _pid; /* which child */ __ARCH_SI_UID_T _uid; /* sender's uid */ int _status; /* exit code */ - __kernel_clock_t _utime; - __kernel_clock_t _stime; + __ARCH_SI_CLOCK_T _utime; + __ARCH_SI_CLOCK_T _stime; } _sigchld; /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ @@ -91,7 +99,7 @@ typedef struct siginfo { int _fd; } _sigpoll; } _sifields; -} siginfo_t; +} __ARCH_SI_ATTRIBUTES siginfo_t; #endif diff --git a/include/asm-generic/statfs.h b/include/asm-generic/statfs.h index 0fd28e028de1..c749af9c0983 100644 --- a/include/asm-generic/statfs.h +++ b/include/asm-generic/statfs.h @@ -15,7 +15,7 @@ typedef __kernel_fsid_t fsid_t; * with a 10' pole. */ #ifndef __statfs_word -#if BITS_PER_LONG == 64 +#if __BITS_PER_LONG == 64 #define __statfs_word long #else #define __statfs_word __u32 diff --git a/include/crypto/internal/aead.h b/include/crypto/internal/aead.h index d838c945575a..2eba340230a7 100644 --- a/include/crypto/internal/aead.h +++ b/include/crypto/internal/aead.h @@ -31,6 +31,8 @@ static inline void crypto_set_aead_spawn( crypto_set_spawn(&spawn->base, inst); } +struct crypto_alg *crypto_lookup_aead(const char *name, u32 type, u32 mask); + int crypto_grab_aead(struct crypto_aead_spawn *spawn, const char *name, u32 type, u32 mask); diff --git a/include/crypto/internal/skcipher.h b/include/crypto/internal/skcipher.h index 3a748a6bf772..06e8b32d541c 100644 --- a/include/crypto/internal/skcipher.h +++ b/include/crypto/internal/skcipher.h @@ -34,6 +34,8 @@ static inline void crypto_set_skcipher_spawn( int crypto_grab_skcipher(struct crypto_skcipher_spawn *spawn, const char *name, u32 type, u32 mask); +struct crypto_alg *crypto_lookup_skcipher(const char *name, u32 type, u32 mask); + static inline void crypto_drop_skcipher(struct crypto_skcipher_spawn *spawn) { crypto_drop_spawn(&spawn->base); diff --git a/include/drm/drm.h b/include/drm/drm.h index 34a7b89fd006..64ff02d5b730 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -617,6 +617,17 @@ struct drm_get_cap { __u64 value; }; +#define DRM_CLOEXEC O_CLOEXEC +struct drm_prime_handle { + __u32 handle; + + /** Flags.. only applicable for handle->fd */ + __u32 flags; + + /** Returned dmabuf file descriptor */ + __s32 fd; +}; + #include "drm_mode.h" #define DRM_IOCTL_BASE 'd' @@ -673,7 +684,8 @@ struct drm_get_cap { #define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, struct drm_lock) #define DRM_IOCTL_FINISH DRM_IOW( 0x2c, struct drm_lock) -#define DRM_IOCTL_GEM_PRIME_OPEN DRM_IOWR(0x2e, struct drm_gem_open) +#define DRM_IOCTL_PRIME_HANDLE_TO_FD DRM_IOWR(0x2d, struct drm_prime_handle) +#define DRM_IOCTL_PRIME_FD_TO_HANDLE DRM_IOWR(0x2e, struct drm_prime_handle) #define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) #define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 574bd1c81ebd..dd731043fecd 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -91,6 +91,7 @@ struct drm_device; #define DRM_UT_CORE 0x01 #define DRM_UT_DRIVER 0x02 #define DRM_UT_KMS 0x04 +#define DRM_UT_PRIME 0x08 /* * Three debug levels are defined. * drm_core, drm_driver, drm_kms @@ -150,6 +151,7 @@ int drm_err(const char *func, const char *format, ...); #define DRIVER_IRQ_VBL2 0x800 #define DRIVER_GEM 0x1000 #define DRIVER_MODESET 0x2000 +#define DRIVER_PRIME 0x4000 #define DRIVER_BUS_PCI 0x1 #define DRIVER_BUS_PLATFORM 0x2 @@ -215,6 +217,11 @@ int drm_err(const char *func, const char *format, ...); drm_ut_debug_printk(DRM_UT_KMS, DRM_NAME, \ __func__, fmt, ##args); \ } while (0) +#define DRM_DEBUG_PRIME(fmt, args...) \ + do { \ + drm_ut_debug_printk(DRM_UT_PRIME, DRM_NAME, \ + __func__, fmt, ##args); \ + } while (0) #define DRM_LOG(fmt, args...) \ do { \ drm_ut_debug_printk(DRM_UT_CORE, NULL, \ @@ -238,6 +245,7 @@ int drm_err(const char *func, const char *format, ...); #else #define DRM_DEBUG_DRIVER(fmt, args...) do { } while (0) #define DRM_DEBUG_KMS(fmt, args...) do { } while (0) +#define DRM_DEBUG_PRIME(fmt, args...) do { } while (0) #define DRM_DEBUG(fmt, arg...) do { } while (0) #define DRM_LOG(fmt, arg...) do { } while (0) #define DRM_LOG_KMS(fmt, args...) do { } while (0) @@ -410,6 +418,12 @@ struct drm_pending_event { void (*destroy)(struct drm_pending_event *event); }; +/* initial implementaton using a linked list - todo hashtab */ +struct drm_prime_file_private { + struct list_head head; + struct mutex lock; +}; + /** File private data */ struct drm_file { int authenticated; @@ -437,6 +451,8 @@ struct drm_file { wait_queue_head_t event_wait; struct list_head event_list; int event_space; + + struct drm_prime_file_private prime; }; /** Wait queue */ @@ -652,6 +668,12 @@ struct drm_gem_object { uint32_t pending_write_domain; void *driver_private; + + /* dma buf exported from this GEM object */ + struct dma_buf *export_dma_buf; + + /* dma buf attachment backing this object */ + struct dma_buf_attachment *import_attach; }; #include "drm_crtc.h" @@ -890,6 +912,20 @@ struct drm_driver { int (*gem_open_object) (struct drm_gem_object *, struct drm_file *); void (*gem_close_object) (struct drm_gem_object *, struct drm_file *); + /* prime: */ + /* export handle -> fd (see drm_gem_prime_handle_to_fd() helper) */ + int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv, + uint32_t handle, uint32_t flags, int *prime_fd); + /* import fd -> handle (see drm_gem_prime_fd_to_handle() helper) */ + int (*prime_fd_to_handle)(struct drm_device *dev, struct drm_file *file_priv, + int prime_fd, uint32_t *handle); + /* export GEM -> dmabuf */ + struct dma_buf * (*gem_prime_export)(struct drm_device *dev, + struct drm_gem_object *obj, int flags); + /* import dmabuf -> GEM */ + struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev, + struct dma_buf *dma_buf); + /* vga arb irq handler */ void (*vgaarb_irq)(struct drm_device *dev, bool state); @@ -1509,6 +1545,32 @@ extern int drm_vblank_info(struct seq_file *m, void *data); extern int drm_clients_info(struct seq_file *m, void* data); extern int drm_gem_name_info(struct seq_file *m, void *data); + +extern int drm_gem_prime_handle_to_fd(struct drm_device *dev, + struct drm_file *file_priv, uint32_t handle, uint32_t flags, + int *prime_fd); +extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, + struct drm_file *file_priv, int prime_fd, uint32_t *handle); + +extern int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +extern struct sg_table *drm_prime_pages_to_sg(struct page **pages, int nr_pages); +extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); + + +void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); +void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); +int drm_prime_add_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle); +int drm_prime_lookup_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); +void drm_prime_remove_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); + +int drm_prime_add_dma_buf(struct drm_device *dev, struct drm_gem_object *obj); +int drm_prime_lookup_obj(struct drm_device *dev, struct dma_buf *buf, + struct drm_gem_object **obj); + #if DRM_DEBUG_CODE extern int drm_vma_info(struct seq_file *m, void *data); #endif diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 3963116083ae..e478de4e5d56 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -85,7 +85,7 @@ struct drm_exynos_gem_mmap { struct drm_exynos_vidi_connection { unsigned int connection; unsigned int extensions; - uint64_t *edid; + uint64_t edid; }; struct drm_exynos_plane_set_zpos { @@ -96,7 +96,8 @@ struct drm_exynos_plane_set_zpos { /* memory type definitions. */ enum e_drm_exynos_gem_mem_type { /* Physically Non-Continuous memory. */ - EXYNOS_BO_NONCONTIG = 1 << 0 + EXYNOS_BO_NONCONTIG = 1 << 0, + EXYNOS_BO_MASK = EXYNOS_BO_NONCONTIG }; #define DRM_EXYNOS_GEM_CREATE 0x00 diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 0a0001b9dc78..923afb5dcf0c 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -44,4 +44,8 @@ void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries, /* flag for GFDT type */ #define AGP_USER_CACHED_MEMORY_GFDT (1 << 3) +#ifdef CONFIG_INTEL_IOMMU +extern int intel_iommu_gfx_mapped; +#endif + #endif diff --git a/include/linux/Kbuild b/include/linux/Kbuild index d05df2810354..3c9b616c834a 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -3,6 +3,7 @@ header-y += can/ header-y += caif/ header-y += dvb/ header-y += hdlc/ +header-y += hsi/ header-y += isdn/ header-y += mmc/ header-y += nfsd/ diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 7847e197730a..8d54f79457ba 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -30,7 +30,6 @@ struct amba_device { struct device dev; struct resource res; struct clk *pclk; - struct regulator *vcore; u64 dma_mask; unsigned int periphid; unsigned int irq[AMBA_NR_IRQS]; @@ -75,12 +74,6 @@ void amba_release_regions(struct amba_device *); #define amba_pclk_disable(d) \ do { if (!IS_ERR((d)->pclk)) clk_disable((d)->pclk); } while (0) -#define amba_vcore_enable(d) \ - (IS_ERR((d)->vcore) ? 0 : regulator_enable((d)->vcore)) - -#define amba_vcore_disable(d) \ - do { if (!IS_ERR((d)->vcore)) regulator_disable((d)->vcore); } while (0) - /* Some drivers don't use the struct amba_device */ #define AMBA_CONFIG_BITS(a) (((a) >> 24) & 0xff) #define AMBA_REV_BITS(a) (((a) >> 20) & 0x0f) diff --git a/include/linux/amba/pl022.h b/include/linux/amba/pl022.h index b8c51124ed19..76dd1b199a1b 100644 --- a/include/linux/amba/pl022.h +++ b/include/linux/amba/pl022.h @@ -25,6 +25,8 @@ #ifndef _SSP_PL022_H #define _SSP_PL022_H +#include <linux/types.h> + /** * whether SSP is in loopback mode or not */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 606cf339bb56..2aa24664a5b5 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -426,14 +426,10 @@ struct request_queue { (1 << QUEUE_FLAG_SAME_COMP) | \ (1 << QUEUE_FLAG_ADD_RANDOM)) -static inline int queue_is_locked(struct request_queue *q) +static inline void queue_lockdep_assert_held(struct request_queue *q) { -#ifdef CONFIG_SMP - spinlock_t *lock = q->queue_lock; - return lock && spin_is_locked(lock); -#else - return 1; -#endif + if (q->queue_lock) + lockdep_assert_held(q->queue_lock); } static inline void queue_flag_set_unlocked(unsigned int flag, @@ -445,7 +441,7 @@ static inline void queue_flag_set_unlocked(unsigned int flag, static inline int queue_flag_test_and_clear(unsigned int flag, struct request_queue *q) { - WARN_ON_ONCE(!queue_is_locked(q)); + queue_lockdep_assert_held(q); if (test_bit(flag, &q->queue_flags)) { __clear_bit(flag, &q->queue_flags); @@ -458,7 +454,7 @@ static inline int queue_flag_test_and_clear(unsigned int flag, static inline int queue_flag_test_and_set(unsigned int flag, struct request_queue *q) { - WARN_ON_ONCE(!queue_is_locked(q)); + queue_lockdep_assert_held(q); if (!test_bit(flag, &q->queue_flags)) { __set_bit(flag, &q->queue_flags); @@ -470,7 +466,7 @@ static inline int queue_flag_test_and_set(unsigned int flag, static inline void queue_flag_set(unsigned int flag, struct request_queue *q) { - WARN_ON_ONCE(!queue_is_locked(q)); + queue_lockdep_assert_held(q); __set_bit(flag, &q->queue_flags); } @@ -487,7 +483,7 @@ static inline int queue_in_flight(struct request_queue *q) static inline void queue_flag_clear(unsigned int flag, struct request_queue *q) { - WARN_ON_ONCE(!queue_is_locked(q)); + queue_lockdep_assert_held(q); __clear_bit(flag, &q->queue_flags); } diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 1ffdb9856bb9..a2c819d3c96e 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -764,12 +764,6 @@ static inline const struct cpumask *get_cpu_mask(unsigned int cpu) * */ #ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS -/* These strip const, as traditionally they weren't const. */ -#define cpu_possible_map (*(cpumask_t *)cpu_possible_mask) -#define cpu_online_map (*(cpumask_t *)cpu_online_mask) -#define cpu_present_map (*(cpumask_t *)cpu_present_mask) -#define cpu_active_map (*(cpumask_t *)cpu_active_mask) - #define cpumask_of_cpu(cpu) (*get_cpu_mask(cpu)) #define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) diff --git a/include/linux/cryptouser.h b/include/linux/cryptouser.h index 532fb58f16bf..4abf2ea6a887 100644 --- a/include/linux/cryptouser.h +++ b/include/linux/cryptouser.h @@ -100,3 +100,6 @@ struct crypto_report_rng { char type[CRYPTO_MAX_NAME]; unsigned int seedsize; }; + +#define CRYPTO_REPORT_MAXSIZE (sizeof(struct crypto_user_alg) + \ + sizeof(struct crypto_report_blkcipher)) diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h index 71ad34eca6e3..547ab568d3ae 100644 --- a/include/linux/dma-attrs.h +++ b/include/linux/dma-attrs.h @@ -13,6 +13,8 @@ enum dma_attr { DMA_ATTR_WRITE_BARRIER, DMA_ATTR_WEAK_ORDERING, + DMA_ATTR_WRITE_COMBINE, + DMA_ATTR_NON_CONSISTENT, DMA_ATTR_MAX, }; diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 5a736af3cc7a..dfc099e56a66 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -9,10 +9,15 @@ #include <linux/scatterlist.h> struct dma_map_ops { - void* (*alloc_coherent)(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp); - void (*free_coherent)(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle); + void* (*alloc)(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs); + void (*free)(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs); + int (*mmap)(struct device *, struct vm_area_struct *, + void *, dma_addr_t, size_t, struct dma_attrs *attrs); + dma_addr_t (*map_page)(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 676f967390ae..f9a2e5e67a54 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -974,6 +974,7 @@ int dma_async_device_register(struct dma_device *device); void dma_async_device_unregister(struct dma_device *device); void dma_run_dependencies(struct dma_async_tx_descriptor *tx); struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type); +struct dma_chan *net_dma_find_channel(void); #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y) /* --- Helper iov-locking functions --- */ diff --git a/include/linux/efi.h b/include/linux/efi.h index 88ec80670d5f..ec45ccd8708a 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -554,7 +554,18 @@ extern int __init efi_setup_pcdp_console(char *); #define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 #define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 #define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 - +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x0000000000000008 +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x0000000000000010 +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x0000000000000020 +#define EFI_VARIABLE_APPEND_WRITE 0x0000000000000040 + +#define EFI_VARIABLE_MASK (EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ + EFI_VARIABLE_RUNTIME_ACCESS | \ + EFI_VARIABLE_HARDWARE_ERROR_RECORD | \ + EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \ + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \ + EFI_VARIABLE_APPEND_WRITE) /* * The type of search to perform when calling boottime->locate_handle */ diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 8a1835855faa..fe5136d81454 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -159,7 +159,8 @@ static inline void eth_hw_addr_random(struct net_device *dev) * @addr1: Pointer to a six-byte array containing the Ethernet address * @addr2: Pointer other six-byte array containing the Ethernet address * - * Compare two ethernet addresses, returns 0 if equal + * Compare two ethernet addresses, returns 0 if equal, non-zero otherwise. + * Unlike memcmp(), it doesn't return a value suitable for sorting. */ static inline unsigned compare_ether_addr(const u8 *addr1, const u8 *addr2) { @@ -184,10 +185,10 @@ static inline unsigned long zap_last_2bytes(unsigned long value) * @addr1: Pointer to an array of 8 bytes * @addr2: Pointer to an other array of 8 bytes * - * Compare two ethernet addresses, returns 0 if equal. - * Same result than "memcmp(addr1, addr2, ETH_ALEN)" but without conditional - * branches, and possibly long word memory accesses on CPU allowing cheap - * unaligned memory reads. + * Compare two ethernet addresses, returns 0 if equal, non-zero otherwise. + * Unlike memcmp(), it doesn't return a value suitable for sorting. + * The function doesn't need any conditional branches and possibly uses + * word memory accesses on CPU allowing cheap unaligned memory reads. * arrays = { byte1, byte2, byte3, byte4, byte6, byte7, pad1, pad2} * * Please note that alignment of addr1 & addr2 is only guaranted to be 16 bits. diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index e1d9e0ede309..f5647b59a90e 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -896,8 +896,7 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings) * * All operations are optional (i.e. the function pointer may be set * to %NULL) and callers must take this into account. Callers must - * hold the RTNL, except that for @get_drvinfo the caller may or may - * not hold the RTNL. + * hold the RTNL lock. * * See the structures used by these operations for further documentation. * diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 4db7b68f0582..cdc9b719e9c7 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -2,6 +2,7 @@ #define _LINUX_FIREWIRE_H #include <linux/completion.h> +#include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/kernel.h> #include <linux/kref.h> @@ -64,8 +65,6 @@ #define CSR_MODEL 0x17 #define CSR_DIRECTORY_ID 0x20 -struct device; - struct fw_csr_iterator { const u32 *p; const u32 *end; diff --git a/include/linux/fs.h b/include/linux/fs.h index 135693e79f2b..8de675523e46 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1215,6 +1215,7 @@ extern int vfs_setlease(struct file *, long, struct file_lock **); extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); +extern void locks_delete_block(struct file_lock *waiter); extern void lock_flocks(void); extern void unlock_flocks(void); #else /* !CONFIG_FILE_LOCKING */ @@ -1359,6 +1360,10 @@ static inline int lock_may_write(struct inode *inode, loff_t start, return 1; } +static inline void locks_delete_block(struct file_lock *waiter) +{ +} + static inline void lock_flocks(void) { } @@ -2506,6 +2511,7 @@ extern int dcache_readdir(struct file *, void *, filldir_t); extern int simple_setattr(struct dentry *, struct iattr *); extern int simple_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int simple_statfs(struct dentry *, struct kstatfs *); +extern int simple_open(struct inode *inode, struct file *file); extern int simple_link(struct dentry *, struct inode *, struct dentry *); extern int simple_unlink(struct inode *, struct dentry *); extern int simple_rmdir(struct inode *, struct dentry *); diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 8ba2c9460b28..8f2ab8fef929 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -593,7 +593,7 @@ struct fuse_dirent { __u64 off; __u32 namelen; __u32 type; - char name[0]; + char name[]; }; #define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) diff --git a/include/linux/gpio-pxa.h b/include/linux/gpio-pxa.h index 05071ee34c3f..d755b28ba635 100644 --- a/include/linux/gpio-pxa.h +++ b/include/linux/gpio-pxa.h @@ -13,4 +13,8 @@ extern int pxa_last_gpio; extern int pxa_irq_to_gpio(int irq); +struct pxa_gpio_platform_data { + int (*gpio_set_wake)(unsigned int gpio, unsigned int on); +}; + #endif /* __GPIO_PXA_H */ diff --git a/include/linux/hsi/Kbuild b/include/linux/hsi/Kbuild new file mode 100644 index 000000000000..271a770b4784 --- /dev/null +++ b/include/linux/hsi/Kbuild @@ -0,0 +1 @@ +header-y += hsi_char.h diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h new file mode 100644 index 000000000000..56fae865e272 --- /dev/null +++ b/include/linux/hsi/hsi.h @@ -0,0 +1,413 @@ +/* + * HSI core header file. + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * + * Contact: Carlos Chinea <carlos.chinea@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __LINUX_HSI_H__ +#define __LINUX_HSI_H__ + +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/scatterlist.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/notifier.h> + +/* HSI message ttype */ +#define HSI_MSG_READ 0 +#define HSI_MSG_WRITE 1 + +/* HSI configuration values */ +enum { + HSI_MODE_STREAM = 1, + HSI_MODE_FRAME, +}; + +enum { + HSI_FLOW_SYNC, /* Synchronized flow */ + HSI_FLOW_PIPE, /* Pipelined flow */ +}; + +enum { + HSI_ARB_RR, /* Round-robin arbitration */ + HSI_ARB_PRIO, /* Channel priority arbitration */ +}; + +#define HSI_MAX_CHANNELS 16 + +/* HSI message status codes */ +enum { + HSI_STATUS_COMPLETED, /* Message transfer is completed */ + HSI_STATUS_PENDING, /* Message pending to be read/write (POLL) */ + HSI_STATUS_PROCEEDING, /* Message transfer is ongoing */ + HSI_STATUS_QUEUED, /* Message waiting to be served */ + HSI_STATUS_ERROR, /* Error when message transfer was ongoing */ +}; + +/* HSI port event codes */ +enum { + HSI_EVENT_START_RX, + HSI_EVENT_STOP_RX, +}; + +/** + * struct hsi_config - Configuration for RX/TX HSI modules + * @mode: Bit transmission mode (STREAM or FRAME) + * @channels: Number of channels to use [1..16] + * @speed: Max bit transmission speed (Kbit/s) + * @flow: RX flow type (SYNCHRONIZED or PIPELINE) + * @arb_mode: Arbitration mode for TX frame (Round robin, priority) + */ +struct hsi_config { + unsigned int mode; + unsigned int channels; + unsigned int speed; + union { + unsigned int flow; /* RX only */ + unsigned int arb_mode; /* TX only */ + }; +}; + +/** + * struct hsi_board_info - HSI client board info + * @name: Name for the HSI device + * @hsi_id: HSI controller id where the client sits + * @port: Port number in the controller where the client sits + * @tx_cfg: HSI TX configuration + * @rx_cfg: HSI RX configuration + * @platform_data: Platform related data + * @archdata: Architecture-dependent device data + */ +struct hsi_board_info { + const char *name; + unsigned int hsi_id; + unsigned int port; + struct hsi_config tx_cfg; + struct hsi_config rx_cfg; + void *platform_data; + struct dev_archdata *archdata; +}; + +#ifdef CONFIG_HSI_BOARDINFO +extern int hsi_register_board_info(struct hsi_board_info const *info, + unsigned int len); +#else +static inline int hsi_register_board_info(struct hsi_board_info const *info, + unsigned int len) +{ + return 0; +} +#endif /* CONFIG_HSI_BOARDINFO */ + +/** + * struct hsi_client - HSI client attached to an HSI port + * @device: Driver model representation of the device + * @tx_cfg: HSI TX configuration + * @rx_cfg: HSI RX configuration + * @e_handler: Callback for handling port events (RX Wake High/Low) + * @pclaimed: Keeps tracks if the clients claimed its associated HSI port + * @nb: Notifier block for port events + */ +struct hsi_client { + struct device device; + struct hsi_config tx_cfg; + struct hsi_config rx_cfg; + /* private: */ + void (*ehandler)(struct hsi_client *, unsigned long); + unsigned int pclaimed:1; + struct notifier_block nb; +}; + +#define to_hsi_client(dev) container_of(dev, struct hsi_client, device) + +static inline void hsi_client_set_drvdata(struct hsi_client *cl, void *data) +{ + dev_set_drvdata(&cl->device, data); +} + +static inline void *hsi_client_drvdata(struct hsi_client *cl) +{ + return dev_get_drvdata(&cl->device); +} + +int hsi_register_port_event(struct hsi_client *cl, + void (*handler)(struct hsi_client *, unsigned long)); +int hsi_unregister_port_event(struct hsi_client *cl); + +/** + * struct hsi_client_driver - Driver associated to an HSI client + * @driver: Driver model representation of the driver + */ +struct hsi_client_driver { + struct device_driver driver; +}; + +#define to_hsi_client_driver(drv) container_of(drv, struct hsi_client_driver,\ + driver) + +int hsi_register_client_driver(struct hsi_client_driver *drv); + +static inline void hsi_unregister_client_driver(struct hsi_client_driver *drv) +{ + driver_unregister(&drv->driver); +} + +/** + * struct hsi_msg - HSI message descriptor + * @link: Free to use by the current descriptor owner + * @cl: HSI device client that issues the transfer + * @sgt: Head of the scatterlist array + * @context: Client context data associated to the transfer + * @complete: Transfer completion callback + * @destructor: Destructor to free resources when flushing + * @status: Status of the transfer when completed + * @actual_len: Actual length of data transfered on completion + * @channel: Channel were to TX/RX the message + * @ttype: Transfer type (TX if set, RX otherwise) + * @break_frame: if true HSI will send/receive a break frame. Data buffers are + * ignored in the request. + */ +struct hsi_msg { + struct list_head link; + struct hsi_client *cl; + struct sg_table sgt; + void *context; + + void (*complete)(struct hsi_msg *msg); + void (*destructor)(struct hsi_msg *msg); + + int status; + unsigned int actual_len; + unsigned int channel; + unsigned int ttype:1; + unsigned int break_frame:1; +}; + +struct hsi_msg *hsi_alloc_msg(unsigned int n_frag, gfp_t flags); +void hsi_free_msg(struct hsi_msg *msg); + +/** + * struct hsi_port - HSI port device + * @device: Driver model representation of the device + * @tx_cfg: Current TX path configuration + * @rx_cfg: Current RX path configuration + * @num: Port number + * @shared: Set when port can be shared by different clients + * @claimed: Reference count of clients which claimed the port + * @lock: Serialize port claim + * @async: Asynchronous transfer callback + * @setup: Callback to set the HSI client configuration + * @flush: Callback to clean the HW state and destroy all pending transfers + * @start_tx: Callback to inform that a client wants to TX data + * @stop_tx: Callback to inform that a client no longer wishes to TX data + * @release: Callback to inform that a client no longer uses the port + * @n_head: Notifier chain for signaling port events to the clients. + */ +struct hsi_port { + struct device device; + struct hsi_config tx_cfg; + struct hsi_config rx_cfg; + unsigned int num; + unsigned int shared:1; + int claimed; + struct mutex lock; + int (*async)(struct hsi_msg *msg); + int (*setup)(struct hsi_client *cl); + int (*flush)(struct hsi_client *cl); + int (*start_tx)(struct hsi_client *cl); + int (*stop_tx)(struct hsi_client *cl); + int (*release)(struct hsi_client *cl); + /* private */ + struct atomic_notifier_head n_head; +}; + +#define to_hsi_port(dev) container_of(dev, struct hsi_port, device) +#define hsi_get_port(cl) to_hsi_port((cl)->device.parent) + +int hsi_event(struct hsi_port *port, unsigned long event); +int hsi_claim_port(struct hsi_client *cl, unsigned int share); +void hsi_release_port(struct hsi_client *cl); + +static inline int hsi_port_claimed(struct hsi_client *cl) +{ + return cl->pclaimed; +} + +static inline void hsi_port_set_drvdata(struct hsi_port *port, void *data) +{ + dev_set_drvdata(&port->device, data); +} + +static inline void *hsi_port_drvdata(struct hsi_port *port) +{ + return dev_get_drvdata(&port->device); +} + +/** + * struct hsi_controller - HSI controller device + * @device: Driver model representation of the device + * @owner: Pointer to the module owning the controller + * @id: HSI controller ID + * @num_ports: Number of ports in the HSI controller + * @port: Array of HSI ports + */ +struct hsi_controller { + struct device device; + struct module *owner; + unsigned int id; + unsigned int num_ports; + struct hsi_port **port; +}; + +#define to_hsi_controller(dev) container_of(dev, struct hsi_controller, device) + +struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags); +void hsi_put_controller(struct hsi_controller *hsi); +int hsi_register_controller(struct hsi_controller *hsi); +void hsi_unregister_controller(struct hsi_controller *hsi); + +static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi, + void *data) +{ + dev_set_drvdata(&hsi->device, data); +} + +static inline void *hsi_controller_drvdata(struct hsi_controller *hsi) +{ + return dev_get_drvdata(&hsi->device); +} + +static inline struct hsi_port *hsi_find_port_num(struct hsi_controller *hsi, + unsigned int num) +{ + return (num < hsi->num_ports) ? hsi->port[num] : NULL; +} + +/* + * API for HSI clients + */ +int hsi_async(struct hsi_client *cl, struct hsi_msg *msg); + +/** + * hsi_id - Get HSI controller ID associated to a client + * @cl: Pointer to a HSI client + * + * Return the controller id where the client is attached to + */ +static inline unsigned int hsi_id(struct hsi_client *cl) +{ + return to_hsi_controller(cl->device.parent->parent)->id; +} + +/** + * hsi_port_id - Gets the port number a client is attached to + * @cl: Pointer to HSI client + * + * Return the port number associated to the client + */ +static inline unsigned int hsi_port_id(struct hsi_client *cl) +{ + return to_hsi_port(cl->device.parent)->num; +} + +/** + * hsi_setup - Configure the client's port + * @cl: Pointer to the HSI client + * + * When sharing ports, clients should either relay on a single + * client setup or have the same setup for all of them. + * + * Return -errno on failure, 0 on success + */ +static inline int hsi_setup(struct hsi_client *cl) +{ + if (!hsi_port_claimed(cl)) + return -EACCES; + return hsi_get_port(cl)->setup(cl); +} + +/** + * hsi_flush - Flush all pending transactions on the client's port + * @cl: Pointer to the HSI client + * + * This function will destroy all pending hsi_msg in the port and reset + * the HW port so it is ready to receive and transmit from a clean state. + * + * Return -errno on failure, 0 on success + */ +static inline int hsi_flush(struct hsi_client *cl) +{ + if (!hsi_port_claimed(cl)) + return -EACCES; + return hsi_get_port(cl)->flush(cl); +} + +/** + * hsi_async_read - Submit a read transfer + * @cl: Pointer to the HSI client + * @msg: HSI message descriptor of the transfer + * + * Return -errno on failure, 0 on success + */ +static inline int hsi_async_read(struct hsi_client *cl, struct hsi_msg *msg) +{ + msg->ttype = HSI_MSG_READ; + return hsi_async(cl, msg); +} + +/** + * hsi_async_write - Submit a write transfer + * @cl: Pointer to the HSI client + * @msg: HSI message descriptor of the transfer + * + * Return -errno on failure, 0 on success + */ +static inline int hsi_async_write(struct hsi_client *cl, struct hsi_msg *msg) +{ + msg->ttype = HSI_MSG_WRITE; + return hsi_async(cl, msg); +} + +/** + * hsi_start_tx - Signal the port that the client wants to start a TX + * @cl: Pointer to the HSI client + * + * Return -errno on failure, 0 on success + */ +static inline int hsi_start_tx(struct hsi_client *cl) +{ + if (!hsi_port_claimed(cl)) + return -EACCES; + return hsi_get_port(cl)->start_tx(cl); +} + +/** + * hsi_stop_tx - Signal the port that the client no longer wants to transmit + * @cl: Pointer to the HSI client + * + * Return -errno on failure, 0 on success + */ +static inline int hsi_stop_tx(struct hsi_client *cl) +{ + if (!hsi_port_claimed(cl)) + return -EACCES; + return hsi_get_port(cl)->stop_tx(cl); +} +#endif /* __LINUX_HSI_H__ */ diff --git a/include/linux/hsi/hsi_char.h b/include/linux/hsi/hsi_char.h new file mode 100644 index 000000000000..76160b4f455d --- /dev/null +++ b/include/linux/hsi/hsi_char.h @@ -0,0 +1,63 @@ +/* + * Part of the HSI character device driver. + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * + * Contact: Andras Domokos <andras.domokos at nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + + +#ifndef __HSI_CHAR_H +#define __HSI_CHAR_H + +#define HSI_CHAR_MAGIC 'k' +#define HSC_IOW(num, dtype) _IOW(HSI_CHAR_MAGIC, num, dtype) +#define HSC_IOR(num, dtype) _IOR(HSI_CHAR_MAGIC, num, dtype) +#define HSC_IOWR(num, dtype) _IOWR(HSI_CHAR_MAGIC, num, dtype) +#define HSC_IO(num) _IO(HSI_CHAR_MAGIC, num) + +#define HSC_RESET HSC_IO(16) +#define HSC_SET_PM HSC_IO(17) +#define HSC_SEND_BREAK HSC_IO(18) +#define HSC_SET_RX HSC_IOW(19, struct hsc_rx_config) +#define HSC_GET_RX HSC_IOW(20, struct hsc_rx_config) +#define HSC_SET_TX HSC_IOW(21, struct hsc_tx_config) +#define HSC_GET_TX HSC_IOW(22, struct hsc_tx_config) + +#define HSC_PM_DISABLE 0 +#define HSC_PM_ENABLE 1 + +#define HSC_MODE_STREAM 1 +#define HSC_MODE_FRAME 2 +#define HSC_FLOW_SYNC 0 +#define HSC_ARB_RR 0 +#define HSC_ARB_PRIO 1 + +struct hsc_rx_config { + uint32_t mode; + uint32_t flow; + uint32_t channels; +}; + +struct hsc_tx_config { + uint32_t mode; + uint32_t channels; + uint32_t speed; + uint32_t arb_mode; +}; + +#endif /* __HSI_CHAR_H */ diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 2463b6100333..1f90de0cfdbe 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -666,23 +666,11 @@ struct twl4030_codec_data { unsigned int check_defaults:1; unsigned int reset_registers:1; unsigned int hs_extmute:1; - u16 hs_left_step; - u16 hs_right_step; - u16 hf_left_step; - u16 hf_right_step; void (*set_hs_extmute)(int mute); }; struct twl4030_vibra_data { unsigned int coexist; - - /* twl6040 */ - unsigned int vibldrv_res; /* left driver resistance */ - unsigned int vibrdrv_res; /* right driver resistance */ - unsigned int viblmotor_res; /* left motor resistance */ - unsigned int vibrmotor_res; /* right motor resistance */ - int vddvibl_uV; /* VDDVIBL volt, set 0 for fixed reg */ - int vddvibr_uV; /* VDDVIBR volt, set 0 for fixed reg */ }; struct twl4030_audio_data { diff --git a/include/linux/if_eql.h b/include/linux/if_eql.h index 79c4f268410d..18a5d02a8644 100644 --- a/include/linux/if_eql.h +++ b/include/linux/if_eql.h @@ -22,7 +22,7 @@ #define EQL_DEFAULT_SLAVE_PRIORITY 28800 #define EQL_DEFAULT_MAX_SLAVES 4 #define EQL_DEFAULT_MTU 576 -#define EQL_DEFAULT_RESCHED_IVAL 100 +#define EQL_DEFAULT_RESCHED_IVAL HZ #define EQL_ENSLAVE (SIOCDEVPRIVATE) #define EQL_EMANCIPATE (SIOCDEVPRIVATE + 1) diff --git a/include/linux/irq.h b/include/linux/irq.h index bff29c58da23..b27cfcfd3a59 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -49,6 +49,12 @@ typedef void (*irq_preflow_handler_t)(struct irq_data *data); * IRQ_TYPE_LEVEL_LOW - low level triggered * IRQ_TYPE_LEVEL_MASK - Mask to filter out the level bits * IRQ_TYPE_SENSE_MASK - Mask for all the above bits + * IRQ_TYPE_DEFAULT - For use by some PICs to ask irq_set_type + * to setup the HW to a sane default (used + * by irqdomain map() callbacks to synchronize + * the HW state and SW flags for a newly + * allocated descriptor). + * * IRQ_TYPE_PROBE - Special flag for probing in progress * * Bits which can be modified via irq_set/clear/modify_status_flags() @@ -77,6 +83,7 @@ enum { IRQ_TYPE_LEVEL_LOW = 0x00000008, IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH), IRQ_TYPE_SENSE_MASK = 0x0000000f, + IRQ_TYPE_DEFAULT = IRQ_TYPE_SENSE_MASK, IRQ_TYPE_PROBE = 0x00000010, @@ -263,6 +270,11 @@ static inline void irqd_clr_chained_irq_inprogress(struct irq_data *d) d->state_use_accessors &= ~IRQD_IRQ_INPROGRESS; } +static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) +{ + return d->hwirq; +} + /** * struct irq_chip - hardware interrupt chip descriptor * diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index ead4a4215797..c65740d76e66 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -42,12 +42,6 @@ struct of_device_id; /* Number of irqs reserved for a legacy isa controller */ #define NUM_ISA_INTERRUPTS 16 -/* This type is the placeholder for a hardware interrupt number. It has to - * be big enough to enclose whatever representation is used by a given - * platform. - */ -typedef unsigned long irq_hw_number_t; - /** * struct irq_domain_ops - Methods for irq_domain objects * @match: Match an interrupt controller device node to a host, returns @@ -104,6 +98,9 @@ struct irq_domain { unsigned int size; unsigned int *revmap; } linear; + struct { + unsigned int max_irq; + } nomap; struct radix_tree_root tree; } revmap_data; const struct irq_domain_ops *ops; @@ -126,6 +123,7 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node, const struct irq_domain_ops *ops, void *host_data); struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, + unsigned int max_irq, const struct irq_domain_ops *ops, void *host_data); struct irq_domain *irq_domain_add_tree(struct device_node *of_node, @@ -134,7 +132,6 @@ struct irq_domain *irq_domain_add_tree(struct device_node *of_node, extern struct irq_domain *irq_find_host(struct device_node *node); extern void irq_set_default_host(struct irq_domain *host); -extern void irq_set_virq_count(unsigned int count); static inline struct irq_domain *irq_domain_add_legacy_isa( struct device_node *of_node, @@ -146,7 +143,6 @@ static inline struct irq_domain *irq_domain_add_legacy_isa( } extern struct irq_domain *irq_find_host(struct device_node *node); extern void irq_set_default_host(struct irq_domain *host); -extern void irq_set_virq_count(unsigned int count); extern unsigned int irq_create_mapping(struct irq_domain *host, diff --git a/include/linux/kconfig.h b/include/linux/kconfig.h index 067eda0e4b32..be342b94c640 100644 --- a/include/linux/kconfig.h +++ b/include/linux/kconfig.h @@ -4,29 +4,43 @@ #include <generated/autoconf.h> /* - * Helper macros to use CONFIG_ options in C expressions. Note that + * Helper macros to use CONFIG_ options in C/CPP expressions. Note that * these only work with boolean and tristate options. */ /* + * Getting something that works in C and CPP for an arg that may or may + * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1" + * we match on the placeholder define, insert the "0," for arg1 and generate + * the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one). + * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when + * the last step cherry picks the 2nd arg, we get a zero. + */ +#define __ARG_PLACEHOLDER_1 0, +#define config_enabled(cfg) _config_enabled(cfg) +#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value) +#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0) +#define ___config_enabled(__ignored, val, ...) val + +/* * IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm', * 0 otherwise. * */ #define IS_ENABLED(option) \ - (__enabled_ ## option || __enabled_ ## option ## _MODULE) + (config_enabled(option) || config_enabled(option##_MODULE)) /* * IS_BUILTIN(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y', 0 * otherwise. For boolean options, this is equivalent to * IS_ENABLED(CONFIG_FOO). */ -#define IS_BUILTIN(option) __enabled_ ## option +#define IS_BUILTIN(option) config_enabled(option) /* * IS_MODULE(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'm', 0 * otherwise. */ -#define IS_MODULE(option) __enabled_ ## option ## _MODULE +#define IS_MODULE(option) config_enabled(option##_MODULE) #endif /* __LINUX_KCONFIG_H */ diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h index fa391835508d..c4d2fc194ede 100644 --- a/include/linux/kgdb.h +++ b/include/linux/kgdb.h @@ -63,7 +63,8 @@ enum kgdb_bptype { BP_HARDWARE_BREAKPOINT, BP_WRITE_WATCHPOINT, BP_READ_WATCHPOINT, - BP_ACCESS_WATCHPOINT + BP_ACCESS_WATCHPOINT, + BP_POKE_BREAKPOINT, }; enum kgdb_bpstate { @@ -207,8 +208,8 @@ extern void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc); /* Optional functions. */ extern int kgdb_validate_break_address(unsigned long addr); -extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr); -extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle); +extern int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt); +extern int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt); /** * kgdb_arch_late - Perform any architecture specific initalization. diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 9efeae679106..dd99c329e161 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -110,12 +110,29 @@ call_usermodehelper(char *path, char **argv, char **envp, int wait) extern struct ctl_table usermodehelper_table[]; +enum umh_disable_depth { + UMH_ENABLED = 0, + UMH_FREEZING, + UMH_DISABLED, +}; + extern void usermodehelper_init(void); -extern int usermodehelper_disable(void); -extern void usermodehelper_enable(void); -extern bool usermodehelper_is_disabled(void); -extern void read_lock_usermodehelper(void); -extern void read_unlock_usermodehelper(void); +extern int __usermodehelper_disable(enum umh_disable_depth depth); +extern void __usermodehelper_set_disable_depth(enum umh_disable_depth depth); + +static inline int usermodehelper_disable(void) +{ + return __usermodehelper_disable(UMH_DISABLED); +} + +static inline void usermodehelper_enable(void) +{ + __usermodehelper_set_disable_depth(UMH_ENABLED); +} + +extern int usermodehelper_read_trylock(void); +extern long usermodehelper_read_lock_wait(long timeout); +extern void usermodehelper_read_unlock(void); #endif /* __LINUX_KMOD_H__ */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 665a260c7e09..72cbf08d45fb 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -596,6 +596,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); #ifdef CONFIG_IOMMU_API int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot); +void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot); int kvm_iommu_map_guest(struct kvm *kvm); int kvm_iommu_unmap_guest(struct kvm *kvm); int kvm_assign_device(struct kvm *kvm, @@ -609,6 +610,11 @@ static inline int kvm_iommu_map_pages(struct kvm *kvm, return 0; } +static inline void kvm_iommu_unmap_pages(struct kvm *kvm, + struct kvm_memory_slot *slot) +{ +} + static inline int kvm_iommu_map_guest(struct kvm *kvm) { return -ENODEV; diff --git a/include/linux/libata.h b/include/linux/libata.h index 42378d637ffb..e926df7b54c9 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -996,7 +996,8 @@ extern int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *dev, extern void ata_sas_port_destroy(struct ata_port *); extern struct ata_port *ata_sas_port_alloc(struct ata_host *, struct ata_port_info *, struct Scsi_Host *); -extern int ata_sas_async_port_init(struct ata_port *); +extern void ata_sas_async_probe(struct ata_port *ap); +extern int ata_sas_sync_probe(struct ata_port *ap); extern int ata_sas_port_init(struct ata_port *); extern int ata_sas_port_start(struct ata_port *ap); extern void ata_sas_port_stop(struct ata_port *ap); diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index eab507f2b1cb..fad48aab893b 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -22,6 +22,23 @@ #include <linux/key.h> #include <linux/skbuff.h> +struct lsm_network_audit { + int netif; + struct sock *sk; + u16 family; + __be16 dport; + __be16 sport; + union { + struct { + __be32 daddr; + __be32 saddr; + } v4; + struct { + struct in6_addr daddr; + struct in6_addr saddr; + } v6; + } fam; +}; /* Auxiliary data to use in generating the audit record. */ struct common_audit_data { @@ -41,23 +58,7 @@ struct common_audit_data { struct path path; struct dentry *dentry; struct inode *inode; - struct { - int netif; - struct sock *sk; - u16 family; - __be16 dport; - __be16 sport; - union { - struct { - __be32 daddr; - __be32 saddr; - } v4; - struct { - struct in6_addr daddr; - struct in6_addr saddr; - } v6; - } fam; - } net; + struct lsm_network_audit *net; int cap; int ipc_id; struct task_struct *tsk; @@ -72,64 +73,15 @@ struct common_audit_data { /* this union contains LSM specific data */ union { #ifdef CONFIG_SECURITY_SMACK - /* SMACK data */ - struct smack_audit_data { - const char *function; - char *subject; - char *object; - char *request; - int result; - } smack_audit_data; + struct smack_audit_data *smack_audit_data; #endif #ifdef CONFIG_SECURITY_SELINUX - /* SELinux data */ - struct { - u32 ssid; - u32 tsid; - u16 tclass; - u32 requested; - u32 audited; - u32 denied; - /* - * auditdeny is a bit tricky and unintuitive. See the - * comments in avc.c for it's meaning and usage. - */ - u32 auditdeny; - struct av_decision *avd; - int result; - } selinux_audit_data; + struct selinux_audit_data *selinux_audit_data; #endif #ifdef CONFIG_SECURITY_APPARMOR - struct { - int error; - int op; - int type; - void *profile; - const char *name; - const char *info; - union { - void *target; - struct { - long pos; - void *target; - } iface; - struct { - int rlim; - unsigned long max; - } rlim; - struct { - const char *target; - u32 request; - u32 denied; - uid_t ouid; - } fs; - }; - } apparmor_audit_data; + struct apparmor_audit_data *apparmor_audit_data; #endif - }; - /* these callback will be implemented by a specific LSM */ - void (*lsm_pre_audit)(struct audit_buffer *, void *); - void (*lsm_post_audit)(struct audit_buffer *, void *); + }; /* per LSM data pointer union */ }; #define v4info fam.v4 @@ -146,6 +98,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, { memset((_d), 0, sizeof(struct common_audit_data)); \ (_d)->type = LSM_AUDIT_DATA_##_t; } -void common_lsm_audit(struct common_audit_data *a); +void common_lsm_audit(struct common_audit_data *a, + void (*pre_audit)(struct audit_buffer *, void *), + void (*post_audit)(struct audit_buffer *, void *)); #endif diff --git a/include/linux/mfd/db5500-prcmu.h b/include/linux/mfd/db5500-prcmu.h index 9890687f582d..5a049dfaf153 100644 --- a/include/linux/mfd/db5500-prcmu.h +++ b/include/linux/mfd/db5500-prcmu.h @@ -8,41 +8,14 @@ #ifndef __MFD_DB5500_PRCMU_H #define __MFD_DB5500_PRCMU_H -#ifdef CONFIG_MFD_DB5500_PRCMU - -void db5500_prcmu_early_init(void); -int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state); -int db5500_prcmu_set_display_clocks(void); -int db5500_prcmu_disable_dsipll(void); -int db5500_prcmu_enable_dsipll(void); -int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); -int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); -void db5500_prcmu_enable_wakeups(u32 wakeups); -int db5500_prcmu_request_clock(u8 clock, bool enable); -void db5500_prcmu_config_abb_event_readout(u32 abb_events); -void db5500_prcmu_get_abb_event_buffer(void __iomem **buf); -int prcmu_resetout(u8 resoutn, u8 state); -int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, - bool keep_ap_pll); -int db5500_prcmu_config_esram0_deep_sleep(u8 state); -void db5500_prcmu_system_reset(u16 reset_code); -u16 db5500_prcmu_get_reset_code(void); -bool db5500_prcmu_is_ac_wake_requested(void); -int db5500_prcmu_set_arm_opp(u8 opp); -int db5500_prcmu_get_arm_opp(void); - -#else /* !CONFIG_UX500_SOC_DB5500 */ - -static inline void db5500_prcmu_early_init(void) {} - -static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) +static inline int prcmu_resetout(u8 resoutn, u8 state) { - return -ENOSYS; + return 0; } -static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) +static inline int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state) { - return -ENOSYS; + return 0; } static inline int db5500_prcmu_request_clock(u8 clock, bool enable) @@ -50,69 +23,82 @@ static inline int db5500_prcmu_request_clock(u8 clock, bool enable) return 0; } -static inline int db5500_prcmu_set_display_clocks(void) +static inline int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll) { return 0; } -static inline int db5500_prcmu_disable_dsipll(void) +static inline int db5500_prcmu_config_esram0_deep_sleep(u8 state) { return 0; } -static inline int db5500_prcmu_enable_dsipll(void) +static inline u16 db5500_prcmu_get_reset_code(void) { return 0; } -static inline int db5500_prcmu_config_esram0_deep_sleep(u8 state) +static inline bool db5500_prcmu_is_ac_wake_requested(void) { return 0; } -static inline void db5500_prcmu_enable_wakeups(u32 wakeups) {} - -static inline int prcmu_resetout(u8 resoutn, u8 state) +static inline int db5500_prcmu_set_arm_opp(u8 opp) { return 0; } -static inline int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state) +static inline int db5500_prcmu_get_arm_opp(void) { return 0; } -static inline void db5500_prcmu_get_abb_event_buffer(void __iomem **buf) {} static inline void db5500_prcmu_config_abb_event_readout(u32 abb_events) {} -static inline int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, - bool keep_ap_pll) -{ - return 0; -} +static inline void db5500_prcmu_get_abb_event_buffer(void __iomem **buf) {} static inline void db5500_prcmu_system_reset(u16 reset_code) {} -static inline u16 db5500_prcmu_get_reset_code(void) +static inline void db5500_prcmu_enable_wakeups(u32 wakeups) {} + +#ifdef CONFIG_MFD_DB5500_PRCMU + +void db5500_prcmu_early_init(void); +int db5500_prcmu_set_display_clocks(void); +int db5500_prcmu_disable_dsipll(void); +int db5500_prcmu_enable_dsipll(void); +int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); +int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); + +#else /* !CONFIG_UX500_SOC_DB5500 */ + +static inline void db5500_prcmu_early_init(void) {} + +static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) { - return 0; + return -ENOSYS; } -static inline bool db5500_prcmu_is_ac_wake_requested(void) +static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) { - return 0; + return -ENOSYS; } -static inline int db5500_prcmu_set_arm_opp(u8 opp) +static inline int db5500_prcmu_set_display_clocks(void) { return 0; } -static inline int db5500_prcmu_get_arm_opp(void) +static inline int db5500_prcmu_disable_dsipll(void) { return 0; } +static inline int db5500_prcmu_enable_dsipll(void) +{ + return 0; +} #endif /* CONFIG_MFD_DB5500_PRCMU */ diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index 10e038bac8dd..bf070755982e 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -170,6 +170,16 @@ struct mc13xxx_ts_platform_data { bool atox; }; +enum mc13783_ssi_port { + MC13783_SSI1_PORT, + MC13783_SSI2_PORT, +}; + +struct mc13xxx_codec_platform_data { + enum mc13783_ssi_port adc_ssi_port; + enum mc13783_ssi_port dac_ssi_port; +}; + struct mc13xxx_platform_data { #define MC13XXX_USE_TOUCHSCREEN (1 << 0) #define MC13XXX_USE_CODEC (1 << 1) @@ -181,6 +191,7 @@ struct mc13xxx_platform_data { struct mc13xxx_leds_platform_data *leds; struct mc13xxx_buttons_platform_data *buttons; struct mc13xxx_ts_platform_data touch; + struct mc13xxx_codec_platform_data *codec; }; #define MC13XXX_ADC_MODE_TS 1 diff --git a/include/linux/mfd/rc5t583.h b/include/linux/mfd/rc5t583.h index a2c61609d21d..0b64b19d81ab 100644 --- a/include/linux/mfd/rc5t583.h +++ b/include/linux/mfd/rc5t583.h @@ -26,6 +26,7 @@ #include <linux/mutex.h> #include <linux/types.h> +#include <linux/regmap.h> #define RC5T583_MAX_REGS 0xF8 @@ -279,14 +280,44 @@ struct rc5t583_platform_data { bool enable_shutdown; }; -int rc5t583_write(struct device *dev, u8 reg, uint8_t val); -int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val); -int rc5t583_set_bits(struct device *dev, unsigned int reg, - unsigned int bit_mask); -int rc5t583_clear_bits(struct device *dev, unsigned int reg, - unsigned int bit_mask); -int rc5t583_update(struct device *dev, unsigned int reg, - unsigned int val, unsigned int mask); +static inline int rc5t583_write(struct device *dev, uint8_t reg, uint8_t val) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + return regmap_write(rc5t583->regmap, reg, val); +} + +static inline int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + unsigned int ival; + int ret; + ret = regmap_read(rc5t583->regmap, reg, &ival); + if (!ret) + *val = (uint8_t)ival; + return ret; +} + +static inline int rc5t583_set_bits(struct device *dev, unsigned int reg, + unsigned int bit_mask) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + return regmap_update_bits(rc5t583->regmap, reg, bit_mask, bit_mask); +} + +static inline int rc5t583_clear_bits(struct device *dev, unsigned int reg, + unsigned int bit_mask) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + return regmap_update_bits(rc5t583->regmap, reg, bit_mask, 0); +} + +static inline int rc5t583_update(struct device *dev, unsigned int reg, + unsigned int val, unsigned int mask) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + return regmap_update_bits(rc5t583->regmap, reg, mask, val); +} + int rc5t583_ext_power_req_config(struct device *dev, int deepsleep_id, int ext_pwr_req, int deepsleep_slot_nr); int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base); diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 9bc9ac651dad..b15b5f03f5c4 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -174,8 +174,35 @@ #define TWL6040_SYSCLK_SEL_LPPLL 0 #define TWL6040_SYSCLK_SEL_HPPLL 1 +struct twl6040_codec_data { + u16 hs_left_step; + u16 hs_right_step; + u16 hf_left_step; + u16 hf_right_step; +}; + +struct twl6040_vibra_data { + unsigned int vibldrv_res; /* left driver resistance */ + unsigned int vibrdrv_res; /* right driver resistance */ + unsigned int viblmotor_res; /* left motor resistance */ + unsigned int vibrmotor_res; /* right motor resistance */ + int vddvibl_uV; /* VDDVIBL volt, set 0 for fixed reg */ + int vddvibr_uV; /* VDDVIBR volt, set 0 for fixed reg */ +}; + +struct twl6040_platform_data { + int audpwron_gpio; /* audio power-on gpio */ + unsigned int irq_base; + + struct twl6040_codec_data *codec; + struct twl6040_vibra_data *vibra; +}; + +struct regmap; + struct twl6040 { struct device *dev; + struct regmap *regmap; struct mutex mutex; struct mutex io_mutex; struct mutex irq_mutex; diff --git a/include/linux/mm.h b/include/linux/mm.h index d8738a464b94..74aa71bea1e4 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1393,29 +1393,20 @@ extern int install_special_mapping(struct mm_struct *mm, extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); -extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, - unsigned long len, unsigned long prot, - unsigned long flag, unsigned long pgoff); extern unsigned long mmap_region(struct file *file, unsigned long addr, unsigned long len, unsigned long flags, vm_flags_t vm_flags, unsigned long pgoff); - -static inline unsigned long do_mmap(struct file *file, unsigned long addr, - unsigned long len, unsigned long prot, - unsigned long flag, unsigned long offset) -{ - unsigned long ret = -EINVAL; - if ((offset + PAGE_ALIGN(len)) < offset) - goto out; - if (!(offset & ~PAGE_MASK)) - ret = do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT); -out: - return ret; -} - +extern unsigned long do_mmap(struct file *, unsigned long, + unsigned long, unsigned long, + unsigned long, unsigned long); extern int do_munmap(struct mm_struct *, unsigned long, size_t); -extern unsigned long do_brk(unsigned long, unsigned long); +/* These take the mm semaphore themselves */ +extern unsigned long vm_brk(unsigned long, unsigned long); +extern int vm_munmap(unsigned long, size_t); +extern unsigned long vm_mmap(struct file *, unsigned long, + unsigned long, unsigned long, + unsigned long, unsigned long); /* truncate.c */ extern void truncate_inode_pages(struct address_space *, loff_t); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 01beae78f079..629b823f8836 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -481,7 +481,7 @@ struct mmc_driver { struct device_driver drv; int (*probe)(struct mmc_card *); void (*remove)(struct mmc_card *); - int (*suspend)(struct mmc_card *, pm_message_t); + int (*suspend)(struct mmc_card *); int (*resume)(struct mmc_card *); }; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1f77540bdc95..33900a53c990 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1403,15 +1403,6 @@ static inline bool netdev_uses_dsa_tags(struct net_device *dev) return 0; } -#ifndef CONFIG_NET_NS -static inline void skb_set_dev(struct sk_buff *skb, struct net_device *dev) -{ - skb->dev = dev; -} -#else /* CONFIG_NET_NS */ -void skb_set_dev(struct sk_buff *skb, struct net_device *dev); -#endif - static inline bool netdev_uses_trailer_tags(struct net_device *dev) { #ifdef CONFIG_NET_DSA_TAG_TRAILER @@ -2604,8 +2595,6 @@ extern void net_disable_timestamp(void); extern void *dev_seq_start(struct seq_file *seq, loff_t *pos); extern void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos); extern void dev_seq_stop(struct seq_file *seq, void *v); -extern int dev_seq_open_ops(struct inode *inode, struct file *file, - const struct seq_operations *ops); #endif extern int netdev_class_create_file(struct class_attribute *class_attr); diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h index c0405ac92870..e3a9978f259f 100644 --- a/include/linux/netfilter/xt_set.h +++ b/include/linux/netfilter/xt_set.h @@ -58,8 +58,8 @@ struct xt_set_info_target_v1 { struct xt_set_info_target_v2 { struct xt_set_info add_set; struct xt_set_info del_set; - u32 flags; - u32 timeout; + __u32 flags; + __u32 timeout; }; #endif /*_XT_SET_H*/ diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 0ddd161f3b06..31d2844e6572 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -104,9 +104,18 @@ struct bridge_skb_cb { } daddr; }; +static inline void br_drop_fake_rtable(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + + if (dst && (dst->flags & DST_FAKE_RTABLE)) + skb_dst_drop(skb); +} + #else #define nf_bridge_maybe_copy_header(skb) (0) #define nf_bridge_pad(skb) (0) +#define br_drop_fake_rtable(skb) do { } while (0) #endif /* CONFIG_BRIDGE_NETFILTER */ #endif /* __KERNEL__ */ diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index f549adccc94c..1bc898b14a80 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -287,7 +287,17 @@ extern unsigned int ip6t_do_table(struct sk_buff *skb, struct xt_table *table); /* Check for an extension */ -extern int ip6t_ext_hdr(u8 nexthdr); +static inline int +ip6t_ext_hdr(u8 nexthdr) +{ return (nexthdr == IPPROTO_HOPOPTS) || + (nexthdr == IPPROTO_ROUTING) || + (nexthdr == IPPROTO_FRAGMENT) || + (nexthdr == IPPROTO_ESP) || + (nexthdr == IPPROTO_AH) || + (nexthdr == IPPROTO_NONE) || + (nexthdr == IPPROTO_DSTOPTS); +} + /* find specified header and get offset to it */ extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index bfd0d1bf6707..7ba3551a0414 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -312,6 +312,11 @@ struct nfs4_layoutreturn { int rpc_status; }; +struct stateowner_id { + __u64 create_time; + __u32 uniquifier; +}; + /* * Arguments to the open call. */ @@ -321,7 +326,7 @@ struct nfs_openargs { int open_flags; fmode_t fmode; __u64 clientid; - __u64 id; + struct stateowner_id id; union { struct { struct iattr * attrs; /* UNCHECKED, GUARDED */ diff --git a/include/linux/nfsd/Kbuild b/include/linux/nfsd/Kbuild index b8d4001212b3..5b7d84ac954a 100644 --- a/include/linux/nfsd/Kbuild +++ b/include/linux/nfsd/Kbuild @@ -1,3 +1,4 @@ +header-y += cld.h header-y += debug.h header-y += export.h header-y += nfsfh.h diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index fee4349364f7..e4d1de742502 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -12,6 +12,8 @@ #ifndef __LINUX_PINCTRL_MACHINE_H #define __LINUX_PINCTRL_MACHINE_H +#include <linux/bug.h> + #include "pinctrl-state.h" enum pinctrl_map_type { @@ -148,7 +150,7 @@ struct pinctrl_map { #define PIN_MAP_CONFIGS_GROUP_HOG_DEFAULT(dev, grp, cfgs) \ PIN_MAP_CONFIGS_GROUP(dev, PINCTRL_STATE_DEFAULT, dev, grp, cfgs) -#ifdef CONFIG_PINMUX +#ifdef CONFIG_PINCTRL extern int pinctrl_register_mappings(struct pinctrl_map const *map, unsigned num_maps); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 6d626ff0cfd0..e1ac1ce16fb0 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -6,6 +6,7 @@ #define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ #define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ +#define PIPE_BUF_FLAG_PACKET 0x08 /* read() as a packet */ /** * struct pipe_buffer - a linux kernel pipe buffer diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index d056263545b1..b0f2c56a8ea2 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -4,8 +4,8 @@ * GPL v2 Only */ -#ifndef __ATMEL_NAND_H__ -#define __ATMEL_NAND_H__ +#ifndef __ATMEL_H__ +#define __ATMEL_H__ #include <linux/mtd/nand.h> @@ -24,4 +24,4 @@ struct atmel_nand_data { unsigned int num_parts; }; -#endif /* __ATMEL_NAND_H__ */ +#endif /* __ATMEL_H__ */ diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 2e9191a712f3..233149cb19f4 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -8,6 +8,7 @@ #include <linux/notifier.h> #include <linux/miscdevice.h> #include <linux/device.h> +#include <linux/workqueue.h> enum { PM_QOS_RESERVED = 0, @@ -29,6 +30,7 @@ enum { struct pm_qos_request { struct plist_node node; int pm_qos_class; + struct delayed_work work; /* for pm_qos_update_request_timeout */ }; struct dev_pm_qos_request { @@ -73,6 +75,8 @@ void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value); void pm_qos_update_request(struct pm_qos_request *req, s32 new_value); +void pm_qos_update_request_timeout(struct pm_qos_request *req, + s32 new_value, unsigned long timeout_us); void pm_qos_remove_request(struct pm_qos_request *req); int pm_qos_request(int pm_qos_class); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a90abb6bfa64..0258bcd6258d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -46,7 +46,13 @@ struct reg_default { /** * Configuration for the register map of a device. * + * @name: Optional name of the regmap. Useful when a device has multiple + * register regions. + * * @reg_bits: Number of bits in a register address, mandatory. + * @reg_stride: The register address stride. Valid register addresses are a + * multiple of this value. If set to 0, a value of 1 will be + * used. * @pad_bits: Number of bits of padding between register and value. * @val_bits: Number of bits in a register value, mandatory. * @@ -77,7 +83,10 @@ struct reg_default { * @num_reg_defaults_raw: Number of elements in reg_defaults_raw. */ struct regmap_config { + const char *name; + int reg_bits; + int reg_stride; int pad_bits; int val_bits; @@ -97,18 +106,21 @@ struct regmap_config { u8 write_flag_mask; }; -typedef int (*regmap_hw_write)(struct device *dev, const void *data, +typedef int (*regmap_hw_write)(void *context, const void *data, size_t count); -typedef int (*regmap_hw_gather_write)(struct device *dev, +typedef int (*regmap_hw_gather_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len); -typedef int (*regmap_hw_read)(struct device *dev, +typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); +typedef void (*regmap_hw_free_context)(void *context); /** * Description of a hardware bus for the register map infrastructure. * + * @fast_io: Register IO is fast. Use a spinlock instead of a mutex + * to perform locking. * @write: Write operation. * @gather_write: Write operation with split register/value, return -ENOTSUPP * if not implemented on a given device. @@ -118,27 +130,37 @@ typedef int (*regmap_hw_read)(struct device *dev, * a read. */ struct regmap_bus { + bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_read read; + regmap_hw_free_context free_context; u8 read_flag_mask; }; struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config); struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config); struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config); struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *devm_regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config); void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 7abb16093312..b02108446be7 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -71,7 +71,7 @@ struct regulator_state { * @uV_offset: Offset applied to voltages from consumer to compensate for * voltage drops. * - * @min_uA: Smallest consumers consumers may set. + * @min_uA: Smallest current consumers may set. * @max_uA: Largest current consumers may set. * * @valid_modes_mask: Mask of modes which may be configured by consumers. @@ -134,10 +134,8 @@ struct regulation_constraints { /** * struct regulator_consumer_supply - supply -> device mapping * - * This maps a supply name to a device. Only one of dev or dev_name - * can be specified. Use of dev_name allows support for buses which - * make struct device available late such as I2C and is the preferred - * form. + * This maps a supply name to a device. Use of dev_name allows support for + * buses which make struct device available late such as I2C. * * @dev_name: Result of dev_name() for the consumer. * @supply: Name for the supply. diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index c6db9fb33c44..600060e25ec6 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -141,7 +141,7 @@ static inline unsigned __read_seqcount_begin(const seqcount_t *s) unsigned ret; repeat: - ret = s->sequence; + ret = ACCESS_ONCE(s->sequence); if (unlikely(ret & 1)) { cpu_relax(); goto repeat; @@ -166,6 +166,27 @@ static inline unsigned read_seqcount_begin(const seqcount_t *s) } /** + * raw_seqcount_begin - begin a seq-read critical section + * @s: pointer to seqcount_t + * Returns: count to be passed to read_seqcount_retry + * + * raw_seqcount_begin opens a read critical section of the given seqcount. + * Validity of the critical section is tested by checking read_seqcount_retry + * function. + * + * Unlike read_seqcount_begin(), this function will not wait for the count + * to stabilize. If a writer is active when we begin, we will fail the + * read_seqcount_retry() instead of stabilizing at the beginning of the + * critical section. + */ +static inline unsigned raw_seqcount_begin(const seqcount_t *s) +{ + unsigned ret = ACCESS_ONCE(s->sequence); + smp_rmb(); + return ret & ~1; +} + +/** * __read_seqcount_retry - end a seq-read critical section (without barrier) * @s: pointer to seqcount_t * @start: count, from read_seqcount_begin diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f51bf2e70c69..2db407a40051 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -357,7 +357,7 @@ struct uart_port { #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) #define UPF_EXAR_EFR ((__force upf_t) (1 << 25)) -#define UPF_IIR_ONCE ((__force upf_t) (1 << 26)) +#define UPF_BUG_THRE ((__force upf_t) (1 << 26)) /* The exact UART type is known and should not be probed. */ #define UPF_FIXED_TYPE ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 33370271b8b2..111f26b6e28b 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -238,11 +238,12 @@ enum { /* * The callback notifies userspace to release buffers when skb DMA is done in * lower device, the skb last reference should be 0 when calling this. - * The desc is used to track userspace buffer index. + * The ctx field is used to track device context. + * The desc field is used to track userspace buffer index. */ struct ubuf_info { - void (*callback)(void *); - void *arg; + void (*callback)(struct ubuf_info *); + void *ctx; unsigned long desc; }; @@ -481,6 +482,7 @@ struct sk_buff { union { __u32 mark; __u32 dropcount; + __u32 avail_size; }; sk_buff_data_t transport_header; @@ -1018,7 +1020,7 @@ static inline void skb_queue_splice(const struct sk_buff_head *list, } /** - * skb_queue_splice - join two skb lists and reinitialise the emptied list + * skb_queue_splice_init - join two skb lists and reinitialise the emptied list * @list: the new list to add * @head: the place to add it in the first list * @@ -1049,7 +1051,7 @@ static inline void skb_queue_splice_tail(const struct sk_buff_head *list, } /** - * skb_queue_splice_tail - join two skb lists and reinitialise the emptied list + * skb_queue_splice_tail_init - join two skb lists and reinitialise the emptied list * @list: the new list to add * @head: the place to add it in the first list * @@ -1366,6 +1368,18 @@ static inline int skb_tailroom(const struct sk_buff *skb) } /** + * skb_availroom - bytes at buffer end + * @skb: buffer to check + * + * Return the number of bytes of free space at the tail of an sk_buff + * allocated by sk_stream_alloc() + */ +static inline int skb_availroom(const struct sk_buff *skb) +{ + return skb_is_nonlinear(skb) ? 0 : skb->avail_size - skb->len; +} + +/** * skb_reserve - adjust headroom * @skb: buffer to alter * @len: bytes to move diff --git a/include/linux/socket.h b/include/linux/socket.h index da2d3e2543f3..b84bbd48b874 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -265,7 +265,7 @@ struct ucred { #define MSG_NOSIGNAL 0x4000 /* Do not generate SIGPIPE */ #define MSG_MORE 0x8000 /* Sender will send more */ #define MSG_WAITFORONE 0x10000 /* recvmmsg(): block until 1+ packets avail */ - +#define MSG_SENDPAGE_NOTLAST 0x20000 /* sendpage() internal : not the last page */ #define MSG_EOF MSG_FIN #define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exit for file diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 98679b061b63..fa702aeb5038 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -254,7 +254,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * driver is finished with this message, it must call * spi_finalize_current_message() so the subsystem can issue the next * transfer - * @prepare_transfer_hardware: there are currently no more messages on the + * @unprepare_transfer_hardware: there are currently no more messages on the * queue so the subsystem notifies the driver that it may relax the * hardware by issuing this call * diff --git a/include/linux/stddef.h b/include/linux/stddef.h index 6a40c76bdcf1..1747b6787b9e 100644 --- a/include/linux/stddef.h +++ b/include/linux/stddef.h @@ -3,14 +3,10 @@ #include <linux/compiler.h> +#ifdef __KERNEL__ + #undef NULL -#if defined(__cplusplus) -#define NULL 0 -#else #define NULL ((void *)0) -#endif - -#ifdef __KERNEL__ enum { false = 0, diff --git a/include/linux/swap.h b/include/linux/swap.h index 8dc0ea7caf02..b1fd5c7925fe 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -305,6 +305,13 @@ static inline int mem_cgroup_swappiness(struct mem_cgroup *mem) return vm_swappiness; } #endif +#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +extern void mem_cgroup_uncharge_swap(swp_entry_t ent); +#else +static inline void mem_cgroup_uncharge_swap(swp_entry_t ent) +{ +} +#endif #ifdef CONFIG_SWAP /* linux/mm/page_io.c */ extern int swap_readpage(struct page *); @@ -375,13 +382,6 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout) { } #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP -extern void mem_cgroup_uncharge_swap(swp_entry_t ent); -#else -static inline void mem_cgroup_uncharge_swap(swp_entry_t ent) -{ -} -#endif #else /* CONFIG_SWAP */ diff --git a/include/linux/types.h b/include/linux/types.h index e5fa50345516..7f480db60231 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -210,6 +210,12 @@ typedef u32 phys_addr_t; typedef phys_addr_t resource_size_t; +/* + * This type is the placeholder for a hardware interrupt number. It has to be + * big enough to enclose whatever representation is used by a given platform. + */ +typedef unsigned long irq_hw_number_t; + typedef struct { int counter; } atomic_t; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 5de415707c23..d28cc78a38e4 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -126,6 +126,8 @@ struct usb_hcd { unsigned wireless:1; /* Wireless USB HCD */ unsigned authorized_default:1; unsigned has_tt:1; /* Integrated TT in root hub */ + unsigned broken_pci_sleep:1; /* Don't put the + controller in PCI-D3 for system sleep */ unsigned int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index f67810f8f21b..38ab3f46346f 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -94,6 +94,7 @@ struct usb_phy { struct usb_otg *otg; + struct device *io_dev; struct usb_phy_io_ops *io_ops; void __iomem *io_priv; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index fbb666b1b670..474283888233 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -28,13 +28,6 @@ /* parity check flag */ #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) -enum port_dev_state { - PORT_UNREGISTERED, - PORT_REGISTERING, - PORT_REGISTERED, - PORT_UNREGISTERING, -}; - /* USB serial flags */ #define USB_SERIAL_WRITE_BUSY 0 @@ -124,7 +117,6 @@ struct usb_serial_port { char throttle_req; unsigned long sysrq; /* sysrq timeout */ struct device dev; - enum port_dev_state dev_state; }; #define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev) diff --git a/include/linux/vgaarb.h b/include/linux/vgaarb.h index 9c3120dca294..b572f80bdfd5 100644 --- a/include/linux/vgaarb.h +++ b/include/linux/vgaarb.h @@ -47,6 +47,8 @@ */ #define VGA_DEFAULT_DEVICE (NULL) +struct pci_dev; + /* For use by clients */ /** diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h index 03b90cdc1921..06f8e3858251 100644 --- a/include/linux/vm_event_item.h +++ b/include/linux/vm_event_item.h @@ -26,13 +26,14 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, PGFREE, PGACTIVATE, PGDEACTIVATE, PGFAULT, PGMAJFAULT, FOR_ALL_ZONES(PGREFILL), - FOR_ALL_ZONES(PGSTEAL), + FOR_ALL_ZONES(PGSTEAL_KSWAPD), + FOR_ALL_ZONES(PGSTEAL_DIRECT), FOR_ALL_ZONES(PGSCAN_KSWAPD), FOR_ALL_ZONES(PGSCAN_DIRECT), #ifdef CONFIG_NUMA PGSCAN_ZONE_RECLAIM_FAILED, #endif - PGINODESTEAL, SLABS_SCANNED, KSWAPD_STEAL, KSWAPD_INODESTEAL, + PGINODESTEAL, SLABS_SCANNED, KSWAPD_INODESTEAL, KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY, KSWAPD_SKIP_CONGESTION_WAIT, PAGEOUTRUN, ALLOCSTALL, PGROTATED, diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 344b0f972828..d47e523c9d83 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -92,6 +92,7 @@ enum { HCI_SERVICE_CACHE, HCI_LINK_KEYS, HCI_DEBUG_KEYS, + HCI_UNREGISTER, HCI_LE_SCAN, HCI_SSP_ENABLED, @@ -1327,8 +1328,8 @@ struct sockaddr_hci { #define HCI_DEV_NONE 0xffff #define HCI_CHANNEL_RAW 0 -#define HCI_CHANNEL_CONTROL 1 #define HCI_CHANNEL_MONITOR 2 +#define HCI_CHANNEL_CONTROL 3 struct hci_filter { unsigned long type_mask; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index daefaac51131..db1c5df45224 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -314,6 +314,7 @@ struct hci_conn { __u8 remote_cap; __u8 remote_auth; + bool flush_key; unsigned int sent; @@ -427,7 +428,7 @@ enum { static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - return (test_bit(HCI_SSP_ENABLED, &hdev->flags) && + return (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)); } @@ -907,11 +908,13 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) { - u8 field_len; - size_t parsed; + size_t parsed = 0; - for (parsed = 0; parsed < data_len - 1; parsed += field_len) { - field_len = data[0]; + if (data_len < 2) + return false; + + while (parsed < data_len - 1) { + u8 field_len = data[0]; if (field_len == 0) break; @@ -978,7 +981,7 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); int mgmt_connectable(struct hci_dev *hdev, u8 connectable); int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, - u8 persistent); + bool persistent); int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u32 flags, u8 *name, u8 name_len, u8 *dev_class); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index ffc1377e092e..ebfd91fc20f8 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -117,7 +117,7 @@ struct mgmt_mode { #define MGMT_OP_SET_DISCOVERABLE 0x0006 struct mgmt_cp_set_discoverable { __u8 val; - __u16 timeout; + __le16 timeout; } __packed; #define MGMT_SET_DISCOVERABLE_SIZE 3 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 248fb05feb63..83d800c31e3c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -620,8 +620,10 @@ struct sta_bss_parameters { * @llid: mesh local link id * @plid: mesh peer link id * @plink_state: mesh peer link state - * @signal: signal strength of last received packet in dBm - * @signal_avg: signal strength average in dBm + * @signal: the signal strength, type depends on the wiphy's signal_type + NOTE: For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. + * @signal_avg: avg signal strength, type depends on the wiphy's signal_type + NOTE: For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. * @txrate: current unicast bitrate from this station * @rxrate: current unicast bitrate to this station * @rx_packets: packets received from this station diff --git a/include/net/dst.h b/include/net/dst.h index 59c5d18cc385..bed833d9796a 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -36,7 +36,11 @@ struct dst_entry { struct net_device *dev; struct dst_ops *ops; unsigned long _metrics; - unsigned long expires; + union { + unsigned long expires; + /* point to where the dst_entry copied from */ + struct dst_entry *from; + }; struct dst_entry *path; struct neighbour __rcu *_neighbour; #ifdef CONFIG_XFRM @@ -55,6 +59,7 @@ struct dst_entry { #define DST_NOCACHE 0x0010 #define DST_NOCOUNT 0x0020 #define DST_NOPEER 0x0040 +#define DST_FAKE_RTABLE 0x0080 short error; short obsolete; diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index b26bb8101981..0ae759a6c76e 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -123,6 +123,54 @@ static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) return ((struct rt6_info *)dst)->rt6i_idev; } +static inline void rt6_clean_expires(struct rt6_info *rt) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) + dst_release(rt->dst.from); + + rt->rt6i_flags &= ~RTF_EXPIRES; + rt->dst.from = NULL; +} + +static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) + dst_release(rt->dst.from); + + rt->rt6i_flags |= RTF_EXPIRES; + rt->dst.expires = expires; +} + +static inline void rt6_update_expires(struct rt6_info *rt, int timeout) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES)) { + if (rt->dst.from) + dst_release(rt->dst.from); + /* dst_set_expires relies on expires == 0 + * if it has not been set previously. + */ + rt->dst.expires = 0; + } + + dst_set_expires(&rt->dst, timeout); + rt->rt6i_flags |= RTF_EXPIRES; +} + +static inline void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) +{ + struct dst_entry *new = (struct dst_entry *) from; + + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) { + if (new == rt->dst.from) + return; + dst_release(rt->dst.from); + } + + rt->rt6i_flags &= ~RTF_EXPIRES; + rt->dst.from = new; + dst_hold(new); +} + struct fib6_walker_t { struct list_head lh; struct fib6_node *root, *node; diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 2bdee51ba30d..72522f087375 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -393,7 +393,7 @@ struct ip_vs_protocol { void (*exit)(struct ip_vs_protocol *pp); - void (*init_netns)(struct net *net, struct ip_vs_proto_data *pd); + int (*init_netns)(struct net *net, struct ip_vs_proto_data *pd); void (*exit_netns)(struct net *net, struct ip_vs_proto_data *pd); @@ -1203,6 +1203,8 @@ ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol, extern int ip_vs_use_count_inc(void); extern void ip_vs_use_count_dec(void); +extern int ip_vs_register_nl_ioctl(void); +extern void ip_vs_unregister_nl_ioctl(void); extern int ip_vs_control_init(void); extern void ip_vs_control_cleanup(void); extern struct ip_vs_dest * diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 87d203ff7a8a..9210bdc7bd8d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1327,7 +1327,7 @@ static inline struct ieee80211_rate * ieee80211_get_tx_rate(const struct ieee80211_hw *hw, const struct ieee80211_tx_info *c) { - if (WARN_ON(c->control.rates[0].idx < 0)) + if (WARN_ON_ONCE(c->control.rates[0].idx < 0)) return NULL; return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx]; } diff --git a/include/net/netfilter/xt_log.h b/include/net/netfilter/xt_log.h index 7e1544e8f70d..9d9756cca013 100644 --- a/include/net/netfilter/xt_log.h +++ b/include/net/netfilter/xt_log.h @@ -47,7 +47,7 @@ static void sb_close(struct sbuff *m) if (likely(m != &emergency)) kfree(m); else { - xchg(&emergency_ptr, m); + emergency_ptr = m; local_bh_enable(); } } diff --git a/include/net/red.h b/include/net/red.h index 77d4c3745cb5..ef46058d35bf 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -245,7 +245,7 @@ static inline unsigned long red_calc_qavg_from_idle_time(const struct red_parms * * dummy packets as a burst after idle time, i.e. * - * p->qavg *= (1-W)^m + * v->qavg *= (1-W)^m * * This is an apparently overcomplicated solution (f.e. we have to * precompute a table to make this calculation in reasonable time) @@ -279,7 +279,7 @@ static inline unsigned long red_calc_qavg_no_idle_time(const struct red_parms *p unsigned int backlog) { /* - * NOTE: p->qavg is fixed point number with point at Wlog. + * NOTE: v->qavg is fixed point number with point at Wlog. * The formula below is equvalent to floating point * version: * @@ -390,7 +390,7 @@ static inline void red_adaptative_algo(struct red_parms *p, struct red_vars *v) if (red_is_idling(v)) qavg = red_calc_qavg_from_idle_time(p, v); - /* p->qavg is fixed point number with point at Wlog */ + /* v->qavg is fixed point number with point at Wlog */ qavg >>= p->Wlog; if (qavg > p->target_max && p->max_P <= MAX_P_MAX) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 6ee44b24864a..a2ef81466b00 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -704,4 +704,17 @@ static inline void sctp_v4_map_v6(union sctp_addr *addr) addr->v6.sin6_addr.s6_addr32[2] = htonl(0x0000ffff); } +/* The cookie is always 0 since this is how it's used in the + * pmtu code. + */ +static inline struct dst_entry *sctp_transport_dst_check(struct sctp_transport *t) +{ + if (t->dst && !dst_check(t->dst, 0)) { + dst_release(t->dst); + t->dst = NULL; + } + + return t->dst; +} + #endif /* __net_sctp_h__ */ diff --git a/include/net/sock.h b/include/net/sock.h index a6ba1f8871fd..5a0a58ac4126 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -246,6 +246,7 @@ struct cg_proto; * @sk_user_data: RPC layer private data * @sk_sndmsg_page: cached page for sendmsg * @sk_sndmsg_off: cached offset for sendmsg + * @sk_peek_off: current peek_offset value * @sk_send_head: front of stuff to transmit * @sk_security: used by security modules * @sk_mark: generic packet mark @@ -1128,9 +1129,9 @@ sk_sockets_allocated_read_positive(struct sock *sk) struct proto *prot = sk->sk_prot; if (mem_cgroup_sockets_enabled && sk->sk_cgrp) - return percpu_counter_sum_positive(sk->sk_cgrp->sockets_allocated); + return percpu_counter_read_positive(sk->sk_cgrp->sockets_allocated); - return percpu_counter_sum_positive(prot->sockets_allocated); + return percpu_counter_read_positive(prot->sockets_allocated); } static inline int diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 5f5ed1b8b41b..f4f1c96dca72 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -217,11 +217,29 @@ struct domain_device { struct kref kref; }; -struct sas_discovery_event { +struct sas_work { + struct list_head drain_node; struct work_struct work; +}; + +static inline void INIT_SAS_WORK(struct sas_work *sw, void (*fn)(struct work_struct *)) +{ + INIT_WORK(&sw->work, fn); + INIT_LIST_HEAD(&sw->drain_node); +} + +struct sas_discovery_event { + struct sas_work work; struct asd_sas_port *port; }; +static inline struct sas_discovery_event *to_sas_discovery_event(struct work_struct *work) +{ + struct sas_discovery_event *ev = container_of(work, typeof(*ev), work.work); + + return ev; +} + struct sas_discovery { struct sas_discovery_event disc_work[DISC_NUM_EVENTS]; unsigned long pending; @@ -244,7 +262,7 @@ struct asd_sas_port { struct list_head destroy_list; enum sas_linkrate linkrate; - struct work_struct work; + struct sas_work work; /* public: */ int id; @@ -270,10 +288,17 @@ struct asd_sas_port { }; struct asd_sas_event { - struct work_struct work; + struct sas_work work; struct asd_sas_phy *phy; }; +static inline struct asd_sas_event *to_asd_sas_event(struct work_struct *work) +{ + struct asd_sas_event *ev = container_of(work, typeof(*ev), work.work); + + return ev; +} + /* The phy pretty much is controlled by the LLDD. * The class only reads those fields. */ @@ -333,10 +358,17 @@ struct scsi_core { }; struct sas_ha_event { - struct work_struct work; + struct sas_work work; struct sas_ha_struct *ha; }; +static inline struct sas_ha_event *to_sas_ha_event(struct work_struct *work) +{ + struct sas_ha_event *ev = container_of(work, typeof(*ev), work.work); + + return ev; +} + enum sas_ha_state { SAS_HA_REGISTERED, SAS_HA_DRAINING, diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index cdccd2eb7b6c..77670e823ed8 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -37,7 +37,7 @@ static inline int dev_is_sata(struct domain_device *dev) } int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy); -int sas_ata_init_host_and_port(struct domain_device *found_dev); +int sas_ata_init(struct domain_device *dev); void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, @@ -52,7 +52,7 @@ static inline int dev_is_sata(struct domain_device *dev) { return 0; } -static inline int sas_ata_init_host_and_port(struct domain_device *found_dev) +static inline int sas_ata_init(struct domain_device *dev) { return 0; } diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 377df4a28512..1e1198546c72 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -134,6 +134,9 @@ struct scsi_cmnd { static inline struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) { + if (!cmd->request->rq_disk) + return NULL; + return *(struct scsi_driver **)cmd->request->rq_disk->private_data; } diff --git a/include/sound/cs42l52.h b/include/sound/cs42l52.h new file mode 100644 index 000000000000..4c68955f7330 --- /dev/null +++ b/include/sound/cs42l52.h @@ -0,0 +1,36 @@ +/* + * linux/sound/cs42l52.h -- Platform data for CS42L52 + * + * Copyright (c) 2012 Cirrus Logic Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __CS42L52_H +#define __CS42L52_H + +struct cs42l52_platform_data { + + /* MICBIAS Level. Check datasheet Pg48 */ + unsigned int micbias_lvl; + + /* MICA mode selection 0=Single 1=Differential */ + unsigned int mica_cfg; + + /* MICB mode selection 0=Single 1=Differential */ + unsigned int micb_cfg; + + /* MICA Select 0=MIC1A 1=MIC2A */ + unsigned int mica_sel; + + /* MICB Select 0=MIC2A 1=MIC2B */ + unsigned int micb_sel; + + /* Charge Pump Freq. Check datasheet Pg73 */ + unsigned int chgfreq; + +}; + +#endif /* __CS42L52_H */ diff --git a/include/sound/max98095.h b/include/sound/max98095.h index 7513a42dd4aa..e87ae67b0a55 100644 --- a/include/sound/max98095.h +++ b/include/sound/max98095.h @@ -49,6 +49,18 @@ struct max98095_pdata { */ unsigned int digmic_left_mode:1; unsigned int digmic_right_mode:1; + + /* Pin5 is the mechanical method of sensing jack insertion + * but it is something that might not be supported. + * 0 = PIN5 not supported + * 1 = PIN5 supported + */ + unsigned int jack_detect_pin5en:1; + + /* Slew amount for jack detection. Calculated as 4 * (delay + 1). + * Default delay is 24 to get a time of 100ms. + */ + unsigned int jack_detect_delay; }; #endif diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index b457e87fbd08..956e30e89ea8 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -84,16 +84,4 @@ struct sh_fsi_platform_info { struct sh_fsi_port_info port_b; }; -/* - * for fsi-ak4642 - */ -struct fsi_ak4642_info { - const char *name; - const char *card; - const char *cpu_dai; - const char *codec; - const char *platform; - int id; -}; - #endif /* __SOUND_FSI_H */ diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h new file mode 100644 index 000000000000..4b62b8dc6a4f --- /dev/null +++ b/include/sound/simple_card.h @@ -0,0 +1,38 @@ +/* + * ASoC simple sound card support + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SIMPLE_CARD_H +#define __SIMPLE_CARD_H + +#include <sound/soc.h> + +struct asoc_simple_dai_init_info { + unsigned int fmt; + unsigned int cpu_daifmt; + unsigned int codec_daifmt; + unsigned int sysclk; +}; + +struct asoc_simple_card_info { + const char *name; + const char *card; + const char *cpu_dai; + const char *codec; + const char *platform; + const char *codec_dai; + struct asoc_simple_dai_init_info *init; /* for snd_link.init */ + + /* used in simple-card.c */ + struct snd_soc_dai_link snd_link; + struct snd_soc_card snd_card; +}; + +#endif /* __SIMPLE_CARD_H */ diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index c429f248cf4e..1f69e0af2941 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -173,6 +173,8 @@ struct snd_soc_dai_ops { struct snd_soc_dai *); int (*trigger)(struct snd_pcm_substream *, int, struct snd_soc_dai *); + int (*bespoke_trigger)(struct snd_pcm_substream *, int, + struct snd_soc_dai *); /* * For hardware based FIFO caused delay reporting. * Optional. @@ -196,6 +198,7 @@ struct snd_soc_dai_driver { const char *name; unsigned int id; int ac97_control; + unsigned int base; /* DAI driver callbacks */ int (*probe)(struct snd_soc_dai *dai); @@ -241,6 +244,7 @@ struct snd_soc_dai { struct snd_soc_dapm_widget *playback_widget; struct snd_soc_dapm_widget *capture_widget; + struct snd_soc_dapm_context dapm; /* DAI DMA data */ void *playback_dma_data; diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8da3c2409060..e3833d9f1914 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -141,10 +141,6 @@ struct device; { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ .invert = winvert, .kcontrol_news = wcontrols, \ .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags} -#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \ -{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \ - .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ @@ -324,6 +320,8 @@ struct snd_soc_dapm_path; struct snd_soc_dapm_pin; struct snd_soc_dapm_route; struct snd_soc_dapm_context; +struct regulator; +struct snd_soc_dapm_widget_list; int dapm_reg_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -359,6 +357,10 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, struct snd_soc_dai *dai); int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); +int snd_soc_dapm_new_pcm(struct snd_soc_card *card, + const struct snd_soc_pcm_stream *params, + struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink); /* dapm path setup */ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); @@ -369,8 +371,8 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); /* dapm events */ -int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, - struct snd_soc_dai *dai, int event); +void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, + int event); void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ @@ -402,6 +404,10 @@ void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec); /* Mostly internal - should not normally be used */ void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason); +/* dapm path query */ +int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, + struct snd_soc_dapm_widget_list **list); + /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ @@ -430,6 +436,12 @@ enum snd_soc_dapm_type { snd_soc_dapm_aif_out, /* audio interface output */ snd_soc_dapm_siggen, /* signal generator */ snd_soc_dapm_dai, /* link to DAI structure */ + snd_soc_dapm_dai_link, /* link between two DAI structures */ +}; + +enum snd_soc_dapm_subclass { + SND_SOC_DAPM_CLASS_INIT = 0, + SND_SOC_DAPM_CLASS_RUNTIME = 1, }; /* @@ -482,9 +494,11 @@ struct snd_soc_dapm_widget { struct snd_soc_dapm_context *dapm; void *priv; /* widget specific data */ + struct regulator *regulator; /* attached regulator */ + const struct snd_soc_pcm_stream *params; /* params for dai links */ /* dapm control */ - short reg; /* negative reg = no direct dapm */ + int reg; /* negative reg = no direct dapm */ unsigned char shift; /* bits to shift */ unsigned int saved_value; /* widget saved value */ unsigned int value; /* widget current value */ diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h new file mode 100644 index 000000000000..04598f1efd77 --- /dev/null +++ b/include/sound/soc-dpcm.h @@ -0,0 +1,138 @@ +/* + * linux/sound/soc-dpcm.h -- ALSA SoC Dynamic PCM Support + * + * Author: Liam Girdwood <lrg@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_SND_SOC_DPCM_H +#define __LINUX_SND_SOC_DPCM_H + +#include <linux/list.h> +#include <sound/pcm.h> + +struct snd_soc_pcm_runtime; + +/* + * Types of runtime_update to perform. e.g. originated from FE PCM ops + * or audio route changes triggered by muxes/mixers. + */ +enum snd_soc_dpcm_update { + SND_SOC_DPCM_UPDATE_NO = 0, + SND_SOC_DPCM_UPDATE_BE, + SND_SOC_DPCM_UPDATE_FE, +}; + +/* + * Dynamic PCM Frontend -> Backend link management states. + */ +enum snd_soc_dpcm_link_state { + SND_SOC_DPCM_LINK_STATE_NEW = 0, /* newly created link */ + SND_SOC_DPCM_LINK_STATE_FREE, /* link to be dismantled */ +}; + +/* + * Dynamic PCM Frontend -> Backend link PCM states. + */ +enum snd_soc_dpcm_state { + SND_SOC_DPCM_STATE_NEW = 0, + SND_SOC_DPCM_STATE_OPEN, + SND_SOC_DPCM_STATE_HW_PARAMS, + SND_SOC_DPCM_STATE_PREPARE, + SND_SOC_DPCM_STATE_START, + SND_SOC_DPCM_STATE_STOP, + SND_SOC_DPCM_STATE_PAUSED, + SND_SOC_DPCM_STATE_SUSPEND, + SND_SOC_DPCM_STATE_HW_FREE, + SND_SOC_DPCM_STATE_CLOSE, +}; + +/* + * Dynamic PCM trigger ordering. Triggering flexibility is required as some + * DSPs require triggering before/after their CPU platform and DAIs. + * + * i.e. some clients may want to manually order this call in their PCM + * trigger() whilst others will just use the regular core ordering. + */ +enum snd_soc_dpcm_trigger { + SND_SOC_DPCM_TRIGGER_PRE = 0, + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_BESPOKE, +}; + +/* + * Dynamic PCM link + * This links together a FE and BE DAI at runtime and stores the link + * state information and the hw_params configuration. + */ +struct snd_soc_dpcm { + /* FE and BE DAIs*/ + struct snd_soc_pcm_runtime *be; + struct snd_soc_pcm_runtime *fe; + + /* link state */ + enum snd_soc_dpcm_link_state state; + + /* list of BE and FE for this DPCM link */ + struct list_head list_be; + struct list_head list_fe; + + /* hw params for this link - may be different for each link */ + struct snd_pcm_hw_params hw_params; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_state; +#endif +}; + +/* + * Dynamic PCM runtime data. + */ +struct snd_soc_dpcm_runtime { + struct list_head be_clients; + struct list_head fe_clients; + + int users; + struct snd_pcm_runtime *runtime; + struct snd_pcm_hw_params hw_params; + + /* state and update */ + enum snd_soc_dpcm_update runtime_update; + enum snd_soc_dpcm_state state; +}; + +/* can this BE stop and free */ +int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream); + +/* can this BE perform a hw_params() */ +int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream); + +/* is the current PCM operation for this FE ? */ +int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream); + +/* is the current PCM operation for this BE ? */ +int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream); + +/* get the substream for this BE */ +struct snd_pcm_substream * + snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream); + +/* get the BE runtime state */ +enum snd_soc_dpcm_state + snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream); + +/* set the BE runtime state */ +void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, + enum snd_soc_dpcm_state state); + +/* internal use only */ +int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); +int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); +int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *); + +#endif diff --git a/include/sound/soc.h b/include/sound/soc.h index 2ebf7877c148..c703871f5f65 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -55,6 +55,18 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +#define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array),\ + .info = snd_soc_info_volsw, \ + .get = snd_soc_get_volsw_sx,\ + .put = snd_soc_put_volsw_sx, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xreg, \ + .shift = xshift, .rshift = xshift, \ + .max = xmax, .min = xmin} } #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ @@ -85,6 +97,18 @@ .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ xmax, xinvert) } +#define SOC_DOUBLE_R_SX_TLV(xname, xreg, xrreg, xshift, xmin, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_get_volsw_sx, \ + .put = snd_soc_put_volsw_sx, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xrreg, \ + .shift = xshift, .rshift = xshift, \ + .max = xmax, .min = xmin} } #define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ @@ -171,20 +195,6 @@ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&xenum } -#define SOC_DOUBLE_R_SX_TLV(xname, xreg_left, xreg_right, xshift,\ - xmin, xmax, tlv_array) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw_2r_sx, \ - .get = snd_soc_get_volsw_2r_sx, \ - .put = snd_soc_put_volsw_2r_sx, \ - .private_value = (unsigned long)&(struct soc_mixer_control) \ - {.reg = xreg_left, \ - .rreg = xreg_right, .shift = xshift, \ - .min = xmin, .max = xmax} } - #define SND_SOC_BYTES(xname, xbase, xregs) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ @@ -200,6 +210,19 @@ {.base = xbase, .num_regs = xregs, \ .mask = xmask }) } +#define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \ + xmin, xmax, xinvert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_xr_sx, .get = snd_soc_get_xr_sx, \ + .put = snd_soc_put_xr_sx, \ + .private_value = (unsigned long)&(struct soc_mreg_control) \ + {.regbase = xregbase, .regcount = xregcount, .nbits = xnbits, \ + .invert = xinvert, .min = xmin, .max = xmax} } + +#define SOC_SINGLE_STROBE(xname, xreg, xshift, xinvert) \ + SOC_SINGLE_EXT(xname, xreg, xshift, 1, xinvert, \ + snd_soc_get_strobe, snd_soc_put_strobe) + /* * Simplified versions of above macros, declaring a struct and calculating * ARRAY_SIZE internally @@ -264,6 +287,7 @@ struct snd_soc_jack_zone; struct snd_soc_jack_pin; struct snd_soc_cache_ops; #include <sound/soc-dapm.h> +#include <sound/soc-dpcm.h> #ifdef CONFIG_GPIOLIB struct snd_soc_jack_gpio; @@ -288,6 +312,11 @@ enum snd_soc_pcm_subclass { SND_SOC_PCM_CLASS_BE = 1, }; +enum snd_soc_card_subclass { + SND_SOC_CARD_CLASS_INIT = 0, + SND_SOC_CARD_CLASS_RUNTIME = 1, +}; + int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source, unsigned int freq, int dir); int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, @@ -333,6 +362,11 @@ int snd_soc_platform_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val); int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); +struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, + const char *dai_link, int stream); +struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, + const char *dai_link); + /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); @@ -343,6 +377,9 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms); int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, const struct snd_pcm_hardware *hw); +int snd_soc_platform_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_platform *platform); + /* Jack reporting */ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, struct snd_soc_jack *jack); @@ -413,6 +450,10 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); #define snd_soc_get_volsw_2r snd_soc_get_volsw #define snd_soc_put_volsw_2r snd_soc_put_volsw +int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, @@ -421,19 +462,22 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_limit_volume(struct snd_soc_codec *codec, const char *name, int max); -int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); - +int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); /** * struct snd_soc_reg_access - Describes whether a given register is @@ -513,6 +557,7 @@ struct snd_soc_jack_gpio { #endif struct snd_soc_jack { + struct mutex mutex; struct snd_jack *jack; struct snd_soc_codec *codec; struct list_head pins; @@ -711,6 +756,7 @@ struct snd_soc_platform_driver { /* platform IO - used for platform DAPM */ unsigned int (*read)(struct snd_soc_platform *, unsigned int); int (*write)(struct snd_soc_platform *, unsigned int, unsigned int); + int (*bespoke_trigger)(struct snd_pcm_substream *, int); }; struct snd_soc_platform { @@ -746,21 +792,36 @@ struct snd_soc_dai_link { const char *cpu_dai_name; const struct device_node *cpu_dai_of_node; const char *codec_dai_name; + int be_id; /* optional ID for machine driver BE identification */ + + const struct snd_soc_pcm_stream *params; unsigned int dai_fmt; /* format to set on init */ + enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */ + /* Keep DAI active over suspend */ unsigned int ignore_suspend:1; /* Symmetry requirements */ unsigned int symmetric_rates:1; + /* Do not create a PCM for this DAI link (Backend link) */ + unsigned int no_pcm:1; + + /* This DAI link can route to other DAI links at runtime (Frontend)*/ + unsigned int dynamic:1; + /* pmdown_time is ignored at stop */ unsigned int ignore_pmdown_time:1; /* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_pcm_runtime *rtd); + /* optional hw_params re-writing for BE and FE sync */ + int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); + /* machine stream operations */ struct snd_soc_ops *ops; }; @@ -800,6 +861,7 @@ struct snd_soc_card { struct list_head list; struct mutex mutex; + struct mutex dapm_mutex; bool instantiated; @@ -889,9 +951,11 @@ struct snd_soc_pcm_runtime { enum snd_soc_pcm_subclass pcm_subclass; struct snd_pcm_ops ops; - unsigned int complete:1; unsigned int dev_registered:1; + /* Dynamic PCM BE runtime data */ + struct snd_soc_dpcm_runtime dpcm[2]; + long pmdown_time; /* runtime devices */ @@ -902,6 +966,10 @@ struct snd_soc_pcm_runtime { struct snd_soc_dai *cpu_dai; struct delayed_work delayed_work; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_dpcm_root; + struct dentry *debugfs_dpcm_state; +#endif }; /* mixer control */ @@ -916,6 +984,12 @@ struct soc_bytes { u32 mask; }; +/* multi register control */ +struct soc_mreg_control { + long min, max; + unsigned int regbase, regcount, nbits, invert; +}; + /* enumerated kcontrol */ struct soc_enum { unsigned short reg; diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index ab26f8aa3c78..5fc2dcdd21cd 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -7,6 +7,8 @@ #include <linux/ktime.h> #include <linux/tracepoint.h> +#define DAPM_DIRECT "(direct)" + struct snd_soc_jack; struct snd_soc_codec; struct snd_soc_platform; @@ -241,6 +243,84 @@ TRACE_EVENT(snd_soc_dapm_walk_done, (int)__entry->path_checks, (int)__entry->neighbour_checks) ); +TRACE_EVENT(snd_soc_dapm_output_path, + + TP_PROTO(struct snd_soc_dapm_widget *widget, + struct snd_soc_dapm_path *path), + + TP_ARGS(widget, path), + + TP_STRUCT__entry( + __string( wname, widget->name ) + __string( pname, path->name ? path->name : DAPM_DIRECT) + __string( psname, path->sink->name ) + __field( int, path_sink ) + __field( int, path_connect ) + ), + + TP_fast_assign( + __assign_str(wname, widget->name); + __assign_str(pname, path->name ? path->name : DAPM_DIRECT); + __assign_str(psname, path->sink->name); + __entry->path_connect = path->connect; + __entry->path_sink = (long)path->sink; + ), + + TP_printk("%c%s -> %s -> %s\n", + (int) __entry->path_sink && + (int) __entry->path_connect ? '*' : ' ', + __get_str(wname), __get_str(pname), __get_str(psname)) +); + +TRACE_EVENT(snd_soc_dapm_input_path, + + TP_PROTO(struct snd_soc_dapm_widget *widget, + struct snd_soc_dapm_path *path), + + TP_ARGS(widget, path), + + TP_STRUCT__entry( + __string( wname, widget->name ) + __string( pname, path->name ? path->name : DAPM_DIRECT) + __string( psname, path->source->name ) + __field( int, path_source ) + __field( int, path_connect ) + ), + + TP_fast_assign( + __assign_str(wname, widget->name); + __assign_str(pname, path->name ? path->name : DAPM_DIRECT); + __assign_str(psname, path->source->name); + __entry->path_connect = path->connect; + __entry->path_source = (long)path->source; + ), + + TP_printk("%c%s <- %s <- %s\n", + (int) __entry->path_source && + (int) __entry->path_connect ? '*' : ' ', + __get_str(wname), __get_str(pname), __get_str(psname)) +); + +TRACE_EVENT(snd_soc_dapm_connected, + + TP_PROTO(int paths, int stream), + + TP_ARGS(paths, stream), + + TP_STRUCT__entry( + __field( int, paths ) + __field( int, stream ) + ), + + TP_fast_assign( + __entry->paths = paths; + __entry->stream = stream; + ), + + TP_printk("%s: found %d paths\n", + __entry->stream ? "capture" : "playback", __entry->paths) +); + TRACE_EVENT(snd_soc_jack_irq, TP_PROTO(const char *name), diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index fbc7b1ad929b..ea7a2035456d 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -295,7 +295,7 @@ TRACE_EVENT(sched_process_exec, TP_fast_assign( __assign_str(filename, bprm->filename); __entry->pid = p->pid; - __entry->old_pid = p->pid; + __entry->old_pid = old_pid; ), TP_printk("filename=%s pid=%d old_pid=%d", __get_str(filename), diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h index 2ea2fdc79c16..4f4d449f00f6 100644 --- a/include/xen/swiotlb-xen.h +++ b/include/xen/swiotlb-xen.h @@ -7,11 +7,13 @@ extern void xen_swiotlb_init(int verbose); extern void *xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, - dma_addr_t *dma_handle, gfp_t flags); + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs); extern void xen_swiotlb_free_coherent(struct device *hwdev, size_t size, - void *vaddr, dma_addr_t dma_handle); + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs); extern dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, diff --git a/init/Kconfig b/init/Kconfig index 72f33faca44f..6cfd71d06463 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1414,8 +1414,8 @@ endif # MODULES config INIT_ALL_POSSIBLE bool help - Back when each arch used to define their own cpu_online_map and - cpu_possible_map, some of them chose to initialize cpu_possible_map + Back when each arch used to define their own cpu_online_mask and + cpu_possible_mask, some of them chose to initialize cpu_possible_mask with all 1s, and others with all 0s. When they were centralised, it was better to provide this option than to break all the archs and have several arch maintainers pursuing me down dark alleys. diff --git a/init/do_mounts.c b/init/do_mounts.c index 0e93f92a0345..42b0707c3481 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -472,7 +472,7 @@ void __init change_floppy(char *fmt, ...) void __init mount_root(void) { #ifdef CONFIG_ROOT_NFS - if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { + if (ROOT_DEV == Root_NFS) { if (mount_nfs_root()) return; diff --git a/init/main.c b/init/main.c index 9d454f09f3b1..44b2433334c7 100644 --- a/init/main.c +++ b/init/main.c @@ -225,13 +225,9 @@ static int __init loglevel(char *str) early_param("loglevel", loglevel); -/* - * Unknown boot options get handed to init, unless they look like - * unused parameters (modprobe will find them in /proc/cmdline). - */ -static int __init unknown_bootoption(char *param, char *val) +/* Change NUL term back to "=", to make "param" the whole string. */ +static int __init repair_env_string(char *param, char *val) { - /* Change NUL term back to "=", to make "param" the whole string. */ if (val) { /* param=val or param="val"? */ if (val == param+strlen(param)+1) @@ -243,6 +239,16 @@ static int __init unknown_bootoption(char *param, char *val) } else BUG(); } + return 0; +} + +/* + * Unknown boot options get handed to init, unless they look like + * unused parameters (modprobe will find them in /proc/cmdline). + */ +static int __init unknown_bootoption(char *param, char *val) +{ + repair_env_string(param, val); /* Handle obsolete-style parameters */ if (obsolete_checksetup(param)) @@ -732,11 +738,6 @@ static char *initcall_level_names[] __initdata = { "late parameters", }; -static int __init ignore_unknown_bootoption(char *param, char *val) -{ - return 0; -} - static void __init do_initcall_level(int level) { extern const struct kernel_param __start___param[], __stop___param[]; @@ -747,7 +748,7 @@ static void __init do_initcall_level(int level) static_command_line, __start___param, __stop___param - __start___param, level, level, - ignore_unknown_bootoption); + repair_env_string); for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) do_one_initcall(*fn); diff --git a/kernel/compat.c b/kernel/compat.c index 74ff8498809a..d2c67aa49ae6 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -372,25 +372,54 @@ asmlinkage long compat_sys_sigpending(compat_old_sigset_t __user *set) #ifdef __ARCH_WANT_SYS_SIGPROCMASK -asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t __user *set, - compat_old_sigset_t __user *oset) +/* + * sys_sigprocmask SIG_SETMASK sets the first (compat) word of the + * blocked set of signals to the supplied signal set + */ +static inline void compat_sig_setmask(sigset_t *blocked, compat_sigset_word set) { - old_sigset_t s; - long ret; - mm_segment_t old_fs; + memcpy(blocked->sig, &set, sizeof(set)); +} - if (set && get_user(s, set)) - return -EFAULT; - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = sys_sigprocmask(how, - set ? (old_sigset_t __user *) &s : NULL, - oset ? (old_sigset_t __user *) &s : NULL); - set_fs(old_fs); - if (ret == 0) - if (oset) - ret = put_user(s, oset); - return ret; +asmlinkage long compat_sys_sigprocmask(int how, + compat_old_sigset_t __user *nset, + compat_old_sigset_t __user *oset) +{ + old_sigset_t old_set, new_set; + sigset_t new_blocked; + + old_set = current->blocked.sig[0]; + + if (nset) { + if (get_user(new_set, nset)) + return -EFAULT; + new_set &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP)); + + new_blocked = current->blocked; + + switch (how) { + case SIG_BLOCK: + sigaddsetmask(&new_blocked, new_set); + break; + case SIG_UNBLOCK: + sigdelsetmask(&new_blocked, new_set); + break; + case SIG_SETMASK: + compat_sig_setmask(&new_blocked, new_set); + break; + default: + return -EINVAL; + } + + set_current_blocked(&new_blocked); + } + + if (oset) { + if (put_user(old_set, oset)) + return -EFAULT; + } + + return 0; } #endif diff --git a/kernel/cpuset.c b/kernel/cpuset.c index b96ad75b7e64..14f7070b4ba2 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -270,11 +270,11 @@ static struct file_system_type cpuset_fs_type = { * are online. If none are online, walk up the cpuset hierarchy * until we find one that does have some online cpus. If we get * all the way to the top and still haven't found any online cpus, - * return cpu_online_map. Or if passed a NULL cs from an exit'ing - * task, return cpu_online_map. + * return cpu_online_mask. Or if passed a NULL cs from an exit'ing + * task, return cpu_online_mask. * * One way or another, we guarantee to return some non-empty subset - * of cpu_online_map. + * of cpu_online_mask. * * Call with callback_mutex held. */ @@ -867,7 +867,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, int retval; int is_load_balanced; - /* top_cpuset.cpus_allowed tracks cpu_online_map; it's read-only */ + /* top_cpuset.cpus_allowed tracks cpu_online_mask; it's read-only */ if (cs == &top_cpuset) return -EACCES; @@ -2149,7 +2149,7 @@ void __init cpuset_init_smp(void) * * Description: Returns the cpumask_var_t cpus_allowed of the cpuset * attached to the specified @tsk. Guaranteed to return some non-empty - * subset of cpu_online_map, even if this means going outside the + * subset of cpu_online_mask, even if this means going outside the * tasks cpuset. **/ diff --git a/kernel/cred.c b/kernel/cred.c index 97b36eeca4c9..e70683d9ec32 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -386,6 +386,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) struct cred *new; int ret; + p->replacement_session_keyring = NULL; + if ( #ifdef CONFIG_KEYS !p->cred->thread_keyring && diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 1dc53bae56e1..0557f24c6bca 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -160,37 +160,39 @@ early_param("nokgdbroundup", opt_nokgdbroundup); * Weak aliases for breakpoint management, * can be overriden by architectures when needed: */ -int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) +int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) { int err; - err = probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE); + err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr, + BREAK_INSTR_SIZE); if (err) return err; - - return probe_kernel_write((char *)addr, arch_kgdb_ops.gdb_bpt_instr, - BREAK_INSTR_SIZE); + err = probe_kernel_write((char *)bpt->bpt_addr, + arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE); + return err; } -int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) +int __weak kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) { - return probe_kernel_write((char *)addr, - (char *)bundle, BREAK_INSTR_SIZE); + return probe_kernel_write((char *)bpt->bpt_addr, + (char *)bpt->saved_instr, BREAK_INSTR_SIZE); } int __weak kgdb_validate_break_address(unsigned long addr) { - char tmp_variable[BREAK_INSTR_SIZE]; + struct kgdb_bkpt tmp; int err; - /* Validate setting the breakpoint and then removing it. In the + /* Validate setting the breakpoint and then removing it. If the * remove fails, the kernel needs to emit a bad message because we * are deep trouble not being able to put things back the way we * found them. */ - err = kgdb_arch_set_breakpoint(addr, tmp_variable); + tmp.bpt_addr = addr; + err = kgdb_arch_set_breakpoint(&tmp); if (err) return err; - err = kgdb_arch_remove_breakpoint(addr, tmp_variable); + err = kgdb_arch_remove_breakpoint(&tmp); if (err) printk(KERN_ERR "KGDB: Critical breakpoint error, kernel " "memory destroyed at: %lx", addr); @@ -234,7 +236,6 @@ static void kgdb_flush_swbreak_addr(unsigned long addr) */ int dbg_activate_sw_breakpoints(void) { - unsigned long addr; int error; int ret = 0; int i; @@ -243,16 +244,15 @@ int dbg_activate_sw_breakpoints(void) if (kgdb_break[i].state != BP_SET) continue; - addr = kgdb_break[i].bpt_addr; - error = kgdb_arch_set_breakpoint(addr, - kgdb_break[i].saved_instr); + error = kgdb_arch_set_breakpoint(&kgdb_break[i]); if (error) { ret = error; - printk(KERN_INFO "KGDB: BP install failed: %lx", addr); + printk(KERN_INFO "KGDB: BP install failed: %lx", + kgdb_break[i].bpt_addr); continue; } - kgdb_flush_swbreak_addr(addr); + kgdb_flush_swbreak_addr(kgdb_break[i].bpt_addr); kgdb_break[i].state = BP_ACTIVE; } return ret; @@ -301,7 +301,6 @@ int dbg_set_sw_break(unsigned long addr) int dbg_deactivate_sw_breakpoints(void) { - unsigned long addr; int error; int ret = 0; int i; @@ -309,15 +308,14 @@ int dbg_deactivate_sw_breakpoints(void) for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if (kgdb_break[i].state != BP_ACTIVE) continue; - addr = kgdb_break[i].bpt_addr; - error = kgdb_arch_remove_breakpoint(addr, - kgdb_break[i].saved_instr); + error = kgdb_arch_remove_breakpoint(&kgdb_break[i]); if (error) { - printk(KERN_INFO "KGDB: BP remove failed: %lx\n", addr); + printk(KERN_INFO "KGDB: BP remove failed: %lx\n", + kgdb_break[i].bpt_addr); ret = error; } - kgdb_flush_swbreak_addr(addr); + kgdb_flush_swbreak_addr(kgdb_break[i].bpt_addr); kgdb_break[i].state = BP_SET; } return ret; @@ -351,7 +349,6 @@ int kgdb_isremovedbreak(unsigned long addr) int dbg_remove_all_break(void) { - unsigned long addr; int error; int i; @@ -359,12 +356,10 @@ int dbg_remove_all_break(void) for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if (kgdb_break[i].state != BP_ACTIVE) goto setundefined; - addr = kgdb_break[i].bpt_addr; - error = kgdb_arch_remove_breakpoint(addr, - kgdb_break[i].saved_instr); + error = kgdb_arch_remove_breakpoint(&kgdb_break[i]); if (error) printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n", - addr); + kgdb_break[i].bpt_addr); setundefined: kgdb_break[i].state = BP_UNDEFINED; } diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index 9b5f17da1c56..bb9520f0f6ff 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -743,7 +743,7 @@ kdb_printit: kdb_input_flush(); c = console_drivers; - if (!dbg_io_ops->is_console) { + if (dbg_io_ops && !dbg_io_ops->is_console) { len = strlen(moreprompt); cp = moreprompt; while (len--) { diff --git a/kernel/events/core.c b/kernel/events/core.c index a6a9ec4cd8f5..fd126f82b57c 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3183,7 +3183,7 @@ static void perf_event_for_each(struct perf_event *event, perf_event_for_each_child(event, func); func(event); list_for_each_entry(sibling, &event->sibling_list, group_entry) - perf_event_for_each_child(event, func); + perf_event_for_each_child(sibling, func); mutex_unlock(&ctx->mutex); } diff --git a/kernel/fork.c b/kernel/fork.c index b9372a0bff18..687a15d56243 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -47,6 +47,7 @@ #include <linux/audit.h> #include <linux/memcontrol.h> #include <linux/ftrace.h> +#include <linux/proc_fs.h> #include <linux/profile.h> #include <linux/rmap.h> #include <linux/ksm.h> @@ -1464,6 +1465,8 @@ bad_fork_cleanup_io: if (p->io_context) exit_io_context(p); bad_fork_cleanup_namespaces: + if (unlikely(clone_flags & CLONE_NEWPID)) + pid_ns_release_proc(p->nsproxy->pid_ns); exit_task_namespaces(p); bad_fork_cleanup_mm: if (p->mm) diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index cf1a4a68ce44..d1a758bc972a 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -62,7 +62,7 @@ config IRQ_DOMAIN_DEBUG help This option will show the mapping relationship between hardware irq numbers and Linux irq numbers. The mapping is exposed via debugfs - in the file "virq_mapping". + in the file "irq_domain_mapping". If you don't know what this means you don't need it. diff --git a/kernel/irq/debug.h b/kernel/irq/debug.h index 97a8bfadc88a..e75e29e4434a 100644 --- a/kernel/irq/debug.h +++ b/kernel/irq/debug.h @@ -4,10 +4,10 @@ #include <linux/kallsyms.h> -#define P(f) if (desc->status_use_accessors & f) printk("%14s set\n", #f) -#define PS(f) if (desc->istate & f) printk("%14s set\n", #f) +#define ___P(f) if (desc->status_use_accessors & f) printk("%14s set\n", #f) +#define ___PS(f) if (desc->istate & f) printk("%14s set\n", #f) /* FIXME */ -#define PD(f) do { } while (0) +#define ___PD(f) do { } while (0) static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) { @@ -23,23 +23,23 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) print_symbol("%s\n", (unsigned long)desc->action->handler); } - P(IRQ_LEVEL); - P(IRQ_PER_CPU); - P(IRQ_NOPROBE); - P(IRQ_NOREQUEST); - P(IRQ_NOTHREAD); - P(IRQ_NOAUTOEN); + ___P(IRQ_LEVEL); + ___P(IRQ_PER_CPU); + ___P(IRQ_NOPROBE); + ___P(IRQ_NOREQUEST); + ___P(IRQ_NOTHREAD); + ___P(IRQ_NOAUTOEN); - PS(IRQS_AUTODETECT); - PS(IRQS_REPLAY); - PS(IRQS_WAITING); - PS(IRQS_PENDING); + ___PS(IRQS_AUTODETECT); + ___PS(IRQS_REPLAY); + ___PS(IRQS_WAITING); + ___PS(IRQS_PENDING); - PD(IRQS_INPROGRESS); - PD(IRQS_DISABLED); - PD(IRQS_MASKED); + ___PD(IRQS_INPROGRESS); + ___PD(IRQS_DISABLED); + ___PD(IRQS_MASKED); } -#undef P -#undef PS -#undef PD +#undef ___P +#undef ___PS +#undef ___PD diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 3601f3fbf67c..0e0ba5f840b2 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -23,7 +23,6 @@ static LIST_HEAD(irq_domain_list); static DEFINE_MUTEX(irq_domain_mutex); static DEFINE_MUTEX(revmap_trees_mutex); -static unsigned int irq_virq_count = NR_IRQS; static struct irq_domain *irq_default_domain; /** @@ -184,13 +183,16 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node, } struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, + unsigned int max_irq, const struct irq_domain_ops *ops, void *host_data) { struct irq_domain *domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_NOMAP, ops, host_data); - if (domain) + if (domain) { + domain->revmap_data.nomap.max_irq = max_irq ? max_irq : ~0; irq_domain_add(domain); + } return domain; } @@ -262,22 +264,6 @@ void irq_set_default_host(struct irq_domain *domain) irq_default_domain = domain; } -/** - * irq_set_virq_count() - Set the maximum number of linux irqs - * @count: number of linux irqs, capped with NR_IRQS - * - * This is mainly for use by platforms like iSeries who want to program - * the virtual irq number in the controller to avoid the reverse mapping - */ -void irq_set_virq_count(unsigned int count) -{ - pr_debug("irq: Trying to set virq count to %d\n", count); - - BUG_ON(count < NUM_ISA_INTERRUPTS); - if (count < NR_IRQS) - irq_virq_count = count; -} - static int irq_setup_virq(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hwirq) { @@ -320,13 +306,12 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) pr_debug("irq: create_direct virq allocation failed\n"); return 0; } - if (virq >= irq_virq_count) { + if (virq >= domain->revmap_data.nomap.max_irq) { pr_err("ERROR: no free irqs available below %i maximum\n", - irq_virq_count); + domain->revmap_data.nomap.max_irq); irq_free_desc(virq); return 0; } - pr_debug("irq: create_direct obtained virq %d\n", virq); if (irq_setup_virq(domain, virq, virq)) { @@ -350,7 +335,8 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) unsigned int irq_create_mapping(struct irq_domain *domain, irq_hw_number_t hwirq) { - unsigned int virq, hint; + unsigned int hint; + int virq; pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); @@ -377,13 +363,13 @@ unsigned int irq_create_mapping(struct irq_domain *domain, return irq_domain_legacy_revmap(domain, hwirq); /* Allocate a virtual interrupt number */ - hint = hwirq % irq_virq_count; + hint = hwirq % nr_irqs; if (hint == 0) hint++; virq = irq_alloc_desc_from(hint, 0); - if (!virq) + if (virq <= 0) virq = irq_alloc_desc_from(1, 0); - if (!virq) { + if (virq <= 0) { pr_debug("irq: -> virq allocation failed\n"); return 0; } @@ -515,7 +501,7 @@ unsigned int irq_find_mapping(struct irq_domain *domain, irq_hw_number_t hwirq) { unsigned int i; - unsigned int hint = hwirq % irq_virq_count; + unsigned int hint = hwirq % nr_irqs; /* Look for default domain if nececssary */ if (domain == NULL) @@ -536,7 +522,7 @@ unsigned int irq_find_mapping(struct irq_domain *domain, if (data && (data->domain == domain) && (data->hwirq == hwirq)) return i; i++; - if (i >= irq_virq_count) + if (i >= nr_irqs) i = 1; } while(i != hint); return 0; @@ -642,8 +628,9 @@ static int virq_debug_show(struct seq_file *m, void *private) void *data; int i; - seq_printf(m, "%-5s %-7s %-15s %-18s %s\n", "virq", "hwirq", - "chip name", "chip data", "domain name"); + seq_printf(m, "%-5s %-7s %-15s %-*s %s\n", "irq", "hwirq", + "chip name", (int)(2 * sizeof(void *) + 2), "chip data", + "domain name"); for (i = 1; i < nr_irqs; i++) { desc = irq_to_desc(i); @@ -666,7 +653,7 @@ static int virq_debug_show(struct seq_file *m, void *private) seq_printf(m, "%-15s ", p); data = irq_desc_get_chip_data(desc); - seq_printf(m, "0x%16p ", data); + seq_printf(m, data ? "0x%p " : " %p ", data); if (desc->irq_data.domain && desc->irq_data.domain->of_node) p = desc->irq_data.domain->of_node->full_name; diff --git a/kernel/irq_work.c b/kernel/irq_work.c index c3c46c72046e..1588e3b2871b 100644 --- a/kernel/irq_work.c +++ b/kernel/irq_work.c @@ -5,11 +5,13 @@ * context. The enqueueing is NMI-safe. */ +#include <linux/bug.h> #include <linux/kernel.h> #include <linux/export.h> #include <linux/irq_work.h> #include <linux/percpu.h> #include <linux/hardirq.h> +#include <linux/irqflags.h> #include <asm/processor.h> /* diff --git a/kernel/itimer.c b/kernel/itimer.c index 22000c3db0dd..8d262b467573 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -284,8 +284,12 @@ SYSCALL_DEFINE3(setitimer, int, which, struct itimerval __user *, value, if (value) { if(copy_from_user(&set_buffer, value, sizeof(set_buffer))) return -EFAULT; - } else - memset((char *) &set_buffer, 0, sizeof(set_buffer)); + } else { + memset(&set_buffer, 0, sizeof(set_buffer)); + printk_once(KERN_WARNING "%s calls setitimer() with new_value NULL pointer." + " Misfeature support will be removed\n", + current->comm); + } error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : NULL); if (error || !ovalue) diff --git a/kernel/kmod.c b/kernel/kmod.c index 957a7aab8ebc..05698a7415fe 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -322,7 +322,7 @@ static void __call_usermodehelper(struct work_struct *work) * land has been frozen during a system-wide hibernation or suspend operation). * Should always be manipulated under umhelper_sem acquired for write. */ -static int usermodehelper_disabled = 1; +static enum umh_disable_depth usermodehelper_disabled = UMH_DISABLED; /* Number of helpers running */ static atomic_t running_helpers = ATOMIC_INIT(0); @@ -334,32 +334,110 @@ static atomic_t running_helpers = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq); /* + * Used by usermodehelper_read_lock_wait() to wait for usermodehelper_disabled + * to become 'false'. + */ +static DECLARE_WAIT_QUEUE_HEAD(usermodehelper_disabled_waitq); + +/* * Time to wait for running_helpers to become zero before the setting of * usermodehelper_disabled in usermodehelper_disable() fails */ #define RUNNING_HELPERS_TIMEOUT (5 * HZ) -void read_lock_usermodehelper(void) +int usermodehelper_read_trylock(void) { + DEFINE_WAIT(wait); + int ret = 0; + down_read(&umhelper_sem); + for (;;) { + prepare_to_wait(&usermodehelper_disabled_waitq, &wait, + TASK_INTERRUPTIBLE); + if (!usermodehelper_disabled) + break; + + if (usermodehelper_disabled == UMH_DISABLED) + ret = -EAGAIN; + + up_read(&umhelper_sem); + + if (ret) + break; + + schedule(); + try_to_freeze(); + + down_read(&umhelper_sem); + } + finish_wait(&usermodehelper_disabled_waitq, &wait); + return ret; +} +EXPORT_SYMBOL_GPL(usermodehelper_read_trylock); + +long usermodehelper_read_lock_wait(long timeout) +{ + DEFINE_WAIT(wait); + + if (timeout < 0) + return -EINVAL; + + down_read(&umhelper_sem); + for (;;) { + prepare_to_wait(&usermodehelper_disabled_waitq, &wait, + TASK_UNINTERRUPTIBLE); + if (!usermodehelper_disabled) + break; + + up_read(&umhelper_sem); + + timeout = schedule_timeout(timeout); + if (!timeout) + break; + + down_read(&umhelper_sem); + } + finish_wait(&usermodehelper_disabled_waitq, &wait); + return timeout; } -EXPORT_SYMBOL_GPL(read_lock_usermodehelper); +EXPORT_SYMBOL_GPL(usermodehelper_read_lock_wait); -void read_unlock_usermodehelper(void) +void usermodehelper_read_unlock(void) { up_read(&umhelper_sem); } -EXPORT_SYMBOL_GPL(read_unlock_usermodehelper); +EXPORT_SYMBOL_GPL(usermodehelper_read_unlock); /** - * usermodehelper_disable - prevent new helpers from being started + * __usermodehelper_set_disable_depth - Modify usermodehelper_disabled. + * depth: New value to assign to usermodehelper_disabled. + * + * Change the value of usermodehelper_disabled (under umhelper_sem locked for + * writing) and wakeup tasks waiting for it to change. */ -int usermodehelper_disable(void) +void __usermodehelper_set_disable_depth(enum umh_disable_depth depth) +{ + down_write(&umhelper_sem); + usermodehelper_disabled = depth; + wake_up(&usermodehelper_disabled_waitq); + up_write(&umhelper_sem); +} + +/** + * __usermodehelper_disable - Prevent new helpers from being started. + * @depth: New value to assign to usermodehelper_disabled. + * + * Set usermodehelper_disabled to @depth and wait for running helpers to exit. + */ +int __usermodehelper_disable(enum umh_disable_depth depth) { long retval; + if (!depth) + return -EINVAL; + down_write(&umhelper_sem); - usermodehelper_disabled = 1; + usermodehelper_disabled = depth; up_write(&umhelper_sem); /* @@ -374,31 +452,10 @@ int usermodehelper_disable(void) if (retval) return 0; - down_write(&umhelper_sem); - usermodehelper_disabled = 0; - up_write(&umhelper_sem); + __usermodehelper_set_disable_depth(UMH_ENABLED); return -EAGAIN; } -/** - * usermodehelper_enable - allow new helpers to be started again - */ -void usermodehelper_enable(void) -{ - down_write(&umhelper_sem); - usermodehelper_disabled = 0; - up_write(&umhelper_sem); -} - -/** - * usermodehelper_is_disabled - check if new helpers are allowed to be started - */ -bool usermodehelper_is_disabled(void) -{ - return usermodehelper_disabled; -} -EXPORT_SYMBOL_GPL(usermodehelper_is_disabled); - static void helper_lock(void) { atomic_inc(&running_helpers); diff --git a/kernel/padata.c b/kernel/padata.c index 6f10eb285ece..89fe3d1b9efb 100644 --- a/kernel/padata.c +++ b/kernel/padata.c @@ -1,6 +1,8 @@ /* * padata.c - generic interface to process data streams in parallel * + * See Documentation/padata.txt for an api documentation. + * * Copyright (C) 2008, 2009 secunet Security Networks AG * Copyright (C) 2008, 2009 Steffen Klassert <steffen.klassert@secunet.com> * @@ -354,13 +356,13 @@ static int padata_setup_cpumasks(struct parallel_data *pd, if (!alloc_cpumask_var(&pd->cpumask.pcpu, GFP_KERNEL)) return -ENOMEM; - cpumask_and(pd->cpumask.pcpu, pcpumask, cpu_active_mask); + cpumask_and(pd->cpumask.pcpu, pcpumask, cpu_online_mask); if (!alloc_cpumask_var(&pd->cpumask.cbcpu, GFP_KERNEL)) { free_cpumask_var(pd->cpumask.cbcpu); return -ENOMEM; } - cpumask_and(pd->cpumask.cbcpu, cbcpumask, cpu_active_mask); + cpumask_and(pd->cpumask.cbcpu, cbcpumask, cpu_online_mask); return 0; } @@ -564,7 +566,7 @@ EXPORT_SYMBOL(padata_unregister_cpumask_notifier); static bool padata_validate_cpumask(struct padata_instance *pinst, const struct cpumask *cpumask) { - if (!cpumask_intersects(cpumask, cpu_active_mask)) { + if (!cpumask_intersects(cpumask, cpu_online_mask)) { pinst->flags |= PADATA_INVALID; return false; } @@ -678,7 +680,7 @@ static int __padata_add_cpu(struct padata_instance *pinst, int cpu) { struct parallel_data *pd; - if (cpumask_test_cpu(cpu, cpu_active_mask)) { + if (cpumask_test_cpu(cpu, cpu_online_mask)) { pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu, pinst->cpumask.cbcpu); if (!pd) @@ -746,6 +748,9 @@ static int __padata_remove_cpu(struct padata_instance *pinst, int cpu) return -ENOMEM; padata_replace(pinst, pd); + + cpumask_clear_cpu(cpu, pd->cpumask.cbcpu); + cpumask_clear_cpu(cpu, pd->cpumask.pcpu); } return 0; diff --git a/kernel/panic.c b/kernel/panic.c index 80aed44e345a..8ed89a175d79 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -97,7 +97,7 @@ void panic(const char *fmt, ...) /* * Avoid nested stack-dumping if a panic occurs during oops processing */ - if (!oops_in_progress) + if (!test_taint(TAINT_DIE) && oops_in_progress <= 1) dump_stack(); #endif diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 0a186cfde788..e09dfbfeecee 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -16,7 +16,6 @@ #include <linux/string.h> #include <linux/device.h> #include <linux/async.h> -#include <linux/kmod.h> #include <linux/delay.h> #include <linux/fs.h> #include <linux/mount.h> @@ -611,14 +610,10 @@ int hibernate(void) if (error) goto Exit; - error = usermodehelper_disable(); - if (error) - goto Exit; - /* Allocate memory management structures */ error = create_basic_memory_bitmaps(); if (error) - goto Enable_umh; + goto Exit; printk(KERN_INFO "PM: Syncing filesystems ... "); sys_sync(); @@ -661,8 +656,6 @@ int hibernate(void) Free_bitmaps: free_basic_memory_bitmaps(); - Enable_umh: - usermodehelper_enable(); Exit: pm_notifier_call_chain(PM_POST_HIBERNATION); pm_restore_console(); @@ -777,15 +770,9 @@ static int software_resume(void) if (error) goto close_finish; - error = usermodehelper_disable(); - if (error) - goto close_finish; - error = create_basic_memory_bitmaps(); - if (error) { - usermodehelper_enable(); + if (error) goto close_finish; - } pr_debug("PM: Preparing processes for restore.\n"); error = freeze_processes(); @@ -806,7 +793,6 @@ static int software_resume(void) thaw_processes(); Done: free_basic_memory_bitmaps(); - usermodehelper_enable(); Finish: pm_notifier_call_chain(PM_POST_RESTORE); pm_restore_console(); diff --git a/kernel/power/process.c b/kernel/power/process.c index 0d2aeb226108..19db29f67558 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -16,6 +16,7 @@ #include <linux/freezer.h> #include <linux/delay.h> #include <linux/workqueue.h> +#include <linux/kmod.h> /* * Timeout for stopping processes @@ -122,6 +123,10 @@ int freeze_processes(void) { int error; + error = __usermodehelper_disable(UMH_FREEZING); + if (error) + return error; + if (!pm_freezing) atomic_inc(&system_freezing_cnt); @@ -130,6 +135,7 @@ int freeze_processes(void) error = try_to_freeze_tasks(true); if (!error) { printk("done."); + __usermodehelper_set_disable_depth(UMH_DISABLED); oom_killer_disable(); } printk("\n"); @@ -187,6 +193,8 @@ void thaw_processes(void) } while_each_thread(g, p); read_unlock(&tasklist_lock); + usermodehelper_enable(); + schedule(); printk("done.\n"); } diff --git a/kernel/power/qos.c b/kernel/power/qos.c index d6d6dbd1ecc0..6a031e684026 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -230,6 +230,21 @@ int pm_qos_request_active(struct pm_qos_request *req) EXPORT_SYMBOL_GPL(pm_qos_request_active); /** + * pm_qos_work_fn - the timeout handler of pm_qos_update_request_timeout + * @work: work struct for the delayed work (timeout) + * + * This cancels the timeout request by falling back to the default at timeout. + */ +static void pm_qos_work_fn(struct work_struct *work) +{ + struct pm_qos_request *req = container_of(to_delayed_work(work), + struct pm_qos_request, + work); + + pm_qos_update_request(req, PM_QOS_DEFAULT_VALUE); +} + +/** * pm_qos_add_request - inserts new qos request into the list * @req: pointer to a preallocated handle * @pm_qos_class: identifies which list of qos request to use @@ -253,6 +268,7 @@ void pm_qos_add_request(struct pm_qos_request *req, return; } req->pm_qos_class = pm_qos_class; + INIT_DELAYED_WORK(&req->work, pm_qos_work_fn); pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, &req->node, PM_QOS_ADD_REQ, value); } @@ -279,6 +295,9 @@ void pm_qos_update_request(struct pm_qos_request *req, return; } + if (delayed_work_pending(&req->work)) + cancel_delayed_work_sync(&req->work); + if (new_value != req->node.prio) pm_qos_update_target( pm_qos_array[req->pm_qos_class]->constraints, @@ -287,6 +306,34 @@ void pm_qos_update_request(struct pm_qos_request *req, EXPORT_SYMBOL_GPL(pm_qos_update_request); /** + * pm_qos_update_request_timeout - modifies an existing qos request temporarily. + * @req : handle to list element holding a pm_qos request to use + * @new_value: defines the temporal qos request + * @timeout_us: the effective duration of this qos request in usecs. + * + * After timeout_us, this qos request is cancelled automatically. + */ +void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value, + unsigned long timeout_us) +{ + if (!req) + return; + if (WARN(!pm_qos_request_active(req), + "%s called for unknown object.", __func__)) + return; + + if (delayed_work_pending(&req->work)) + cancel_delayed_work_sync(&req->work); + + if (new_value != req->node.prio) + pm_qos_update_target( + pm_qos_array[req->pm_qos_class]->constraints, + &req->node, PM_QOS_UPDATE_REQ, new_value); + + schedule_delayed_work(&req->work, usecs_to_jiffies(timeout_us)); +} + +/** * pm_qos_remove_request - modifies an existing qos request * @req: handle to request list element * @@ -305,6 +352,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) return; } + if (delayed_work_pending(&req->work)) + cancel_delayed_work_sync(&req->work); + pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints, &req->node, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 88e5c967370d..396d262b8fd0 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/init.h> -#include <linux/kmod.h> #include <linux/console.h> #include <linux/cpu.h> #include <linux/syscalls.h> @@ -102,17 +101,12 @@ static int suspend_prepare(void) if (error) goto Finish; - error = usermodehelper_disable(); - if (error) - goto Finish; - error = suspend_freeze_processes(); if (!error) return 0; suspend_stats.failed_freeze++; dpm_save_failed_step(SUSPEND_FREEZE); - usermodehelper_enable(); Finish: pm_notifier_call_chain(PM_POST_SUSPEND); pm_restore_console(); @@ -259,7 +253,6 @@ int suspend_devices_and_enter(suspend_state_t state) static void suspend_finish(void) { suspend_thaw_processes(); - usermodehelper_enable(); pm_notifier_call_chain(PM_POST_SUSPEND); pm_restore_console(); } diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 8742fd013a94..eef311a58a64 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -51,6 +51,23 @@ #define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(sector_t) - 1) +/* + * Number of free pages that are not high. + */ +static inline unsigned long low_free_pages(void) +{ + return nr_free_pages() - nr_free_highpages(); +} + +/* + * Number of pages required to be kept free while writing the image. Always + * half of all available low pages before the writing starts. + */ +static inline unsigned long reqd_free_pages(void) +{ + return low_free_pages() / 2; +} + struct swap_map_page { sector_t entries[MAP_PAGE_ENTRIES]; sector_t next_swap; @@ -72,7 +89,7 @@ struct swap_map_handle { sector_t cur_swap; sector_t first_sector; unsigned int k; - unsigned long nr_free_pages, written; + unsigned long reqd_free_pages; u32 crc32; }; @@ -316,8 +333,7 @@ static int get_swap_writer(struct swap_map_handle *handle) goto err_rel; } handle->k = 0; - handle->nr_free_pages = nr_free_pages() >> 1; - handle->written = 0; + handle->reqd_free_pages = reqd_free_pages(); handle->first_sector = handle->cur_swap; return 0; err_rel: @@ -352,11 +368,11 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, handle->cur_swap = offset; handle->k = 0; } - if (bio_chain && ++handle->written > handle->nr_free_pages) { + if (bio_chain && low_free_pages() <= handle->reqd_free_pages) { error = hib_wait_on_bio_chain(bio_chain); if (error) goto out; - handle->written = 0; + handle->reqd_free_pages = reqd_free_pages(); } out: return error; @@ -618,7 +634,7 @@ static int save_image_lzo(struct swap_map_handle *handle, * Adjust number of free pages after all allocations have been done. * We don't want to run out of pages when writing. */ - handle->nr_free_pages = nr_free_pages() >> 1; + handle->reqd_free_pages = reqd_free_pages(); /* * Start the CRC32 thread. diff --git a/kernel/power/user.c b/kernel/power/user.c index 33c4329205af..91b0fd021a95 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -12,7 +12,6 @@ #include <linux/suspend.h> #include <linux/syscalls.h> #include <linux/reboot.h> -#include <linux/kmod.h> #include <linux/string.h> #include <linux/device.h> #include <linux/miscdevice.h> @@ -222,14 +221,8 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, sys_sync(); printk("done.\n"); - error = usermodehelper_disable(); - if (error) - break; - error = freeze_processes(); - if (error) - usermodehelper_enable(); - else + if (!error) data->frozen = 1; break; @@ -238,7 +231,6 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, break; pm_restore_gfp_mask(); thaw_processes(); - usermodehelper_enable(); data->frozen = 0; break; diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 1050d6d3922c..d0c5baf1ab18 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1820,7 +1820,6 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), * a quiescent state betweentimes. */ local_irq_save(flags); - WARN_ON_ONCE(cpu_is_offline(smp_processor_id())); rdp = this_cpu_ptr(rsp->rda); /* Add the callback to our list. */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 4603b9d8f30a..0533a688ce22 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6405,16 +6405,26 @@ static void __sdt_free(const struct cpumask *cpu_map) struct sd_data *sdd = &tl->data; for_each_cpu(j, cpu_map) { - struct sched_domain *sd = *per_cpu_ptr(sdd->sd, j); - if (sd && (sd->flags & SD_OVERLAP)) - free_sched_groups(sd->groups, 0); - kfree(*per_cpu_ptr(sdd->sd, j)); - kfree(*per_cpu_ptr(sdd->sg, j)); - kfree(*per_cpu_ptr(sdd->sgp, j)); + struct sched_domain *sd; + + if (sdd->sd) { + sd = *per_cpu_ptr(sdd->sd, j); + if (sd && (sd->flags & SD_OVERLAP)) + free_sched_groups(sd->groups, 0); + kfree(*per_cpu_ptr(sdd->sd, j)); + } + + if (sdd->sg) + kfree(*per_cpu_ptr(sdd->sg, j)); + if (sdd->sgp) + kfree(*per_cpu_ptr(sdd->sgp, j)); } free_percpu(sdd->sd); + sdd->sd = NULL; free_percpu(sdd->sg); + sdd->sg = NULL; free_percpu(sdd->sgp); + sdd->sgp = NULL; } } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0d97ebdc58f0..e9553640c1c3 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -784,7 +784,7 @@ account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se) update_load_add(&rq_of(cfs_rq)->load, se->load.weight); #ifdef CONFIG_SMP if (entity_is_task(se)) - list_add_tail(&se->group_node, &rq_of(cfs_rq)->cfs_tasks); + list_add(&se->group_node, &rq_of(cfs_rq)->cfs_tasks); #endif cfs_rq->nr_running++; } @@ -3215,6 +3215,8 @@ static int move_one_task(struct lb_env *env) static unsigned long task_h_load(struct task_struct *p); +static const unsigned int sched_nr_migrate_break = 32; + /* * move_tasks tries to move up to load_move weighted load from busiest to * this_rq, as part of a balancing operation within domain "sd". @@ -3242,7 +3244,7 @@ static int move_tasks(struct lb_env *env) /* take a breather every nr_migrate tasks */ if (env->loop > env->loop_break) { - env->loop_break += sysctl_sched_nr_migrate; + env->loop_break += sched_nr_migrate_break; env->flags |= LBF_NEED_BREAK; break; } @@ -3252,7 +3254,7 @@ static int move_tasks(struct lb_env *env) load = task_h_load(p); - if (load < 16 && !env->sd->nr_balance_failed) + if (sched_feat(LB_MIN) && load < 16 && !env->sd->nr_balance_failed) goto next; if ((load / 2) > env->load_move) @@ -4407,7 +4409,7 @@ static int load_balance(int this_cpu, struct rq *this_rq, .dst_cpu = this_cpu, .dst_rq = this_rq, .idle = idle, - .loop_break = sysctl_sched_nr_migrate, + .loop_break = sched_nr_migrate_break, }; cpumask_copy(cpus, cpu_active_mask); @@ -4445,10 +4447,10 @@ redo: * correctly treated as an imbalance. */ env.flags |= LBF_ALL_PINNED; - env.load_move = imbalance; - env.src_cpu = busiest->cpu; - env.src_rq = busiest; - env.loop_max = busiest->nr_running; + env.load_move = imbalance; + env.src_cpu = busiest->cpu; + env.src_rq = busiest; + env.loop_max = min_t(unsigned long, sysctl_sched_nr_migrate, busiest->nr_running); more_balance: local_irq_save(flags); diff --git a/kernel/sched/features.h b/kernel/sched/features.h index e61fd73913d0..de00a486c5c6 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -68,3 +68,4 @@ SCHED_FEAT(TTWU_QUEUE, true) SCHED_FEAT(FORCE_SD_OVERLAP, false) SCHED_FEAT(RT_RUNTIME_SHARE, true) +SCHED_FEAT(LB_MIN, false) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 52b3a06a02f8..4ab11879aeb4 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -170,7 +170,7 @@ static int proc_taint(struct ctl_table *table, int write, #endif #ifdef CONFIG_PRINTK -static int proc_dmesg_restrict(struct ctl_table *table, int write, +static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif @@ -703,7 +703,7 @@ static struct ctl_table kern_table[] = { .data = &dmesg_restrict, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dointvec_minmax_sysadmin, .extra1 = &zero, .extra2 = &one, }, @@ -712,7 +712,7 @@ static struct ctl_table kern_table[] = { .data = &kptr_restrict, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dmesg_restrict, + .proc_handler = proc_dointvec_minmax_sysadmin, .extra1 = &zero, .extra2 = &two, }, @@ -1943,7 +1943,7 @@ static int proc_taint(struct ctl_table *table, int write, } #ifdef CONFIG_PRINTK -static int proc_dmesg_restrict(struct ctl_table *table, int write, +static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { if (write && !capable(CAP_SYS_ADMIN)) diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index 2cf9cc7aa103..a20dc8a3c949 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig @@ -1,6 +1,10 @@ # # Timer subsystem related configuration options # + +# Core internal switch. Selected by NO_HZ / HIGH_RES_TIMERS. This is +# only related to the tick functionality. Oneshot clockevent devices +# are supported independ of this. config TICK_ONESHOT bool diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index e883f57a3cd3..f113755695e2 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -346,7 +346,8 @@ int tick_resume_broadcast(void) tick_get_broadcast_mask()); break; case TICKDEV_MODE_ONESHOT: - broadcast = tick_resume_broadcast_oneshot(bc); + if (!cpumask_empty(tick_get_broadcast_mask())) + broadcast = tick_resume_broadcast_oneshot(bc); break; } } @@ -373,6 +374,9 @@ static int tick_broadcast_set_event(ktime_t expires, int force) { struct clock_event_device *bc = tick_broadcast_device.evtdev; + if (bc->mode != CLOCK_EVT_MODE_ONESHOT) + clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); + return clockevents_program_event(bc, expires, force); } @@ -531,7 +535,6 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) int was_periodic = bc->mode == CLOCK_EVT_MODE_PERIODIC; bc->event_handler = tick_handle_oneshot_broadcast; - clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); /* Take the do_timer update */ tick_do_timer_cpu = cpu; @@ -549,6 +552,7 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) to_cpumask(tmpmask)); if (was_periodic && !cpumask_empty(to_cpumask(tmpmask))) { + clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); tick_broadcast_init_next_event(to_cpumask(tmpmask), tick_next_period); tick_broadcast_set_event(tick_next_period, 1); @@ -575,15 +579,12 @@ void tick_broadcast_switch_to_oneshot(void) unsigned long flags; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); - if (cpumask_empty(tick_get_broadcast_mask())) - goto end; tick_broadcast_device.mode = TICKDEV_MODE_ONESHOT; bc = tick_broadcast_device.evtdev; if (bc) tick_broadcast_setup_oneshot(bc); -end: raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); } diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 3526038f2836..6a3a5b9ff561 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -534,9 +534,9 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now) hrtimer_get_expires(&ts->sched_timer), 0)) break; } - /* Update jiffies and reread time */ - tick_do_update_jiffies64(now); + /* Reread time and update jiffies */ now = ktime_get(); + tick_do_update_jiffies64(now); } } diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index cdea7b56b0c9..c0bd0308741c 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -311,13 +311,6 @@ int blk_trace_remove(struct request_queue *q) } EXPORT_SYMBOL_GPL(blk_trace_remove); -static int blk_dropped_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - - return 0; -} - static ssize_t blk_dropped_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { @@ -331,18 +324,11 @@ static ssize_t blk_dropped_read(struct file *filp, char __user *buffer, static const struct file_operations blk_dropped_fops = { .owner = THIS_MODULE, - .open = blk_dropped_open, + .open = simple_open, .read = blk_dropped_read, .llseek = default_llseek, }; -static int blk_msg_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - - return 0; -} - static ssize_t blk_msg_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos) { @@ -371,7 +357,7 @@ static ssize_t blk_msg_write(struct file *filp, const char __user *buffer, static const struct file_operations blk_msg_fops = { .owner = THIS_MODULE, - .open = blk_msg_open, + .open = simple_open, .write = blk_msg_write, .llseek = noop_llseek, }; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index ed7b5d1e12f4..2a22255c1010 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4629,7 +4629,8 @@ static ssize_t rb_simple_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - struct ring_buffer *buffer = filp->private_data; + struct trace_array *tr = filp->private_data; + struct ring_buffer *buffer = tr->buffer; char buf[64]; int r; @@ -4647,7 +4648,8 @@ static ssize_t rb_simple_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - struct ring_buffer *buffer = filp->private_data; + struct trace_array *tr = filp->private_data; + struct ring_buffer *buffer = tr->buffer; unsigned long val; int ret; @@ -4734,7 +4736,7 @@ static __init int tracer_init_debugfs(void) &trace_clock_fops); trace_create_file("tracing_on", 0644, d_tracer, - global_trace.buffer, &rb_simple_fops); + &global_trace, &rb_simple_fops); #ifdef CONFIG_DYNAMIC_FTRACE trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 95059f091a24..f95d65da6db8 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -836,11 +836,11 @@ extern const char *__stop___trace_bprintk_fmt[]; filter) #include "trace_entries.h" -#ifdef CONFIG_FUNCTION_TRACER +#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_FUNCTION_TRACER) int perf_ftrace_event_register(struct ftrace_event_call *call, enum trace_reg type, void *data); #else #define perf_ftrace_event_register NULL -#endif /* CONFIG_FUNCTION_TRACER */ +#endif #endif /* _LINUX_KERNEL_TRACE_H */ diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 859fae6b1825..df611a0e76c5 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -652,6 +652,8 @@ int trace_print_lat_context(struct trace_iterator *iter) { u64 next_ts; int ret; + /* trace_find_next_entry will reset ent_size */ + int ent_size = iter->ent_size; struct trace_seq *s = &iter->seq; struct trace_entry *entry = iter->ent, *next_entry = trace_find_next_entry(iter, NULL, @@ -660,6 +662,9 @@ int trace_print_lat_context(struct trace_iterator *iter) unsigned long abs_usecs = ns2usecs(iter->ts - iter->tr->time_start); unsigned long rel_usecs; + /* Restore the original ent_size */ + iter->ent_size = ent_size; + if (!next_entry) next_ts = iter->ts; rel_usecs = ns2usecs(next_ts - iter->ts); diff --git a/lib/kobject.c b/lib/kobject.c index 21dee7c19afd..aeefa8bc8b1c 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -192,14 +192,14 @@ static int kobject_add_internal(struct kobject *kobj) /* be noisy on error issues */ if (error == -EEXIST) - printk(KERN_ERR "%s failed for %s with " - "-EEXIST, don't try to register things with " - "the same name in the same directory.\n", - __func__, kobject_name(kobj)); + WARN(1, "%s failed for %s with " + "-EEXIST, don't try to register things with " + "the same name in the same directory.\n", + __func__, kobject_name(kobj)); else - printk(KERN_ERR "%s failed for %s (%d)\n", - __func__, kobject_name(kobj), error); - dump_stack(); + WARN(1, "%s failed for %s (error: %d parent: %s)\n", + __func__, kobject_name(kobj), error, + parent ? kobject_name(parent) : "'none'"); } else kobj->state_in_sysfs = 1; diff --git a/lib/mpi/mpi-bit.c b/lib/mpi/mpi-bit.c index 2f526627e4f5..0c505361da19 100644 --- a/lib/mpi/mpi-bit.c +++ b/lib/mpi/mpi-bit.c @@ -177,8 +177,8 @@ int mpi_rshift(MPI x, MPI a, unsigned n) */ int mpi_lshift_limbs(MPI a, unsigned int count) { - mpi_ptr_t ap = a->d; - int n = a->nlimbs; + const int n = a->nlimbs; + mpi_ptr_t ap; int i; if (!count || !n) @@ -187,6 +187,7 @@ int mpi_lshift_limbs(MPI a, unsigned int count) if (RESIZE_IF_NEEDED(a, n + count) < 0) return -ENOMEM; + ap = a->d; for (i = n - 1; i >= 0; i--) ap[i + count] = ap[i]; for (i = 0; i < count; i++) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index b8ce6f450956..ae8f708e3d75 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -532,7 +532,7 @@ static struct page *dequeue_huge_page_vma(struct hstate *h, struct vm_area_struct *vma, unsigned long address, int avoid_reserve) { - struct page *page; + struct page *page = NULL; struct mempolicy *mpol; nodemask_t *nodemask; struct zonelist *zonelist; @@ -2498,7 +2498,6 @@ retry_avoidcopy: if (outside_reserve) { BUG_ON(huge_pte_none(pte)); if (unmap_ref_private(mm, vma, old_page, address)) { - BUG_ON(page_count(old_page) != 1); BUG_ON(huge_pte_none(pte)); spin_lock(&mm->page_table_lock); ptep = huge_pte_offset(mm, address & huge_page_mask(h)); @@ -2791,6 +2790,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, * so no worry about deadlock. */ page = pte_page(entry); + get_page(page); if (page != pagecache_page) lock_page(page); @@ -2822,6 +2822,7 @@ out_page_table_lock: } if (page != pagecache_page) unlock_page(page); + put_page(page); out_mutex: mutex_unlock(&hugetlb_instantiation_mutex); diff --git a/mm/memblock.c b/mm/memblock.c index 99f285599501..a44eab3157f8 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -330,6 +330,9 @@ static int __init_memblock memblock_add_region(struct memblock_type *type, phys_addr_t end = base + memblock_cap_size(base, &size); int i, nr_new; + if (!size) + return 0; + /* special case for empty array */ if (type->regions[0].size == 0) { WARN_ON(type->cnt != 1 || type->total_size); @@ -430,6 +433,9 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type, *start_rgn = *end_rgn = 0; + if (!size) + return 0; + /* we'll create at most two more regions */ while (type->cnt + 2 > type->max) if (memblock_double_array(type) < 0) @@ -514,7 +520,6 @@ int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size) (unsigned long long)base, (unsigned long long)base + size, (void *)_RET_IP_); - BUG_ON(0 == size); return memblock_add_region(_rgn, base, size, MAX_NUMNODES); } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 7d698df4a067..b659260c56ad 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2165,7 +2165,7 @@ static int __cpuinit memcg_cpu_hotplug_callback(struct notifier_block *nb, if (action == CPU_ONLINE) return NOTIFY_OK; - if ((action != CPU_DEAD) || action != CPU_DEAD_FROZEN) + if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) return NOTIFY_OK; for_each_mem_cgroup(iter) @@ -2476,10 +2476,10 @@ struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page) static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg, struct page *page, unsigned int nr_pages, - struct page_cgroup *pc, enum charge_type ctype, bool lrucare) { + struct page_cgroup *pc = lookup_page_cgroup(page); struct zone *uninitialized_var(zone); bool was_on_lru = false; bool anon; @@ -2716,7 +2716,6 @@ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, { struct mem_cgroup *memcg = NULL; unsigned int nr_pages = 1; - struct page_cgroup *pc; bool oom = true; int ret; @@ -2730,11 +2729,10 @@ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, oom = false; } - pc = lookup_page_cgroup(page); ret = __mem_cgroup_try_charge(mm, gfp_mask, nr_pages, &memcg, oom); if (ret == -ENOMEM) return ret; - __mem_cgroup_commit_charge(memcg, page, nr_pages, pc, ctype, false); + __mem_cgroup_commit_charge(memcg, page, nr_pages, ctype, false); return 0; } @@ -2831,16 +2829,13 @@ static void __mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *memcg, enum charge_type ctype) { - struct page_cgroup *pc; - if (mem_cgroup_disabled()) return; if (!memcg) return; cgroup_exclude_rmdir(&memcg->css); - pc = lookup_page_cgroup(page); - __mem_cgroup_commit_charge(memcg, page, 1, pc, ctype, true); + __mem_cgroup_commit_charge(memcg, page, 1, ctype, true); /* * Now swap is on-memory. This means this page may be * counted both as mem and swap....double count. @@ -3298,14 +3293,13 @@ int mem_cgroup_prepare_migration(struct page *page, * page. In the case new page is migrated but not remapped, new page's * mapcount will be finally 0 and we call uncharge in end_migration(). */ - pc = lookup_page_cgroup(newpage); if (PageAnon(page)) ctype = MEM_CGROUP_CHARGE_TYPE_MAPPED; else if (page_is_file_cache(page)) ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; else ctype = MEM_CGROUP_CHARGE_TYPE_SHMEM; - __mem_cgroup_commit_charge(memcg, newpage, 1, pc, ctype, false); + __mem_cgroup_commit_charge(memcg, newpage, 1, ctype, false); return ret; } @@ -3392,7 +3386,7 @@ void mem_cgroup_replace_page_cache(struct page *oldpage, * the newpage may be on LRU(or pagevec for LRU) already. We lock * LRU while we overwrite pc->mem_cgroup. */ - __mem_cgroup_commit_charge(memcg, newpage, 1, pc, type, true); + __mem_cgroup_commit_charge(memcg, newpage, 1, type, true); } #ifdef CONFIG_DEBUG_VM @@ -3763,7 +3757,7 @@ move_account: goto try_to_free; cond_resched(); /* "ret" should also be checked to ensure all lists are empty. */ - } while (memcg->res.usage > 0 || ret); + } while (res_counter_read_u64(&memcg->res, RES_USAGE) > 0 || ret); out: css_put(&memcg->css); return ret; @@ -3778,7 +3772,7 @@ try_to_free: lru_add_drain_all(); /* try to free all pages in this cgroup */ shrink = 1; - while (nr_retries && memcg->res.usage > 0) { + while (nr_retries && res_counter_read_u64(&memcg->res, RES_USAGE) > 0) { int progress; if (signal_pending(current)) { @@ -4513,6 +4507,12 @@ static void mem_cgroup_usage_unregister_event(struct cgroup *cgrp, swap_buffers: /* Swap primary and spare array */ thresholds->spare = thresholds->primary; + /* If all events are unregistered, free the spare array */ + if (!new) { + kfree(thresholds->spare); + thresholds->spare = NULL; + } + rcu_assign_pointer(thresholds->primary, new); /* To be sure that nobody uses thresholds */ diff --git a/mm/mempolicy.c b/mm/mempolicy.c index cfb6c8678754..b19569137529 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1361,11 +1361,14 @@ SYSCALL_DEFINE4(migrate_pages, pid_t, pid, unsigned long, maxnode, mm = get_task_mm(task); put_task_struct(task); - if (mm) - err = do_migrate_pages(mm, old, new, - capable(CAP_SYS_NICE) ? MPOL_MF_MOVE_ALL : MPOL_MF_MOVE); - else + + if (!mm) { err = -EINVAL; + goto out; + } + + err = do_migrate_pages(mm, old, new, + capable(CAP_SYS_NICE) ? MPOL_MF_MOVE_ALL : MPOL_MF_MOVE); mmput(mm); out: diff --git a/mm/migrate.c b/mm/migrate.c index 51c08a0c6f68..11072383ae12 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1388,14 +1388,14 @@ SYSCALL_DEFINE6(move_pages, pid_t, pid, unsigned long, nr_pages, mm = get_task_mm(task); put_task_struct(task); - if (mm) { - if (nodes) - err = do_pages_move(mm, task_nodes, nr_pages, pages, - nodes, status, flags); - else - err = do_pages_stat(mm, nr_pages, pages, status); - } else - err = -EINVAL; + if (!mm) + return -EINVAL; + + if (nodes) + err = do_pages_move(mm, task_nodes, nr_pages, pages, + nodes, status, flags); + else + err = do_pages_stat(mm, nr_pages, pages, status); mmput(mm); return err; diff --git a/mm/mmap.c b/mm/mmap.c index a7bf6a31c9f6..848ef52d9603 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -240,6 +240,8 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) return next; } +static unsigned long do_brk(unsigned long addr, unsigned long len); + SYSCALL_DEFINE1(brk, unsigned long, brk) { unsigned long rlim, retval; @@ -951,7 +953,7 @@ static inline unsigned long round_hint_to_min(unsigned long hint) * The caller must hold down_write(¤t->mm->mmap_sem). */ -unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, +static unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long pgoff) { @@ -1087,7 +1089,32 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, return mmap_region(file, addr, len, flags, vm_flags, pgoff); } -EXPORT_SYMBOL(do_mmap_pgoff); + +unsigned long do_mmap(struct file *file, unsigned long addr, + unsigned long len, unsigned long prot, + unsigned long flag, unsigned long offset) +{ + if (unlikely(offset + PAGE_ALIGN(len) < offset)) + return -EINVAL; + if (unlikely(offset & ~PAGE_MASK)) + return -EINVAL; + return do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT); +} +EXPORT_SYMBOL(do_mmap); + +unsigned long vm_mmap(struct file *file, unsigned long addr, + unsigned long len, unsigned long prot, + unsigned long flag, unsigned long offset) +{ + unsigned long ret; + struct mm_struct *mm = current->mm; + + down_write(&mm->mmap_sem); + ret = do_mmap(file, addr, len, prot, flag, offset); + up_write(&mm->mmap_sem); + return ret; +} +EXPORT_SYMBOL(vm_mmap); SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len, unsigned long, prot, unsigned long, flags, @@ -2105,21 +2132,25 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len) return 0; } - EXPORT_SYMBOL(do_munmap); -SYSCALL_DEFINE2(munmap, unsigned long, addr, size_t, len) +int vm_munmap(unsigned long start, size_t len) { int ret; struct mm_struct *mm = current->mm; - profile_munmap(addr); - down_write(&mm->mmap_sem); - ret = do_munmap(mm, addr, len); + ret = do_munmap(mm, start, len); up_write(&mm->mmap_sem); return ret; } +EXPORT_SYMBOL(vm_munmap); + +SYSCALL_DEFINE2(munmap, unsigned long, addr, size_t, len) +{ + profile_munmap(addr); + return vm_munmap(addr, len); +} static inline void verify_mm_writelocked(struct mm_struct *mm) { @@ -2136,7 +2167,7 @@ static inline void verify_mm_writelocked(struct mm_struct *mm) * anonymous maps. eventually we may be able to do some * brk-specific accounting here. */ -unsigned long do_brk(unsigned long addr, unsigned long len) +static unsigned long do_brk(unsigned long addr, unsigned long len) { struct mm_struct * mm = current->mm; struct vm_area_struct * vma, * prev; @@ -2232,7 +2263,17 @@ out: return addr; } -EXPORT_SYMBOL(do_brk); +unsigned long vm_brk(unsigned long addr, unsigned long len) +{ + struct mm_struct *mm = current->mm; + unsigned long ret; + + down_write(&mm->mmap_sem); + ret = do_brk(addr, len); + up_write(&mm->mmap_sem); + return ret; +} +EXPORT_SYMBOL(vm_brk); /* Release all mmaps. */ void exit_mmap(struct mm_struct *mm) diff --git a/mm/nobootmem.c b/mm/nobootmem.c index 24f0fc1a56d6..1983fb1c7026 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -82,8 +82,7 @@ void __init free_bootmem_late(unsigned long addr, unsigned long size) static void __init __free_pages_memory(unsigned long start, unsigned long end) { - int i; - unsigned long start_aligned, end_aligned; + unsigned long i, start_aligned, end_aligned; int order = ilog2(BITS_PER_LONG); start_aligned = (start + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1); @@ -298,13 +297,19 @@ void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, if (WARN_ON_ONCE(slab_is_available())) return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id); +again: ptr = __alloc_memory_core_early(pgdat->node_id, size, align, goal, -1ULL); if (ptr) return ptr; - return __alloc_memory_core_early(MAX_NUMNODES, size, align, - goal, -1ULL); + ptr = __alloc_memory_core_early(MAX_NUMNODES, size, align, + goal, -1ULL); + if (!ptr && goal) { + goal = 0; + goto again; + } + return ptr; } void * __init __alloc_bootmem_node_high(pg_data_t *pgdat, unsigned long size, diff --git a/mm/nommu.c b/mm/nommu.c index f59e170fceb4..bb8f4f004a82 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1233,7 +1233,7 @@ enomem: /* * handle mapping creation for uClinux */ -unsigned long do_mmap_pgoff(struct file *file, +static unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, @@ -1470,7 +1470,32 @@ error_getting_region: show_free_areas(0); return -ENOMEM; } -EXPORT_SYMBOL(do_mmap_pgoff); + +unsigned long do_mmap(struct file *file, unsigned long addr, + unsigned long len, unsigned long prot, + unsigned long flag, unsigned long offset) +{ + if (unlikely(offset + PAGE_ALIGN(len) < offset)) + return -EINVAL; + if (unlikely(offset & ~PAGE_MASK)) + return -EINVAL; + return do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT); +} +EXPORT_SYMBOL(do_mmap); + +unsigned long vm_mmap(struct file *file, unsigned long addr, + unsigned long len, unsigned long prot, + unsigned long flag, unsigned long offset) +{ + unsigned long ret; + struct mm_struct *mm = current->mm; + + down_write(&mm->mmap_sem); + ret = do_mmap(file, addr, len, prot, flag, offset); + up_write(&mm->mmap_sem); + return ret; +} +EXPORT_SYMBOL(vm_mmap); SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len, unsigned long, prot, unsigned long, flags, @@ -1709,16 +1734,22 @@ erase_whole_vma: } EXPORT_SYMBOL(do_munmap); -SYSCALL_DEFINE2(munmap, unsigned long, addr, size_t, len) +int vm_munmap(unsigned long addr, size_t len) { - int ret; struct mm_struct *mm = current->mm; + int ret; down_write(&mm->mmap_sem); ret = do_munmap(mm, addr, len); up_write(&mm->mmap_sem); return ret; } +EXPORT_SYMBOL(vm_munmap); + +SYSCALL_DEFINE2(munmap, unsigned long, addr, size_t, len) +{ + return vm_munmap(addr, len); +} /* * release all the mappings made in a process's VM space @@ -1744,7 +1775,7 @@ void exit_mmap(struct mm_struct *mm) kleave(""); } -unsigned long do_brk(unsigned long addr, unsigned long len) +unsigned long vm_brk(unsigned long addr, unsigned long len) { return -ENOMEM; } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a712fb9e04ce..918330f71dba 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5203,7 +5203,7 @@ int percpu_pagelist_fraction_sysctl_handler(ctl_table *table, int write, int ret; ret = proc_dointvec_minmax(table, write, buffer, length, ppos); - if (!write || (ret == -EINVAL)) + if (!write || (ret < 0)) return ret; for_each_populated_zone(zone) { for_each_possible_cpu(cpu) { diff --git a/mm/percpu.c b/mm/percpu.c index f47af9123af7..bb4be7435ce3 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -1132,20 +1132,20 @@ static void pcpu_dump_alloc_info(const char *lvl, for (alloc_end += gi->nr_units / upa; alloc < alloc_end; alloc++) { if (!(alloc % apl)) { - printk("\n"); + printk(KERN_CONT "\n"); printk("%spcpu-alloc: ", lvl); } - printk("[%0*d] ", group_width, group); + printk(KERN_CONT "[%0*d] ", group_width, group); for (unit_end += upa; unit < unit_end; unit++) if (gi->cpu_map[unit] != NR_CPUS) - printk("%0*d ", cpu_width, + printk(KERN_CONT "%0*d ", cpu_width, gi->cpu_map[unit]); else - printk("%s ", empty_str); + printk(KERN_CONT "%s ", empty_str); } } - printk("\n"); + printk(KERN_CONT "\n"); } /** @@ -1650,6 +1650,16 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size, areas[group] = ptr; base = min(ptr, base); + } + + /* + * Copy data and free unused parts. This should happen after all + * allocations are complete; otherwise, we may end up with + * overlapping groups. + */ + for (group = 0; group < ai->nr_groups; group++) { + struct pcpu_group_info *gi = &ai->groups[group]; + void *ptr = areas[group]; for (i = 0; i < gi->nr_units; i++, ptr += ai->unit_size) { if (gi->cpu_map[i] == NR_CPUS) { @@ -1885,6 +1895,8 @@ void __init setup_per_cpu_areas(void) fc = __alloc_bootmem(unit_size, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)); if (!ai || !fc) panic("Failed to allocate memory for percpu areas."); + /* kmemleak tracks the percpu allocations separately */ + kmemleak_free(fc); ai->dyn_size = unit_size; ai->unit_size = unit_size; diff --git a/mm/swap_state.c b/mm/swap_state.c index 9d3dd3763cf7..4c5ff7f284d9 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -26,7 +26,7 @@ */ static const struct address_space_operations swap_aops = { .writepage = swap_writepage, - .set_page_dirty = __set_page_dirty_nobuffers, + .set_page_dirty = __set_page_dirty_no_writeback, .migratepage = migrate_page, }; diff --git a/mm/vmscan.c b/mm/vmscan.c index 33c332bbab73..33dc256033b5 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1568,9 +1568,14 @@ shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz, reclaim_stat->recent_scanned[0] += nr_anon; reclaim_stat->recent_scanned[1] += nr_file; - if (current_is_kswapd()) - __count_vm_events(KSWAPD_STEAL, nr_reclaimed); - __count_zone_vm_events(PGSTEAL, zone, nr_reclaimed); + if (global_reclaim(sc)) { + if (current_is_kswapd()) + __count_zone_vm_events(PGSTEAL_KSWAPD, zone, + nr_reclaimed); + else + __count_zone_vm_events(PGSTEAL_DIRECT, zone, + nr_reclaimed); + } putback_inactive_pages(mz, &page_list); @@ -2107,12 +2112,7 @@ restart: * with multiple processes reclaiming pages, the total * freeing target can get unreasonably large. */ - if (nr_reclaimed >= nr_to_reclaim) - nr_to_reclaim = 0; - else - nr_to_reclaim -= nr_reclaimed; - - if (!nr_to_reclaim && priority < DEF_PRIORITY) + if (nr_reclaimed >= nr_to_reclaim && priority < DEF_PRIORITY) break; } blk_finish_plug(&plug); diff --git a/mm/vmstat.c b/mm/vmstat.c index f600557a7659..7db1b9bab492 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -738,7 +738,8 @@ const char * const vmstat_text[] = { "pgmajfault", TEXTS_FOR_ZONES("pgrefill") - TEXTS_FOR_ZONES("pgsteal") + TEXTS_FOR_ZONES("pgsteal_kswapd") + TEXTS_FOR_ZONES("pgsteal_direct") TEXTS_FOR_ZONES("pgscan_kswapd") TEXTS_FOR_ZONES("pgscan_direct") @@ -747,7 +748,6 @@ const char * const vmstat_text[] = { #endif "pginodesteal", "slabs_scanned", - "kswapd_steal", "kswapd_inodesteal", "kswapd_low_wmark_hit_quickly", "kswapd_high_wmark_hit_quickly", diff --git a/net/802/garp.c b/net/802/garp.c index 8e21b6db3981..a5c224830439 100644 --- a/net/802/garp.c +++ b/net/802/garp.c @@ -167,7 +167,8 @@ static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app, return NULL; } -static void garp_attr_insert(struct garp_applicant *app, struct garp_attr *new) +static struct garp_attr *garp_attr_create(struct garp_applicant *app, + const void *data, u8 len, u8 type) { struct rb_node *parent = NULL, **p = &app->gid.rb_node; struct garp_attr *attr; @@ -176,21 +177,16 @@ static void garp_attr_insert(struct garp_applicant *app, struct garp_attr *new) while (*p) { parent = *p; attr = rb_entry(parent, struct garp_attr, node); - d = garp_attr_cmp(attr, new->data, new->dlen, new->type); + d = garp_attr_cmp(attr, data, len, type); if (d < 0) p = &parent->rb_left; else if (d > 0) p = &parent->rb_right; + else { + /* The attribute already exists; re-use it. */ + return attr; + } } - rb_link_node(&new->node, parent, p); - rb_insert_color(&new->node, &app->gid); -} - -static struct garp_attr *garp_attr_create(struct garp_applicant *app, - const void *data, u8 len, u8 type) -{ - struct garp_attr *attr; - attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC); if (!attr) return attr; @@ -198,7 +194,9 @@ static struct garp_attr *garp_attr_create(struct garp_applicant *app, attr->type = type; attr->dlen = len; memcpy(attr->data, data, len); - garp_attr_insert(app, attr); + + rb_link_node(&attr->node, parent, p); + rb_insert_color(&attr->node, &app->gid); return attr; } diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 9988d4abb372..9757c193c86b 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -157,7 +157,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, skb = __vlan_hwaccel_put_tag(skb, vlan_tci); } - skb_set_dev(skb, vlan_dev_priv(dev)->real_dev); + skb->dev = vlan_dev_priv(dev)->real_dev; len = skb->len; if (netpoll_tx_running(dev)) return skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 0906c194a413..9d9a6a3edbd5 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -2011,16 +2011,17 @@ static void __exit ax25_exit(void) proc_net_remove(&init_net, "ax25_route"); proc_net_remove(&init_net, "ax25"); proc_net_remove(&init_net, "ax25_calls"); - ax25_rt_free(); - ax25_uid_free(); - ax25_dev_free(); - ax25_unregister_sysctl(); unregister_netdevice_notifier(&ax25_dev_notifier); + ax25_unregister_sysctl(); dev_remove_pack(&ax25_packet_type); sock_unregister(PF_AX25); proto_unregister(&ax25_proto); + + ax25_rt_free(); + ax25_uid_free(); + ax25_dev_free(); } module_exit(ax25_exit); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e33af63a884a..edfd61addcec 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -665,6 +665,11 @@ int hci_dev_open(__u16 dev) hci_req_lock(hdev); + if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) { + ret = -ENODEV; + goto done; + } + if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) { ret = -ERFKILL; goto done; @@ -1210,40 +1215,40 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) return NULL; } -static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, +static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, u8 key_type, u8 old_key_type) { /* Legacy key */ if (key_type < 0x03) - return 1; + return true; /* Debug keys are insecure so don't store them persistently */ if (key_type == HCI_LK_DEBUG_COMBINATION) - return 0; + return false; /* Changed combination key and there's no previous one */ if (key_type == HCI_LK_CHANGED_COMBINATION && old_key_type == 0xff) - return 0; + return false; /* Security mode 3 case */ if (!conn) - return 1; + return true; /* Neither local nor remote side had no-bonding as requirement */ if (conn->auth_type > 0x01 && conn->remote_auth > 0x01) - return 1; + return true; /* Local side had dedicated bonding as requirement */ if (conn->auth_type == 0x02 || conn->auth_type == 0x03) - return 1; + return true; /* Remote side had dedicated bonding as requirement */ if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) - return 1; + return true; /* If none of the above criteria match, then don't store the key * persistently */ - return 0; + return false; } struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) @@ -1280,7 +1285,8 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) { struct link_key *key, *old_key; - u8 old_key_type, persistent; + u8 old_key_type; + bool persistent; old_key = hci_find_link_key(hdev, bdaddr); if (old_key) { @@ -1323,10 +1329,8 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, mgmt_new_link_key(hdev, key, persistent); - if (!persistent) { - list_del(&key->list); - kfree(key); - } + if (conn) + conn->flush_key = !persistent; return 0; } @@ -1849,6 +1853,8 @@ void hci_unregister_dev(struct hci_dev *hdev) BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); + set_bit(HCI_UNREGISTER, &hdev->dev_flags); + write_lock(&hci_dev_list_lock); list_del(&hdev->list); write_unlock(&hci_dev_list_lock); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b37531094c49..6c065254afc0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1901,6 +1901,8 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff } if (ev->status == 0) { + if (conn->type == ACL_LINK && conn->flush_key) + hci_remove_link_key(hdev, &conn->dst); hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); } @@ -2311,6 +2313,7 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk case HCI_OP_USER_PASSKEY_NEG_REPLY: hci_cc_user_passkey_neg_reply(hdev, skb); + break; case HCI_OP_LE_SET_SCAN_PARAM: hci_cc_le_set_scan_param(hdev, skb); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b8e17e4dac8b..94552b33d528 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1308,6 +1308,7 @@ static void l2cap_monitor_timeout(struct work_struct *work) if (chan->retry_count >= chan->remote_max_tx) { l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); l2cap_chan_unlock(chan); + l2cap_chan_put(chan); return; } @@ -1316,6 +1317,7 @@ static void l2cap_monitor_timeout(struct work_struct *work) l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); l2cap_chan_unlock(chan); + l2cap_chan_put(chan); } static void l2cap_retrans_timeout(struct work_struct *work) @@ -1335,6 +1337,7 @@ static void l2cap_retrans_timeout(struct work_struct *work) l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); l2cap_chan_unlock(chan); + l2cap_chan_put(chan); } static void l2cap_drop_acked_frames(struct l2cap_chan *chan) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index c4fe583b0af6..29122ed28ea9 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -82,7 +82,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) } if (la.l2_cid) - err = l2cap_add_scid(chan, la.l2_cid); + err = l2cap_add_scid(chan, __le16_to_cpu(la.l2_cid)); else err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm); @@ -123,7 +123,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al if (la.l2_cid && la.l2_psm) return -EINVAL; - err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr); + err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), + &la.l2_bdaddr); if (err) return err; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7fcff8887131..4bb03b111122 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2523,13 +2523,18 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, if (cp->val) { type = PAGE_SCAN_TYPE_INTERLACED; - acp.interval = 0x0024; /* 22.5 msec page scan interval */ + + /* 22.5 msec page scan interval */ + acp.interval = __constant_cpu_to_le16(0x0024); } else { type = PAGE_SCAN_TYPE_STANDARD; /* default */ - acp.interval = 0x0800; /* default 1.28 sec page scan */ + + /* default 1.28 sec page scan */ + acp.interval = __constant_cpu_to_le16(0x0800); } - acp.window = 0x0012; /* default 11.25 msec page scan window */ + /* default 11.25 msec page scan window */ + acp.window = __constant_cpu_to_le16(0x0012); err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), &acp); @@ -2879,7 +2884,7 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) return 0; } -int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, u8 persistent) +int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent) { struct mgmt_ev_new_link_key ev; @@ -2936,7 +2941,7 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, name, name_len); if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0) - eir_len = eir_append_data(&ev->eir[eir_len], eir_len, + eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, dev_class, 3); put_unaligned_le16(eir_len, &ev->eir_len); diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 61f65344e711..a2098e3de500 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -47,6 +47,7 @@ int br_dev_queue_push_xmit(struct sk_buff *skb) kfree_skb(skb); } else { skb_push(skb, ETH_HLEN); + br_drop_fake_rtable(skb); dev_queue_xmit(skb); } diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 702a1ae9220b..27ca25ed7021 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -241,7 +241,6 @@ static void br_multicast_group_expired(unsigned long data) hlist_del_rcu(&mp->hlist[mdb->ver]); mdb->size--; - del_timer(&mp->query_timer); call_rcu_bh(&mp->rcu, br_multicast_free_group); out: @@ -271,7 +270,6 @@ static void br_multicast_del_pg(struct net_bridge *br, rcu_assign_pointer(*pp, p->next); hlist_del_init(&p->mglist); del_timer(&p->timer); - del_timer(&p->query_timer); call_rcu_bh(&p->rcu, br_multicast_free_pg); if (!mp->ports && !mp->mglist && @@ -507,74 +505,6 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br, return NULL; } -static void br_multicast_send_group_query(struct net_bridge_mdb_entry *mp) -{ - struct net_bridge *br = mp->br; - struct sk_buff *skb; - - skb = br_multicast_alloc_query(br, &mp->addr); - if (!skb) - goto timer; - - netif_rx(skb); - -timer: - if (++mp->queries_sent < br->multicast_last_member_count) - mod_timer(&mp->query_timer, - jiffies + br->multicast_last_member_interval); -} - -static void br_multicast_group_query_expired(unsigned long data) -{ - struct net_bridge_mdb_entry *mp = (void *)data; - struct net_bridge *br = mp->br; - - spin_lock(&br->multicast_lock); - if (!netif_running(br->dev) || !mp->mglist || - mp->queries_sent >= br->multicast_last_member_count) - goto out; - - br_multicast_send_group_query(mp); - -out: - spin_unlock(&br->multicast_lock); -} - -static void br_multicast_send_port_group_query(struct net_bridge_port_group *pg) -{ - struct net_bridge_port *port = pg->port; - struct net_bridge *br = port->br; - struct sk_buff *skb; - - skb = br_multicast_alloc_query(br, &pg->addr); - if (!skb) - goto timer; - - br_deliver(port, skb); - -timer: - if (++pg->queries_sent < br->multicast_last_member_count) - mod_timer(&pg->query_timer, - jiffies + br->multicast_last_member_interval); -} - -static void br_multicast_port_group_query_expired(unsigned long data) -{ - struct net_bridge_port_group *pg = (void *)data; - struct net_bridge_port *port = pg->port; - struct net_bridge *br = port->br; - - spin_lock(&br->multicast_lock); - if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) || - pg->queries_sent >= br->multicast_last_member_count) - goto out; - - br_multicast_send_port_group_query(pg); - -out: - spin_unlock(&br->multicast_lock); -} - static struct net_bridge_mdb_entry *br_multicast_get_group( struct net_bridge *br, struct net_bridge_port *port, struct br_ip *group, int hash) @@ -690,8 +620,6 @@ rehash: mp->addr = *group; setup_timer(&mp->timer, br_multicast_group_expired, (unsigned long)mp); - setup_timer(&mp->query_timer, br_multicast_group_query_expired, - (unsigned long)mp); hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]); mdb->size++; @@ -746,8 +674,6 @@ static int br_multicast_add_group(struct net_bridge *br, hlist_add_head(&p->mglist, &port->mglist); setup_timer(&p->timer, br_multicast_port_group_expired, (unsigned long)p); - setup_timer(&p->query_timer, br_multicast_port_group_query_expired, - (unsigned long)p); rcu_assign_pointer(*pp, p); @@ -1291,9 +1217,6 @@ static void br_multicast_leave_group(struct net_bridge *br, time_after(mp->timer.expires, time) : try_to_del_timer_sync(&mp->timer) >= 0)) { mod_timer(&mp->timer, time); - - mp->queries_sent = 0; - mod_timer(&mp->query_timer, now); } goto out; @@ -1310,9 +1233,6 @@ static void br_multicast_leave_group(struct net_bridge *br, time_after(p->timer.expires, time) : try_to_del_timer_sync(&p->timer) >= 0)) { mod_timer(&p->timer, time); - - p->queries_sent = 0; - mod_timer(&p->query_timer, now); } break; @@ -1681,7 +1601,6 @@ void br_multicast_stop(struct net_bridge *br) hlist_for_each_entry_safe(mp, p, n, &mdb->mhash[i], hlist[ver]) { del_timer(&mp->timer); - del_timer(&mp->query_timer); call_rcu_bh(&mp->rcu, br_multicast_free_group); } } diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index dec4f3817133..d7f49b63ab0f 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -156,7 +156,7 @@ void br_netfilter_rtable_init(struct net_bridge *br) rt->dst.dev = br->dev; rt->dst.path = &rt->dst; dst_init_metrics(&rt->dst, br_dst_default_metrics, true); - rt->dst.flags = DST_NOXFRM | DST_NOPEER; + rt->dst.flags = DST_NOXFRM | DST_NOPEER | DST_FAKE_RTABLE; rt->dst.ops = &fake_dst_ops; } @@ -694,11 +694,7 @@ static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff *skb, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - struct rtable *rt = skb_rtable(skb); - - if (rt && rt == bridge_parent_rtable(in)) - skb_dst_drop(skb); - + br_drop_fake_rtable(skb); return NF_ACCEPT; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 0b67a63ad7a8..e1d882257877 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -82,9 +82,7 @@ struct net_bridge_port_group { struct hlist_node mglist; struct rcu_head rcu; struct timer_list timer; - struct timer_list query_timer; struct br_ip addr; - u32 queries_sent; }; struct net_bridge_mdb_entry @@ -94,10 +92,8 @@ struct net_bridge_mdb_entry struct net_bridge_port_group __rcu *ports; struct rcu_head rcu; struct timer_list timer; - struct timer_list query_timer; struct br_ip addr; bool mglist; - u32 queries_sent; }; struct net_bridge_mdb_htable diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index 20618dd3088b..d09340e1523f 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -103,6 +103,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) skb->protocol = htons(ETH_P_IPV6); break; default: + kfree_skb(skb); priv->netdev->stats.rx_errors++; return -EINVAL; } @@ -220,14 +221,16 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->len > priv->netdev->mtu) { pr_warn("Size of skb exceeded MTU\n"); + kfree_skb(skb); dev->stats.tx_errors++; - return -ENOSPC; + return NETDEV_TX_OK; } if (!priv->flowenabled) { pr_debug("dropping packets flow off\n"); + kfree_skb(skb); dev->stats.tx_dropped++; - return NETDEV_TX_BUSY; + return NETDEV_TX_OK; } if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) @@ -242,7 +245,7 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev) result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); if (result) { dev->stats.tx_dropped++; - return result; + return NETDEV_TX_OK; } /* Update statistics. */ diff --git a/net/core/dev.c b/net/core/dev.c index 5d59155adf2a..99e1d759f41e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1409,14 +1409,34 @@ EXPORT_SYMBOL(register_netdevice_notifier); * register_netdevice_notifier(). The notifier is unlinked into the * kernel structures and may then be reused. A negative errno code * is returned on a failure. + * + * After unregistering unregister and down device events are synthesized + * for all devices on the device list to the removed notifier to remove + * the need for special case cleanup code. */ int unregister_netdevice_notifier(struct notifier_block *nb) { + struct net_device *dev; + struct net *net; int err; rtnl_lock(); err = raw_notifier_chain_unregister(&netdev_chain, nb); + if (err) + goto unlock; + + for_each_net(net) { + for_each_netdev(net, dev) { + if (dev->flags & IFF_UP) { + nb->notifier_call(nb, NETDEV_GOING_DOWN, dev); + nb->notifier_call(nb, NETDEV_DOWN, dev); + } + nb->notifier_call(nb, NETDEV_UNREGISTER, dev); + nb->notifier_call(nb, NETDEV_UNREGISTER_BATCH, dev); + } + } +unlock: rtnl_unlock(); return err; } @@ -1596,10 +1616,15 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) kfree_skb(skb); return NET_RX_DROP; } - skb_set_dev(skb, dev); + skb->skb_iif = 0; + skb->dev = dev; + skb_dst_drop(skb); skb->tstamp.tv64 = 0; skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, dev); + skb->mark = 0; + secpath_reset(skb); + nf_reset(skb); return netif_rx(skb); } EXPORT_SYMBOL_GPL(dev_forward_skb); @@ -1848,36 +1873,6 @@ void netif_device_attach(struct net_device *dev) } EXPORT_SYMBOL(netif_device_attach); -/** - * skb_dev_set -- assign a new device to a buffer - * @skb: buffer for the new device - * @dev: network device - * - * If an skb is owned by a device already, we have to reset - * all data private to the namespace a device belongs to - * before assigning it a new device. - */ -#ifdef CONFIG_NET_NS -void skb_set_dev(struct sk_buff *skb, struct net_device *dev) -{ - skb_dst_drop(skb); - if (skb->dev && !net_eq(dev_net(skb->dev), dev_net(dev))) { - secpath_reset(skb); - nf_reset(skb); - skb_init_secmark(skb); - skb->mark = 0; - skb->priority = 0; - skb->nf_trace = 0; - skb->ipvs_property = 0; -#ifdef CONFIG_NET_SCHED - skb->tc_index = 0; -#endif - } - skb->dev = dev; -} -EXPORT_SYMBOL(skb_set_dev); -#endif /* CONFIG_NET_NS */ - static void skb_warn_bad_offload(const struct sk_buff *skb) { static const netdev_features_t null_features = 0; @@ -4027,54 +4022,41 @@ static int dev_ifconf(struct net *net, char __user *arg) #ifdef CONFIG_PROC_FS -#define BUCKET_SPACE (32 - NETDEV_HASHBITS) - -struct dev_iter_state { - struct seq_net_private p; - unsigned int pos; /* bucket << BUCKET_SPACE + offset */ -}; +#define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1) #define get_bucket(x) ((x) >> BUCKET_SPACE) #define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1)) #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) -static inline struct net_device *dev_from_same_bucket(struct seq_file *seq) +static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos) { - struct dev_iter_state *state = seq->private; struct net *net = seq_file_net(seq); struct net_device *dev; struct hlist_node *p; struct hlist_head *h; - unsigned int count, bucket, offset; + unsigned int count = 0, offset = get_offset(*pos); - bucket = get_bucket(state->pos); - offset = get_offset(state->pos); - h = &net->dev_name_head[bucket]; - count = 0; + h = &net->dev_name_head[get_bucket(*pos)]; hlist_for_each_entry_rcu(dev, p, h, name_hlist) { - if (count++ == offset) { - state->pos = set_bucket_offset(bucket, count); + if (++count == offset) return dev; - } } return NULL; } -static inline struct net_device *dev_from_new_bucket(struct seq_file *seq) +static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos) { - struct dev_iter_state *state = seq->private; struct net_device *dev; unsigned int bucket; - bucket = get_bucket(state->pos); do { - dev = dev_from_same_bucket(seq); + dev = dev_from_same_bucket(seq, pos); if (dev) return dev; - bucket++; - state->pos = set_bucket_offset(bucket, 0); + bucket = get_bucket(*pos) + 1; + *pos = set_bucket_offset(bucket, 1); } while (bucket < NETDEV_HASHENTRIES); return NULL; @@ -4087,33 +4069,20 @@ static inline struct net_device *dev_from_new_bucket(struct seq_file *seq) void *dev_seq_start(struct seq_file *seq, loff_t *pos) __acquires(RCU) { - struct dev_iter_state *state = seq->private; - rcu_read_lock(); if (!*pos) return SEQ_START_TOKEN; - /* check for end of the hash */ - if (state->pos == 0 && *pos > 1) + if (get_bucket(*pos) >= NETDEV_HASHENTRIES) return NULL; - return dev_from_new_bucket(seq); + return dev_from_bucket(seq, pos); } void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) { - struct net_device *dev; - ++*pos; - - if (v == SEQ_START_TOKEN) - return dev_from_new_bucket(seq); - - dev = dev_from_same_bucket(seq); - if (dev) - return dev; - - return dev_from_new_bucket(seq); + return dev_from_bucket(seq, pos); } void dev_seq_stop(struct seq_file *seq, void *v) @@ -4212,13 +4181,7 @@ static const struct seq_operations dev_seq_ops = { static int dev_seq_open(struct inode *inode, struct file *file) { return seq_open_net(inode, file, &dev_seq_ops, - sizeof(struct dev_iter_state)); -} - -int dev_seq_open_ops(struct inode *inode, struct file *file, - const struct seq_operations *ops) -{ - return seq_open_net(inode, file, ops, sizeof(struct dev_iter_state)); + sizeof(struct seq_net_private)); } static const struct file_operations dev_seq_fops = { diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 29c07fef9228..626698f0db8b 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -696,7 +696,8 @@ static const struct seq_operations dev_mc_seq_ops = { static int dev_mc_seq_open(struct inode *inode, struct file *file) { - return dev_seq_open_ops(inode, file, &dev_mc_seq_ops); + return seq_open_net(inode, file, &dev_mc_seq_ops, + sizeof(struct seq_net_private)); } static const struct file_operations dev_mc_seq_fops = { diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 7f36b38e060f..a7cad741df01 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -42,13 +42,14 @@ static void send_dm_alert(struct work_struct *unused); * netlink alerts */ static int trace_state = TRACE_OFF; -static DEFINE_SPINLOCK(trace_state_lock); +static DEFINE_MUTEX(trace_state_mutex); struct per_cpu_dm_data { struct work_struct dm_alert_work; - struct sk_buff *skb; + struct sk_buff __rcu *skb; atomic_t dm_hit_count; struct timer_list send_timer; + int cpu; }; struct dm_hw_stat_delta { @@ -79,29 +80,53 @@ static void reset_per_cpu_data(struct per_cpu_dm_data *data) size_t al; struct net_dm_alert_msg *msg; struct nlattr *nla; + struct sk_buff *skb; + struct sk_buff *oskb = rcu_dereference_protected(data->skb, 1); al = sizeof(struct net_dm_alert_msg); al += dm_hit_limit * sizeof(struct net_dm_drop_point); al += sizeof(struct nlattr); - data->skb = genlmsg_new(al, GFP_KERNEL); - genlmsg_put(data->skb, 0, 0, &net_drop_monitor_family, - 0, NET_DM_CMD_ALERT); - nla = nla_reserve(data->skb, NLA_UNSPEC, sizeof(struct net_dm_alert_msg)); - msg = nla_data(nla); - memset(msg, 0, al); - atomic_set(&data->dm_hit_count, dm_hit_limit); + skb = genlmsg_new(al, GFP_KERNEL); + + if (skb) { + genlmsg_put(skb, 0, 0, &net_drop_monitor_family, + 0, NET_DM_CMD_ALERT); + nla = nla_reserve(skb, NLA_UNSPEC, + sizeof(struct net_dm_alert_msg)); + msg = nla_data(nla); + memset(msg, 0, al); + } else + schedule_work_on(data->cpu, &data->dm_alert_work); + + /* + * Don't need to lock this, since we are guaranteed to only + * run this on a single cpu at a time. + * Note also that we only update data->skb if the old and new skb + * pointers don't match. This ensures that we don't continually call + * synchornize_rcu if we repeatedly fail to alloc a new netlink message. + */ + if (skb != oskb) { + rcu_assign_pointer(data->skb, skb); + + synchronize_rcu(); + + atomic_set(&data->dm_hit_count, dm_hit_limit); + } + } static void send_dm_alert(struct work_struct *unused) { struct sk_buff *skb; - struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data); + struct per_cpu_dm_data *data = &get_cpu_var(dm_cpu_data); + + WARN_ON_ONCE(data->cpu != smp_processor_id()); /* * Grab the skb we're about to send */ - skb = data->skb; + skb = rcu_dereference_protected(data->skb, 1); /* * Replace it with a new one @@ -111,8 +136,10 @@ static void send_dm_alert(struct work_struct *unused) /* * Ship it! */ - genlmsg_multicast(skb, 0, NET_DM_GRP_ALERT, GFP_KERNEL); + if (skb) + genlmsg_multicast(skb, 0, NET_DM_GRP_ALERT, GFP_KERNEL); + put_cpu_var(dm_cpu_data); } /* @@ -123,9 +150,11 @@ static void send_dm_alert(struct work_struct *unused) */ static void sched_send_work(unsigned long unused) { - struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data); + struct per_cpu_dm_data *data = &get_cpu_var(dm_cpu_data); + + schedule_work_on(smp_processor_id(), &data->dm_alert_work); - schedule_work(&data->dm_alert_work); + put_cpu_var(dm_cpu_data); } static void trace_drop_common(struct sk_buff *skb, void *location) @@ -134,8 +163,15 @@ static void trace_drop_common(struct sk_buff *skb, void *location) struct nlmsghdr *nlh; struct nlattr *nla; int i; - struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data); + struct sk_buff *dskb; + struct per_cpu_dm_data *data = &get_cpu_var(dm_cpu_data); + + + rcu_read_lock(); + dskb = rcu_dereference(data->skb); + if (!dskb) + goto out; if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) { /* @@ -144,12 +180,13 @@ static void trace_drop_common(struct sk_buff *skb, void *location) goto out; } - nlh = (struct nlmsghdr *)data->skb->data; + nlh = (struct nlmsghdr *)dskb->data; nla = genlmsg_data(nlmsg_data(nlh)); msg = nla_data(nla); for (i = 0; i < msg->entries; i++) { if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) { msg->points[i].count++; + atomic_inc(&data->dm_hit_count); goto out; } } @@ -157,7 +194,7 @@ static void trace_drop_common(struct sk_buff *skb, void *location) /* * We need to create a new entry */ - __nla_reserve_nohdr(data->skb, sizeof(struct net_dm_drop_point)); + __nla_reserve_nohdr(dskb, sizeof(struct net_dm_drop_point)); nla->nla_len += NLA_ALIGN(sizeof(struct net_dm_drop_point)); memcpy(msg->points[msg->entries].pc, &location, sizeof(void *)); msg->points[msg->entries].count = 1; @@ -169,6 +206,8 @@ static void trace_drop_common(struct sk_buff *skb, void *location) } out: + rcu_read_unlock(); + put_cpu_var(dm_cpu_data); return; } @@ -213,7 +252,7 @@ static int set_all_monitor_traces(int state) struct dm_hw_stat_delta *new_stat = NULL; struct dm_hw_stat_delta *temp; - spin_lock(&trace_state_lock); + mutex_lock(&trace_state_mutex); if (state == trace_state) { rc = -EAGAIN; @@ -252,7 +291,7 @@ static int set_all_monitor_traces(int state) rc = -EINPROGRESS; out_unlock: - spin_unlock(&trace_state_lock); + mutex_unlock(&trace_state_mutex); return rc; } @@ -295,12 +334,12 @@ static int dropmon_net_event(struct notifier_block *ev_block, new_stat->dev = dev; new_stat->last_rx = jiffies; - spin_lock(&trace_state_lock); + mutex_lock(&trace_state_mutex); list_add_rcu(&new_stat->list, &hw_stats_list); - spin_unlock(&trace_state_lock); + mutex_unlock(&trace_state_mutex); break; case NETDEV_UNREGISTER: - spin_lock(&trace_state_lock); + mutex_lock(&trace_state_mutex); list_for_each_entry_safe(new_stat, tmp, &hw_stats_list, list) { if (new_stat->dev == dev) { new_stat->dev = NULL; @@ -311,7 +350,7 @@ static int dropmon_net_event(struct notifier_block *ev_block, } } } - spin_unlock(&trace_state_lock); + mutex_unlock(&trace_state_mutex); break; } out: @@ -367,13 +406,15 @@ static int __init init_net_drop_monitor(void) for_each_present_cpu(cpu) { data = &per_cpu(dm_cpu_data, cpu); - reset_per_cpu_data(data); + data->cpu = cpu; INIT_WORK(&data->dm_alert_work, send_dm_alert); init_timer(&data->send_timer); data->send_timer.data = cpu; data->send_timer.function = sched_send_work; + reset_per_cpu_data(data); } + goto out; out_unreg: diff --git a/net/core/filter.c b/net/core/filter.c index cf4989ac503b..6f755cca4520 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -39,8 +39,11 @@ #include <linux/reciprocal_div.h> #include <linux/ratelimit.h> -/* No hurry in this branch */ -static void *__load_pointer(const struct sk_buff *skb, int k, unsigned int size) +/* No hurry in this branch + * + * Exported for the bpf jit load helper. + */ +void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, unsigned int size) { u8 *ptr = NULL; @@ -59,7 +62,7 @@ static inline void *load_pointer(const struct sk_buff *skb, int k, { if (k >= 0) return skb_header_pointer(skb, k, size, buffer); - return __load_pointer(skb, k, size); + return bpf_internal_load_pointer_neg_helper(skb, k, size); } /** diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 0e950fda9a0a..31a5ae51a45c 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -83,21 +83,29 @@ assign: static int ops_init(const struct pernet_operations *ops, struct net *net) { - int err; + int err = -ENOMEM; + void *data = NULL; + if (ops->id && ops->size) { - void *data = kzalloc(ops->size, GFP_KERNEL); + data = kzalloc(ops->size, GFP_KERNEL); if (!data) - return -ENOMEM; + goto out; err = net_assign_generic(net, *ops->id, data); - if (err) { - kfree(data); - return err; - } + if (err) + goto cleanup; } + err = 0; if (ops->init) - return ops->init(net); - return 0; + err = ops->init(net); + if (!err) + return 0; + +cleanup: + kfree(data); + +out: + return err; } static void ops_free(const struct pernet_operations *ops, struct net *net) @@ -448,12 +456,7 @@ static void __unregister_pernet_operations(struct pernet_operations *ops) static int __register_pernet_operations(struct list_head *list, struct pernet_operations *ops) { - int err = 0; - err = ops_init(ops, &init_net); - if (err) - ops_free(ops, &init_net); - return err; - + return ops_init(ops, &init_net); } static void __unregister_pernet_operations(struct pernet_operations *ops) diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 4d8ce93cd503..77a59980b579 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -1931,7 +1931,7 @@ static int pktgen_device_event(struct notifier_block *unused, { struct net_device *dev = ptr; - if (!net_eq(dev_net(dev), &init_net)) + if (!net_eq(dev_net(dev), &init_net) || pktgen_exiting) return NOTIFY_DONE; /* It is OK that we do not hold the group lock right now, @@ -3755,12 +3755,18 @@ static void __exit pg_cleanup(void) { struct pktgen_thread *t; struct list_head *q, *n; + struct list_head list; /* Stop all interfaces & threads */ pktgen_exiting = true; - list_for_each_safe(q, n, &pktgen_threads) { + mutex_lock(&pktgen_thread_lock); + list_splice(&list, &pktgen_threads); + mutex_unlock(&pktgen_thread_lock); + + list_for_each_safe(q, n, &list) { t = list_entry(q, struct pktgen_thread, th_list); + list_del(&t->th_list); kthread_stop(t->tsk); kfree(t); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index f223cdc75da6..e59840010d45 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -952,9 +952,11 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, goto adjust_others; } - data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); + data = kmalloc(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), + gfp_mask); if (!data) goto nodata; + size = SKB_WITH_OVERHEAD(ksize(data)); /* Copy only real data... and, alas, header. This should be * optimized for the cases when header is void. @@ -3161,6 +3163,8 @@ static void sock_rmem_free(struct sk_buff *skb) */ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) { + int len = skb->len; + if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= (unsigned)sk->sk_rcvbuf) return -ENOMEM; @@ -3175,7 +3179,7 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) skb_queue_tail(&sk->sk_error_queue, skb); if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk, skb->len); + sk->sk_data_ready(sk, len); return 0; } EXPORT_SYMBOL(sock_queue_err_skb); diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 368515885368..840821b90bcd 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -1044,6 +1044,24 @@ static void lowpan_dev_free(struct net_device *dev) free_netdev(dev); } +static struct wpan_phy *lowpan_get_phy(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + return ieee802154_mlme_ops(real_dev)->get_phy(real_dev); +} + +static u16 lowpan_get_pan_id(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); +} + +static u16 lowpan_get_short_addr(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); +} + static struct header_ops lowpan_header_ops = { .create = lowpan_header_create, }; @@ -1053,6 +1071,12 @@ static const struct net_device_ops lowpan_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, }; +static struct ieee802154_mlme_ops lowpan_mlme = { + .get_pan_id = lowpan_get_pan_id, + .get_phy = lowpan_get_phy, + .get_short_addr = lowpan_get_short_addr, +}; + static void lowpan_setup(struct net_device *dev) { pr_debug("(%s)\n", __func__); @@ -1070,6 +1094,7 @@ static void lowpan_setup(struct net_device *dev) dev->netdev_ops = &lowpan_netdev_ops; dev->header_ops = &lowpan_header_ops; + dev->ml_priv = &lowpan_mlme; dev->destructor = lowpan_dev_free; } @@ -1143,6 +1168,8 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, list_add_tail(&entry->list, &lowpan_devices); mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); + spin_lock_init(&flist_lock); + register_netdevice(dev); return 0; @@ -1152,11 +1179,20 @@ static void lowpan_dellink(struct net_device *dev, struct list_head *head) { struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); struct net_device *real_dev = lowpan_dev->real_dev; - struct lowpan_dev_record *entry; - struct lowpan_dev_record *tmp; + struct lowpan_dev_record *entry, *tmp; + struct lowpan_fragment *frame, *tframe; ASSERT_RTNL(); + spin_lock(&flist_lock); + list_for_each_entry_safe(frame, tframe, &lowpan_fragments, list) { + del_timer(&frame->timer); + list_del(&frame->list); + dev_kfree_skb(frame->skb); + kfree(frame); + } + spin_unlock(&flist_lock); + mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { if (entry->ldev == dev) { diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index bce36f1a37b4..30b88d7b4bd6 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1370,6 +1370,8 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) continue; + if (fi->fib_dead) + continue; if (fa->fa_info->fib_scope < flp->flowi4_scope) continue; fib_alias_accessed(fa); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 8d25a1c557eb..8f8db724bfaf 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -141,7 +141,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, goto rtattr_failure; if (icsk == NULL) { - r->idiag_rqueue = r->idiag_wqueue = 0; + handler->idiag_get_info(sk, r, NULL); goto out; } diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index de9da21113a1..cf73cc70ed2d 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -74,16 +74,24 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); if (iph == NULL) - return -NF_DROP; + return -NF_ACCEPT; /* Conntrack defragments packets, we might still see fragments * inside ICMP packets though. */ if (iph->frag_off & htons(IP_OFFSET)) - return -NF_DROP; + return -NF_ACCEPT; *dataoff = nhoff + (iph->ihl << 2); *protonum = iph->protocol; + /* Check bogus IP headers */ + if (*dataoff > skb->len) { + pr_debug("nf_conntrack_ipv4: bogus IPv4 packet: " + "nhoff %u, ihl %u, skblen %u\n", + nhoff, iph->ihl << 2, skb->len); + return -NF_ACCEPT; + } + return NF_ACCEPT; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4dc1c104c942..167ea10b521a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2041,7 +2041,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (err < 0) goto e_err; } - rth = rt_dst_alloc(init_net.loopback_dev, + rth = rt_dst_alloc(dev_net(dev)->loopback_dev, IN_DEV_CONF_GET(in_dev, NOPOLICY), false); if (!rth) goto e_nobufs; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index cfd7edda0a8e..1272a88c2a63 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -701,11 +701,12 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp) skb = alloc_skb_fclone(size + sk->sk_prot->max_header, gfp); if (skb) { if (sk_wmem_schedule(sk, skb->truesize)) { + skb_reserve(skb, sk->sk_prot->max_header); /* * Make sure that we have exactly size bytes * available to the caller, no more, no less. */ - skb_reserve(skb, skb_tailroom(skb) - size); + skb->avail_size = size; return skb; } __kfree_skb(skb); @@ -860,7 +861,7 @@ wait_for_memory: } out: - if (copied) + if (copied && !(flags & MSG_SENDPAGE_NOTLAST)) tcp_push(sk, flags, mss_now, tp->nonagle); return copied; @@ -995,10 +996,9 @@ new_segment: copy = seglen; /* Where to copy to? */ - if (skb_tailroom(skb) > 0) { + if (skb_availroom(skb) > 0) { /* We have some space in skb head. Superb! */ - if (copy > skb_tailroom(skb)) - copy = skb_tailroom(skb); + copy = min_t(int, copy, skb_availroom(skb)); err = skb_add_data_nocache(sk, skb, from, copy); if (err) goto do_fault; @@ -1452,7 +1452,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if ((available < target) && (len > sysctl_tcp_dma_copybreak) && !(flags & MSG_PEEK) && !sysctl_tcp_low_latency && - dma_find_channel(DMA_MEMCPY)) { + net_dma_find_channel()) { preempt_enable_no_resched(); tp->ucopy.pinned_list = dma_pin_iovec_pages(msg->msg_iov, len); @@ -1667,7 +1667,7 @@ do_prequeue: if (!(flags & MSG_TRUNC)) { #ifdef CONFIG_NET_DMA if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY); + tp->ucopy.dma_chan = net_dma_find_channel(); if (tp->ucopy.dma_chan) { tp->ucopy.dma_cookie = dma_skb_copy_datagram_iovec( @@ -3243,7 +3243,7 @@ void __init tcp_init(void) { struct sk_buff *skb = NULL; unsigned long limit; - int max_share, cnt; + int max_rshare, max_wshare, cnt; unsigned int i; unsigned long jiffy = jiffies; @@ -3302,17 +3302,17 @@ void __init tcp_init(void) tcp_init_mem(&init_net); /* Set per-socket limits to no more than 1/128 the pressure threshold */ - limit = nr_free_buffer_pages() << (PAGE_SHIFT - 10); - limit = max(limit, 128UL); - max_share = min(4UL*1024*1024, limit); + limit = nr_free_buffer_pages() << (PAGE_SHIFT - 7); + max_wshare = min(4UL*1024*1024, limit); + max_rshare = min(6UL*1024*1024, limit); sysctl_tcp_wmem[0] = SK_MEM_QUANTUM; sysctl_tcp_wmem[1] = 16*1024; - sysctl_tcp_wmem[2] = max(64*1024, max_share); + sysctl_tcp_wmem[2] = max(64*1024, max_wshare); sysctl_tcp_rmem[0] = SK_MEM_QUANTUM; sysctl_tcp_rmem[1] = 87380; - sysctl_tcp_rmem[2] = max(87380, max_share); + sysctl_tcp_rmem[2] = max(87380, max_rshare); pr_info("Hash tables configured (established %u bind %u)\n", tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e886e2f7fa8d..257b61789eeb 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -85,7 +85,7 @@ int sysctl_tcp_ecn __read_mostly = 2; EXPORT_SYMBOL(sysctl_tcp_ecn); int sysctl_tcp_dsack __read_mostly = 1; int sysctl_tcp_app_win __read_mostly = 31; -int sysctl_tcp_adv_win_scale __read_mostly = 2; +int sysctl_tcp_adv_win_scale __read_mostly = 1; EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); int sysctl_tcp_stdurg __read_mostly; @@ -335,6 +335,7 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb) incr = __tcp_grow_window(sk, skb); if (incr) { + incr = max_t(int, incr, 2 * skb->len); tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr, tp->window_clamp); inet_csk(sk)->icsk_ack.quick |= 1; @@ -474,8 +475,11 @@ static void tcp_rcv_rtt_update(struct tcp_sock *tp, u32 sample, int win_dep) if (!win_dep) { m -= (new_sample >> 3); new_sample += m; - } else if (m < new_sample) - new_sample = m << 3; + } else { + m <<= 3; + if (m < new_sample) + new_sample = m; + } } else { /* No previous measure. */ new_sample = m << 3; @@ -491,7 +495,7 @@ static inline void tcp_rcv_rtt_measure(struct tcp_sock *tp) goto new_measure; if (before(tp->rcv_nxt, tp->rcv_rtt_est.seq)) return; - tcp_rcv_rtt_update(tp, jiffies - tp->rcv_rtt_est.time, 1); + tcp_rcv_rtt_update(tp, tcp_time_stamp - tp->rcv_rtt_est.time, 1); new_measure: tp->rcv_rtt_est.seq = tp->rcv_nxt + tp->rcv_wnd; @@ -2864,11 +2868,14 @@ static inline void tcp_complete_cwr(struct sock *sk) /* Do not moderate cwnd if it's already undone in cwr or recovery. */ if (tp->undo_marker) { - if (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR) + if (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR) { tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); - else /* PRR */ + tp->snd_cwnd_stamp = tcp_time_stamp; + } else if (tp->snd_ssthresh < TCP_INFINITE_SSTHRESH) { + /* PRR algorithm. */ tp->snd_cwnd = tp->snd_ssthresh; - tp->snd_cwnd_stamp = tcp_time_stamp; + tp->snd_cwnd_stamp = tcp_time_stamp; + } } tcp_ca_event(sk, CA_EVENT_COMPLETE_CWR); } @@ -5225,7 +5232,7 @@ static int tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, return 0; if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY); + tp->ucopy.dma_chan = net_dma_find_channel(); if (tp->ucopy.dma_chan && skb_csum_unnecessary(skb)) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 3a25cf743f8b..0cb86ceb652f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1730,7 +1730,7 @@ process: #ifdef CONFIG_NET_DMA struct tcp_sock *tp = tcp_sk(sk); if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY); + tp->ucopy.dma_chan = net_dma_find_channel(); if (tp->ucopy.dma_chan) ret = tcp_v4_do_rcv(sk, skb); else diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 364784a91939..7ac6423117ad 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1096,6 +1096,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) eat = min_t(int, len, skb_headlen(skb)); if (eat) { __skb_pull(skb, eat); + skb->avail_size -= eat; len -= eat; if (!len) return; @@ -2060,7 +2061,7 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to, /* Punt if not enough space exists in the first SKB for * the data in the second */ - if (skb->len > skb_tailroom(to)) + if (skb->len > skb_availroom(to)) break; if (after(TCP_SKB_CB(skb)->end_seq, tcp_wnd_end(tp))) diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 8a949f19deb6..a7f86a3cd502 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -146,9 +146,17 @@ static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, return udp_dump_one(&udp_table, in_skb, nlh, req); } +static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, + void *info) +{ + r->idiag_rqueue = sk_rmem_alloc_get(sk); + r->idiag_wqueue = sk_wmem_alloc_get(sk); +} + static const struct inet_diag_handler udp_diag_handler = { .dump = udp_diag_dump, .dump_one = udp_diag_dump_one, + .idiag_get_info = udp_diag_get_info, .idiag_type = IPPROTO_UDP, }; @@ -167,6 +175,7 @@ static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr * static const struct inet_diag_handler udplite_diag_handler = { .dump = udplite_diag_dump, .dump_one = udplite_diag_dump_one, + .idiag_get_info = udp_diag_get_info, .idiag_type = IPPROTO_UDPLITE, }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 6a3bb6077e19..7d5cb975cc6f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -803,8 +803,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) ip6_del_rt(rt); rt = NULL; } else if (!(rt->rt6i_flags & RTF_EXPIRES)) { - rt->dst.expires = expires; - rt->rt6i_flags |= RTF_EXPIRES; + rt6_set_expires(rt, expires); } } dst_release(&rt->dst); @@ -1887,11 +1886,9 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) rt = NULL; } else if (addrconf_finite_timeout(rt_expires)) { /* not infinity */ - rt->dst.expires = jiffies + rt_expires; - rt->rt6i_flags |= RTF_EXPIRES; + rt6_set_expires(rt, jiffies + rt_expires); } else { - rt->rt6i_flags &= ~RTF_EXPIRES; - rt->dst.expires = 0; + rt6_clean_expires(rt); } } else if (valid_lft) { clock_t expires = 0; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 5b27fbcae346..93717435013e 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -673,11 +673,10 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, &rt->rt6i_gateway)) { if (!(iter->rt6i_flags & RTF_EXPIRES)) return -EEXIST; - iter->dst.expires = rt->dst.expires; - if (!(rt->rt6i_flags & RTF_EXPIRES)) { - iter->rt6i_flags &= ~RTF_EXPIRES; - iter->dst.expires = 0; - } + if (!(rt->rt6i_flags & RTF_EXPIRES)) + rt6_clean_expires(iter); + else + rt6_set_expires(iter, rt->dst.expires); return -EEXIST; } } diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 16c33e308121..b2869cab2092 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -2044,7 +2044,7 @@ static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca, if (!delta) pmc->mca_sfcount[sfmode]--; for (j=0; j<i; j++) - (void) ip6_mc_del1_src(pmc, sfmode, &psfsrc[i]); + ip6_mc_del1_src(pmc, sfmode, &psfsrc[j]); } else if (isexclude != (pmc->mca_sfcount[MCAST_EXCLUDE] != 0)) { struct ip6_sf_list *psf; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 3dcdb81ec3e8..176b469322ac 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1264,8 +1264,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) } if (rt) - rt->dst.expires = jiffies + (HZ * lifetime); - + rt6_set_expires(rt, jiffies + (HZ * lifetime)); if (ra_msg->icmph.icmp6_hop_limit) { in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; if (rt) diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 94874b0bdcdc..9d4e15559319 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -78,19 +78,6 @@ EXPORT_SYMBOL_GPL(ip6t_alloc_initial_table); Hence the start of any table is given by get_table() below. */ -/* Check for an extension */ -int -ip6t_ext_hdr(u8 nexthdr) -{ - return (nexthdr == IPPROTO_HOPOPTS) || - (nexthdr == IPPROTO_ROUTING) || - (nexthdr == IPPROTO_FRAGMENT) || - (nexthdr == IPPROTO_ESP) || - (nexthdr == IPPROTO_AH) || - (nexthdr == IPPROTO_NONE) || - (nexthdr == IPPROTO_DSTOPTS); -} - /* Returns whether matches rule or not. */ /* Performance critical - called for every packet */ static inline bool @@ -2366,7 +2353,6 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, EXPORT_SYMBOL(ip6t_register_table); EXPORT_SYMBOL(ip6t_unregister_table); EXPORT_SYMBOL(ip6t_do_table); -EXPORT_SYMBOL(ip6t_ext_hdr); EXPORT_SYMBOL(ipv6_find_hdr); module_init(ip6_tables_init); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 496b62712fe8..bc4888d902b2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -62,7 +62,7 @@ #include <linux/sysctl.h> #endif -static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort, +static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, const struct in6_addr *dest); static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); static unsigned int ip6_default_advmss(const struct dst_entry *dst); @@ -285,6 +285,10 @@ static void ip6_dst_destroy(struct dst_entry *dst) rt->rt6i_idev = NULL; in6_dev_put(idev); } + + if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from) + dst_release(dst->from); + if (peer) { rt->rt6i_peer = NULL; inet_putpeer(peer); @@ -329,8 +333,17 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, static __inline__ int rt6_check_expired(const struct rt6_info *rt) { - return (rt->rt6i_flags & RTF_EXPIRES) && - time_after(jiffies, rt->dst.expires); + struct rt6_info *ort = NULL; + + if (rt->rt6i_flags & RTF_EXPIRES) { + if (time_after(jiffies, rt->dst.expires)) + return 1; + } else if (rt->dst.from) { + ort = (struct rt6_info *) rt->dst.from; + return (ort->rt6i_flags & RTF_EXPIRES) && + time_after(jiffies, ort->dst.expires); + } + return 0; } static inline int rt6_need_strict(const struct in6_addr *daddr) @@ -620,12 +633,11 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); if (rt) { - if (!addrconf_finite_timeout(lifetime)) { - rt->rt6i_flags &= ~RTF_EXPIRES; - } else { - rt->dst.expires = jiffies + HZ * lifetime; - rt->rt6i_flags |= RTF_EXPIRES; - } + if (!addrconf_finite_timeout(lifetime)) + rt6_clean_expires(rt); + else + rt6_set_expires(rt, jiffies + HZ * lifetime); + dst_release(&rt->dst); } return 0; @@ -730,7 +742,7 @@ int ip6_ins_rt(struct rt6_info *rt) return __ip6_ins_rt(rt, &info); } -static struct rt6_info *rt6_alloc_cow(const struct rt6_info *ort, +static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, const struct in6_addr *daddr, const struct in6_addr *saddr) { @@ -881,6 +893,16 @@ static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table * return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); } +static struct dst_entry *ip6_route_input_lookup(struct net *net, + struct net_device *dev, + struct flowi6 *fl6, int flags) +{ + if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) + flags |= RT6_LOOKUP_F_IFACE; + + return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); +} + void ip6_route_input(struct sk_buff *skb) { const struct ipv6hdr *iph = ipv6_hdr(skb); @@ -895,10 +917,7 @@ void ip6_route_input(struct sk_buff *skb) .flowi6_proto = iph->nexthdr, }; - if (rt6_need_strict(&iph->daddr) && skb->dev->type != ARPHRD_PIMREG) - flags |= RT6_LOOKUP_F_IFACE; - - skb_dst_set(skb, fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input)); + skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); } static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, @@ -947,10 +966,10 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori rt->rt6i_idev = ort->rt6i_idev; if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); - rt->dst.expires = 0; rt->rt6i_gateway = ort->rt6i_gateway; - rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES; + rt->rt6i_flags = ort->rt6i_flags; + rt6_clean_expires(rt); rt->rt6i_metric = 0; memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); @@ -1012,10 +1031,9 @@ static void ip6_link_failure(struct sk_buff *skb) rt = (struct rt6_info *) skb_dst(skb); if (rt) { - if (rt->rt6i_flags & RTF_CACHE) { - dst_set_expires(&rt->dst, 0); - rt->rt6i_flags |= RTF_EXPIRES; - } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) + if (rt->rt6i_flags & RTF_CACHE) + rt6_update_expires(rt, 0); + else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) rt->rt6i_node->fn_sernum = -1; } } @@ -1282,9 +1300,12 @@ int ip6_route_add(struct fib6_config *cfg) } rt->dst.obsolete = -1; - rt->dst.expires = (cfg->fc_flags & RTF_EXPIRES) ? - jiffies + clock_t_to_jiffies(cfg->fc_expires) : - 0; + + if (cfg->fc_flags & RTF_EXPIRES) + rt6_set_expires(rt, jiffies + + clock_t_to_jiffies(cfg->fc_expires)); + else + rt6_clean_expires(rt); if (cfg->fc_protocol == RTPROT_UNSPEC) cfg->fc_protocol = RTPROT_BOOT; @@ -1729,8 +1750,8 @@ again: features |= RTAX_FEATURE_ALLFRAG; dst_metric_set(&rt->dst, RTAX_FEATURES, features); } - dst_set_expires(&rt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires); - rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES; + rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); + rt->rt6i_flags |= RTF_MODIFIED; goto out; } @@ -1758,9 +1779,8 @@ again: * which is 10 mins. After 10 mins the decreased pmtu is expired * and detecting PMTU increase will be automatically happened. */ - dst_set_expires(&nrt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires); - nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES; - + rt6_update_expires(nrt, net->ipv6.sysctl.ip6_rt_mtu_expires); + nrt->rt6i_flags |= RTF_DYNAMIC; ip6_ins_rt(nrt); } out: @@ -1792,7 +1812,7 @@ void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *sad * Misc support functions */ -static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort, +static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, const struct in6_addr *dest) { struct net *net = dev_net(ort->dst.dev); @@ -1812,10 +1832,14 @@ static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort, if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); rt->dst.lastuse = jiffies; - rt->dst.expires = 0; rt->rt6i_gateway = ort->rt6i_gateway; - rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES; + rt->rt6i_flags = ort->rt6i_flags; + if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) == + (RTF_DEFAULT | RTF_ADDRCONF)) + rt6_set_from(rt, ort); + else + rt6_clean_expires(rt); rt->rt6i_metric = 0; #ifdef CONFIG_IPV6_SUBTREES @@ -2537,7 +2561,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void struct sk_buff *skb; struct rtmsg *rtm; struct flowi6 fl6; - int err, iif = 0; + int err, iif = 0, oif = 0; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); if (err < 0) @@ -2564,15 +2588,29 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void iif = nla_get_u32(tb[RTA_IIF]); if (tb[RTA_OIF]) - fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]); + oif = nla_get_u32(tb[RTA_OIF]); if (iif) { struct net_device *dev; + int flags = 0; + dev = __dev_get_by_index(net, iif); if (!dev) { err = -ENODEV; goto errout; } + + fl6.flowi6_iif = iif; + + if (!ipv6_addr_any(&fl6.saddr)) + flags |= RT6_LOOKUP_F_HAS_SADDR; + + rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, + flags); + } else { + fl6.flowi6_oif = oif; + + rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); } skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); @@ -2587,7 +2625,6 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void skb_reset_mac_header(skb); skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); - rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6); skb_dst_set(skb, &rt->dst); err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 12c6ece67f39..98256cf72f9d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1383,6 +1383,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, tcp_mtup_init(newsk); tcp_sync_mss(newsk, dst_mtu(dst)); newtp->advmss = dst_metric_advmss(dst); + if (tcp_sk(sk)->rx_opt.user_mss && + tcp_sk(sk)->rx_opt.user_mss < newtp->advmss) + newtp->advmss = tcp_sk(sk)->rx_opt.user_mss; + tcp_initialize_rcv_mss(newsk); if (tcp_rsk(req)->snt_synack) tcp_valid_rtt_meas(newsk, @@ -1645,7 +1649,7 @@ process: #ifdef CONFIG_NET_DMA struct tcp_sock *tp = tcp_sk(sk); if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY); + tp->ucopy.dma_chan = net_dma_find_channel(); if (tp->ucopy.dma_chan) ret = tcp_v6_do_rcv(sk, skb); else diff --git a/net/key/af_key.c b/net/key/af_key.c index 11dbb2255ccb..7e5d927b576f 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3480,7 +3480,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, /* Addresses to be used by KM for negotiation, if ext is available */ if (k != NULL && (set_sadb_kmaddress(skb, k) < 0)) - return -EINVAL; + goto err; /* selector src */ set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_SRC, sel); diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 55670ec3cd0f..6274f0be82b0 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -232,7 +232,7 @@ static void l2tp_ip_close(struct sock *sk, long timeout) { write_lock_bh(&l2tp_ip_lock); hlist_del_init(&sk->sk_bind_node); - hlist_del_init(&sk->sk_node); + sk_del_node_init(sk); write_unlock_bh(&l2tp_ip_lock); sk_common_release(sk); } @@ -271,7 +271,8 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) goto out; - inet->inet_rcv_saddr = inet->inet_saddr = addr->l2tp_addr.s_addr; + if (addr->l2tp_addr.s_addr) + inet->inet_rcv_saddr = inet->inet_saddr = addr->l2tp_addr.s_addr; if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) inet->inet_saddr = 0; /* Use device */ sk_dst_reset(sk); @@ -441,8 +442,9 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m daddr = lip->l2tp_addr.s_addr; } else { + rc = -EDESTADDRREQ; if (sk->sk_state != TCP_ESTABLISHED) - return -EDESTADDRREQ; + goto out; daddr = inet->inet_daddr; connected = 1; diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 1068f668ac4e..64d3ce5ea1a0 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -49,6 +49,8 @@ static void ieee80211_free_tid_rx(struct rcu_head *h) container_of(h, struct tid_ampdu_rx, rcu_head); int i; + del_timer_sync(&tid_rx->reorder_timer); + for (i = 0; i < tid_rx->buf_size; i++) dev_kfree_skb(tid_rx->reorder_buf[i]); kfree(tid_rx->reorder_buf); @@ -91,7 +93,6 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, tid, WLAN_BACK_RECIPIENT, reason); del_timer_sync(&tid_rx->session_timer); - del_timer_sync(&tid_rx->reorder_timer); call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx); } diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index cc5b7a6e7e0b..778e5916d7c3 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -15,12 +15,6 @@ #include "rate.h" #include "debugfs.h" -int mac80211_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - #define DEBUGFS_FORMAT_BUFFER_SIZE 100 int mac80211_format_buffer(char __user *userbuf, size_t count, @@ -50,7 +44,7 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \ #define DEBUGFS_READONLY_FILE_OPS(name) \ static const struct file_operations name## _ops = { \ .read = name## _read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -93,7 +87,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf, static const struct file_operations reset_ops = { .write = reset_write, - .open = mac80211_open_file_generic, + .open = simple_open, .llseek = noop_llseek, }; @@ -254,7 +248,7 @@ static ssize_t stats_ ##name## _read(struct file *file, \ \ static const struct file_operations stats_ ##name## _ops = { \ .read = stats_ ##name## _read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h index 7c87529630f5..9be4e6d71d00 100644 --- a/net/mac80211/debugfs.h +++ b/net/mac80211/debugfs.h @@ -3,7 +3,6 @@ #ifdef CONFIG_MAC80211_DEBUGFS extern void debugfs_hw_add(struct ieee80211_local *local); -extern int mac80211_open_file_generic(struct inode *inode, struct file *file); extern int mac80211_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...); #else diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 59edcd95a58d..7932767bb482 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -30,7 +30,7 @@ static ssize_t key_##name##_read(struct file *file, \ #define KEY_OPS(name) \ static const struct file_operations key_ ##name## _ops = { \ .read = key_##name##_read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } @@ -45,7 +45,7 @@ static const struct file_operations key_ ##name## _ops = { \ #define KEY_CONF_OPS(name) \ static const struct file_operations key_ ##name## _ops = { \ .read = key_conf_##name##_read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index a32eeda04aa3..30f99c344847 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -135,7 +135,7 @@ static ssize_t ieee80211_if_read_##name(struct file *file, \ static const struct file_operations name##_ops = { \ .read = ieee80211_if_read_##name, \ .write = (_write), \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 6d45804d09bc..832b2da5e4cd 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -33,7 +33,7 @@ static ssize_t sta_ ##name## _read(struct file *file, \ #define STA_OPS(name) \ static const struct file_operations sta_ ##name## _ops = { \ .read = sta_##name##_read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } @@ -41,7 +41,7 @@ static const struct file_operations sta_ ##name## _ops = { \ static const struct file_operations sta_ ##name## _ops = { \ .read = sta_##name##_read, \ .write = sta_##name##_write, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 33fd8d9f714e..cef7c29214a8 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -457,8 +457,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * fall back to HT20 if we don't use or use * the other extension channel */ - if ((channel_type == NL80211_CHAN_HT40MINUS || - channel_type == NL80211_CHAN_HT40PLUS) && + if (!(channel_type == NL80211_CHAN_HT40MINUS || + channel_type == NL80211_CHAN_HT40PLUS) || channel_type != sdata->u.ibss.channel_type) sta_ht_cap_new.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d9798a307f20..db8fae51714c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1210,7 +1210,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata); -void ieee80211_mgd_teardown(struct ieee80211_sub_if_data *sdata); +void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata); /* IBSS code */ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 401c01f0731e..c20051b7ffcd 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -486,6 +486,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, /* free all potentially still buffered bcast frames */ local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps_bc_buf); skb_queue_purge(&sdata->u.ap.ps_bc_buf); + } else if (sdata->vif.type == NL80211_IFTYPE_STATION) { + ieee80211_mgd_stop(sdata); } if (going_down) @@ -644,8 +646,6 @@ static void ieee80211_teardown_sdata(struct net_device *dev) if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_rmc_free(sdata); - else if (sdata->vif.type == NL80211_IFTYPE_STATION) - ieee80211_mgd_teardown(sdata); flushed = sta_info_flush(local, sdata); WARN_ON(flushed); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b581a24fa15c..16336480c631 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -102,9 +102,6 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) might_sleep(); - /* If this off-channel logic ever changes, ieee80211_on_oper_channel - * may need to change as well. - */ offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; if (local->scan_channel) { chan = local->scan_channel; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 576fb25456dd..20c680bfc3ae 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3387,8 +3387,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, */ printk(KERN_DEBUG "%s: waiting for beacon from %pM\n", sdata->name, ifmgd->bssid); - assoc_data->timeout = jiffies + - TU_TO_EXP_TIME(req->bss->beacon_interval); + assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); } else { assoc_data->have_beacon = true; assoc_data->sent_assoc = false; @@ -3498,7 +3497,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, return 0; } -void ieee80211_mgd_teardown(struct ieee80211_sub_if_data *sdata) +void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index b4f7600a3e36..3313c117b322 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -145,7 +145,7 @@ static ssize_t rcname_read(struct file *file, char __user *userbuf, static const struct file_operations rcname_ops = { .read = rcname_read, - .open = mac80211_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; #endif diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index bcfe8c77c839..d64e285400aa 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -103,7 +103,7 @@ static void ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_rate *rate, - int rtap_len) + int rtap_len, bool has_fcs) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_radiotap_header *rthdr; @@ -134,7 +134,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, } /* IEEE80211_RADIOTAP_FLAGS */ - if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) + if (has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)) *pos |= IEEE80211_RADIOTAP_F_FCS; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) *pos |= IEEE80211_RADIOTAP_F_BADFCS; @@ -294,7 +294,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, } /* prepend radiotap information */ - ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom); + ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, + true); skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -2571,7 +2572,8 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, goto out_free_skb; /* prepend radiotap information */ - ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom); + ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, + false); skb_set_mac_header(skb, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 33cd16901378..c70e17677135 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -370,7 +370,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) */ drv_sw_scan_start(local); - local->leave_oper_channel_time = 0; + local->leave_oper_channel_time = jiffies; local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 782a60198df4..e76facc69e95 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1158,7 +1158,8 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->sta = rcu_dereference(sdata->u.vlan.sta); if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr) return TX_DROP; - } else if (info->flags & IEEE80211_TX_CTL_INJECTED) { + } else if (info->flags & IEEE80211_TX_CTL_INJECTED || + tx->sdata->control_port_protocol == tx->skb->protocol) { tx->sta = sta_info_get_bss(sdata, hdr->addr1); } if (!tx->sta) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 2555816e7788..00bdb1d9d690 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1924,6 +1924,7 @@ protocol_fail: control_fail: ip_vs_estimator_net_cleanup(net); estimator_fail: + net->ipvs = NULL; return -ENOMEM; } @@ -1936,6 +1937,7 @@ static void __net_exit __ip_vs_cleanup(struct net *net) ip_vs_control_net_cleanup(net); ip_vs_estimator_net_cleanup(net); IP_VS_DBG(2, "ipvs netns %d released\n", net_ipvs(net)->gen); + net->ipvs = NULL; } static void __net_exit __ip_vs_dev_cleanup(struct net *net) @@ -1993,10 +1995,18 @@ static int __init ip_vs_init(void) goto cleanup_dev; } + ret = ip_vs_register_nl_ioctl(); + if (ret < 0) { + pr_err("can't register netlink/ioctl.\n"); + goto cleanup_hooks; + } + pr_info("ipvs loaded.\n"); return ret; +cleanup_hooks: + nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); cleanup_dev: unregister_pernet_device(&ipvs_core_dev_ops); cleanup_sub: @@ -2012,6 +2022,7 @@ exit: static void __exit ip_vs_cleanup(void) { + ip_vs_unregister_nl_ioctl(); nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); unregister_pernet_device(&ipvs_core_dev_ops); unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index b3afe189af61..f5589987fc80 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3680,7 +3680,7 @@ int __net_init ip_vs_control_net_init_sysctl(struct net *net) return 0; } -void __net_init ip_vs_control_net_cleanup_sysctl(struct net *net) +void __net_exit ip_vs_control_net_cleanup_sysctl(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -3692,7 +3692,7 @@ void __net_init ip_vs_control_net_cleanup_sysctl(struct net *net) #else int __net_init ip_vs_control_net_init_sysctl(struct net *net) { return 0; } -void __net_init ip_vs_control_net_cleanup_sysctl(struct net *net) { } +void __net_exit ip_vs_control_net_cleanup_sysctl(struct net *net) { } #endif @@ -3750,21 +3750,10 @@ void __net_exit ip_vs_control_net_cleanup(struct net *net) free_percpu(ipvs->tot_stats.cpustats); } -int __init ip_vs_control_init(void) +int __init ip_vs_register_nl_ioctl(void) { - int idx; int ret; - EnterFunction(2); - - /* Initialize svc_table, ip_vs_svc_fwm_table, rs_table */ - for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { - INIT_LIST_HEAD(&ip_vs_svc_table[idx]); - INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]); - } - - smp_wmb(); /* Do we really need it now ? */ - ret = nf_register_sockopt(&ip_vs_sockopts); if (ret) { pr_err("cannot register sockopt.\n"); @@ -3776,28 +3765,47 @@ int __init ip_vs_control_init(void) pr_err("cannot register Generic Netlink interface.\n"); goto err_genl; } - - ret = register_netdevice_notifier(&ip_vs_dst_notifier); - if (ret < 0) - goto err_notf; - - LeaveFunction(2); return 0; -err_notf: - ip_vs_genl_unregister(); err_genl: nf_unregister_sockopt(&ip_vs_sockopts); err_sock: return ret; } +void ip_vs_unregister_nl_ioctl(void) +{ + ip_vs_genl_unregister(); + nf_unregister_sockopt(&ip_vs_sockopts); +} + +int __init ip_vs_control_init(void) +{ + int idx; + int ret; + + EnterFunction(2); + + /* Initialize svc_table, ip_vs_svc_fwm_table, rs_table */ + for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { + INIT_LIST_HEAD(&ip_vs_svc_table[idx]); + INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]); + } + + smp_wmb(); /* Do we really need it now ? */ + + ret = register_netdevice_notifier(&ip_vs_dst_notifier); + if (ret < 0) + return ret; + + LeaveFunction(2); + return 0; +} + void ip_vs_control_cleanup(void) { EnterFunction(2); unregister_netdevice_notifier(&ip_vs_dst_notifier); - ip_vs_genl_unregister(); - nf_unregister_sockopt(&ip_vs_sockopts); LeaveFunction(2); } diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 538d74ee4f68..e39f693dd3e4 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -439,6 +439,8 @@ static int __net_init __ip_vs_ftp_init(struct net *net) struct ip_vs_app *app; struct netns_ipvs *ipvs = net_ipvs(net); + if (!ipvs) + return -ENOENT; app = kmemdup(&ip_vs_ftp, sizeof(struct ip_vs_app), GFP_KERNEL); if (!app) return -ENOMEM; diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 0f16283fd058..caa43704e55e 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -551,6 +551,9 @@ static int __net_init __ip_vs_lblc_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); + if (!ipvs) + return -ENOENT; + if (!net_eq(net, &init_net)) { ipvs->lblc_ctl_table = kmemdup(vs_vars_table, sizeof(vs_vars_table), diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index eec797f8cce7..548bf37aa29e 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -745,6 +745,9 @@ static int __net_init __ip_vs_lblcr_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); + if (!ipvs) + return -ENOENT; + if (!net_eq(net, &init_net)) { ipvs->lblcr_ctl_table = kmemdup(vs_vars_table, sizeof(vs_vars_table), diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index f843a8833250..ed835e67a07e 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -59,9 +59,6 @@ static int __used __init register_ip_vs_protocol(struct ip_vs_protocol *pp) return 0; } -#if defined(CONFIG_IP_VS_PROTO_TCP) || defined(CONFIG_IP_VS_PROTO_UDP) || \ - defined(CONFIG_IP_VS_PROTO_SCTP) || defined(CONFIG_IP_VS_PROTO_AH) || \ - defined(CONFIG_IP_VS_PROTO_ESP) /* * register an ipvs protocols netns related data */ @@ -81,12 +78,18 @@ register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp) ipvs->proto_data_table[hash] = pd; atomic_set(&pd->appcnt, 0); /* Init app counter */ - if (pp->init_netns != NULL) - pp->init_netns(net, pd); + if (pp->init_netns != NULL) { + int ret = pp->init_netns(net, pd); + if (ret) { + /* unlink an free proto data */ + ipvs->proto_data_table[hash] = pd->next; + kfree(pd); + return ret; + } + } return 0; } -#endif /* * unregister an ipvs protocol @@ -316,22 +319,35 @@ ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, */ int __net_init ip_vs_protocol_net_init(struct net *net) { + int i, ret; + static struct ip_vs_protocol *protos[] = { #ifdef CONFIG_IP_VS_PROTO_TCP - register_ip_vs_proto_netns(net, &ip_vs_protocol_tcp); + &ip_vs_protocol_tcp, #endif #ifdef CONFIG_IP_VS_PROTO_UDP - register_ip_vs_proto_netns(net, &ip_vs_protocol_udp); + &ip_vs_protocol_udp, #endif #ifdef CONFIG_IP_VS_PROTO_SCTP - register_ip_vs_proto_netns(net, &ip_vs_protocol_sctp); + &ip_vs_protocol_sctp, #endif #ifdef CONFIG_IP_VS_PROTO_AH - register_ip_vs_proto_netns(net, &ip_vs_protocol_ah); + &ip_vs_protocol_ah, #endif #ifdef CONFIG_IP_VS_PROTO_ESP - register_ip_vs_proto_netns(net, &ip_vs_protocol_esp); + &ip_vs_protocol_esp, #endif + }; + + for (i = 0; i < ARRAY_SIZE(protos); i++) { + ret = register_ip_vs_proto_netns(net, protos[i]); + if (ret < 0) + goto cleanup; + } return 0; + +cleanup: + ip_vs_protocol_net_cleanup(net); + return ret; } void __net_exit ip_vs_protocol_net_cleanup(struct net *net) diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 1fbf7a2816f5..9f3fb751c491 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -1090,7 +1090,7 @@ out: * timeouts is netns related now. * --------------------------------------------- */ -static void __ip_vs_sctp_init(struct net *net, struct ip_vs_proto_data *pd) +static int __ip_vs_sctp_init(struct net *net, struct ip_vs_proto_data *pd) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -1098,6 +1098,9 @@ static void __ip_vs_sctp_init(struct net *net, struct ip_vs_proto_data *pd) spin_lock_init(&ipvs->sctp_app_lock); pd->timeout_table = ip_vs_create_timeout_table((int *)sctp_timeouts, sizeof(sctp_timeouts)); + if (!pd->timeout_table) + return -ENOMEM; + return 0; } static void __ip_vs_sctp_exit(struct net *net, struct ip_vs_proto_data *pd) diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index ef8641f7af83..cd609cc62721 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -677,7 +677,7 @@ void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp) * timeouts is netns related now. * --------------------------------------------- */ -static void __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd) +static int __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -685,7 +685,10 @@ static void __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd) spin_lock_init(&ipvs->tcp_app_lock); pd->timeout_table = ip_vs_create_timeout_table((int *)tcp_timeouts, sizeof(tcp_timeouts)); + if (!pd->timeout_table) + return -ENOMEM; pd->tcp_state_table = tcp_states; + return 0; } static void __ip_vs_tcp_exit(struct net *net, struct ip_vs_proto_data *pd) diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index f4b7262896bb..2fedb2dcb3d1 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -467,7 +467,7 @@ udp_state_transition(struct ip_vs_conn *cp, int direction, cp->timeout = pd->timeout_table[IP_VS_UDP_S_NORMAL]; } -static void __udp_init(struct net *net, struct ip_vs_proto_data *pd) +static int __udp_init(struct net *net, struct ip_vs_proto_data *pd) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -475,6 +475,9 @@ static void __udp_init(struct net *net, struct ip_vs_proto_data *pd) spin_lock_init(&ipvs->udp_app_lock); pd->timeout_table = ip_vs_create_timeout_table((int *)udp_timeouts, sizeof(udp_timeouts)); + if (!pd->timeout_table) + return -ENOMEM; + return 0; } static void __udp_exit(struct net *net, struct ip_vs_proto_data *pd) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index cbdb754dbb10..729f157a0efa 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -735,6 +735,7 @@ __nf_conntrack_alloc(struct net *net, u16 zone, #ifdef CONFIG_NF_CONNTRACK_ZONES out_free: + atomic_dec(&net->ct.count); kmem_cache_free(net->ct.nf_conntrack_cachep, ct); return ERR_PTR(-ENOMEM); #endif @@ -1591,7 +1592,7 @@ static int nf_conntrack_init_net(struct net *net) return 0; err_timeout: - nf_conntrack_timeout_fini(net); + nf_conntrack_ecache_fini(net); err_ecache: nf_conntrack_tstamp_fini(net); err_tstamp: diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 361eade62a09..0d07a1dcf605 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -584,8 +584,8 @@ static bool tcp_in_window(const struct nf_conn *ct, * Let's try to use the data from the packet. */ sender->td_end = end; - win <<= sender->td_scale; - sender->td_maxwin = (win == 0 ? 1 : win); + swin = win << sender->td_scale; + sender->td_maxwin = (swin == 0 ? 1 : swin); sender->td_maxend = end + sender->td_maxwin; /* * We haven't seen traffic in the other direction yet diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index 3eb348bfc4fb..d98c868c148b 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/skbuff.h> +#include <linux/atomic.h> #include <linux/netlink.h> #include <linux/rculist.h> #include <linux/slab.h> @@ -17,7 +18,6 @@ #include <linux/errno.h> #include <net/netlink.h> #include <net/sock.h> -#include <asm/atomic.h> #include <linux/netfilter.h> #include <linux/netfilter/nfnetlink.h> diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index 0c8e43810ce3..3746d8b9a478 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -150,6 +150,17 @@ err1: return ret; } +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT +static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout) +{ + typeof(nf_ct_timeout_put_hook) timeout_put; + + timeout_put = rcu_dereference(nf_ct_timeout_put_hook); + if (timeout_put) + timeout_put(timeout); +} +#endif + static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) { struct xt_ct_target_info_v1 *info = par->targinfo; @@ -158,7 +169,9 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) struct nf_conn *ct; int ret = 0; u8 proto; - +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + struct ctnl_timeout *timeout; +#endif if (info->flags & ~XT_CT_NOTRACK) return -EINVAL; @@ -214,9 +227,8 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) } #ifdef CONFIG_NF_CONNTRACK_TIMEOUT - if (info->timeout) { + if (info->timeout[0]) { typeof(nf_ct_timeout_find_get_hook) timeout_find_get; - struct ctnl_timeout *timeout; struct nf_conn_timeout *timeout_ext; rcu_read_lock(); @@ -245,7 +257,7 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) pr_info("Timeout policy `%s' can only be " "used by L3 protocol number %d\n", info->timeout, timeout->l3num); - goto err4; + goto err5; } /* Make sure the timeout policy matches any existing * protocol tracker, otherwise default to generic. @@ -258,13 +270,13 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) "used by L4 protocol number %d\n", info->timeout, timeout->l4proto->l4proto); - goto err4; + goto err5; } timeout_ext = nf_ct_timeout_ext_add(ct, timeout, - GFP_KERNEL); + GFP_ATOMIC); if (timeout_ext == NULL) { ret = -ENOMEM; - goto err4; + goto err5; } } else { ret = -ENOENT; @@ -281,8 +293,12 @@ out: info->ct = ct; return 0; +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT +err5: + __xt_ct_tg_timeout_put(timeout); err4: rcu_read_unlock(); +#endif err3: nf_conntrack_free(ct); err2: diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 32bb75324e76..faa48f70b7c9 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -829,12 +829,19 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb, return 0; } -int netlink_sendskb(struct sock *sk, struct sk_buff *skb) +static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb) { int len = skb->len; skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk, len); + return len; +} + +int netlink_sendskb(struct sock *sk, struct sk_buff *skb) +{ + int len = __netlink_sendskb(sk, skb); + sock_put(sk); return len; } @@ -957,8 +964,7 @@ static int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf && !test_bit(0, &nlk->state)) { skb_set_owner_r(skb, sk); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); + __netlink_sendskb(sk, skb); return atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1); } return -1; @@ -1698,10 +1704,8 @@ static int netlink_dump(struct sock *sk) if (sk_filter(sk, skb)) kfree_skb(skb); - else { - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); - } + else + __netlink_sendskb(sk, skb); return 0; } @@ -1715,10 +1719,8 @@ static int netlink_dump(struct sock *sk) if (sk_filter(sk, skb)) kfree_skb(skb); - else { - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); - } + else + __netlink_sendskb(sk, skb); if (cb->done) cb->done(cb); diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index 7b76eb7192f3..ef10ffcb4b6f 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -474,7 +474,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, while (remaining_len > 0) { - frag_len = min_t(u16, local->remote_miu, remaining_len); + frag_len = min_t(size_t, local->remote_miu, remaining_len); pr_debug("Fragment %zd bytes remaining %zd", frag_len, remaining_len); @@ -497,7 +497,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, release_sock(sk); remaining_len -= frag_len; - msg_ptr += len; + msg_ptr += frag_len; } kfree(msg_data); diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index e44e631ea952..777716bc80f7 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -421,6 +421,19 @@ static int validate_sample(const struct nlattr *attr, return validate_actions(actions, key, depth + 1); } +static int validate_tp_port(const struct sw_flow_key *flow_key) +{ + if (flow_key->eth.type == htons(ETH_P_IP)) { + if (flow_key->ipv4.tp.src && flow_key->ipv4.tp.dst) + return 0; + } else if (flow_key->eth.type == htons(ETH_P_IPV6)) { + if (flow_key->ipv6.tp.src && flow_key->ipv6.tp.dst) + return 0; + } + + return -EINVAL; +} + static int validate_set(const struct nlattr *a, const struct sw_flow_key *flow_key) { @@ -462,18 +475,13 @@ static int validate_set(const struct nlattr *a, if (flow_key->ip.proto != IPPROTO_TCP) return -EINVAL; - if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst) - return -EINVAL; - - break; + return validate_tp_port(flow_key); case OVS_KEY_ATTR_UDP: if (flow_key->ip.proto != IPPROTO_UDP) return -EINVAL; - if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst) - return -EINVAL; - break; + return validate_tp_port(flow_key); default: return -EINVAL; @@ -1641,10 +1649,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq, OVS_VPORT_CMD_NEW); if (IS_ERR(reply)) { - err = PTR_ERR(reply); netlink_set_err(init_net.genl_sock, 0, - ovs_dp_vport_multicast_group.id, err); - return 0; + ovs_dp_vport_multicast_group.id, PTR_ERR(reply)); + goto exit_unlock; } genl_notify(reply, genl_info_net(info), info->snd_pid, diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 1252c3081ef1..2a11ec2383ee 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -183,7 +183,8 @@ void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) u8 tcp_flags = 0; if (flow->key.eth.type == htons(ETH_P_IP) && - flow->key.ip.proto == IPPROTO_TCP) { + flow->key.ip.proto == IPPROTO_TCP && + likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) { u8 *tcp = (u8 *)tcp_hdr(skb); tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK; } diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 9f60008740e3..9726fe684ab8 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -1130,6 +1130,9 @@ static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, int flags = msg->msg_flags; int err, done; + if (len > USHRT_MAX) + return -EMSGSIZE; + if ((msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL| MSG_CMSG_COMPAT)) || !(msg->msg_flags & MSG_EOR)) diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 9b9a85ecc4c7..bf5cf69c820a 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -331,23 +331,6 @@ static int __net_init phonet_init_net(struct net *net) static void __net_exit phonet_exit_net(struct net *net) { - struct phonet_net *pnn = phonet_pernet(net); - struct net_device *dev; - unsigned i; - - rtnl_lock(); - for_each_netdev(net, dev) - phonet_device_destroy(dev); - - for (i = 0; i < 64; i++) { - dev = pnn->routes.table[i]; - if (dev) { - rtm_phonet_notify(RTM_DELROUTE, dev, i); - dev_put(dev); - } - } - rtnl_unlock(); - proc_net_remove(net, "phonet"); } @@ -361,7 +344,7 @@ static struct pernet_operations phonet_net_ops = { /* Initialize Phonet devices list */ int __init phonet_device_init(void) { - int err = register_pernet_device(&phonet_net_ops); + int err = register_pernet_subsys(&phonet_net_ops); if (err) return err; @@ -377,7 +360,7 @@ void phonet_device_exit(void) { rtnl_unregister_all(PF_PHONET); unregister_netdevice_notifier(&phonet_device_notifier); - unregister_pernet_device(&phonet_net_ops); + unregister_pernet_subsys(&phonet_net_ops); proc_net_remove(&init_net, "pnresource"); } diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 1ab8689726ec..906cc05bba63 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -95,11 +95,11 @@ static int rose_set_mac_address(struct net_device *dev, void *addr) struct sockaddr *sa = addr; int err; - if (!memcpy(dev->dev_addr, sa->sa_data, dev->addr_len)) + if (!memcmp(dev->dev_addr, sa->sa_data, dev->addr_len)) return 0; if (dev->flags & IFF_UP) { - err = rose_add_loopback_node((rose_address *)dev->dev_addr); + err = rose_add_loopback_node((rose_address *)sa->sa_data); if (err) return err; diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c index 0b15236be7b6..8179494c269a 100644 --- a/net/sched/sch_gred.c +++ b/net/sched/sch_gred.c @@ -565,11 +565,8 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb) opt.packets = q->packetsin; opt.bytesin = q->bytesin; - if (gred_wred_mode(table)) { - q->vars.qidlestart = - table->tab[table->def]->vars.qidlestart; - q->vars.qavg = table->tab[table->def]->vars.qavg; - } + if (gred_wred_mode(table)) + gred_load_wred_set(table, q); opt.qave = red_calc_qavg(&q->parms, &q->vars, q->vars.qavg); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 5da548fa7ae9..ebd22966f748 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -408,10 +408,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) { if (!(skb = skb_unshare(skb, GFP_ATOMIC)) || (skb->ip_summed == CHECKSUM_PARTIAL && - skb_checksum_help(skb))) { - sch->qstats.drops++; - return NET_XMIT_DROP; - } + skb_checksum_help(skb))) + return qdisc_drop(skb, sch); skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8); } diff --git a/net/sctp/output.c b/net/sctp/output.c index 817174eb5f41..8fc4dcd294ab 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -377,9 +377,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) */ skb_set_owner_w(nskb, sk); - /* The 'obsolete' field of dst is set to 2 when a dst is freed. */ - if (!dst || (dst->obsolete > 1)) { - dst_release(dst); + if (!sctp_transport_dst_check(tp)) { sctp_transport_route(tp, NULL, sctp_sk(sk)); if (asoc && (asoc->param_flags & SPP_PMTUD_ENABLE)) { sctp_assoc_sync_pmtu(asoc); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 06b42b7f5a02..92ba71dfe080 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4133,9 +4133,10 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len, static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval, int __user *optlen) { - if (len < sizeof(struct sctp_event_subscribe)) + if (len <= 0) return -EINVAL; - len = sizeof(struct sctp_event_subscribe); + if (len > sizeof(struct sctp_event_subscribe)) + len = sizeof(struct sctp_event_subscribe); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len)) diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 3889330b7b04..b026ba0c6992 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -226,23 +226,6 @@ void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk) transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT; } -/* this is a complete rip-off from __sk_dst_check - * the cookie is always 0 since this is how it's used in the - * pmtu code - */ -static struct dst_entry *sctp_transport_dst_check(struct sctp_transport *t) -{ - struct dst_entry *dst = t->dst; - - if (dst && dst->obsolete && dst->ops->check(dst, 0) == NULL) { - dst_release(t->dst); - t->dst = NULL; - return NULL; - } - - return dst; -} - void sctp_transport_update_pmtu(struct sctp_transport *t, u32 pmtu) { struct dst_entry *dst; diff --git a/net/socket.c b/net/socket.c index 484cc6953fc6..851edcd6b098 100644 --- a/net/socket.c +++ b/net/socket.c @@ -811,9 +811,9 @@ static ssize_t sock_sendpage(struct file *file, struct page *page, sock = file->private_data; - flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT; - if (more) - flags |= MSG_MORE; + flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0; + /* more is a combination of MSG_MORE and MSG_SENDPAGE_NOTLAST */ + flags |= more; return kernel_sendpage(sock, page, offset, size, flags); } diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index ca8cad8251c7..782bfe1b6465 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c @@ -242,12 +242,13 @@ EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor); int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr) { struct gss_api_mech *pos = NULL; - int i = 0; + int j, i = 0; spin_lock(®istered_mechs_lock); list_for_each_entry(pos, ®istered_mechs, gm_list) { - array_ptr[i] = pos->gm_pfs->pseudoflavor; - i++; + for (j=0; j < pos->gm_pf_num; j++) { + array_ptr[i++] = pos->gm_pfs[j].pseudoflavor; + } } spin_unlock(®istered_mechs_lock); return i; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 67972462a543..adf2990acebf 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -176,16 +176,22 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name) return 0; } -static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event, - struct super_block *sb) +static inline int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event) +{ + if (((event == RPC_PIPEFS_MOUNT) && clnt->cl_dentry) || + ((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry)) + return 1; + return 0; +} + +static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event, + struct super_block *sb) { struct dentry *dentry; int err = 0; switch (event) { case RPC_PIPEFS_MOUNT: - if (clnt->cl_program->pipe_dir_name == NULL) - break; dentry = rpc_setup_pipedir_sb(sb, clnt, clnt->cl_program->pipe_dir_name); BUG_ON(dentry == NULL); @@ -208,6 +214,20 @@ static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event, return err; } +static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event, + struct super_block *sb) +{ + int error = 0; + + for (;; clnt = clnt->cl_parent) { + if (!rpc_clnt_skip_event(clnt, event)) + error = __rpc_clnt_handle_event(clnt, event, sb); + if (error || clnt == clnt->cl_parent) + break; + } + return error; +} + static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event) { struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); @@ -215,10 +235,12 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event) spin_lock(&sn->rpc_client_lock); list_for_each_entry(clnt, &sn->all_clients, cl_clients) { - if (((event == RPC_PIPEFS_MOUNT) && clnt->cl_dentry) || - ((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry)) + if (clnt->cl_program->pipe_dir_name == NULL) + break; + if (rpc_clnt_skip_event(clnt, event)) + continue; + if (atomic_inc_not_zero(&clnt->cl_count) == 0) continue; - atomic_inc(&clnt->cl_count); spin_unlock(&sn->rpc_client_lock); return clnt; } @@ -257,6 +279,14 @@ void rpc_clients_notifier_unregister(void) return rpc_pipefs_notifier_unregister(&rpc_clients_block); } +static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename) +{ + clnt->cl_nodelen = strlen(nodename); + if (clnt->cl_nodelen > UNX_MAXNODENAME) + clnt->cl_nodelen = UNX_MAXNODENAME; + memcpy(clnt->cl_nodename, nodename, clnt->cl_nodelen); +} + static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt) { const struct rpc_program *program = args->program; @@ -337,10 +367,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru } /* save the nodename */ - clnt->cl_nodelen = strlen(init_utsname()->nodename); - if (clnt->cl_nodelen > UNX_MAXNODENAME) - clnt->cl_nodelen = UNX_MAXNODENAME; - memcpy(clnt->cl_nodename, init_utsname()->nodename, clnt->cl_nodelen); + rpc_clnt_set_nodename(clnt, utsname()->nodename); rpc_register_client(clnt); return clnt; @@ -499,6 +526,7 @@ rpc_clone_client(struct rpc_clnt *clnt) err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); if (err != 0) goto out_no_path; + rpc_clnt_set_nodename(new, utsname()->nodename); if (new->cl_auth) atomic_inc(&new->cl_auth->au_count); atomic_inc(&clnt->cl_count); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 0af37fc46818..3b62cf288031 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1126,19 +1126,20 @@ rpc_fill_super(struct super_block *sb, void *data, int silent) return -ENOMEM; dprintk("RPC: sending pipefs MOUNT notification for net %p%s\n", net, NET_NAME(net)); + sn->pipefs_sb = sb; err = blocking_notifier_call_chain(&rpc_pipefs_notifier_list, RPC_PIPEFS_MOUNT, sb); if (err) goto err_depopulate; sb->s_fs_info = get_net(net); - sn->pipefs_sb = sb; return 0; err_depopulate: blocking_notifier_call_chain(&rpc_pipefs_notifier_list, RPC_PIPEFS_UMOUNT, sb); + sn->pipefs_sb = NULL; __rpc_depopulate(root, files, RPCAUTH_lockd, RPCAUTH_RootEOF); return err; } diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 8adfc88e793a..3d6498af9adc 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -75,20 +75,21 @@ static struct pernet_operations sunrpc_net_ops = { static int __init init_sunrpc(void) { - int err = register_rpc_pipefs(); + int err = rpc_init_mempool(); if (err) goto out; - err = rpc_init_mempool(); - if (err) - goto out2; err = rpcauth_init_module(); if (err) - goto out3; + goto out2; cache_initialize(); err = register_pernet_subsys(&sunrpc_net_ops); if (err) + goto out3; + + err = register_rpc_pipefs(); + if (err) goto out4; #ifdef RPC_DEBUG rpc_register_sysctl(); @@ -98,11 +99,11 @@ init_sunrpc(void) return 0; out4: - rpcauth_remove_module(); + unregister_pernet_subsys(&sunrpc_net_ops); out3: - rpc_destroy_mempool(); + rpcauth_remove_module(); out2: - unregister_rpc_pipefs(); + rpc_destroy_mempool(); out: return err; } diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 39765bcfb472..920cabe0461b 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -13,12 +13,6 @@ #include "core.h" #include "debugfs.h" -static int cfg80211_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ static ssize_t name## _read(struct file *file, char __user *userbuf, \ size_t count, loff_t *ppos) \ @@ -33,7 +27,7 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \ \ static const struct file_operations name## _ops = { \ .read = name## _read, \ - .open = cfg80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -102,7 +96,7 @@ static ssize_t ht40allow_map_read(struct file *file, static const struct file_operations ht40allow_map_ops = { .read = ht40allow_map_read, - .open = cfg80211_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4c1eb9472ddb..f432c57af05d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1294,6 +1294,11 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) goto bad_res; } + if (!netif_running(netdev)) { + result = -ENETDOWN; + goto bad_res; + } + nla_for_each_nested(nl_txq_params, info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], rem_txq_params) { @@ -2386,7 +2391,9 @@ nla_put_failure: } static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, - int flags, struct net_device *dev, + int flags, + struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo) { void *hdr; @@ -2425,12 +2432,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, if (sinfo->filled & STATION_INFO_PLINK_STATE) NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, sinfo->plink_state); - if (sinfo->filled & STATION_INFO_SIGNAL) - NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, - sinfo->signal); - if (sinfo->filled & STATION_INFO_SIGNAL_AVG) - NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, - sinfo->signal_avg); + switch (rdev->wiphy.signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + if (sinfo->filled & STATION_INFO_SIGNAL) + NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, + sinfo->signal); + if (sinfo->filled & STATION_INFO_SIGNAL_AVG) + NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, + sinfo->signal_avg); + break; + default: + break; + } if (sinfo->filled & STATION_INFO_TX_BITRATE) { if (!nl80211_put_sta_rate(msg, &sinfo->txrate, NL80211_STA_INFO_TX_BITRATE)) @@ -2523,7 +2536,7 @@ static int nl80211_dump_station(struct sk_buff *skb, if (nl80211_send_station(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - netdev, mac_addr, + dev, netdev, mac_addr, &sinfo) < 0) goto out; @@ -2568,7 +2581,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, - dev, mac_addr, &sinfo) < 0) { + rdev, dev, mac_addr, &sinfo) < 0) { nlmsg_free(msg); return -ENOBUFS; } @@ -6376,7 +6389,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_get_key, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6408,7 +6421,7 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .doit = nl80211_set_beacon, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6416,7 +6429,7 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .doit = nl80211_start_ap, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6424,7 +6437,7 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .doit = nl80211_stop_ap, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6440,7 +6453,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_set_station, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6456,7 +6469,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_del_station, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6489,7 +6502,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_del_mpath, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6497,7 +6510,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_set_bss, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6523,7 +6536,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_get_mesh_config, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6656,7 +6669,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_setdel_pmksa, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6664,7 +6677,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_setdel_pmksa, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6672,7 +6685,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_flush_pmksa, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -6832,7 +6845,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_probe_client, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -7596,7 +7609,8 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { + if (nl80211_send_station(msg, 0, 0, 0, + rdev, dev, mac_addr, sinfo) < 0) { nlmsg_free(msg); return; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 1b7a08df933c..957f25621617 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -989,7 +989,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, if (rdev->wiphy.software_iftypes & BIT(iftype)) continue; for (j = 0; j < c->n_limits; j++) { - if (!(limits[j].types & iftype)) + if (!(limits[j].types & BIT(iftype))) continue; if (limits[j].max < num[iftype]) goto cont; diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index 0af7f54e4f61..af648e08e61b 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -780,8 +780,10 @@ static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, if (cmd == SIOCSIWENCODEEXT) { struct iw_encode_ext *ee = (void *) extra; - if (iwp->length < sizeof(*ee) + ee->key_len) - return -EFAULT; + if (iwp->length < sizeof(*ee) + ee->key_len) { + err = -EFAULT; + goto out; + } } } diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index de639eeeed50..faea0ec612bf 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1869,12 +1869,6 @@ sub process { "No space is necessary after a cast\n" . $hereprev); } - if ($rawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $prevrawline =~ /^\+[ \t]*$/) { - CHK("BLOCK_COMMENT_STYLE", - "Don't begin block comments with only a /* line, use /* comment...\n" . $hereprev); - } - # check for spaces at the beginning of a line. # Exceptions: # 1) within comments diff --git a/scripts/coccinelle/api/simple_open.cocci b/scripts/coccinelle/api/simple_open.cocci new file mode 100644 index 000000000000..05962f7be155 --- /dev/null +++ b/scripts/coccinelle/api/simple_open.cocci @@ -0,0 +1,70 @@ +/// This removes an open coded simple_open() function +/// and replaces file operations references to the function +/// with simple_open() instead. +/// +// Confidence: High +// Comments: +// Options: -no_includes -include_headers + +virtual patch +virtual report + +@ open depends on patch @ +identifier open_f != simple_open; +identifier i, f; +@@ +-int open_f(struct inode *i, struct file *f) +-{ +( +-if (i->i_private) +-f->private_data = i->i_private; +| +-f->private_data = i->i_private; +) +-return 0; +-} + +@ has_open depends on open @ +identifier fops; +identifier open.open_f; +@@ +struct file_operations fops = { +..., +-.open = open_f, ++.open = simple_open, +... +}; + +@ openr depends on report @ +identifier open_f != simple_open; +identifier i, f; +position p; +@@ +int open_f@p(struct inode *i, struct file *f) +{ +( +if (i->i_private) +f->private_data = i->i_private; +| +f->private_data = i->i_private; +) +return 0; +} + +@ has_openr depends on openr @ +identifier fops; +identifier openr.open_f; +position p; +@@ +struct file_operations fops = { +..., +.open = open_f@p, +... +}; + +@script:python@ +pf << openr.p; +ps << has_openr.p; +@@ + +coccilib.report.print_report(pf[0],"WARNING opportunity for simple_open, see also structure on line %s"%(ps[0].line)) diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 0586085136d1..52577f052bc1 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -540,35 +540,6 @@ static struct conf_printer header_printer_cb = }; /* - * Generate the __enabled_CONFIG_* and __enabled_CONFIG_*_MODULE macros for - * use by the IS_{ENABLED,BUILTIN,MODULE} macros. The _MODULE variant is - * generated even for booleans so that the IS_ENABLED() macro works. - */ -static void -header_print__enabled_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) -{ - - switch (sym->type) { - case S_BOOLEAN: - case S_TRISTATE: { - fprintf(fp, "#define __enabled_" CONFIG_ "%s %d\n", - sym->name, (*value == 'y')); - fprintf(fp, "#define __enabled_" CONFIG_ "%s_MODULE %d\n", - sym->name, (*value == 'm')); - break; - } - default: - break; - } -} - -static struct conf_printer header__enabled_printer_cb = -{ - .print_symbol = header_print__enabled_symbol, - .print_comment = header_print_comment, -}; - -/* * Tristate printer * * This printer is used when generating the `include/config/tristate.conf' file. @@ -949,16 +920,11 @@ int conf_write_autoconf(void) conf_write_heading(out_h, &header_printer_cb, NULL); for_all_symbols(i, sym) { - if (!sym->name) - continue; - sym_calc_value(sym); - - conf_write_symbol(out_h, sym, &header__enabled_printer_cb, NULL); - - if (!(sym->flags & SYMBOL_WRITE)) + if (!(sym->flags & SYMBOL_WRITE) || !sym->name) continue; + /* write symbol to auto.conf, tristate and header files */ conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1); conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1); diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 8e730ccc3f2b..44ddaa542db6 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1100,6 +1100,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, if (!sym->st_shndx || get_secindex(info, sym) >= info->num_sections) return; + /* We're looking for an object */ + if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) + return; + /* All our symbols are of form <prefix>__mod_XXX_device_table. */ name = strstr(symname, "__mod_"); if (!name) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 3f01fd908730..c4e7d1510f9d 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -132,8 +132,10 @@ static struct module *new_module(char *modname) /* strip trailing .o */ s = strrchr(p, '.'); if (s != NULL) - if (strcmp(s, ".o") == 0) + if (strcmp(s, ".o") == 0) { *s = '\0'; + mod->is_dot_o = 1; + } /* add to list */ mod->name = p; @@ -587,7 +589,8 @@ static void handle_modversions(struct module *mod, struct elf_info *info, unsigned int crc; enum export export; - if (!is_vmlinux(mod->name) && strncmp(symname, "__ksymtab", 9) == 0) + if ((!is_vmlinux(mod->name) || mod->is_dot_o) && + strncmp(symname, "__ksymtab", 9) == 0) export = export_from_secname(info, get_secindex(info, sym)); else export = export_from_sec(info, get_secindex(info, sym)); diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index 2031119080dc..51207e4d5f8b 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -113,6 +113,7 @@ struct module { int has_cleanup; struct buffer dev_table_buf; char srcversion[25]; + int is_dot_o; }; struct elf_info { diff --git a/scripts/tags.sh b/scripts/tags.sh index 0d6004e20658..cf7b12fee573 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -254,6 +254,6 @@ case "$1" in esac # Remove structure forward declarations. -if [ -n $remove_structs ]; then +if [ -n "$remove_structs" ]; then LANG=C sed -i -e '/^\([a-zA-Z_][a-zA-Z0-9_]*\)\t.*\t\/\^struct \1;.*\$\/;"\tx$/d' $1 fi diff --git a/scripts/xz_wrap.sh b/scripts/xz_wrap.sh index 17a5798c29da..7a2d372f4885 100644 --- a/scripts/xz_wrap.sh +++ b/scripts/xz_wrap.sh @@ -12,8 +12,8 @@ BCJ= LZMA2OPTS= -case $ARCH in - x86|x86_64) BCJ=--x86 ;; +case $SRCARCH in + x86) BCJ=--x86 ;; powerpc) BCJ=--powerpc ;; ia64) BCJ=--ia64; LZMA2OPTS=pb=4 ;; arm) BCJ=--arm ;; diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 5ff67776a5ad..cc3520d39a78 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -115,23 +115,23 @@ static void audit_pre(struct audit_buffer *ab, void *ca) if (aa_g_audit_header) { audit_log_format(ab, "apparmor="); - audit_log_string(ab, aa_audit_type[sa->aad.type]); + audit_log_string(ab, aa_audit_type[sa->aad->type]); } - if (sa->aad.op) { + if (sa->aad->op) { audit_log_format(ab, " operation="); - audit_log_string(ab, op_table[sa->aad.op]); + audit_log_string(ab, op_table[sa->aad->op]); } - if (sa->aad.info) { + if (sa->aad->info) { audit_log_format(ab, " info="); - audit_log_string(ab, sa->aad.info); - if (sa->aad.error) - audit_log_format(ab, " error=%d", sa->aad.error); + audit_log_string(ab, sa->aad->info); + if (sa->aad->error) + audit_log_format(ab, " error=%d", sa->aad->error); } - if (sa->aad.profile) { - struct aa_profile *profile = sa->aad.profile; + if (sa->aad->profile) { + struct aa_profile *profile = sa->aad->profile; pid_t pid; rcu_read_lock(); pid = rcu_dereference(tsk->real_parent)->pid; @@ -145,9 +145,9 @@ static void audit_pre(struct audit_buffer *ab, void *ca) audit_log_untrustedstring(ab, profile->base.hname); } - if (sa->aad.name) { + if (sa->aad->name) { audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, sa->aad.name); + audit_log_untrustedstring(ab, sa->aad->name); } } @@ -159,10 +159,8 @@ static void audit_pre(struct audit_buffer *ab, void *ca) void aa_audit_msg(int type, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)) { - sa->aad.type = type; - sa->lsm_pre_audit = audit_pre; - sa->lsm_post_audit = cb; - common_lsm_audit(sa); + sa->aad->type = type; + common_lsm_audit(sa, audit_pre, cb); } /** @@ -184,7 +182,7 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, BUG_ON(!profile); if (type == AUDIT_APPARMOR_AUTO) { - if (likely(!sa->aad.error)) { + if (likely(!sa->aad->error)) { if (AUDIT_MODE(profile) != AUDIT_ALL) return 0; type = AUDIT_APPARMOR_AUDIT; @@ -196,21 +194,21 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, if (AUDIT_MODE(profile) == AUDIT_QUIET || (type == AUDIT_APPARMOR_DENIED && AUDIT_MODE(profile) == AUDIT_QUIET)) - return sa->aad.error; + return sa->aad->error; if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) type = AUDIT_APPARMOR_KILL; if (!unconfined(profile)) - sa->aad.profile = profile; + sa->aad->profile = profile; aa_audit_msg(type, sa, cb); - if (sa->aad.type == AUDIT_APPARMOR_KILL) + if (sa->aad->type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current); - if (sa->aad.type == AUDIT_APPARMOR_ALLOWED) - return complain_error(sa->aad.error); + if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) + return complain_error(sa->aad->error); - return sa->aad.error; + return sa->aad->error; } diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 9982c48def4e..088dba3bf7dc 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -64,11 +64,13 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task, struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, CAP); + sa.aad = &aad; sa.tsk = task; sa.u.cap = cap; - sa.aad.op = OP_CAPABLE; - sa.aad.error = error; + sa.aad->op = OP_CAPABLE; + sa.aad->error = error; if (likely(!error)) { /* test if auditing is being forced */ diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 5d176f2530c9..2f8fcba9ce4b 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -67,22 +67,22 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) struct common_audit_data *sa = va; uid_t fsuid = current_fsuid(); - if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { + if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " requested_mask="); - audit_file_mask(ab, sa->aad.fs.request); + audit_file_mask(ab, sa->aad->fs.request); } - if (sa->aad.fs.denied & AA_AUDIT_FILE_MASK) { + if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " denied_mask="); - audit_file_mask(ab, sa->aad.fs.denied); + audit_file_mask(ab, sa->aad->fs.denied); } - if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { + if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " fsuid=%d", fsuid); - audit_log_format(ab, " ouid=%d", sa->aad.fs.ouid); + audit_log_format(ab, " ouid=%d", sa->aad->fs.ouid); } - if (sa->aad.fs.target) { + if (sa->aad->fs.target) { audit_log_format(ab, " target="); - audit_log_untrustedstring(ab, sa->aad.fs.target); + audit_log_untrustedstring(ab, sa->aad->fs.target); } } @@ -107,45 +107,47 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, { int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, NONE); - sa.aad.op = op, - sa.aad.fs.request = request; - sa.aad.name = name; - sa.aad.fs.target = target; - sa.aad.fs.ouid = ouid; - sa.aad.info = info; - sa.aad.error = error; - - if (likely(!sa.aad.error)) { + sa.aad = &aad; + aad.op = op, + aad.fs.request = request; + aad.name = name; + aad.fs.target = target; + aad.fs.ouid = ouid; + aad.info = info; + aad.error = error; + + if (likely(!sa.aad->error)) { u32 mask = perms->audit; if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) mask = 0xffff; /* mask off perms that are not being force audited */ - sa.aad.fs.request &= mask; + sa.aad->fs.request &= mask; - if (likely(!sa.aad.fs.request)) + if (likely(!sa.aad->fs.request)) return 0; type = AUDIT_APPARMOR_AUDIT; } else { /* only report permissions that were denied */ - sa.aad.fs.request = sa.aad.fs.request & ~perms->allow; + sa.aad->fs.request = sa.aad->fs.request & ~perms->allow; - if (sa.aad.fs.request & perms->kill) + if (sa.aad->fs.request & perms->kill) type = AUDIT_APPARMOR_KILL; /* quiet known rejects, assumes quiet and kill do not overlap */ - if ((sa.aad.fs.request & perms->quiet) && + if ((sa.aad->fs.request & perms->quiet) && AUDIT_MODE(profile) != AUDIT_NOQUIET && AUDIT_MODE(profile) != AUDIT_ALL) - sa.aad.fs.request &= ~perms->quiet; + sa.aad->fs.request &= ~perms->quiet; - if (!sa.aad.fs.request) - return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + if (!sa.aad->fs.request) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; } - sa.aad.fs.denied = sa.aad.fs.request & ~perms->allow; + sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow; return aa_audit(type, profile, gfp, &sa, file_audit_cb); } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4ba78c203af1..3868b1e5d5ba 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -103,7 +103,33 @@ enum aa_ops { }; -/* define a short hand for apparmor_audit_data portion of common_audit_data */ +struct apparmor_audit_data { + int error; + int op; + int type; + void *profile; + const char *name; + const char *info; + union { + void *target; + struct { + long pos; + void *target; + } iface; + struct { + int rlim; + unsigned long max; + } rlim; + struct { + const char *target; + u32 request; + u32 denied; + uid_t ouid; + } fs; + }; +}; + +/* define a short hand for apparmor_audit_data structure */ #define aad apparmor_audit_data void aa_audit_msg(int type, struct common_audit_data *sa, diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 7ee05c6f3c64..c3da93a5150d 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -26,7 +26,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; audit_log_format(ab, " target="); - audit_log_untrustedstring(ab, sa->aad.target); + audit_log_untrustedstring(ab, sa->aad->target); } /** @@ -41,10 +41,12 @@ static int aa_audit_ptrace(struct aa_profile *profile, struct aa_profile *target, int error) { struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, NONE); - sa.aad.op = OP_PTRACE; - sa.aad.target = target; - sa.aad.error = error; + sa.aad = &aad; + aad.op = OP_PTRACE; + aad.target = target; + aad.error = error; return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, audit_cb); diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 9516948041ad..e75829ba0ff9 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -65,8 +65,10 @@ void aa_info_message(const char *str) { if (audit_enabled) { struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, NONE); - sa.aad.info = str; + sa.aad = &aad; + aad.info = str; aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); } printk(KERN_INFO "AppArmor: %s\n", str); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 97ce8fae49b3..ad05d391974d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -588,10 +588,12 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, error = aa_setprocattr_permipc(args); } else { struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, NONE); - sa.aad.op = OP_SETPROCATTR; - sa.aad.info = name; - sa.aad.error = -EINVAL; + sa.aad = &aad; + aad.op = OP_SETPROCATTR; + aad.info = name; + aad.error = -EINVAL; return aa_audit(AUDIT_APPARMOR_DENIED, __aa_current_profile(), GFP_KERNEL, &sa, NULL); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 906414383022..f1f7506a464d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -964,11 +964,13 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, int error) { struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, NONE); - sa.aad.op = op; - sa.aad.name = name; - sa.aad.info = info; - sa.aad.error = error; + sa.aad = &aad; + aad.op = op; + aad.name = name; + aad.info = info; + aad.error = error; return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, &sa, NULL); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 25fd51edc8da..deab7c7e8dc0 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -70,13 +70,13 @@ struct aa_ext { static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; - if (sa->aad.iface.target) { - struct aa_profile *name = sa->aad.iface.target; + if (sa->aad->iface.target) { + struct aa_profile *name = sa->aad->iface.target; audit_log_format(ab, " name="); audit_log_untrustedstring(ab, name->base.hname); } - if (sa->aad.iface.pos) - audit_log_format(ab, " offset=%ld", sa->aad.iface.pos); + if (sa->aad->iface.pos) + audit_log_format(ab, " offset=%ld", sa->aad->iface.pos); } /** @@ -94,13 +94,15 @@ static int audit_iface(struct aa_profile *new, const char *name, { struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad = &aad; if (e) - sa.aad.iface.pos = e->pos - e->start; - sa.aad.iface.target = new; - sa.aad.name = name; - sa.aad.info = info; - sa.aad.error = error; + aad.iface.pos = e->pos - e->start; + aad.iface.target = new; + aad.name = name; + aad.info = info; + aad.error = error; return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, audit_cb); diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 72c25a4f2cfd..2fe8613efe33 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -34,7 +34,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) struct common_audit_data *sa = va; audit_log_format(ab, " rlimit=%s value=%lu", - rlim_names[sa->aad.rlim.rlim], sa->aad.rlim.max); + rlim_names[sa->aad->rlim.rlim], sa->aad->rlim.max); } /** @@ -50,12 +50,14 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource, unsigned long value, int error) { struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, NONE); - sa.aad.op = OP_SETRLIMIT, - sa.aad.rlim.rlim = resource; - sa.aad.rlim.max = value; - sa.aad.error = error; + sa.aad = &aad; + aad.op = OP_SETRLIMIT, + aad.rlim.rlim = resource; + aad.rlim.max = value; + aad.error = error; return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, audit_cb); } diff --git a/security/commoncap.c b/security/commoncap.c index 0cf4b53480a7..71a166a05975 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -29,6 +29,7 @@ #include <linux/securebits.h> #include <linux/user_namespace.h> #include <linux/binfmts.h> +#include <linux/personality.h> /* * If a non-root user executes a setuid-root binary in @@ -505,6 +506,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) } skip: + /* if we have fs caps, clear dangerous personality flags */ + if (!cap_issubset(new->cap_permitted, old->cap_permitted)) + bprm->per_clear |= PER_CLEAR_ON_SETID; + + /* Don't let someone trace a set[ug]id/setpcap binary with the revised * credentials unless they have the appropriate permit */ diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 8b8f0902f6e5..90c129b0102f 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -49,8 +49,8 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, if (ih == NULL) return -EINVAL; - ad->u.net.v4info.saddr = ih->saddr; - ad->u.net.v4info.daddr = ih->daddr; + ad->u.net->v4info.saddr = ih->saddr; + ad->u.net->v4info.daddr = ih->daddr; if (proto) *proto = ih->protocol; @@ -64,8 +64,8 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, if (th == NULL) break; - ad->u.net.sport = th->source; - ad->u.net.dport = th->dest; + ad->u.net->sport = th->source; + ad->u.net->dport = th->dest; break; } case IPPROTO_UDP: { @@ -73,8 +73,8 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, if (uh == NULL) break; - ad->u.net.sport = uh->source; - ad->u.net.dport = uh->dest; + ad->u.net->sport = uh->source; + ad->u.net->dport = uh->dest; break; } case IPPROTO_DCCP: { @@ -82,16 +82,16 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, if (dh == NULL) break; - ad->u.net.sport = dh->dccph_sport; - ad->u.net.dport = dh->dccph_dport; + ad->u.net->sport = dh->dccph_sport; + ad->u.net->dport = dh->dccph_dport; break; } case IPPROTO_SCTP: { struct sctphdr *sh = sctp_hdr(skb); if (sh == NULL) break; - ad->u.net.sport = sh->source; - ad->u.net.dport = sh->dest; + ad->u.net->sport = sh->source; + ad->u.net->dport = sh->dest; break; } default: @@ -119,8 +119,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, ip6 = ipv6_hdr(skb); if (ip6 == NULL) return -EINVAL; - ad->u.net.v6info.saddr = ip6->saddr; - ad->u.net.v6info.daddr = ip6->daddr; + ad->u.net->v6info.saddr = ip6->saddr; + ad->u.net->v6info.daddr = ip6->daddr; ret = 0; /* IPv6 can have several extension header before the Transport header * skip them */ @@ -140,8 +140,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, if (th == NULL) break; - ad->u.net.sport = th->source; - ad->u.net.dport = th->dest; + ad->u.net->sport = th->source; + ad->u.net->dport = th->dest; break; } case IPPROTO_UDP: { @@ -151,8 +151,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, if (uh == NULL) break; - ad->u.net.sport = uh->source; - ad->u.net.dport = uh->dest; + ad->u.net->sport = uh->source; + ad->u.net->dport = uh->dest; break; } case IPPROTO_DCCP: { @@ -162,8 +162,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, if (dh == NULL) break; - ad->u.net.sport = dh->dccph_sport; - ad->u.net.dport = dh->dccph_dport; + ad->u.net->sport = dh->dccph_sport; + ad->u.net->dport = dh->dccph_dport; break; } case IPPROTO_SCTP: { @@ -172,8 +172,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph); if (sh == NULL) break; - ad->u.net.sport = sh->source; - ad->u.net.dport = sh->dest; + ad->u.net->sport = sh->source; + ad->u.net->dport = sh->dest; break; } default: @@ -281,8 +281,8 @@ static void dump_common_audit_data(struct audit_buffer *ab, } break; case LSM_AUDIT_DATA_NET: - if (a->u.net.sk) { - struct sock *sk = a->u.net.sk; + if (a->u.net->sk) { + struct sock *sk = a->u.net->sk; struct unix_sock *u; int len = 0; char *p = NULL; @@ -330,29 +330,29 @@ static void dump_common_audit_data(struct audit_buffer *ab, } } - switch (a->u.net.family) { + switch (a->u.net->family) { case AF_INET: - print_ipv4_addr(ab, a->u.net.v4info.saddr, - a->u.net.sport, + print_ipv4_addr(ab, a->u.net->v4info.saddr, + a->u.net->sport, "saddr", "src"); - print_ipv4_addr(ab, a->u.net.v4info.daddr, - a->u.net.dport, + print_ipv4_addr(ab, a->u.net->v4info.daddr, + a->u.net->dport, "daddr", "dest"); break; case AF_INET6: - print_ipv6_addr(ab, &a->u.net.v6info.saddr, - a->u.net.sport, + print_ipv6_addr(ab, &a->u.net->v6info.saddr, + a->u.net->sport, "saddr", "src"); - print_ipv6_addr(ab, &a->u.net.v6info.daddr, - a->u.net.dport, + print_ipv6_addr(ab, &a->u.net->v6info.daddr, + a->u.net->dport, "daddr", "dest"); break; } - if (a->u.net.netif > 0) { + if (a->u.net->netif > 0) { struct net_device *dev; /* NOTE: we always use init's namespace */ - dev = dev_get_by_index(&init_net, a->u.net.netif); + dev = dev_get_by_index(&init_net, a->u.net->netif); if (dev) { audit_log_format(ab, " netif=%s", dev->name); dev_put(dev); @@ -378,11 +378,15 @@ static void dump_common_audit_data(struct audit_buffer *ab, /** * common_lsm_audit - generic LSM auditing function * @a: auxiliary audit data + * @pre_audit: lsm-specific pre-audit callback + * @post_audit: lsm-specific post-audit callback * * setup the audit buffer for common security information * uses callback to print LSM specific information */ -void common_lsm_audit(struct common_audit_data *a) +void common_lsm_audit(struct common_audit_data *a, + void (*pre_audit)(struct audit_buffer *, void *), + void (*post_audit)(struct audit_buffer *, void *)) { struct audit_buffer *ab; @@ -394,13 +398,13 @@ void common_lsm_audit(struct common_audit_data *a) if (ab == NULL) return; - if (a->lsm_pre_audit) - a->lsm_pre_audit(ab, a); + if (pre_audit) + pre_audit(ab, a); dump_common_audit_data(ab, a); - if (a->lsm_post_audit) - a->lsm_post_audit(ab, a); + if (post_audit) + post_audit(ab, a); audit_log_end(ab); } diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 1a70fa26da72..8ee42b2a5f19 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -436,9 +436,9 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) { struct common_audit_data *ad = a; audit_log_format(ab, "avc: %s ", - ad->selinux_audit_data.denied ? "denied" : "granted"); - avc_dump_av(ab, ad->selinux_audit_data.tclass, - ad->selinux_audit_data.audited); + ad->selinux_audit_data->slad->denied ? "denied" : "granted"); + avc_dump_av(ab, ad->selinux_audit_data->slad->tclass, + ad->selinux_audit_data->slad->audited); audit_log_format(ab, " for "); } @@ -452,22 +452,25 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) { struct common_audit_data *ad = a; audit_log_format(ab, " "); - avc_dump_query(ab, ad->selinux_audit_data.ssid, - ad->selinux_audit_data.tsid, - ad->selinux_audit_data.tclass); + avc_dump_query(ab, ad->selinux_audit_data->slad->ssid, + ad->selinux_audit_data->slad->tsid, + ad->selinux_audit_data->slad->tclass); } /* This is the slow part of avc audit with big stack footprint */ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, u32 requested, u32 audited, u32 denied, - struct av_decision *avd, struct common_audit_data *a, + struct common_audit_data *a, unsigned flags) { struct common_audit_data stack_data; + struct selinux_audit_data sad = {0,}; + struct selinux_late_audit_data slad; if (!a) { a = &stack_data; COMMON_AUDIT_DATA_INIT(a, NONE); + a->selinux_audit_data = &sad; } /* @@ -481,15 +484,15 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, (flags & MAY_NOT_BLOCK)) return -ECHILD; - a->selinux_audit_data.tclass = tclass; - a->selinux_audit_data.requested = requested; - a->selinux_audit_data.ssid = ssid; - a->selinux_audit_data.tsid = tsid; - a->selinux_audit_data.audited = audited; - a->selinux_audit_data.denied = denied; - a->lsm_pre_audit = avc_audit_pre_callback; - a->lsm_post_audit = avc_audit_post_callback; - common_lsm_audit(a); + slad.tclass = tclass; + slad.requested = requested; + slad.ssid = ssid; + slad.tsid = tsid; + slad.audited = audited; + slad.denied = denied; + + a->selinux_audit_data->slad = &slad; + common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback); return 0; } @@ -523,7 +526,7 @@ inline int avc_audit(u32 ssid, u32 tsid, if (unlikely(denied)) { audited = denied & avd->auditdeny; /* - * a->selinux_audit_data.auditdeny is TRICKY! Setting a bit in + * a->selinux_audit_data->auditdeny is TRICKY! Setting a bit in * this field means that ANY denials should NOT be audited if * the policy contains an explicit dontaudit rule for that * permission. Take notice that this is unrelated to the @@ -532,15 +535,15 @@ inline int avc_audit(u32 ssid, u32 tsid, * * denied == READ * avd.auditdeny & ACCESS == 0 (not set means explicit rule) - * selinux_audit_data.auditdeny & ACCESS == 1 + * selinux_audit_data->auditdeny & ACCESS == 1 * * We will NOT audit the denial even though the denied * permission was READ and the auditdeny checks were for * ACCESS */ if (a && - a->selinux_audit_data.auditdeny && - !(a->selinux_audit_data.auditdeny & avd->auditdeny)) + a->selinux_audit_data->auditdeny && + !(a->selinux_audit_data->auditdeny & avd->auditdeny)) audited = 0; } else if (result) audited = denied = requested; @@ -551,7 +554,7 @@ inline int avc_audit(u32 ssid, u32 tsid, return slow_avc_audit(ssid, tsid, tclass, requested, audited, denied, - avd, a, flags); + a, flags); } /** diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 28482f9e15b8..d85b793c9321 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1420,6 +1420,7 @@ static int cred_has_capability(const struct cred *cred, int cap, int audit) { struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; struct av_decision avd; u16 sclass; u32 sid = cred_sid(cred); @@ -1427,6 +1428,7 @@ static int cred_has_capability(const struct cred *cred, int rc; COMMON_AUDIT_DATA_INIT(&ad, CAP); + ad.selinux_audit_data = &sad; ad.tsk = current; ad.u.cap = cap; @@ -1492,9 +1494,11 @@ static int inode_has_perm_noadp(const struct cred *cred, unsigned flags) { struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; COMMON_AUDIT_DATA_INIT(&ad, INODE); ad.u.inode = inode; + ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, perms, &ad, flags); } @@ -1507,9 +1511,11 @@ static inline int dentry_has_perm(const struct cred *cred, { struct inode *inode = dentry->d_inode; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; COMMON_AUDIT_DATA_INIT(&ad, DENTRY); ad.u.dentry = dentry; + ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); } @@ -1522,9 +1528,11 @@ static inline int path_has_perm(const struct cred *cred, { struct inode *inode = path->dentry->d_inode; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; COMMON_AUDIT_DATA_INIT(&ad, PATH); ad.u.path = *path; + ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); } @@ -1543,11 +1551,13 @@ static int file_has_perm(const struct cred *cred, struct file_security_struct *fsec = file->f_security; struct inode *inode = file->f_path.dentry->d_inode; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = cred_sid(cred); int rc; COMMON_AUDIT_DATA_INIT(&ad, PATH); ad.u.path = file->f_path; + ad.selinux_audit_data = &sad; if (sid != fsec->sid) { rc = avc_has_perm(sid, fsec->sid, @@ -1577,6 +1587,7 @@ static int may_create(struct inode *dir, struct superblock_security_struct *sbsec; u32 sid, newsid; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; int rc; dsec = dir->i_security; @@ -1587,6 +1598,7 @@ static int may_create(struct inode *dir, COMMON_AUDIT_DATA_INIT(&ad, DENTRY); ad.u.dentry = dentry; + ad.selinux_audit_data = &sad; rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, @@ -1631,6 +1643,7 @@ static int may_link(struct inode *dir, { struct inode_security_struct *dsec, *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); u32 av; int rc; @@ -1640,6 +1653,7 @@ static int may_link(struct inode *dir, COMMON_AUDIT_DATA_INIT(&ad, DENTRY); ad.u.dentry = dentry; + ad.selinux_audit_data = &sad; av = DIR__SEARCH; av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); @@ -1674,6 +1688,7 @@ static inline int may_rename(struct inode *old_dir, { struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); u32 av; int old_is_dir, new_is_dir; @@ -1685,6 +1700,7 @@ static inline int may_rename(struct inode *old_dir, new_dsec = new_dir->i_security; COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.selinux_audit_data = &sad; ad.u.dentry = old_dentry; rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, @@ -1970,6 +1986,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) struct task_security_struct *new_tsec; struct inode_security_struct *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; struct inode *inode = bprm->file->f_path.dentry->d_inode; int rc; @@ -2009,6 +2026,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) } COMMON_AUDIT_DATA_INIT(&ad, PATH); + ad.selinux_audit_data = &sad; ad.u.path = bprm->file->f_path; if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) @@ -2098,6 +2116,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, struct files_struct *files) { struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; struct file *file, *devnull = NULL; struct tty_struct *tty; struct fdtable *fdt; @@ -2135,6 +2154,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, /* Revalidate access to inherited open files. */ COMMON_AUDIT_DATA_INIT(&ad, INODE); + ad.selinux_audit_data = &sad; spin_lock(&files->file_lock); for (;;) { @@ -2472,6 +2492,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) { const struct cred *cred = current_cred(); struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; int rc; rc = superblock_doinit(sb, data); @@ -2483,6 +2504,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) return 0; COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.selinux_audit_data = &sad; ad.u.dentry = sb->s_root; return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); } @@ -2491,8 +2513,10 @@ static int selinux_sb_statfs(struct dentry *dentry) { const struct cred *cred = current_cred(); struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.selinux_audit_data = &sad; ad.u.dentry = dentry->d_sb->s_root; return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); } @@ -2656,6 +2680,7 @@ static int selinux_inode_permission(struct inode *inode, int mask) { const struct cred *cred = current_cred(); struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 perms; bool from_access; unsigned flags = mask & MAY_NOT_BLOCK; @@ -2668,10 +2693,11 @@ static int selinux_inode_permission(struct inode *inode, int mask) return 0; COMMON_AUDIT_DATA_INIT(&ad, INODE); + ad.selinux_audit_data = &sad; ad.u.inode = inode; if (from_access) - ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS; + ad.selinux_audit_data->auditdeny |= FILE__AUDIT_ACCESS; perms = file_mask_to_av(inode->i_mode, mask); @@ -2737,6 +2763,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, struct inode_security_struct *isec = inode->i_security; struct superblock_security_struct *sbsec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 newsid, sid = current_sid(); int rc = 0; @@ -2751,6 +2778,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, return -EPERM; COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.selinux_audit_data = &sad; ad.u.dentry = dentry; rc = avc_has_perm(sid, isec->sid, isec->sclass, @@ -3345,10 +3373,12 @@ static int selinux_kernel_module_request(char *kmod_name) { u32 sid; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; sid = task_sid(current); COMMON_AUDIT_DATA_INIT(&ad, KMOD); + ad.selinux_audit_data = &sad; ad.u.kmod_name = kmod_name; return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, @@ -3487,8 +3517,8 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, if (ihlen < sizeof(_iph)) goto out; - ad->u.net.v4info.saddr = ih->saddr; - ad->u.net.v4info.daddr = ih->daddr; + ad->u.net->v4info.saddr = ih->saddr; + ad->u.net->v4info.daddr = ih->daddr; ret = 0; if (proto) @@ -3506,8 +3536,8 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, if (th == NULL) break; - ad->u.net.sport = th->source; - ad->u.net.dport = th->dest; + ad->u.net->sport = th->source; + ad->u.net->dport = th->dest; break; } @@ -3522,8 +3552,8 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, if (uh == NULL) break; - ad->u.net.sport = uh->source; - ad->u.net.dport = uh->dest; + ad->u.net->sport = uh->source; + ad->u.net->dport = uh->dest; break; } @@ -3538,8 +3568,8 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, if (dh == NULL) break; - ad->u.net.sport = dh->dccph_sport; - ad->u.net.dport = dh->dccph_dport; + ad->u.net->sport = dh->dccph_sport; + ad->u.net->dport = dh->dccph_dport; break; } @@ -3566,8 +3596,8 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, if (ip6 == NULL) goto out; - ad->u.net.v6info.saddr = ip6->saddr; - ad->u.net.v6info.daddr = ip6->daddr; + ad->u.net->v6info.saddr = ip6->saddr; + ad->u.net->v6info.daddr = ip6->daddr; ret = 0; nexthdr = ip6->nexthdr; @@ -3587,8 +3617,8 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, if (th == NULL) break; - ad->u.net.sport = th->source; - ad->u.net.dport = th->dest; + ad->u.net->sport = th->source; + ad->u.net->dport = th->dest; break; } @@ -3599,8 +3629,8 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, if (uh == NULL) break; - ad->u.net.sport = uh->source; - ad->u.net.dport = uh->dest; + ad->u.net->sport = uh->source; + ad->u.net->dport = uh->dest; break; } @@ -3611,8 +3641,8 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, if (dh == NULL) break; - ad->u.net.sport = dh->dccph_sport; - ad->u.net.dport = dh->dccph_dport; + ad->u.net->sport = dh->dccph_sport; + ad->u.net->dport = dh->dccph_dport; break; } @@ -3632,13 +3662,13 @@ static int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad, char *addrp; int ret; - switch (ad->u.net.family) { + switch (ad->u.net->family) { case PF_INET: ret = selinux_parse_skb_ipv4(skb, ad, proto); if (ret) goto parse_error; - addrp = (char *)(src ? &ad->u.net.v4info.saddr : - &ad->u.net.v4info.daddr); + addrp = (char *)(src ? &ad->u.net->v4info.saddr : + &ad->u.net->v4info.daddr); goto okay; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -3646,8 +3676,8 @@ static int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad, ret = selinux_parse_skb_ipv6(skb, ad, proto); if (ret) goto parse_error; - addrp = (char *)(src ? &ad->u.net.v6info.saddr : - &ad->u.net.v6info.daddr); + addrp = (char *)(src ? &ad->u.net->v6info.saddr : + &ad->u.net->v6info.daddr); goto okay; #endif /* IPV6 */ default: @@ -3721,13 +3751,17 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) { struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; u32 tsid = task_sid(task); if (sksec->sid == SECINITSID_KERNEL) return 0; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.sk = sk; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->sk = sk; return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); } @@ -3805,6 +3839,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in char *addrp; struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; @@ -3831,8 +3867,10 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (err) goto out; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.sport = htons(snum); - ad.u.net.family = family; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->sport = htons(snum); + ad.u.net->family = family; err = avc_has_perm(sksec->sid, sid, sksec->sclass, SOCKET__NAME_BIND, &ad); @@ -3864,13 +3902,15 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in goto out; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.sport = htons(snum); - ad.u.net.family = family; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->sport = htons(snum); + ad.u.net->family = family; if (family == PF_INET) - ad.u.net.v4info.saddr = addr4->sin_addr.s_addr; + ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; else - ad.u.net.v6info.saddr = addr6->sin6_addr; + ad.u.net->v6info.saddr = addr6->sin6_addr; err = avc_has_perm(sksec->sid, sid, sksec->sclass, node_perm, &ad); @@ -3897,6 +3937,8 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, if (sksec->sclass == SECCLASS_TCP_SOCKET || sksec->sclass == SECCLASS_DCCP_SOCKET) { struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; @@ -3922,8 +3964,10 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.dport = htons(snum); - ad.u.net.family = sk->sk_family; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->dport = htons(snum); + ad.u.net->family = sk->sk_family; err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); if (err) goto out; @@ -4012,10 +4056,14 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, struct sk_security_struct *sksec_other = other->sk_security; struct sk_security_struct *sksec_new = newsk->sk_security; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; int err; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.sk = other; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->sk = other; err = avc_has_perm(sksec_sock->sid, sksec_other->sid, sksec_other->sclass, @@ -4042,9 +4090,13 @@ static int selinux_socket_unix_may_send(struct socket *sock, struct sk_security_struct *ssec = sock->sk->sk_security; struct sk_security_struct *osec = other->sk->sk_security; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.sk = other->sk; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->sk = other->sk; return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, &ad); @@ -4080,11 +4132,15 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, struct sk_security_struct *sksec = sk->sk_security; u32 sk_sid = sksec->sid; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; char *addrp; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = skb->skb_iif; - ad.u.net.family = family; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->netif = skb->skb_iif; + ad.u.net->family = family; err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); if (err) return err; @@ -4111,6 +4167,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) u16 family = sk->sk_family; u32 sk_sid = sksec->sid; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; char *addrp; u8 secmark_active; u8 peerlbl_active; @@ -4135,8 +4193,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) return 0; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = skb->skb_iif; - ad.u.net.family = family; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->netif = skb->skb_iif; + ad.u.net->family = family; err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); if (err) return err; @@ -4471,6 +4531,8 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, char *addrp; u32 peer_sid; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; u8 secmark_active; u8 netlbl_active; u8 peerlbl_active; @@ -4488,8 +4550,10 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, return NF_DROP; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = ifindex; - ad.u.net.family = family; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->netif = ifindex; + ad.u.net->family = family; if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) return NF_DROP; @@ -4576,6 +4640,8 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, struct sock *sk = skb->sk; struct sk_security_struct *sksec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; char *addrp; u8 proto; @@ -4584,8 +4650,10 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, sksec = sk->sk_security; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = ifindex; - ad.u.net.family = family; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->netif = ifindex; + ad.u.net->family = family; if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) return NF_DROP; @@ -4607,6 +4675,8 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, u32 peer_sid; struct sock *sk; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + struct lsm_network_audit net = {0,}; char *addrp; u8 secmark_active; u8 peerlbl_active; @@ -4653,8 +4723,10 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, } COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = ifindex; - ad.u.net.family = family; + ad.selinux_audit_data = &sad; + ad.u.net = &net; + ad.u.net->netif = ifindex; + ad.u.net->family = family; if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL)) return NF_DROP; @@ -4769,11 +4841,13 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, { struct ipc_security_struct *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); isec = ipc_perms->security; COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = ipc_perms->key; return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); @@ -4794,6 +4868,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) { struct ipc_security_struct *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); int rc; @@ -4804,6 +4879,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) isec = msq->q_perm.security; COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, @@ -4824,11 +4900,13 @@ static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg) { struct ipc_security_struct *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); isec = msq->q_perm.security; COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, @@ -4868,6 +4946,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, struct ipc_security_struct *isec; struct msg_security_struct *msec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); int rc; @@ -4889,6 +4968,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, } COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; /* Can this process write to the queue? */ @@ -4913,6 +4993,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, struct ipc_security_struct *isec; struct msg_security_struct *msec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = task_sid(target); int rc; @@ -4920,6 +5001,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, msec = msg->security; COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; rc = avc_has_perm(sid, isec->sid, @@ -4935,6 +5017,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) { struct ipc_security_struct *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); int rc; @@ -4945,6 +5028,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) isec = shp->shm_perm.security; COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM, @@ -4965,11 +5049,13 @@ static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg) { struct ipc_security_struct *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); isec = shp->shm_perm.security; COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; return avc_has_perm(sid, isec->sid, SECCLASS_SHM, @@ -5027,6 +5113,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) { struct ipc_security_struct *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); int rc; @@ -5037,6 +5124,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) isec = sma->sem_perm.security; COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM, @@ -5057,11 +5145,13 @@ static int selinux_sem_associate(struct sem_array *sma, int semflg) { struct ipc_security_struct *isec; struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); isec = sma->sem_perm.security; COMMON_AUDIT_DATA_INIT(&ad, IPC); + ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; return avc_has_perm(sid, isec->sid, SECCLASS_SEM, diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 005a91bcb200..1931370233d7 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -47,6 +47,31 @@ struct avc_cache_stats { }; /* + * We only need this data after we have decided to send an audit message. + */ +struct selinux_late_audit_data { + u32 ssid; + u32 tsid; + u16 tclass; + u32 requested; + u32 audited; + u32 denied; + int result; +}; + +/* + * We collect this at the beginning or during an selinux security operation + */ +struct selinux_audit_data { + /* + * auditdeny is a bit tricky and unintuitive. See the + * comments in avc.c for it's meaning and usage. + */ + u32 auditdeny; + struct selinux_late_audit_data *slad; +}; + +/* * AVC operations */ diff --git a/security/smack/smack.h b/security/smack/smack.h index 2ad00657b801..4ede719922ed 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -185,6 +185,15 @@ struct smack_known { */ #define SMK_NUM_ACCESS_TYPE 5 +/* SMACK data */ +struct smack_audit_data { + const char *function; + char *subject; + char *object; + char *request; + int result; +}; + /* * Smack audit data; is empty if CONFIG_AUDIT not set * to save some stack @@ -192,6 +201,7 @@ struct smack_known { struct smk_audit_info { #ifdef CONFIG_AUDIT struct common_audit_data a; + struct smack_audit_data sad; #endif }; /* @@ -311,7 +321,16 @@ static inline void smk_ad_init(struct smk_audit_info *a, const char *func, { memset(a, 0, sizeof(*a)); a->a.type = type; - a->a.smack_audit_data.function = func; + a->a.smack_audit_data = &a->sad; + a->a.smack_audit_data->function = func; +} + +static inline void smk_ad_init_net(struct smk_audit_info *a, const char *func, + char type, struct lsm_network_audit *net) +{ + smk_ad_init(a, func, type); + memset(net, 0, sizeof(*net)); + a->a.u.net = net; } static inline void smk_ad_setfield_u_tsk(struct smk_audit_info *a, @@ -337,7 +356,7 @@ static inline void smk_ad_setfield_u_fs_path(struct smk_audit_info *a, static inline void smk_ad_setfield_u_net_sk(struct smk_audit_info *a, struct sock *sk) { - a->a.u.net.sk = sk; + a->a.u.net->sk = sk; } #else /* no AUDIT */ diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index cc7cb6edba08..c8115f7308f8 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -275,9 +275,9 @@ static inline void smack_str_from_perm(char *string, int access) static void smack_log_callback(struct audit_buffer *ab, void *a) { struct common_audit_data *ad = a; - struct smack_audit_data *sad = &ad->smack_audit_data; + struct smack_audit_data *sad = ad->smack_audit_data; audit_log_format(ab, "lsm=SMACK fn=%s action=%s", - ad->smack_audit_data.function, + ad->smack_audit_data->function, sad->result ? "denied" : "granted"); audit_log_format(ab, " subject="); audit_log_untrustedstring(ab, sad->subject); @@ -310,19 +310,19 @@ void smack_log(char *subject_label, char *object_label, int request, if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0) return; - if (a->smack_audit_data.function == NULL) - a->smack_audit_data.function = "unknown"; + sad = a->smack_audit_data; + + if (sad->function == NULL) + sad->function = "unknown"; /* end preparing the audit data */ - sad = &a->smack_audit_data; smack_str_from_perm(request_buffer, request); sad->subject = subject_label; sad->object = object_label; sad->request = request_buffer; sad->result = result; - a->lsm_pre_audit = smack_log_callback; - common_lsm_audit(a); + common_lsm_audit(a, smack_log_callback, NULL); } #else /* #ifdef CONFIG_AUDIT */ void smack_log(char *subject_label, char *object_label, int request, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index cd667b4089a5..45c32f074166 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1943,13 +1943,15 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) rcu_read_lock(); hostsp = smack_host_label(sap); if (hostsp != NULL) { - sk_lbl = SMACK_UNLABELED_SOCKET; #ifdef CONFIG_AUDIT - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET); - ad.a.u.net.family = sap->sin_family; - ad.a.u.net.dport = sap->sin_port; - ad.a.u.net.v4info.daddr = sap->sin_addr.s_addr; + struct lsm_network_audit net; + + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); + ad.a.u.net->family = sap->sin_family; + ad.a.u.net->dport = sap->sin_port; + ad.a.u.net->v4info.daddr = sap->sin_addr.s_addr; #endif + sk_lbl = SMACK_UNLABELED_SOCKET; rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE, &ad); } else { sk_lbl = SMACK_CIPSO_SOCKET; @@ -2810,8 +2812,12 @@ static int smack_unix_stream_connect(struct sock *sock, struct smk_audit_info ad; int rc = 0; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET); +#ifdef CONFIG_AUDIT + struct lsm_network_audit net; + + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); smk_ad_setfield_u_net_sk(&ad, other); +#endif if (!capable(CAP_MAC_OVERRIDE)) rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); @@ -2842,8 +2848,12 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) struct smk_audit_info ad; int rc = 0; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET); +#ifdef CONFIG_AUDIT + struct lsm_network_audit net; + + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); smk_ad_setfield_u_net_sk(&ad, other->sk); +#endif if (!capable(CAP_MAC_OVERRIDE)) rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); @@ -2990,6 +3000,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) char *csp; int rc; struct smk_audit_info ad; +#ifdef CONFIG_AUDIT + struct lsm_network_audit net; +#endif if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) return 0; @@ -3007,9 +3020,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) netlbl_secattr_destroy(&secattr); #ifdef CONFIG_AUDIT - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET); - ad.a.u.net.family = sk->sk_family; - ad.a.u.net.netif = skb->skb_iif; + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); + ad.a.u.net->family = sk->sk_family; + ad.a.u.net->netif = skb->skb_iif; ipv4_skb_to_auditdata(skb, &ad.a, NULL); #endif /* @@ -3152,6 +3165,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, char *sp; int rc; struct smk_audit_info ad; +#ifdef CONFIG_AUDIT + struct lsm_network_audit net; +#endif /* handle mapped IPv4 packets arriving via IPv6 sockets */ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) @@ -3166,9 +3182,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, netlbl_secattr_destroy(&secattr); #ifdef CONFIG_AUDIT - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET); - ad.a.u.net.family = family; - ad.a.u.net.netif = skb->skb_iif; + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); + ad.a.u.net->family = family; + ad.a.u.net->netif = skb->skb_iif; ipv4_skb_to_auditdata(skb, &ad.a, NULL); #endif /* @@ -3624,8 +3640,38 @@ struct security_operations smack_ops = { }; -static __init void init_smack_know_list(void) +static __init void init_smack_known_list(void) { + /* + * Initialize CIPSO locks + */ + spin_lock_init(&smack_known_huh.smk_cipsolock); + spin_lock_init(&smack_known_hat.smk_cipsolock); + spin_lock_init(&smack_known_star.smk_cipsolock); + spin_lock_init(&smack_known_floor.smk_cipsolock); + spin_lock_init(&smack_known_invalid.smk_cipsolock); + spin_lock_init(&smack_known_web.smk_cipsolock); + /* + * Initialize rule list locks + */ + mutex_init(&smack_known_huh.smk_rules_lock); + mutex_init(&smack_known_hat.smk_rules_lock); + mutex_init(&smack_known_floor.smk_rules_lock); + mutex_init(&smack_known_star.smk_rules_lock); + mutex_init(&smack_known_invalid.smk_rules_lock); + mutex_init(&smack_known_web.smk_rules_lock); + /* + * Initialize rule lists + */ + INIT_LIST_HEAD(&smack_known_huh.smk_rules); + INIT_LIST_HEAD(&smack_known_hat.smk_rules); + INIT_LIST_HEAD(&smack_known_star.smk_rules); + INIT_LIST_HEAD(&smack_known_floor.smk_rules); + INIT_LIST_HEAD(&smack_known_invalid.smk_rules); + INIT_LIST_HEAD(&smack_known_web.smk_rules); + /* + * Create the known labels list + */ list_add(&smack_known_huh.list, &smack_known_list); list_add(&smack_known_hat.list, &smack_known_list); list_add(&smack_known_star.list, &smack_known_list); @@ -3660,16 +3706,8 @@ static __init int smack_init(void) cred = (struct cred *) current->cred; cred->security = tsp; - /* initialize the smack_know_list */ - init_smack_know_list(); - /* - * Initialize locks - */ - spin_lock_init(&smack_known_huh.smk_cipsolock); - spin_lock_init(&smack_known_hat.smk_cipsolock); - spin_lock_init(&smack_known_star.smk_cipsolock); - spin_lock_init(&smack_known_floor.smk_cipsolock); - spin_lock_init(&smack_known_invalid.smk_cipsolock); + /* initialize the smack_known_list */ + init_smack_known_list(); /* * Register with LSM diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 5c32f36ff706..038811cb7e62 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -1614,20 +1614,6 @@ static int __init init_smk_fs(void) smk_cipso_doi(); smk_unlbl_ambient(NULL); - mutex_init(&smack_known_floor.smk_rules_lock); - mutex_init(&smack_known_hat.smk_rules_lock); - mutex_init(&smack_known_huh.smk_rules_lock); - mutex_init(&smack_known_invalid.smk_rules_lock); - mutex_init(&smack_known_star.smk_rules_lock); - mutex_init(&smack_known_web.smk_rules_lock); - - INIT_LIST_HEAD(&smack_known_floor.smk_rules); - INIT_LIST_HEAD(&smack_known_hat.smk_rules); - INIT_LIST_HEAD(&smack_known_huh.smk_rules); - INIT_LIST_HEAD(&smack_known_invalid.smk_rules); - INIT_LIST_HEAD(&smack_known_star.smk_rules); - INIT_LIST_HEAD(&smack_known_web.smk_rules); - return err; } diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 88168044375f..5ca0939e4223 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -2,8 +2,8 @@ config SND_TEA575X tristate - depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2 - default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2 + depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2 || RADIO_MAXIRADIO + default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2 || RADIO_MAXIRADIO menuconfig SND_PCI bool "PCI sound devices" diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 91c985599d32..40b2ad1bb1cd 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -35,7 +35,6 @@ source "sound/soc/blackfin/Kconfig" source "sound/soc/davinci/Kconfig" source "sound/soc/ep93xx/Kconfig" source "sound/soc/fsl/Kconfig" -source "sound/soc/imx/Kconfig" source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" @@ -48,9 +47,13 @@ source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" +source "sound/soc/ux500/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" +# generic frame-work +source "sound/soc/generic/Kconfig" + endif # SND_SOC diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 2feaf376e94b..70990f4017f4 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -6,13 +6,13 @@ obj-$(CONFIG_SND_SOC_DMAENGINE_PCM) += snd-soc-dmaengine-pcm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ +obj-$(CONFIG_SND_SOC) += generic/ obj-$(CONFIG_SND_SOC) += atmel/ obj-$(CONFIG_SND_SOC) += au1x/ obj-$(CONFIG_SND_SOC) += blackfin/ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += ep93xx/ obj-$(CONFIG_SND_SOC) += fsl/ -obj-$(CONFIG_SND_SOC) += imx/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += mid-x86/ obj-$(CONFIG_SND_SOC) += mxs/ @@ -25,3 +25,4 @@ obj-$(CONFIG_SND_SOC) += s6000/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ +obj-$(CONFIG_SND_SOC) += ux500/ diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c index b39ad356b92b..7dbeef1099b4 100644 --- a/sound/soc/blackfin/bf5xx-ssm2602.c +++ b/sound/soc/blackfin/bf5xx-ssm2602.c @@ -44,16 +44,8 @@ static struct snd_soc_card bf5xx_ssm2602; -static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int bf5xx_ssm2602_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - unsigned int clk = 0; - int ret = 0; - - pr_debug("%s rate %d format %x\n", __func__, params_rate(params), - params_format(params)); /* * If you are using a crystal source which frequency is not 12MHz * then modify the below case statement with frequency of the crystal. @@ -61,31 +53,10 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, * If you are using the SPORT to generate clocking then this is * where to do it. */ - - switch (params_rate(params)) { - case 8000: - case 16000: - case 48000: - case 96000: - case 11025: - case 22050: - case 44100: - clk = 12000000; - break; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, SSM2602_SYSCLK, clk, + return snd_soc_dai_set_sysclk(rtd->codec_dai, SSM2602_SYSCLK, 12000000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - return 0; } -static struct snd_soc_ops bf5xx_ssm2602_ops = { - .hw_params = bf5xx_ssm2602_hw_params, -}; - /* CODEC is master for BCLK and LRC in this configuration. */ #define BF5XX_SSM2602_DAIFMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ SND_SOC_DAIFMT_CBM_CFM) @@ -98,7 +69,7 @@ static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = { .codec_dai_name = "ssm2602-hifi", .platform_name = "bfin-i2s-pcm-audio", .codec_name = "ssm2602.0-001b", - .ops = &bf5xx_ssm2602_ops, + .init = bf5xx_ssm2602_dai_init, .dai_fmt = BF5XX_SSM2602_DAIFMT, }, { @@ -108,7 +79,7 @@ static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = { .codec_dai_name = "ssm2602-hifi", .platform_name = "bfin-i2s-pcm-audio", .codec_name = "ssm2602.0-001b", - .ops = &bf5xx_ssm2602_ops, + .init = bf5xx_ssm2602_dai_init, .dai_fmt = BF5XX_SSM2602_DAIFMT, }, }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6508e8b790bb..22c686444633 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -29,6 +29,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ALC5632 if I2C select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51 if I2C + select SND_SOC_CS42L52 if I2C select SND_SOC_CS42L73 if I2C select SND_SOC_CS4270 if I2C select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI @@ -37,11 +38,14 @@ config SND_SOC_ALL_CODECS select SND_SOC_DFBMCS320 select SND_SOC_JZ4740_CODEC select SND_SOC_LM4857 if I2C + select SND_SOC_LM49453 if I2C select SND_SOC_MAX98088 if I2C select SND_SOC_MAX98095 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9768 if I2C select SND_SOC_MAX9877 if I2C + select SND_SOC_MC13783 if MFD_MC13XXX + select SND_SOC_ML26124 if I2C select SND_SOC_PCM3008 select SND_SOC_RT5631 if I2C select SND_SOC_SGTL5000 if I2C @@ -57,7 +61,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C select SND_SOC_TWL4030 if TWL4030_CORE - select SND_SOC_TWL6040 if TWL4030_CORE + select SND_SOC_TWL6040 if TWL6040_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C select SND_SOC_WL1273 if MFD_WL1273_CORE @@ -181,6 +185,9 @@ config SND_SOC_CQ0093VC config SND_SOC_CS42L51 tristate +config SND_SOC_CS42L52 + tristate + config SND_SOC_CS42L73 tristate @@ -217,6 +224,9 @@ config SND_SOC_DFBMCS320 config SND_SOC_DMIC tristate +config SND_SOC_LM49453 + tristate + config SND_SOC_MAX98088 tristate @@ -276,7 +286,6 @@ config SND_SOC_TWL4030 tristate config SND_SOC_TWL6040 - select TWL6040_CORE tristate config SND_SOC_UDA134X @@ -436,5 +445,11 @@ config SND_SOC_MAX9768 config SND_SOC_MAX9877 tristate +config SND_SOC_MC13783 + tristate + +config SND_SOC_ML26124 + tristate + config SND_SOC_TPA6130A2 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 6662eb0cdcc0..a9663e9c375b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -15,6 +15,7 @@ snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o +snd-soc-cs42l52-objs := cs42l52.o snd-soc-cs42l73-objs := cs42l73.o snd-soc-cs4270-objs := cs4270.o snd-soc-cs4271-objs := cs4271.o @@ -25,10 +26,13 @@ snd-soc-dmic-objs := dmic.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o +snd-soc-lm49453-objs := lm49453.o snd-soc-max9768-objs := max9768.o snd-soc-max98088-objs := max98088.o snd-soc-max98095-objs := max98095.o snd-soc-max9850-objs := max9850.o +snd-soc-mc13783-objs := mc13783.o +snd-soc-ml26124-objs := ml26124.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-rt5631-objs := rt5631.o snd-soc-sgtl5000-objs := sgtl5000.o @@ -121,6 +125,7 @@ obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o +obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o @@ -128,13 +133,16 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o +obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o -obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o +obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o +obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o +obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 1bbad4c16d28..2023c749f232 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -26,13 +26,11 @@ static int ac97_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; - return snd_ac97_set_rate(codec->ac97, reg, runtime->rate); + return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate); } #define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 12e3b4118557..c67b50d8b317 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -162,9 +162,7 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int word_len = 0; - - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; /* bit size */ switch (params_format(params)) { diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index a4a6bef2c0bb..13e62be4f990 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -245,9 +245,7 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int word_len = 0, master_rate = 0; - - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); /* bit size */ diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 78e9ce48bb99..3d50fc8646b6 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -258,8 +258,7 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec, static int adau1701_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; snd_pcm_format_t format; unsigned int val; diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index ceb96ecf5588..31d4483245d0 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -88,8 +88,7 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; int val = 0; /* set the IEC958 bits: consumer mode, no copyright bit */ diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 838ae8b22b50..618fdc30f73e 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -262,8 +262,7 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec); u8 mode2 = snd_soc_read(codec, AK4535_MODE2) & ~(0x3 << 5); int rate = params_rate(params), fs = 256; diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index c4d165a4bddf..543a12f471be 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -296,8 +296,7 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec); int rate = params_rate(params), fs = 256; u8 mode2; @@ -517,67 +516,24 @@ static int ak4641_resume(struct snd_soc_codec *codec) static int ak4641_probe(struct snd_soc_codec *codec) { - struct ak4641_platform_data *pdata = codec->dev->platform_data; int ret; - - if (pdata) { - if (gpio_is_valid(pdata->gpio_power)) { - ret = gpio_request_one(pdata->gpio_power, - GPIOF_OUT_INIT_LOW, "ak4641 power"); - if (ret) - goto err_out; - } - if (gpio_is_valid(pdata->gpio_npdn)) { - ret = gpio_request_one(pdata->gpio_npdn, - GPIOF_OUT_INIT_LOW, "ak4641 npdn"); - if (ret) - goto err_gpio; - - udelay(1); /* > 150 ns */ - gpio_set_value(pdata->gpio_npdn, 1); - } - } - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); if (ret != 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); - goto err_register; + return ret; } /* power on device */ ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; - -err_register: - if (pdata) { - if (gpio_is_valid(pdata->gpio_power)) - gpio_set_value(pdata->gpio_power, 0); - if (gpio_is_valid(pdata->gpio_npdn)) - gpio_free(pdata->gpio_npdn); - } -err_gpio: - if (pdata && gpio_is_valid(pdata->gpio_power)) - gpio_free(pdata->gpio_power); -err_out: - return ret; } static int ak4641_remove(struct snd_soc_codec *codec) { - struct ak4641_platform_data *pdata = codec->dev->platform_data; - ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF); - if (pdata) { - if (gpio_is_valid(pdata->gpio_power)) { - gpio_set_value(pdata->gpio_power, 0); - gpio_free(pdata->gpio_power); - } - if (gpio_is_valid(pdata->gpio_npdn)) - gpio_free(pdata->gpio_npdn); - } return 0; } @@ -604,6 +560,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = { static int __devinit ak4641_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct ak4641_platform_data *pdata = i2c->dev.platform_data; struct ak4641_priv *ak4641; int ret; @@ -612,16 +569,62 @@ static int __devinit ak4641_i2c_probe(struct i2c_client *i2c, if (!ak4641) return -ENOMEM; + if (pdata) { + if (gpio_is_valid(pdata->gpio_power)) { + ret = gpio_request_one(pdata->gpio_power, + GPIOF_OUT_INIT_LOW, "ak4641 power"); + if (ret) + goto err_out; + } + if (gpio_is_valid(pdata->gpio_npdn)) { + ret = gpio_request_one(pdata->gpio_npdn, + GPIOF_OUT_INIT_LOW, "ak4641 npdn"); + if (ret) + goto err_gpio; + + udelay(1); /* > 150 ns */ + gpio_set_value(pdata->gpio_npdn, 1); + } + } + i2c_set_clientdata(i2c, ak4641); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641, ak4641_dai, ARRAY_SIZE(ak4641_dai)); + if (ret != 0) + goto err_gpio2; + + return 0; + +err_gpio2: + if (pdata) { + if (gpio_is_valid(pdata->gpio_power)) + gpio_set_value(pdata->gpio_power, 0); + if (gpio_is_valid(pdata->gpio_npdn)) + gpio_free(pdata->gpio_npdn); + } +err_gpio: + if (pdata && gpio_is_valid(pdata->gpio_power)) + gpio_free(pdata->gpio_power); +err_out: return ret; } static int __devexit ak4641_i2c_remove(struct i2c_client *i2c) { + struct ak4641_platform_data *pdata = i2c->dev.platform_data; + snd_soc_unregister_codec(&i2c->dev); + + if (pdata) { + if (gpio_is_valid(pdata->gpio_power)) { + gpio_set_value(pdata->gpio_power, 0); + gpio_free(pdata->gpio_power); + } + if (gpio_is_valid(pdata->gpio_npdn)) + gpio_free(pdata->gpio_npdn); + } + return 0; } @@ -641,23 +644,7 @@ static struct i2c_driver ak4641_i2c_driver = { .id_table = ak4641_i2c_id, }; -static int __init ak4641_modinit(void) -{ - int ret; - - ret = i2c_add_driver(&ak4641_i2c_driver); - if (ret != 0) - pr_err("Failed to register AK4641 I2C driver: %d\n", ret); - - return ret; -} -module_init(ak4641_modinit); - -static void __exit ak4641_exit(void) -{ - i2c_del_driver(&ak4641_i2c_driver); -} -module_exit(ak4641_exit); +module_i2c_driver(ak4641_i2c_driver); MODULE_DESCRIPTION("SoC AK4641 driver"); MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>"); diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index d47b62ddb210..1960478ce6bb 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -705,8 +705,7 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai, static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); int coeff, rate; u16 iface; @@ -1084,25 +1083,7 @@ static struct i2c_driver alc5623_i2c_driver = { .id_table = alc5623_i2c_table, }; -static int __init alc5623_modinit(void) -{ - int ret; - - ret = i2c_add_driver(&alc5623_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "%s: can't add i2c driver", __func__); - return ret; - } - - return ret; -} -module_init(alc5623_modinit); - -static void __exit alc5623_modexit(void) -{ - i2c_del_driver(&alc5623_i2c_driver); -} -module_exit(alc5623_modexit); +module_i2c_driver(alc5623_i2c_driver); MODULE_DESCRIPTION("ASoC alc5621/2/3 driver"); MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index e2111e0ccad7..7dd02420b36d 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -861,8 +861,7 @@ static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai, static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; int coeff, rate; u16 iface; @@ -1131,7 +1130,7 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, alc5632); - alc5632->regmap = regmap_init_i2c(client, &alc5632_regmap); + alc5632->regmap = devm_regmap_init_i2c(client, &alc5632_regmap); if (IS_ERR(alc5632->regmap)) { ret = PTR_ERR(alc5632->regmap); dev_err(&client->dev, "regmap_init() failed: %d\n", ret); @@ -1143,7 +1142,6 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client, if (ret1 != 0 || ret2 != 0) { dev_err(&client->dev, "Failed to read chip ID: ret1=%d, ret2=%d\n", ret1, ret2); - regmap_exit(alc5632->regmap); return -EIO; } @@ -1152,14 +1150,12 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client, if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) { dev_err(&client->dev, "Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2); - regmap_exit(alc5632->regmap); return -EINVAL; } ret = alc5632_reset(alc5632->regmap); if (ret < 0) { dev_err(&client->dev, "Failed to issue reset\n"); - regmap_exit(alc5632->regmap); return ret; } @@ -1177,7 +1173,6 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client, if (ret < 0) { dev_err(&client->dev, "Failed to register codec: %d\n", ret); - regmap_exit(alc5632->regmap); return ret; } @@ -1186,9 +1181,7 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client, static __devexit int alc5632_i2c_remove(struct i2c_client *client) { - struct alc5632_priv *alc5632 = i2c_get_clientdata(client); snd_soc_unregister_codec(&client->dev); - regmap_exit(alc5632->regmap); return 0; } @@ -1209,25 +1202,7 @@ static struct i2c_driver alc5632_i2c_driver = { .id_table = alc5632_i2c_table, }; -static int __init alc5632_modinit(void) -{ - int ret; - - ret = i2c_add_driver(&alc5632_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "%s: can't add i2c driver", __func__); - return ret; - } - - return ret; -} -module_init(alc5632_modinit); - -static void __exit alc5632_modexit(void) -{ - i2c_del_driver(&alc5632_i2c_driver); -} -module_exit(alc5632_modexit); +module_i2c_driver(alc5632_i2c_driver); MODULE_DESCRIPTION("ASoC ALC5632 driver"); MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>"); diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 1d672f528662..047917f0b8ae 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -307,8 +307,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); int ret; unsigned int i; @@ -600,10 +599,12 @@ static int cs4270_soc_suspend(struct snd_soc_codec *codec) static int cs4270_soc_resume(struct snd_soc_codec *codec) { struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); - int reg; + int reg, ret; - regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), - cs4270->supplies); + ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), + cs4270->supplies); + if (ret != 0) + return ret; /* In case the device was put to hard reset during sleep, we need to * wait 500ns here before any I2C communication. */ diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index bf7141280a74..9eb01d7d58a3 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -318,8 +318,7 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); int i, ret; unsigned int ratio, val; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index a8bf588e8740..091d0193f507 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -141,15 +141,15 @@ static const struct soc_enum cs42l51_chan_mix = static const struct snd_kcontrol_new cs42l51_snd_controls[] = { SOC_DOUBLE_R_SX_TLV("PCM Playback Volume", CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, - 7, 0xffffff99, 0x18, adc_pcm_tlv), + 6, 0x19, 0x7F, adc_pcm_tlv), SOC_DOUBLE_R("PCM Playback Switch", CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1), SOC_DOUBLE_R_SX_TLV("Analog Playback Volume", CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL, - 8, 0xffffff19, 0x18, aout_tlv), + 0, 0x34, 0xE4, aout_tlv), SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, - 7, 0xffffff99, 0x18, adc_pcm_tlv), + 6, 0x19, 0x7F, adc_pcm_tlv), SOC_DOUBLE_R("ADC Mixer Switch", CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1), SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0), @@ -356,8 +356,7 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); int ret; unsigned int i; diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c new file mode 100644 index 000000000000..a7109413aef1 --- /dev/null +++ b/sound/soc/codecs/cs42l52.c @@ -0,0 +1,1295 @@ +/* + * cs42l52.c -- CS42L52 ALSA SoC audio driver + * + * Copyright 2012 CirrusLogic, Inc. + * + * Author: Georgi Vlaev <joe@nucleusys.com> + * Author: Brian Austin <brian.austin@cirrus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/cs42l52.h> +#include "cs42l52.h" + +struct sp_config { + u8 spc, format, spfs; + u32 srate; +}; + +struct cs42l52_private { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct device *dev; + struct sp_config config; + struct cs42l52_platform_data pdata; + u32 sysclk; + u8 mclksel; + u32 mclk; + u8 flags; +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + struct input_dev *beep; + struct work_struct beep_work; + int beep_rate; +#endif +}; + +static const struct reg_default cs42l52_reg_defaults[] = { + { CS42L52_PWRCTL1, 0x9F }, /* r02 PWRCTL 1 */ + { CS42L52_PWRCTL2, 0x07 }, /* r03 PWRCTL 2 */ + { CS42L52_PWRCTL3, 0xFF }, /* r04 PWRCTL 3 */ + { CS42L52_CLK_CTL, 0xA0 }, /* r05 Clocking Ctl */ + { CS42L52_IFACE_CTL1, 0x00 }, /* r06 Interface Ctl 1 */ + { CS42L52_ADC_PGA_A, 0x80 }, /* r08 Input A Select */ + { CS42L52_ADC_PGA_B, 0x80 }, /* r09 Input B Select */ + { CS42L52_ANALOG_HPF_CTL, 0xA5 }, /* r0A Analog HPF Ctl */ + { CS42L52_ADC_HPF_FREQ, 0x00 }, /* r0B ADC HPF Corner Freq */ + { CS42L52_ADC_MISC_CTL, 0x00 }, /* r0C Misc. ADC Ctl */ + { CS42L52_PB_CTL1, 0x60 }, /* r0D Playback Ctl 1 */ + { CS42L52_MISC_CTL, 0x02 }, /* r0E Misc. Ctl */ + { CS42L52_PB_CTL2, 0x00 }, /* r0F Playback Ctl 2 */ + { CS42L52_MICA_CTL, 0x00 }, /* r10 MICA Amp Ctl */ + { CS42L52_MICB_CTL, 0x00 }, /* r11 MICB Amp Ctl */ + { CS42L52_PGAA_CTL, 0x00 }, /* r12 PGAA Vol, Misc. */ + { CS42L52_PGAB_CTL, 0x00 }, /* r13 PGAB Vol, Misc. */ + { CS42L52_PASSTHRUA_VOL, 0x00 }, /* r14 Bypass A Vol */ + { CS42L52_PASSTHRUB_VOL, 0x00 }, /* r15 Bypass B Vol */ + { CS42L52_ADCA_VOL, 0x00 }, /* r16 ADCA Volume */ + { CS42L52_ADCB_VOL, 0x00 }, /* r17 ADCB Volume */ + { CS42L52_ADCA_MIXER_VOL, 0x80 }, /* r18 ADCA Mixer Volume */ + { CS42L52_ADCB_MIXER_VOL, 0x80 }, /* r19 ADCB Mixer Volume */ + { CS42L52_PCMA_MIXER_VOL, 0x00 }, /* r1A PCMA Mixer Volume */ + { CS42L52_PCMB_MIXER_VOL, 0x00 }, /* r1B PCMB Mixer Volume */ + { CS42L52_BEEP_FREQ, 0x00 }, /* r1C Beep Freq on Time */ + { CS42L52_BEEP_VOL, 0x00 }, /* r1D Beep Volume off Time */ + { CS42L52_BEEP_TONE_CTL, 0x00 }, /* r1E Beep Tone Cfg. */ + { CS42L52_TONE_CTL, 0x00 }, /* r1F Tone Ctl */ + { CS42L52_MASTERA_VOL, 0x88 }, /* r20 Master A Volume */ + { CS42L52_MASTERB_VOL, 0x00 }, /* r21 Master B Volume */ + { CS42L52_HPA_VOL, 0x00 }, /* r22 Headphone A Volume */ + { CS42L52_HPB_VOL, 0x00 }, /* r23 Headphone B Volume */ + { CS42L52_SPKA_VOL, 0x00 }, /* r24 Speaker A Volume */ + { CS42L52_SPKB_VOL, 0x00 }, /* r25 Speaker B Volume */ + { CS42L52_ADC_PCM_MIXER, 0x00 }, /* r26 Channel Mixer and Swap */ + { CS42L52_LIMITER_CTL1, 0x00 }, /* r27 Limit Ctl 1 Thresholds */ + { CS42L52_LIMITER_CTL2, 0x7F }, /* r28 Limit Ctl 2 Release Rate */ + { CS42L52_LIMITER_AT_RATE, 0xC0 }, /* r29 Limiter Attack Rate */ + { CS42L52_ALC_CTL, 0x00 }, /* r2A ALC Ctl 1 Attack Rate */ + { CS42L52_ALC_RATE, 0x3F }, /* r2B ALC Release Rate */ + { CS42L52_ALC_THRESHOLD, 0x3f }, /* r2C ALC Thresholds */ + { CS42L52_NOISE_GATE_CTL, 0x00 }, /* r2D Noise Gate Ctl */ + { CS42L52_CLK_STATUS, 0x00 }, /* r2E Overflow and Clock Status */ + { CS42L52_BATT_COMPEN, 0x00 }, /* r2F battery Compensation */ + { CS42L52_BATT_LEVEL, 0x00 }, /* r30 VP Battery Level */ + { CS42L52_SPK_STATUS, 0x00 }, /* r31 Speaker Status */ + { CS42L52_TEM_CTL, 0x3B }, /* r32 Temp Ctl */ + { CS42L52_THE_FOLDBACK, 0x00 }, /* r33 Foldback */ +}; + +static bool cs42l52_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L52_CHIP: + case CS42L52_PWRCTL1: + case CS42L52_PWRCTL2: + case CS42L52_PWRCTL3: + case CS42L52_CLK_CTL: + case CS42L52_IFACE_CTL1: + case CS42L52_IFACE_CTL2: + case CS42L52_ADC_PGA_A: + case CS42L52_ADC_PGA_B: + case CS42L52_ANALOG_HPF_CTL: + case CS42L52_ADC_HPF_FREQ: + case CS42L52_ADC_MISC_CTL: + case CS42L52_PB_CTL1: + case CS42L52_MISC_CTL: + case CS42L52_PB_CTL2: + case CS42L52_MICA_CTL: + case CS42L52_MICB_CTL: + case CS42L52_PGAA_CTL: + case CS42L52_PGAB_CTL: + case CS42L52_PASSTHRUA_VOL: + case CS42L52_PASSTHRUB_VOL: + case CS42L52_ADCA_VOL: + case CS42L52_ADCB_VOL: + case CS42L52_ADCA_MIXER_VOL: + case CS42L52_ADCB_MIXER_VOL: + case CS42L52_PCMA_MIXER_VOL: + case CS42L52_PCMB_MIXER_VOL: + case CS42L52_BEEP_FREQ: + case CS42L52_BEEP_VOL: + case CS42L52_BEEP_TONE_CTL: + case CS42L52_TONE_CTL: + case CS42L52_MASTERA_VOL: + case CS42L52_MASTERB_VOL: + case CS42L52_HPA_VOL: + case CS42L52_HPB_VOL: + case CS42L52_SPKA_VOL: + case CS42L52_SPKB_VOL: + case CS42L52_ADC_PCM_MIXER: + case CS42L52_LIMITER_CTL1: + case CS42L52_LIMITER_CTL2: + case CS42L52_LIMITER_AT_RATE: + case CS42L52_ALC_CTL: + case CS42L52_ALC_RATE: + case CS42L52_ALC_THRESHOLD: + case CS42L52_NOISE_GATE_CTL: + case CS42L52_CLK_STATUS: + case CS42L52_BATT_COMPEN: + case CS42L52_BATT_LEVEL: + case CS42L52_SPK_STATUS: + case CS42L52_TEM_CTL: + case CS42L52_THE_FOLDBACK: + case CS42L52_CHARGE_PUMP: + return true; + default: + return false; + } +} + +static bool cs42l52_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L52_IFACE_CTL2: + case CS42L52_CLK_STATUS: + case CS42L52_BATT_LEVEL: + case CS42L52_SPK_STATUS: + case CS42L52_CHARGE_PUMP: + return 1; + default: + return 0; + } +} + +static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0); + +static DECLARE_TLV_DB_SCALE(hpd_tlv, -9600, 50, 1); + +static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0); + +static DECLARE_TLV_DB_SCALE(mic_tlv, 1600, 100, 0); + +static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0); + +static const unsigned int limiter_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0), +}; + +static const char * const cs42l52_adca_text[] = { + "Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"}; + +static const char * const cs42l52_adcb_text[] = { + "Input1B", "Input2B", "Input3B", "Input4B", "PGA Input Right"}; + +static const struct soc_enum adca_enum = + SOC_ENUM_SINGLE(CS42L52_ADC_PGA_A, 5, + ARRAY_SIZE(cs42l52_adca_text), cs42l52_adca_text); + +static const struct soc_enum adcb_enum = + SOC_ENUM_SINGLE(CS42L52_ADC_PGA_B, 5, + ARRAY_SIZE(cs42l52_adcb_text), cs42l52_adcb_text); + +static const struct snd_kcontrol_new adca_mux = + SOC_DAPM_ENUM("Left ADC Input Capture Mux", adca_enum); + +static const struct snd_kcontrol_new adcb_mux = + SOC_DAPM_ENUM("Right ADC Input Capture Mux", adcb_enum); + +static const char * const mic_bias_level_text[] = { + "0.5 +VA", "0.6 +VA", "0.7 +VA", + "0.8 +VA", "0.83 +VA", "0.91 +VA" +}; + +static const struct soc_enum mic_bias_level_enum = + SOC_ENUM_SINGLE(CS42L52_IFACE_CTL1, 0, + ARRAY_SIZE(mic_bias_level_text), mic_bias_level_text); + +static const char * const cs42l52_mic_text[] = { "Single", "Differential" }; + +static const struct soc_enum mica_enum = + SOC_ENUM_SINGLE(CS42L52_MICA_CTL, 5, + ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text); + +static const struct soc_enum micb_enum = + SOC_ENUM_SINGLE(CS42L52_MICB_CTL, 5, + ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text); + +static const struct snd_kcontrol_new mica_mux = + SOC_DAPM_ENUM("Left Mic Input Capture Mux", mica_enum); + +static const struct snd_kcontrol_new micb_mux = + SOC_DAPM_ENUM("Right Mic Input Capture Mux", micb_enum); + +static const char * const digital_output_mux_text[] = {"ADC", "DSP"}; + +static const struct soc_enum digital_output_mux_enum = + SOC_ENUM_SINGLE(CS42L52_ADC_MISC_CTL, 6, + ARRAY_SIZE(digital_output_mux_text), + digital_output_mux_text); + +static const struct snd_kcontrol_new digital_output_mux = + SOC_DAPM_ENUM("Digital Output Mux", digital_output_mux_enum); + +static const char * const hp_gain_num_text[] = { + "0.3959", "0.4571", "0.5111", "0.6047", + "0.7099", "0.8399", "1.000", "1.1430" +}; + +static const struct soc_enum hp_gain_enum = + SOC_ENUM_SINGLE(CS42L52_PB_CTL1, 4, + ARRAY_SIZE(hp_gain_num_text), hp_gain_num_text); + +static const char * const beep_pitch_text[] = { + "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5", + "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7" +}; + +static const struct soc_enum beep_pitch_enum = + SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 4, + ARRAY_SIZE(beep_pitch_text), beep_pitch_text); + +static const char * const beep_ontime_text[] = { + "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s", + "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s", + "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s" +}; + +static const struct soc_enum beep_ontime_enum = + SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 0, + ARRAY_SIZE(beep_ontime_text), beep_ontime_text); + +static const char * const beep_offtime_text[] = { + "1.23 s", "2.58 s", "3.90 s", "5.20 s", + "6.60 s", "8.05 s", "9.35 s", "10.80 s" +}; + +static const struct soc_enum beep_offtime_enum = + SOC_ENUM_SINGLE(CS42L52_BEEP_VOL, 5, + ARRAY_SIZE(beep_offtime_text), beep_offtime_text); + +static const char * const beep_config_text[] = { + "Off", "Single", "Multiple", "Continuous" +}; + +static const struct soc_enum beep_config_enum = + SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 6, + ARRAY_SIZE(beep_config_text), beep_config_text); + +static const char * const beep_bass_text[] = { + "50 Hz", "100 Hz", "200 Hz", "250 Hz" +}; + +static const struct soc_enum beep_bass_enum = + SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 1, + ARRAY_SIZE(beep_bass_text), beep_bass_text); + +static const char * const beep_treble_text[] = { + "5 kHz", "7 kHz", "10 kHz", " 15 kHz" +}; + +static const struct soc_enum beep_treble_enum = + SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 3, + ARRAY_SIZE(beep_treble_text), beep_treble_text); + +static const char * const ng_threshold_text[] = { + "-34dB", "-37dB", "-40dB", "-43dB", + "-46dB", "-52dB", "-58dB", "-64dB" +}; + +static const struct soc_enum ng_threshold_enum = + SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 2, + ARRAY_SIZE(ng_threshold_text), ng_threshold_text); + +static const char * const cs42l52_ng_delay_text[] = { + "50ms", "100ms", "150ms", "200ms"}; + +static const struct soc_enum ng_delay_enum = + SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 0, + ARRAY_SIZE(cs42l52_ng_delay_text), cs42l52_ng_delay_text); + +static const char * const cs42l52_ng_type_text[] = { + "Apply Specific", "Apply All" +}; + +static const struct soc_enum ng_type_enum = + SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 6, + ARRAY_SIZE(cs42l52_ng_type_text), cs42l52_ng_type_text); + +static const char * const left_swap_text[] = { + "Left", "LR 2", "Right"}; + +static const char * const right_swap_text[] = { + "Right", "LR 2", "Left"}; + +static const unsigned int swap_values[] = { 0, 1, 3 }; + +static const struct soc_enum adca_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 2, 1, + ARRAY_SIZE(left_swap_text), + left_swap_text, + swap_values); + +static const struct snd_kcontrol_new adca_mixer = + SOC_DAPM_ENUM("Route", adca_swap_enum); + +static const struct soc_enum pcma_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 6, 1, + ARRAY_SIZE(left_swap_text), + left_swap_text, + swap_values); + +static const struct snd_kcontrol_new pcma_mixer = + SOC_DAPM_ENUM("Route", pcma_swap_enum); + +static const struct soc_enum adcb_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 0, 1, + ARRAY_SIZE(right_swap_text), + right_swap_text, + swap_values); + +static const struct snd_kcontrol_new adcb_mixer = + SOC_DAPM_ENUM("Route", adcb_swap_enum); + +static const struct soc_enum pcmb_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 4, 1, + ARRAY_SIZE(right_swap_text), + right_swap_text, + swap_values); + +static const struct snd_kcontrol_new pcmb_mixer = + SOC_DAPM_ENUM("Route", pcmb_swap_enum); + + +static const struct snd_kcontrol_new passthrul_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 6, 1, 0); + +static const struct snd_kcontrol_new passthrur_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 7, 1, 0); + +static const struct snd_kcontrol_new spkl_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 0, 1, 1); + +static const struct snd_kcontrol_new spkr_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 2, 1, 1); + +static const struct snd_kcontrol_new hpl_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 4, 1, 1); + +static const struct snd_kcontrol_new hpr_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 6, 1, 1); + +static const struct snd_kcontrol_new cs42l52_snd_controls[] = { + + SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L52_MASTERA_VOL, + CS42L52_MASTERB_VOL, 0, 0x34, 0xE4, hl_tlv), + + SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L52_HPA_VOL, + CS42L52_HPB_VOL, 0, 0x34, 0xCC, hpd_tlv), + + SOC_ENUM("Headphone Analog Gain", hp_gain_enum), + + SOC_DOUBLE_R_SX_TLV("Speaker Volume", CS42L52_SPKA_VOL, + CS42L52_SPKB_VOL, 7, 0x1, 0xff, hl_tlv), + + SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL, + CS42L52_PASSTHRUB_VOL, 6, 0x18, 0x90, pga_tlv), + + SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0), + + SOC_DOUBLE_R_TLV("MIC Gain Volume", CS42L52_MICA_CTL, + CS42L52_MICB_CTL, 0, 0x10, 0, mic_tlv), + + SOC_ENUM("MIC Bias Level", mic_bias_level_enum), + + SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L52_ADCA_VOL, + CS42L52_ADCB_VOL, 7, 0x80, 0xA0, ipd_tlv), + SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", + CS42L52_ADCA_MIXER_VOL, CS42L52_ADCB_MIXER_VOL, + 6, 0x7f, 0x19, ipd_tlv), + + SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0), + + SOC_DOUBLE_R("ADC Mixer Switch", CS42L52_ADCA_MIXER_VOL, + CS42L52_ADCB_MIXER_VOL, 7, 1, 1), + + SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L52_PGAA_CTL, + CS42L52_PGAB_CTL, 0, 0x28, 0x30, pga_tlv), + + SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume", + CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, + 6, 0x7f, 0x19, hl_tlv), + SOC_DOUBLE_R("PCM Mixer Switch", + CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, 7, 1, 1), + + SOC_ENUM("Beep Config", beep_config_enum), + SOC_ENUM("Beep Pitch", beep_pitch_enum), + SOC_ENUM("Beep on Time", beep_ontime_enum), + SOC_ENUM("Beep off Time", beep_offtime_enum), + SOC_SINGLE_TLV("Beep Volume", CS42L52_BEEP_VOL, 0, 0x1f, 0x07, hl_tlv), + SOC_SINGLE("Beep Mixer Switch", CS42L52_BEEP_TONE_CTL, 5, 1, 1), + SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum), + SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum), + + SOC_SINGLE("Tone Control Switch", CS42L52_BEEP_TONE_CTL, 0, 1, 1), + SOC_SINGLE_TLV("Treble Gain Volume", + CS42L52_TONE_CTL, 4, 15, 1, hl_tlv), + SOC_SINGLE_TLV("Bass Gain Volume", + CS42L52_TONE_CTL, 0, 15, 1, hl_tlv), + + /* Limiter */ + SOC_SINGLE_TLV("Limiter Max Threshold Volume", + CS42L52_LIMITER_CTL1, 5, 7, 0, limiter_tlv), + SOC_SINGLE_TLV("Limiter Cushion Threshold Volume", + CS42L52_LIMITER_CTL1, 2, 7, 0, limiter_tlv), + SOC_SINGLE_TLV("Limiter Release Rate Volume", + CS42L52_LIMITER_CTL2, 0, 63, 0, limiter_tlv), + SOC_SINGLE_TLV("Limiter Attack Rate Volume", + CS42L52_LIMITER_AT_RATE, 0, 63, 0, limiter_tlv), + + SOC_SINGLE("Limiter SR Switch", CS42L52_LIMITER_CTL1, 1, 1, 0), + SOC_SINGLE("Limiter ZC Switch", CS42L52_LIMITER_CTL1, 0, 1, 0), + SOC_SINGLE("Limiter Switch", CS42L52_LIMITER_CTL2, 7, 1, 0), + + /* ALC */ + SOC_SINGLE_TLV("ALC Attack Rate Volume", CS42L52_ALC_CTL, + 0, 63, 0, limiter_tlv), + SOC_SINGLE_TLV("ALC Release Rate Volume", CS42L52_ALC_RATE, + 0, 63, 0, limiter_tlv), + SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L52_ALC_THRESHOLD, + 5, 7, 0, limiter_tlv), + SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L52_ALC_THRESHOLD, + 2, 7, 0, limiter_tlv), + + SOC_DOUBLE_R("ALC SR Capture Switch", CS42L52_PGAA_CTL, + CS42L52_PGAB_CTL, 7, 1, 1), + SOC_DOUBLE_R("ALC ZC Capture Switch", CS42L52_PGAA_CTL, + CS42L52_PGAB_CTL, 6, 1, 1), + SOC_DOUBLE("ALC Capture Switch", CS42L52_ALC_CTL, 6, 7, 1, 0), + + /* Noise gate */ + SOC_ENUM("NG Type Switch", ng_type_enum), + SOC_SINGLE("NG Enable Switch", CS42L52_NOISE_GATE_CTL, 6, 1, 0), + SOC_SINGLE("NG Boost Switch", CS42L52_NOISE_GATE_CTL, 5, 1, 1), + SOC_ENUM("NG Threshold", ng_threshold_enum), + SOC_ENUM("NG Delay", ng_delay_enum), + + SOC_DOUBLE("HPF Switch", CS42L52_ANALOG_HPF_CTL, 5, 7, 1, 0), + + SOC_DOUBLE("Analog SR Switch", CS42L52_ANALOG_HPF_CTL, 1, 3, 1, 1), + SOC_DOUBLE("Analog ZC Switch", CS42L52_ANALOG_HPF_CTL, 0, 2, 1, 1), + SOC_SINGLE("Digital SR Switch", CS42L52_MISC_CTL, 1, 1, 0), + SOC_SINGLE("Digital ZC Switch", CS42L52_MISC_CTL, 0, 1, 0), + SOC_SINGLE("Deemphasis Switch", CS42L52_MISC_CTL, 2, 1, 0), + + SOC_SINGLE("Batt Compensation Switch", CS42L52_BATT_COMPEN, 7, 1, 0), + SOC_SINGLE("Batt VP Monitor Switch", CS42L52_BATT_COMPEN, 6, 1, 0), + SOC_SINGLE("Batt VP ref", CS42L52_BATT_COMPEN, 0, 0x0f, 0), + + SOC_SINGLE("PGA AIN1L Switch", CS42L52_ADC_PGA_A, 0, 1, 0), + SOC_SINGLE("PGA AIN1R Switch", CS42L52_ADC_PGA_B, 0, 1, 0), + SOC_SINGLE("PGA AIN2L Switch", CS42L52_ADC_PGA_A, 1, 1, 0), + SOC_SINGLE("PGA AIN2R Switch", CS42L52_ADC_PGA_B, 1, 1, 0), + + SOC_SINGLE("PGA AIN3L Switch", CS42L52_ADC_PGA_A, 2, 1, 0), + SOC_SINGLE("PGA AIN3R Switch", CS42L52_ADC_PGA_B, 2, 1, 0), + + SOC_SINGLE("PGA AIN4L Switch", CS42L52_ADC_PGA_A, 3, 1, 0), + SOC_SINGLE("PGA AIN4R Switch", CS42L52_ADC_PGA_B, 3, 1, 0), + + SOC_SINGLE("PGA MICA Switch", CS42L52_ADC_PGA_A, 4, 1, 0), + SOC_SINGLE("PGA MICB Switch", CS42L52_ADC_PGA_B, 4, 1, 0), + +}; + +static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = { + + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("AIN3L"), + SND_SOC_DAPM_INPUT("AIN3R"), + SND_SOC_DAPM_INPUT("AIN4L"), + SND_SOC_DAPM_INPUT("AIN4R"), + SND_SOC_DAPM_INPUT("MICA"), + SND_SOC_DAPM_INPUT("MICB"), + SND_SOC_DAPM_SIGGEN("Beep"), + + SND_SOC_DAPM_AIF_OUT("AIFOUTL", NULL, 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIFOUTR", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("MICA Mux", SND_SOC_NOPM, 0, 0, &mica_mux), + SND_SOC_DAPM_MUX("MICB Mux", SND_SOC_NOPM, 0, 0, &micb_mux), + + SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L52_PWRCTL1, 1, 1), + SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L52_PWRCTL1, 2, 1), + SND_SOC_DAPM_PGA("PGA Left", CS42L52_PWRCTL1, 3, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA Right", CS42L52_PWRCTL1, 4, 1, NULL, 0), + + SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adca_mux), + SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcb_mux), + + SND_SOC_DAPM_MUX("ADC Left Swap", SND_SOC_NOPM, + 0, 0, &adca_mixer), + SND_SOC_DAPM_MUX("ADC Right Swap", SND_SOC_NOPM, + 0, 0, &adcb_mixer), + + SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, + 0, 0, &digital_output_mux), + + SND_SOC_DAPM_PGA("PGA MICA", CS42L52_PWRCTL2, 1, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA MICB", CS42L52_PWRCTL2, 2, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L52_PWRCTL2, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L52_PWRCTL1, 7, 1, NULL, 0), + + SND_SOC_DAPM_AIF_IN("AIFINL", NULL, 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFINR", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DAC Left", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC Right", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SWITCH("Bypass Left", CS42L52_MISC_CTL, + 6, 0, &passthrul_ctl), + SND_SOC_DAPM_SWITCH("Bypass Right", CS42L52_MISC_CTL, + 7, 0, &passthrur_ctl), + + SND_SOC_DAPM_MUX("PCM Left Swap", SND_SOC_NOPM, + 0, 0, &pcma_mixer), + SND_SOC_DAPM_MUX("PCM Right Swap", SND_SOC_NOPM, + 0, 0, &pcmb_mixer), + + SND_SOC_DAPM_SWITCH("HP Left Amp", SND_SOC_NOPM, 0, 0, &hpl_ctl), + SND_SOC_DAPM_SWITCH("HP Right Amp", SND_SOC_NOPM, 0, 0, &hpr_ctl), + + SND_SOC_DAPM_SWITCH("SPK Left Amp", SND_SOC_NOPM, 0, 0, &spkl_ctl), + SND_SOC_DAPM_SWITCH("SPK Right Amp", SND_SOC_NOPM, 0, 0, &spkr_ctl), + + SND_SOC_DAPM_OUTPUT("HPOUTA"), + SND_SOC_DAPM_OUTPUT("HPOUTB"), + SND_SOC_DAPM_OUTPUT("SPKOUTA"), + SND_SOC_DAPM_OUTPUT("SPKOUTB"), + +}; + +static const struct snd_soc_dapm_route cs42l52_audio_map[] = { + + {"Capture", NULL, "AIFOUTL"}, + {"Capture", NULL, "AIFOUTL"}, + + {"AIFOUTL", NULL, "Output Mux"}, + {"AIFOUTR", NULL, "Output Mux"}, + + {"Output Mux", "ADC", "ADC Left"}, + {"Output Mux", "ADC", "ADC Right"}, + + {"ADC Left", NULL, "Charge Pump"}, + {"ADC Right", NULL, "Charge Pump"}, + + {"Charge Pump", NULL, "ADC Left Mux"}, + {"Charge Pump", NULL, "ADC Right Mux"}, + + {"ADC Left Mux", "Input1A", "AIN1L"}, + {"ADC Right Mux", "Input1B", "AIN1R"}, + {"ADC Left Mux", "Input2A", "AIN2L"}, + {"ADC Right Mux", "Input2B", "AIN2R"}, + {"ADC Left Mux", "Input3A", "AIN3L"}, + {"ADC Right Mux", "Input3B", "AIN3R"}, + {"ADC Left Mux", "Input4A", "AIN4L"}, + {"ADC Right Mux", "Input4B", "AIN4R"}, + {"ADC Left Mux", "PGA Input Left", "PGA Left"}, + {"ADC Right Mux", "PGA Input Right" , "PGA Right"}, + + {"PGA Left", "Switch", "AIN1L"}, + {"PGA Right", "Switch", "AIN1R"}, + {"PGA Left", "Switch", "AIN2L"}, + {"PGA Right", "Switch", "AIN2R"}, + {"PGA Left", "Switch", "AIN3L"}, + {"PGA Right", "Switch", "AIN3R"}, + {"PGA Left", "Switch", "AIN4L"}, + {"PGA Right", "Switch", "AIN4R"}, + + {"PGA Left", "Switch", "PGA MICA"}, + {"PGA MICA", NULL, "MICA"}, + + {"PGA Right", "Switch", "PGA MICB"}, + {"PGA MICB", NULL, "MICB"}, + + {"HPOUTA", NULL, "HP Left Amp"}, + {"HPOUTB", NULL, "HP Right Amp"}, + {"HP Left Amp", NULL, "Bypass Left"}, + {"HP Right Amp", NULL, "Bypass Right"}, + {"Bypass Left", "Switch", "PGA Left"}, + {"Bypass Right", "Switch", "PGA Right"}, + {"HP Left Amp", "Switch", "DAC Left"}, + {"HP Right Amp", "Switch", "DAC Right"}, + + {"SPKOUTA", NULL, "SPK Left Amp"}, + {"SPKOUTB", NULL, "SPK Right Amp"}, + + {"SPK Left Amp", NULL, "Beep"}, + {"SPK Right Amp", NULL, "Beep"}, + {"SPK Left Amp", "Switch", "Playback"}, + {"SPK Right Amp", "Switch", "Playback"}, + + {"DAC Left", NULL, "Beep"}, + {"DAC Right", NULL, "Beep"}, + {"DAC Left", NULL, "Playback"}, + {"DAC Right", NULL, "Playback"}, + + {"Output Mux", "DSP", "Playback"}, + {"Output Mux", "DSP", "Playback"}, + + {"AIFINL", NULL, "Playback"}, + {"AIFINR", NULL, "Playback"}, + +}; + +struct cs42l52_clk_para { + u32 mclk; + u32 rate; + u8 speed; + u8 group; + u8 videoclk; + u8 ratio; + u8 mclkdiv2; +}; + +static const struct cs42l52_clk_para clk_map_table[] = { + /*8k*/ + {12288000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1}, + {27000000, 8000, CLK_QS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0}, + + /*11.025k*/ + {11289600, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {16934400, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + + /*16k*/ + {12288000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1}, + {27000000, 16000, CLK_HS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 1}, + + /*22.05k*/ + {11289600, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {16934400, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + + /* 32k */ + {12288000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1}, + {27000000, 32000, CLK_SS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0}, + + /* 44.1k */ + {11289600, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {16934400, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + + /* 48k */ + {12288000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1}, + {27000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_27M_MCLK, CLK_R_125, 1}, + + /* 88.2k */ + {11289600, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {16934400, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + + /* 96k */ + {12288000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1}, +}; + +static int cs42l52_get_clk(int mclk, int rate) +{ + int i, ret = 0; + u_int mclk1, mclk2 = 0; + + for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { + if (clk_map_table[i].rate == rate) { + mclk1 = clk_map_table[i].mclk; + if (abs(mclk - mclk1) < abs(mclk - mclk2)) { + mclk2 = mclk1; + ret = i; + } + } + } + if (ret > ARRAY_SIZE(clk_map_table)) + return -EINVAL; + return ret; +} + +static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + + if ((freq >= CS42L52_MIN_CLK) && (freq <= CS42L52_MAX_CLK)) { + cs42l52->sysclk = freq; + } else { + dev_err(codec->dev, "Invalid freq paramter\n"); + return -EINVAL; + } + return 0; +} + +static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + u8 iface = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = CS42L52_IFACE_CTL1_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = CS42L52_IFACE_CTL1_SLAVE; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= CS42L52_IFACE_CTL1_ADC_FMT_I2S | + CS42L52_IFACE_CTL1_DAC_FMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface |= CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J | + CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= CS42L52_IFACE_CTL1_DSP_MODE_EN; + break; + case SND_SOC_DAIFMT_DSP_B: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= CS42L52_IFACE_CTL1_INV_SCLK; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= CS42L52_IFACE_CTL1_INV_SCLK; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + default: + ret = -EINVAL; + } + cs42l52->config.format = iface; + snd_soc_write(codec, CS42L52_IFACE_CTL1, cs42l52->config.format); + + return 0; +} + +static int cs42l52_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) + snd_soc_update_bits(codec, CS42L52_PB_CTL1, + CS42L52_PB_CTL1_MUTE_MASK, + CS42L52_PB_CTL1_MUTE); + else + snd_soc_update_bits(codec, CS42L52_PB_CTL1, + CS42L52_PB_CTL1_MUTE_MASK, + CS42L52_PB_CTL1_UNMUTE); + + return 0; +} + +static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + u32 clk = 0; + int index; + + index = cs42l52_get_clk(cs42l52->sysclk, params_rate(params)); + if (index >= 0) { + cs42l52->sysclk = clk_map_table[index].mclk; + + clk |= (clk_map_table[index].speed << CLK_SPEED_SHIFT) | + (clk_map_table[index].group << CLK_32K_SR_SHIFT) | + (clk_map_table[index].videoclk << CLK_27M_MCLK_SHIFT) | + (clk_map_table[index].ratio << CLK_RATIO_SHIFT) | + clk_map_table[index].mclkdiv2; + + snd_soc_write(codec, CS42L52_CLK_CTL, clk); + } else { + dev_err(codec->dev, "can't get correct mclk\n"); + return -EINVAL; + } + + return 0; +} + +static int cs42l52_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + snd_soc_update_bits(codec, CS42L52_PWRCTL1, + CS42L52_PWRCTL1_PDN_CODEC, 0); + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + regcache_cache_only(cs42l52->regmap, false); + regcache_sync(cs42l52->regmap); + } + snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL); + break; + case SND_SOC_BIAS_OFF: + snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL); + regcache_cache_only(cs42l52->regmap, true); + break; + } + codec->dapm.bias_level = level; + + return 0; +} + +#define CS42L52_RATES (SNDRV_PCM_RATE_8000_96000) + +#define CS42L52_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE) + +static struct snd_soc_dai_ops cs42l52_ops = { + .hw_params = cs42l52_pcm_hw_params, + .digital_mute = cs42l52_digital_mute, + .set_fmt = cs42l52_set_fmt, + .set_sysclk = cs42l52_set_sysclk, +}; + +static struct snd_soc_dai_driver cs42l52_dai = { + .name = "cs42l52", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L52_RATES, + .formats = CS42L52_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L52_RATES, + .formats = CS42L52_FORMATS, + }, + .ops = &cs42l52_ops, +}; + +static int cs42l52_suspend(struct snd_soc_codec *codec) +{ + cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int cs42l52_resume(struct snd_soc_codec *codec) +{ + cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int beep_rates[] = { + 261, 522, 585, 667, 706, 774, 889, 1000, + 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182 +}; + +static void cs42l52_beep_work(struct work_struct *work) +{ + struct cs42l52_private *cs42l52 = + container_of(work, struct cs42l52_private, beep_work); + struct snd_soc_codec *codec = cs42l52->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int i; + int val = 0; + int best = 0; + + if (cs42l52->beep_rate) { + for (i = 0; i < ARRAY_SIZE(beep_rates); i++) { + if (abs(cs42l52->beep_rate - beep_rates[i]) < + abs(cs42l52->beep_rate - beep_rates[best])) + best = i; + } + + dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n", + beep_rates[best], cs42l52->beep_rate); + + val = (best << CS42L52_BEEP_RATE_SHIFT); + + snd_soc_dapm_enable_pin(dapm, "Beep"); + } else { + dev_dbg(codec->dev, "Disabling beep\n"); + snd_soc_dapm_disable_pin(dapm, "Beep"); + } + + snd_soc_update_bits(codec, CS42L52_BEEP_FREQ, + CS42L52_BEEP_RATE_MASK, val); + + snd_soc_dapm_sync(dapm); +} + +/* For usability define a way of injecting beep events for the device - + * many systems will not have a keyboard. + */ +static int cs42l52_beep_event(struct input_dev *dev, unsigned int type, + unsigned int code, int hz) +{ + struct snd_soc_codec *codec = input_get_drvdata(dev); + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "Beep event %x %x\n", code, hz); + + switch (code) { + case SND_BELL: + if (hz) + hz = 261; + case SND_TONE: + break; + default: + return -1; + } + + /* Kick the beep from a workqueue */ + cs42l52->beep_rate = hz; + schedule_work(&cs42l52->beep_work); + return 0; +} + +static ssize_t cs42l52_beep_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs42l52_private *cs42l52 = dev_get_drvdata(dev); + long int time; + int ret; + + ret = kstrtol(buf, 10, &time); + if (ret != 0) + return ret; + + input_event(cs42l52->beep, EV_SND, SND_TONE, time); + + return count; +} + +static DEVICE_ATTR(beep, 0200, NULL, cs42l52_beep_set); + +static void cs42l52_init_beep(struct snd_soc_codec *codec) +{ + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + int ret; + + cs42l52->beep = input_allocate_device(); + if (!cs42l52->beep) { + dev_err(codec->dev, "Failed to allocate beep device\n"); + return; + } + + INIT_WORK(&cs42l52->beep_work, cs42l52_beep_work); + cs42l52->beep_rate = 0; + + cs42l52->beep->name = "CS42L52 Beep Generator"; + cs42l52->beep->phys = dev_name(codec->dev); + cs42l52->beep->id.bustype = BUS_I2C; + + cs42l52->beep->evbit[0] = BIT_MASK(EV_SND); + cs42l52->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + cs42l52->beep->event = cs42l52_beep_event; + cs42l52->beep->dev.parent = codec->dev; + input_set_drvdata(cs42l52->beep, codec); + + ret = input_register_device(cs42l52->beep); + if (ret != 0) { + input_free_device(cs42l52->beep); + cs42l52->beep = NULL; + dev_err(codec->dev, "Failed to register beep device\n"); + } + + ret = device_create_file(codec->dev, &dev_attr_beep); + if (ret != 0) { + dev_err(codec->dev, "Failed to create keyclick file: %d\n", + ret); + } +} + +static void cs42l52_free_beep(struct snd_soc_codec *codec) +{ + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + + device_remove_file(codec->dev, &dev_attr_beep); + input_unregister_device(cs42l52->beep); + cancel_work_sync(&cs42l52->beep_work); + cs42l52->beep = NULL; + + snd_soc_update_bits(codec, CS42L52_BEEP_TONE_CTL, + CS42L52_BEEP_EN_MASK, 0); +} +#else +static void cs42l52_init_beep(struct snd_soc_codec *codec) +{ +} + +static void cs42l52_free_beep(struct snd_soc_codec *codec) +{ +} +#endif + +static int cs42l52_probe(struct snd_soc_codec *codec) +{ + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + int ret; + + codec->control_data = cs42l52->regmap; + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + regcache_cache_only(cs42l52->regmap, true); + + cs42l52_init_beep(codec); + + cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + cs42l52->sysclk = CS42L52_DEFAULT_CLK; + cs42l52->config.format = CS42L52_DEFAULT_FORMAT; + + /* Set Platform MICx CFG */ + snd_soc_update_bits(codec, CS42L52_MICA_CTL, + CS42L52_MIC_CTL_TYPE_MASK, + cs42l52->pdata.mica_cfg << + CS42L52_MIC_CTL_TYPE_SHIFT); + + snd_soc_update_bits(codec, CS42L52_MICB_CTL, + CS42L52_MIC_CTL_TYPE_MASK, + cs42l52->pdata.micb_cfg << + CS42L52_MIC_CTL_TYPE_SHIFT); + + /* if Single Ended, Get Mic_Select */ + if (cs42l52->pdata.mica_cfg) + snd_soc_update_bits(codec, CS42L52_MICA_CTL, + CS42L52_MIC_CTL_MIC_SEL_MASK, + cs42l52->pdata.mica_sel << + CS42L52_MIC_CTL_MIC_SEL_SHIFT); + if (cs42l52->pdata.micb_cfg) + snd_soc_update_bits(codec, CS42L52_MICB_CTL, + CS42L52_MIC_CTL_MIC_SEL_MASK, + cs42l52->pdata.micb_sel << + CS42L52_MIC_CTL_MIC_SEL_SHIFT); + + /* Set Platform Charge Pump Freq */ + snd_soc_update_bits(codec, CS42L52_CHARGE_PUMP, + CS42L52_CHARGE_PUMP_MASK, + cs42l52->pdata.chgfreq << + CS42L52_CHARGE_PUMP_SHIFT); + + /* Set Platform Bias Level */ + snd_soc_update_bits(codec, CS42L52_IFACE_CTL2, + CS42L52_IFACE_CTL2_BIAS_LVL, + cs42l52->pdata.micbias_lvl); + + return ret; +} + +static int cs42l52_remove(struct snd_soc_codec *codec) +{ + cs42l52_free_beep(codec); + cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_cs42l52 = { + .probe = cs42l52_probe, + .remove = cs42l52_remove, + .suspend = cs42l52_suspend, + .resume = cs42l52_resume, + .set_bias_level = cs42l52_set_bias_level, + + .dapm_widgets = cs42l52_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets), + .dapm_routes = cs42l52_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map), + + .controls = cs42l52_snd_controls, + .num_controls = ARRAY_SIZE(cs42l52_snd_controls), +}; + +/* Current and threshold powerup sequence Pg37 */ +static const struct reg_default cs42l52_threshold_patch[] = { + + { 0x00, 0x99 }, + { 0x3E, 0xBA }, + { 0x47, 0x80 }, + { 0x32, 0xBB }, + { 0x32, 0x3B }, + { 0x00, 0x00 }, + +}; + +static struct regmap_config cs42l52_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS42L52_MAX_REGISTER, + .reg_defaults = cs42l52_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults), + .readable_reg = cs42l52_readable_register, + .volatile_reg = cs42l52_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int cs42l52_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs42l52_private *cs42l52; + int ret; + unsigned int devid = 0; + unsigned int reg; + + cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private), + GFP_KERNEL); + if (cs42l52 == NULL) + return -ENOMEM; + cs42l52->dev = &i2c_client->dev; + + cs42l52->regmap = regmap_init_i2c(i2c_client, &cs42l52_regmap); + if (IS_ERR(cs42l52->regmap)) { + ret = PTR_ERR(cs42l52->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + goto err; + } + + i2c_set_clientdata(i2c_client, cs42l52); + + if (dev_get_platdata(&i2c_client->dev)) + memcpy(&cs42l52->pdata, dev_get_platdata(&i2c_client->dev), + sizeof(cs42l52->pdata)); + + ret = regmap_register_patch(cs42l52->regmap, cs42l52_threshold_patch, + ARRAY_SIZE(cs42l52_threshold_patch)); + if (ret != 0) + dev_warn(cs42l52->dev, "Failed to apply regmap patch: %d\n", + ret); + + ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, ®); + devid = reg & CS42L52_CHIP_ID_MASK; + if (devid != CS42L52_CHIP_ID) { + ret = -ENODEV; + dev_err(&i2c_client->dev, + "CS42L52 Device ID (%X). Expected %X\n", + devid, CS42L52_CHIP_ID); + goto err_regmap; + } + + regcache_cache_only(cs42l52->regmap, true); + + ret = snd_soc_register_codec(&i2c_client->dev, + &soc_codec_dev_cs42l52, &cs42l52_dai, 1); + if (ret < 0) + goto err_regmap; + return 0; + +err_regmap: + regmap_exit(cs42l52->regmap); + +err: + return ret; +} + +static int cs42l52_i2c_remove(struct i2c_client *client) +{ + struct cs42l52_private *cs42l52 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + regmap_exit(cs42l52->regmap); + + return 0; +} + +static const struct i2c_device_id cs42l52_id[] = { + { "cs42l52", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs42l52_id); + +static struct i2c_driver cs42l52_i2c_driver = { + .driver = { + .name = "cs42l52", + .owner = THIS_MODULE, + }, + .id_table = cs42l52_id, + .probe = cs42l52_i2c_probe, + .remove = __devexit_p(cs42l52_i2c_remove), +}; + +module_i2c_driver(cs42l52_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L52 driver"); +MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h new file mode 100644 index 000000000000..60985c059071 --- /dev/null +++ b/sound/soc/codecs/cs42l52.h @@ -0,0 +1,274 @@ +/* + * cs42l52.h -- CS42L52 ALSA SoC audio driver + * + * Copyright 2012 CirrusLogic, Inc. + * + * Author: Georgi Vlaev <joe@nucleusys.com> + * Author: Brian Austin <brian.austin@cirrus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __CS42L52_H__ +#define __CS42L52_H__ + +#define CS42L52_NAME "CS42L52" +#define CS42L52_DEFAULT_CLK 12000000 +#define CS42L52_MIN_CLK 11000000 +#define CS42L52_MAX_CLK 27000000 +#define CS42L52_DEFAULT_FORMAT SNDRV_PCM_FMTBIT_S16_LE +#define CS42L52_DEFAULT_MAX_CHANS 2 +#define CS42L52_SYSCLK 1 + +#define CS42L52_CHIP_SWICTH (1 << 17) +#define CS42L52_ALL_IN_ONE (1 << 16) +#define CS42L52_CHIP_ONE 0x00 +#define CS42L52_CHIP_TWO 0x01 +#define CS42L52_CHIP_THR 0x02 +#define CS42L52_CHIP_MASK 0x0f + +#define CS42L52_FIX_BITS_CTL 0x00 +#define CS42L52_CHIP 0x01 +#define CS42L52_CHIP_ID 0xE0 +#define CS42L52_CHIP_ID_MASK 0xF8 +#define CS42L52_CHIP_REV_A0 0x00 +#define CS42L52_CHIP_REV_A1 0x01 +#define CS42L52_CHIP_REV_B0 0x02 +#define CS42L52_CHIP_REV_MASK 0x03 + +#define CS42L52_PWRCTL1 0x02 +#define CS42L52_PWRCTL1_PDN_ALL 0x9F +#define CS42L52_PWRCTL1_PDN_CHRG 0x80 +#define CS42L52_PWRCTL1_PDN_PGAB 0x10 +#define CS42L52_PWRCTL1_PDN_PGAA 0x08 +#define CS42L52_PWRCTL1_PDN_ADCB 0x04 +#define CS42L52_PWRCTL1_PDN_ADCA 0x02 +#define CS42L52_PWRCTL1_PDN_CODEC 0x01 + +#define CS42L52_PWRCTL2 0x03 +#define CS42L52_PWRCTL2_OVRDB (1 << 4) +#define CS42L52_PWRCTL2_OVRDA (1 << 3) +#define CS42L52_PWRCTL2_PDN_MICB (1 << 2) +#define CS42L52_PWRCTL2_PDN_MICB_SHIFT 2 +#define CS42L52_PWRCTL2_PDN_MICA (1 << 1) +#define CS42L52_PWRCTL2_PDN_MICA_SHIFT 1 +#define CS42L52_PWRCTL2_PDN_MICBIAS (1 << 0) +#define CS42L52_PWRCTL2_PDN_MICBIAS_SHIFT 0 + +#define CS42L52_PWRCTL3 0x04 +#define CS42L52_PWRCTL3_HPB_PDN_SHIFT 6 +#define CS42L52_PWRCTL3_HPB_ON_LOW 0x00 +#define CS42L52_PWRCTL3_HPB_ON_HIGH 0x01 +#define CS42L52_PWRCTL3_HPB_ALWAYS_ON 0x02 +#define CS42L52_PWRCTL3_HPB_ALWAYS_OFF 0x03 +#define CS42L52_PWRCTL3_HPA_PDN_SHIFT 4 +#define CS42L52_PWRCTL3_HPA_ON_LOW 0x00 +#define CS42L52_PWRCTL3_HPA_ON_HIGH 0x01 +#define CS42L52_PWRCTL3_HPA_ALWAYS_ON 0x02 +#define CS42L52_PWRCTL3_HPA_ALWAYS_OFF 0x03 +#define CS42L52_PWRCTL3_SPKB_PDN_SHIFT 2 +#define CS42L52_PWRCTL3_SPKB_ON_LOW 0x00 +#define CS42L52_PWRCTL3_SPKB_ON_HIGH 0x01 +#define CS42L52_PWRCTL3_SPKB_ALWAYS_ON 0x02 +#define CS42L52_PWRCTL3_PDN_SPKB (1 << 2) +#define CS42L52_PWRCTL3_PDN_SPKA (1 << 0) +#define CS42L52_PWRCTL3_SPKA_PDN_SHIFT 0 +#define CS42L52_PWRCTL3_SPKA_ON_LOW 0x00 +#define CS42L52_PWRCTL3_SPKA_ON_HIGH 0x01 +#define CS42L52_PWRCTL3_SPKA_ALWAYS_ON 0x02 + +#define CS42L52_DEFAULT_OUTPUT_STATE 0x05 +#define CS42L52_PWRCTL3_CONF_MASK 0x03 + +#define CS42L52_CLK_CTL 0x05 +#define CLK_AUTODECT_ENABLE (1 << 7) +#define CLK_SPEED_SHIFT 5 +#define CLK_DS_MODE 0x00 +#define CLK_SS_MODE 0x01 +#define CLK_HS_MODE 0x02 +#define CLK_QS_MODE 0x03 +#define CLK_32K_SR_SHIFT 4 +#define CLK_32K 0x01 +#define CLK_NO_32K 0x00 +#define CLK_27M_MCLK_SHIFT 3 +#define CLK_27M_MCLK 0x01 +#define CLK_NO_27M 0x00 +#define CLK_RATIO_SHIFT 1 +#define CLK_R_128 0x00 +#define CLK_R_125 0x01 +#define CLK_R_132 0x02 +#define CLK_R_136 0x03 + +#define CS42L52_IFACE_CTL1 0x06 +#define CS42L52_IFACE_CTL1_MASTER (1 << 7) +#define CS42L52_IFACE_CTL1_SLAVE (0 << 7) +#define CS42L52_IFACE_CTL1_INV_SCLK (1 << 6) +#define CS42L52_IFACE_CTL1_ADC_FMT_I2S (1 << 5) +#define CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J (0 << 5) +#define CS42L52_IFACE_CTL1_DSP_MODE_EN (1 << 4) +#define CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J (0 << 2) +#define CS42L52_IFACE_CTL1_DAC_FMT_I2S (1 << 2) +#define CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J (2 << 2) +#define CS42L52_IFACE_CTL1_WL_32BIT (0x00) +#define CS42L52_IFACE_CTL1_WL_24BIT (0x01) +#define CS42L52_IFACE_CTL1_WL_20BIT (0x02) +#define CS42L52_IFACE_CTL1_WL_16BIT (0x03) +#define CS42L52_IFACE_CTL1_WL_MASK 0xFFFF + +#define CS42L52_IFACE_CTL2 0x07 +#define CS42L52_IFACE_CTL2_SC_MC_EQ (1 << 6) +#define CS42L52_IFACE_CTL2_LOOPBACK (1 << 5) +#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_EN (0 << 4) +#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_HIZ (1 << 4) +#define CS42L52_IFACE_CTL2_HP_SW_INV (1 << 3) +#define CS42L52_IFACE_CTL2_BIAS_LVL 0x07 + +#define CS42L52_ADC_PGA_A 0x08 +#define CS42L52_ADC_PGA_B 0x09 +#define CS42L52_ADC_SEL_SHIFT 5 +#define CS42L52_ADC_SEL_AIN1 0x00 +#define CS42L52_ADC_SEL_AIN2 0x01 +#define CS42L52_ADC_SEL_AIN3 0x02 +#define CS42L52_ADC_SEL_AIN4 0x03 +#define CS42L52_ADC_SEL_PGA 0x04 + +#define CS42L52_ANALOG_HPF_CTL 0x0A +#define CS42L52_HPF_CTL_ANLGSFTB (1 << 3) +#define CS42L52_HPF_CTL_ANLGSFTA (1 << 0) + +#define CS42L52_ADC_HPF_FREQ 0x0B +#define CS42L52_ADC_MISC_CTL 0x0C +#define CS42L52_ADC_MISC_CTL_SOURCE_DSP (1 << 6) + +#define CS42L52_PB_CTL1 0x0D +#define CS42L52_PB_CTL1_HP_GAIN_SHIFT 5 +#define CS42L52_PB_CTL1_HP_GAIN_03959 0x00 +#define CS42L52_PB_CTL1_HP_GAIN_04571 0x01 +#define CS42L52_PB_CTL1_HP_GAIN_05111 0x02 +#define CS42L52_PB_CTL1_HP_GAIN_06047 0x03 +#define CS42L52_PB_CTL1_HP_GAIN_07099 0x04 +#define CS42L52_PB_CTL1_HP_GAIN_08399 0x05 +#define CS42L52_PB_CTL1_HP_GAIN_10000 0x06 +#define CS42L52_PB_CTL1_HP_GAIN_11430 0x07 +#define CS42L52_PB_CTL1_INV_PCMB (1 << 3) +#define CS42L52_PB_CTL1_INV_PCMA (1 << 2) +#define CS42L52_PB_CTL1_MSTB_MUTE (1 << 1) +#define CS42L52_PB_CTL1_MSTA_MUTE (1 << 0) +#define CS42L52_PB_CTL1_MUTE_MASK 0xFFFD +#define CS42L52_PB_CTL1_MUTE 3 +#define CS42L52_PB_CTL1_UNMUTE 0 + +#define CS42L52_MISC_CTL 0x0E +#define CS42L52_MISC_CTL_DEEMPH (1 << 2) +#define CS42L52_MISC_CTL_DIGSFT (1 << 1) +#define CS42L52_MISC_CTL_DIGZC (1 << 0) + +#define CS42L52_PB_CTL2 0x0F +#define CS42L52_PB_CTL2_HPB_MUTE (1 << 7) +#define CS42L52_PB_CTL2_HPA_MUTE (1 << 6) +#define CS42L52_PB_CTL2_SPKB_MUTE (1 << 5) +#define CS42L52_PB_CTL2_SPKA_MUTE (1 << 4) +#define CS42L52_PB_CTL2_SPK_SWAP (1 << 2) +#define CS42L52_PB_CTL2_SPK_MONO (1 << 1) +#define CS42L52_PB_CTL2_SPK_MUTE50 (1 << 0) + +#define CS42L52_MICA_CTL 0x10 +#define CS42L52_MICB_CTL 0x11 +#define CS42L52_MIC_CTL_MIC_SEL_MASK 0xBF +#define CS42L52_MIC_CTL_MIC_SEL_SHIFT 6 +#define CS42L52_MIC_CTL_TYPE_MASK 0xDF +#define CS42L52_MIC_CTL_TYPE_SHIFT 5 + + +#define CS42L52_PGAA_CTL 0x12 +#define CS42L52_PGAB_CTL 0x13 +#define CS42L52_PGAX_CTL_VOL_12DB 24 +#define CS42L52_PGAX_CTL_VOL_6DB 12 /*step size 0.5db*/ + +#define CS42L52_PASSTHRUA_VOL 0x14 +#define CS42L52_PASSTHRUB_VOL 0x15 + +#define CS42L52_ADCA_VOL 0x16 +#define CS42L52_ADCB_VOL 0x17 +#define CS42L52_ADCX_VOL_24DB 24 /*step size 1db*/ +#define CS42L52_ADCX_VOL_12DB 12 +#define CS42L52_ADCX_VOL_6DB 6 + +#define CS42L52_ADCA_MIXER_VOL 0x18 +#define CS42L52_ADCB_MIXER_VOL 0x19 +#define CS42L52_ADC_MIXER_VOL_12DB 0x18 + +#define CS42L52_PCMA_MIXER_VOL 0x1A +#define CS42L52_PCMB_MIXER_VOL 0x1B + +#define CS42L52_BEEP_FREQ 0x1C +#define CS42L52_BEEP_VOL 0x1D +#define CS42L52_BEEP_TONE_CTL 0x1E +#define CS42L52_BEEP_RATE_SHIFT 4 +#define CS42L52_BEEP_RATE_MASK 0x0F + +#define CS42L52_TONE_CTL 0x1F +#define CS42L52_BEEP_EN_MASK 0x3F + +#define CS42L52_MASTERA_VOL 0x20 +#define CS42L52_MASTERB_VOL 0x21 + +#define CS42L52_HPA_VOL 0x22 +#define CS42L52_HPB_VOL 0x23 +#define CS42L52_DEFAULT_HP_VOL 0xF0 + +#define CS42L52_SPKA_VOL 0x24 +#define CS42L52_SPKB_VOL 0x25 +#define CS42L52_DEFAULT_SPK_VOL 0xF0 + +#define CS42L52_ADC_PCM_MIXER 0x26 + +#define CS42L52_LIMITER_CTL1 0x27 +#define CS42L52_LIMITER_CTL2 0x28 +#define CS42L52_LIMITER_AT_RATE 0x29 + +#define CS42L52_ALC_CTL 0x2A +#define CS42L52_ALC_CTL_ALCB_ENABLE_SHIFT 7 +#define CS42L52_ALC_CTL_ALCA_ENABLE_SHIFT 6 +#define CS42L52_ALC_CTL_FASTEST_ATTACK 0 + +#define CS42L52_ALC_RATE 0x2B +#define CS42L52_ALC_SLOWEST_RELEASE 0x3F + +#define CS42L52_ALC_THRESHOLD 0x2C +#define CS42L52_ALC_MAX_RATE_SHIFT 5 +#define CS42L52_ALC_MIN_RATE_SHIFT 2 +#define CS42L52_ALC_RATE_0DB 0 +#define CS42L52_ALC_RATE_3DB 1 +#define CS42L52_ALC_RATE_6DB 2 + +#define CS42L52_NOISE_GATE_CTL 0x2D +#define CS42L52_NG_ENABLE_SHIFT 6 +#define CS42L52_NG_THRESHOLD_SHIFT 2 +#define CS42L52_NG_MIN_70DB 2 +#define CS42L52_NG_DELAY_SHIFT 0 +#define CS42L52_NG_DELAY_100MS 1 + +#define CS42L52_CLK_STATUS 0x2E +#define CS42L52_BATT_COMPEN 0x2F + +#define CS42L52_BATT_LEVEL 0x30 +#define CS42L52_SPK_STATUS 0x31 +#define CS42L52_SPK_STATUS_PIN_SHIFT 3 +#define CS42L52_SPK_STATUS_PIN_HIGH 1 + +#define CS42L52_TEM_CTL 0x32 +#define CS42L52_TEM_CTL_SET 0x80 +#define CS42L52_THE_FOLDBACK 0x33 +#define CS42L52_CHARGE_PUMP 0x34 +#define CS42L52_CHARGE_PUMP_MASK 0xF0 +#define CS42L52_CHARGE_PUMP_SHIFT 4 +#define CS42L52_FIX_BITS1 0x3E +#define CS42L52_FIX_BITS2 0x47 + +#define CS42L52_MAX_REGISTER 0x34 + +#endif diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 3686417f5ea5..e0d45fdaa750 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -43,9 +43,6 @@ struct cs42l73_private { }; static const struct reg_default cs42l73_reg_defaults[] = { - { 1, 0x42 }, /* r01 - Device ID A&B */ - { 2, 0xA7 }, /* r02 - Device ID C&D */ - { 3, 0x30 }, /* r03 - Device ID E */ { 6, 0xF1 }, /* r06 - Power Ctl 1 */ { 7, 0xDF }, /* r07 - Power Ctl 2 */ { 8, 0x3F }, /* r08 - Power Ctl 3 */ @@ -402,37 +399,37 @@ static const struct snd_kcontrol_new ear_amp_ctl = static const struct snd_kcontrol_new cs42l73_snd_controls[] = { SOC_DOUBLE_R_SX_TLV("Headphone Analog Playback Volume", - CS42L73_HPAAVOL, CS42L73_HPBAVOL, 7, - 0xffffffC1, 0x0C, hpaloa_tlv), + CS42L73_HPAAVOL, CS42L73_HPBAVOL, 0, + 0x41, 0x4B, hpaloa_tlv), SOC_DOUBLE_R_SX_TLV("LineOut Analog Playback Volume", CS42L73_LOAAVOL, - CS42L73_LOBAVOL, 7, 0xffffffC1, 0x0C, hpaloa_tlv), + CS42L73_LOBAVOL, 0, 0x41, 0x4B, hpaloa_tlv), SOC_DOUBLE_R_SX_TLV("Input PGA Analog Volume", CS42L73_MICAPREPGAAVOL, - CS42L73_MICBPREPGABVOL, 5, 0xffffff35, - 0x34, micpga_tlv), + CS42L73_MICBPREPGABVOL, 5, 0x34, + 0x24, micpga_tlv), SOC_DOUBLE_R("MIC Preamp Switch", CS42L73_MICAPREPGAAVOL, CS42L73_MICBPREPGABVOL, 6, 1, 1), SOC_DOUBLE_R_SX_TLV("Input Path Digital Volume", CS42L73_IPADVOL, - CS42L73_IPBDVOL, 7, 0xffffffA0, 0xA0, ipd_tlv), + CS42L73_IPBDVOL, 0, 0xA0, 0x6C, ipd_tlv), SOC_DOUBLE_R_SX_TLV("HL Digital Playback Volume", - CS42L73_HLADVOL, CS42L73_HLBDVOL, 7, 0xffffffE5, - 0xE4, hl_tlv), + CS42L73_HLADVOL, CS42L73_HLBDVOL, + 0, 0x34, 0xE4, hl_tlv), SOC_SINGLE_TLV("ADC A Boost Volume", CS42L73_ADCIPC, 2, 0x01, 1, adc_boost_tlv), SOC_SINGLE_TLV("ADC B Boost Volume", - CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv), + CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv), - SOC_SINGLE_TLV("Speakerphone Digital Playback Volume", - CS42L73_SPKDVOL, 0, 0xE4, 1, hl_tlv), + SOC_SINGLE_SX_TLV("Speakerphone Digital Volume", + CS42L73_SPKDVOL, 0, 0x34, 0xE4, hl_tlv), - SOC_SINGLE_TLV("Ear Speaker Digital Playback Volume", - CS42L73_ESLDVOL, 0, 0xE4, 1, hl_tlv), + SOC_SINGLE_SX_TLV("Ear Speaker Digital Volume", + CS42L73_ESLDVOL, 0, 0x34, 0xE4, hl_tlv), SOC_DOUBLE_R("Headphone Analog Playback Switch", CS42L73_HPAAVOL, CS42L73_HPBAVOL, 7, 1, 1), @@ -599,17 +596,17 @@ static const struct snd_soc_dapm_widget cs42l73_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC2"), SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS42L73_PWRCTL2, 7, 1, NULL, 0), - SND_SOC_DAPM_AIF_OUT("XSPOUTL", "XSP Capture", 0, + SND_SOC_DAPM_AIF_OUT("XSPOUTL", NULL, 0, CS42L73_PWRCTL2, 1, 1), - SND_SOC_DAPM_AIF_OUT("XSPOUTR", "XSP Capture", 0, + SND_SOC_DAPM_AIF_OUT("XSPOUTR", NULL, 0, CS42L73_PWRCTL2, 1, 1), - SND_SOC_DAPM_AIF_OUT("ASPOUTL", "ASP Capture", 0, + SND_SOC_DAPM_AIF_OUT("ASPOUTL", NULL, 0, CS42L73_PWRCTL2, 3, 1), - SND_SOC_DAPM_AIF_OUT("ASPOUTR", "ASP Capture", 0, + SND_SOC_DAPM_AIF_OUT("ASPOUTR", NULL, 0, CS42L73_PWRCTL2, 3, 1), - SND_SOC_DAPM_AIF_OUT("VSPOUTL", "VSP Capture", 0, + SND_SOC_DAPM_AIF_OUT("VSPOUTL", NULL, 0, CS42L73_PWRCTL2, 4, 1), - SND_SOC_DAPM_AIF_OUT("VSPOUTR", "VSP Capture", 0, + SND_SOC_DAPM_AIF_OUT("VSPOUTR", NULL, 0, CS42L73_PWRCTL2, 4, 1), SND_SOC_DAPM_PGA("PGA Left", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -638,21 +635,21 @@ static const struct snd_soc_dapm_widget cs42l73_dapm_widgets[] = { SND_SOC_DAPM_MIXER("VSPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("VSPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_AIF_IN("XSPINL", "XSP Playback", 0, + SND_SOC_DAPM_AIF_IN("XSPINL", NULL, 0, CS42L73_PWRCTL2, 0, 1), - SND_SOC_DAPM_AIF_IN("XSPINR", "XSP Playback", 0, + SND_SOC_DAPM_AIF_IN("XSPINR", NULL, 0, CS42L73_PWRCTL2, 0, 1), - SND_SOC_DAPM_AIF_IN("XSPINM", "XSP Playback", 0, + SND_SOC_DAPM_AIF_IN("XSPINM", NULL, 0, CS42L73_PWRCTL2, 0, 1), - SND_SOC_DAPM_AIF_IN("ASPINL", "ASP Playback", 0, + SND_SOC_DAPM_AIF_IN("ASPINL", NULL, 0, CS42L73_PWRCTL2, 2, 1), - SND_SOC_DAPM_AIF_IN("ASPINR", "ASP Playback", 0, + SND_SOC_DAPM_AIF_IN("ASPINR", NULL, 0, CS42L73_PWRCTL2, 2, 1), - SND_SOC_DAPM_AIF_IN("ASPINM", "ASP Playback", 0, + SND_SOC_DAPM_AIF_IN("ASPINM", NULL, 0, CS42L73_PWRCTL2, 2, 1), - SND_SOC_DAPM_AIF_IN("VSPIN", "VSP Playback", 0, + SND_SOC_DAPM_AIF_IN("VSPIN", NULL, 0, CS42L73_PWRCTL2, 4, 1), SND_SOC_DAPM_MIXER("HL Left Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -776,6 +773,14 @@ static const struct snd_soc_dapm_route cs42l73_audio_map[] = { {"HL Left Mixer", NULL, "VSPIN"}, {"HL Right Mixer", NULL, "VSPIN"}, + {"ASPINL", NULL, "ASP Playback"}, + {"ASPINM", NULL, "ASP Playback"}, + {"ASPINR", NULL, "ASP Playback"}, + {"XSPINL", NULL, "XSP Playback"}, + {"XSPINM", NULL, "XSP Playback"}, + {"XSPINR", NULL, "XSP Playback"}, + {"VSPIN", NULL, "VSP Playback"}, + /* Capture Paths */ {"MIC1", NULL, "MIC1 Bias"}, {"PGA Left Mux", "Mic 1", "MIC1"}, @@ -822,6 +827,13 @@ static const struct snd_soc_dapm_route cs42l73_audio_map[] = { {"VSPOUTL", NULL, "VSPL Output Mixer"}, {"VSPOUTR", NULL, "VSPR Output Mixer"}, + + {"ASP Capture", NULL, "ASPOUTL"}, + {"ASP Capture", NULL, "ASPOUTR"}, + {"XSP Capture", NULL, "XSPOUTL"}, + {"XSP Capture", NULL, "XSPOUTR"}, + {"VSP Capture", NULL, "VSPOUTL"}, + {"VSP Capture", NULL, "VSPOUTR"}, }; struct cs42l73_mclk_div { @@ -1091,8 +1103,7 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); int id = dai->id; int mclk_coeff; @@ -1429,25 +1440,7 @@ static struct i2c_driver cs42l73_i2c_driver = { }; -static int __init cs42l73_modinit(void) -{ - int ret; - ret = i2c_add_driver(&cs42l73_i2c_driver); - if (ret != 0) { - pr_err("Failed to register CS42L73 I2C driver: %d\n", ret); - return ret; - } - return 0; -} - -module_init(cs42l73_modinit); - -static void __exit cs42l73_exit(void) -{ - i2c_del_driver(&cs42l73_i2c_driver); -} - -module_exit(cs42l73_exit); +module_i2c_driver(cs42l73_i2c_driver); MODULE_DESCRIPTION("ASoC CS42L73 driver"); MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>"); diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 7843711729bc..af5db7080519 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include <linux/i2c.h> +#include <linux/spi/spi.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/module.h> @@ -27,6 +28,7 @@ #include <sound/tlv.h> /* DA7210 register space */ +#define DA7210_PAGE_CONTROL 0x00 #define DA7210_CONTROL 0x01 #define DA7210_STATUS 0x02 #define DA7210_STARTUP1 0x03 @@ -146,6 +148,7 @@ #define DA7210_DAI_EN (1 << 7) /*PLL_DIV3 bit fields */ +#define DA7210_PLL_DIV_L_MASK (0xF << 0) #define DA7210_MCLK_RANGE_10_20_MHZ (1 << 4) #define DA7210_PLL_BYP (1 << 6) @@ -162,12 +165,16 @@ #define DA7210_PLL_FS_48000 (0xB << 0) #define DA7210_PLL_FS_88200 (0xE << 0) #define DA7210_PLL_FS_96000 (0xF << 0) +#define DA7210_MCLK_DET_EN (0x1 << 5) +#define DA7210_MCLK_SRM_EN (0x1 << 6) #define DA7210_PLL_EN (0x1 << 7) /* SOFTMUTE bit fields */ #define DA7210_RAMP_EN (1 << 6) /* CONTROL bit fields */ +#define DA7210_REG_EN (1 << 0) +#define DA7210_BIAS_EN (1 << 2) #define DA7210_NOISE_SUP_EN (1 << 3) /* IN_GAIN bit fields */ @@ -206,6 +213,47 @@ #define DA7210_OUT2_OUTMIX_L (1 << 6) #define DA7210_OUT2_EN (1 << 7) +struct pll_div { + int fref; + int fout; + u8 div1; + u8 div2; + u8 div3; + u8 mode; /* 0 = slave, 1 = master */ +}; + +/* PLL dividers table */ +static const struct pll_div da7210_pll_div[] = { + /* for MASTER mode, fs = 44.1Khz */ + { 12000000, 2822400, 0xE8, 0x6C, 0x2, 1}, /* MCLK=12Mhz */ + { 13000000, 2822400, 0xDF, 0x28, 0xC, 1}, /* MCLK=13Mhz */ + { 13500000, 2822400, 0xDB, 0x0A, 0xD, 1}, /* MCLK=13.5Mhz */ + { 14400000, 2822400, 0xD4, 0x5A, 0x2, 1}, /* MCLK=14.4Mhz */ + { 19200000, 2822400, 0xBB, 0x43, 0x9, 1}, /* MCLK=19.2Mhz */ + { 19680000, 2822400, 0xB9, 0x6D, 0xA, 1}, /* MCLK=19.68Mhz */ + { 19800000, 2822400, 0xB8, 0xFB, 0xB, 1}, /* MCLK=19.8Mhz */ + /* for MASTER mode, fs = 48Khz */ + { 12000000, 3072000, 0xF3, 0x12, 0x7, 1}, /* MCLK=12Mhz */ + { 13000000, 3072000, 0xE8, 0xFD, 0x5, 1}, /* MCLK=13Mhz */ + { 13500000, 3072000, 0xE4, 0x82, 0x3, 1}, /* MCLK=13.5Mhz */ + { 14400000, 3072000, 0xDD, 0x3A, 0x0, 1}, /* MCLK=14.4Mhz */ + { 19200000, 3072000, 0xC1, 0xEB, 0x8, 1}, /* MCLK=19.2Mhz */ + { 19680000, 3072000, 0xBF, 0xEC, 0x0, 1}, /* MCLK=19.68Mhz */ + { 19800000, 3072000, 0xBF, 0x70, 0x0, 1}, /* MCLK=19.8Mhz */ + /* for SLAVE mode with SRM */ + { 12000000, 2822400, 0xED, 0xBF, 0x5, 0}, /* MCLK=12Mhz */ + { 13000000, 2822400, 0xE4, 0x13, 0x0, 0}, /* MCLK=13Mhz */ + { 13500000, 2822400, 0xDF, 0xC6, 0x8, 0}, /* MCLK=13.5Mhz */ + { 14400000, 2822400, 0xD8, 0xCA, 0x1, 0}, /* MCLK=14.4Mhz */ + { 19200000, 2822400, 0xBE, 0x97, 0x9, 0}, /* MCLK=19.2Mhz */ + { 19680000, 2822400, 0xBC, 0xAC, 0xD, 0}, /* MCLK=19.68Mhz */ + { 19800000, 2822400, 0xBC, 0x35, 0xE, 0}, /* MCLK=19.8Mhz */ +}; + +enum clk_src { + DA7210_CLKSRC_MCLK +}; + #define DA7210_VERSION "0.0.1" /* @@ -628,9 +676,12 @@ static const struct snd_soc_dapm_route da7210_audio_map[] = { /* Codec private data */ struct da7210_priv { struct regmap *regmap; + unsigned int mclk_rate; + int master; }; static struct reg_default da7210_reg_defaults[] = { + { 0x00, 0x00 }, { 0x01, 0x11 }, { 0x03, 0x00 }, { 0x04, 0x00 }, @@ -713,10 +764,10 @@ static int da7210_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; + struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec); u32 dai_cfg1; - u32 fs, bypass; + u32 fs, sysclk; /* set DAI source to Left and Right ADC */ snd_soc_write(codec, DA7210_DAI_SRC_SEL, @@ -749,43 +800,43 @@ static int da7210_hw_params(struct snd_pcm_substream *substream, switch (params_rate(params)) { case 8000: fs = DA7210_PLL_FS_8000; - bypass = DA7210_PLL_BYP; + sysclk = 3072000; break; case 11025: fs = DA7210_PLL_FS_11025; - bypass = 0; + sysclk = 2822400; break; case 12000: fs = DA7210_PLL_FS_12000; - bypass = DA7210_PLL_BYP; + sysclk = 3072000; break; case 16000: fs = DA7210_PLL_FS_16000; - bypass = DA7210_PLL_BYP; + sysclk = 3072000; break; case 22050: fs = DA7210_PLL_FS_22050; - bypass = 0; + sysclk = 2822400; break; case 32000: fs = DA7210_PLL_FS_32000; - bypass = DA7210_PLL_BYP; + sysclk = 3072000; break; case 44100: fs = DA7210_PLL_FS_44100; - bypass = 0; + sysclk = 2822400; break; case 48000: fs = DA7210_PLL_FS_48000; - bypass = DA7210_PLL_BYP; + sysclk = 3072000; break; case 88200: fs = DA7210_PLL_FS_88200; - bypass = 0; + sysclk = 2822400; break; case 96000: fs = DA7210_PLL_FS_96000; - bypass = DA7210_PLL_BYP; + sysclk = 3072000; break; default: return -EINVAL; @@ -795,8 +846,26 @@ static int da7210_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0); snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs); - snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, bypass); + if (da7210->mclk_rate && (da7210->mclk_rate != sysclk)) { + /* PLL mode, disable PLL bypass */ + snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0); + + if (!da7210->master) { + /* PLL slave mode, also enable SRM */ + snd_soc_update_bits(codec, DA7210_PLL, + (DA7210_MCLK_SRM_EN | + DA7210_MCLK_DET_EN), + (DA7210_MCLK_SRM_EN | + DA7210_MCLK_DET_EN)); + } + } else { + /* PLL bypass mode, enable PLL bypass and Auto Detection */ + snd_soc_update_bits(codec, DA7210_PLL, DA7210_MCLK_DET_EN, + DA7210_MCLK_DET_EN); + snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, + DA7210_PLL_BYP); + } /* Enable active mode */ snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, DA7210_SC_MST_EN); @@ -810,17 +879,24 @@ static int da7210_hw_params(struct snd_pcm_substream *substream, static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt) { struct snd_soc_codec *codec = codec_dai->codec; + struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec); u32 dai_cfg1; u32 dai_cfg3; dai_cfg1 = 0x7f & snd_soc_read(codec, DA7210_DAI_CFG1); dai_cfg3 = 0xfc & snd_soc_read(codec, DA7210_DAI_CFG3); + if ((snd_soc_read(codec, DA7210_PLL) & DA7210_PLL_EN) && + (!(snd_soc_read(codec, DA7210_PLL_DIV3) & DA7210_PLL_BYP))) + return -EINVAL; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: + da7210->master = 1; dai_cfg1 |= DA7210_DAI_MODE_MASTER; break; case SND_SOC_DAIFMT_CBS_CFS: + da7210->master = 0; dai_cfg1 |= DA7210_DAI_MODE_SLAVE; break; default: @@ -872,10 +948,101 @@ static int da7210_mute(struct snd_soc_dai *dai, int mute) #define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case DA7210_CLKSRC_MCLK: + switch (freq) { + case 12000000: + case 13000000: + case 13500000: + case 14400000: + case 19200000: + case 19680000: + case 19800000: + da7210->mclk_rate = freq; + return 0; + default: + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } +} + +/** + * da7210_set_dai_pll :Configure the codec PLL + * @param codec_dai : pointer to codec DAI + * @param pll_id : da7210 has only one pll, so pll_id is always zero + * @param fref : MCLK frequency, should be < 20MHz + * @param fout : FsDM value, Refer page 44 & 45 of datasheet + * @return int : Zero for success, negative error code for error + * + * Note: Supported PLL input frequencies are 12MHz, 13MHz, 13.5MHz, 14.4MHz, + * 19.2MHz, 19.6MHz and 19.8MHz + */ +static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec); + + u8 pll_div1, pll_div2, pll_div3, cnt; + + /* In slave mode, there is only one set of divisors */ + if (!da7210->master) + fout = 2822400; + + /* Search pll div array for correct divisors */ + for (cnt = 0; cnt < ARRAY_SIZE(da7210_pll_div); cnt++) { + /* check fref, mode and fout */ + if ((fref == da7210_pll_div[cnt].fref) && + (da7210->master == da7210_pll_div[cnt].mode) && + (fout == da7210_pll_div[cnt].fout)) { + /* all match, pick up divisors */ + pll_div1 = da7210_pll_div[cnt].div1; + pll_div2 = da7210_pll_div[cnt].div2; + pll_div3 = da7210_pll_div[cnt].div3; + break; + } + } + if (cnt >= ARRAY_SIZE(da7210_pll_div)) + goto err; + + /* Disable active mode */ + snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0); + /* Write PLL dividers */ + snd_soc_write(codec, DA7210_PLL_DIV1, pll_div1); + snd_soc_write(codec, DA7210_PLL_DIV2, pll_div2); + snd_soc_update_bits(codec, DA7210_PLL_DIV3, + DA7210_PLL_DIV_L_MASK, pll_div3); + + /* Enable PLL */ + snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN); + + /* Enable active mode */ + snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, + DA7210_SC_MST_EN); + return 0; +err: + dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n", fref); + return -EINVAL; +} + /* DAI operations */ static const struct snd_soc_dai_ops da7210_dai_ops = { .hw_params = da7210_hw_params, .set_fmt = da7210_set_dai_fmt, + .set_sysclk = da7210_set_dai_sysclk, + .set_pll = da7210_set_dai_pll, .digital_mute = da7210_mute, }; @@ -915,24 +1082,11 @@ static int da7210_probe(struct snd_soc_codec *codec) return ret; } - /* FIXME - * - * This driver use fixed value here - * And below settings expects MCLK = 12.288MHz - * - * When you select different MCLK, please check... - * DA7210_PLL_DIV1 val - * DA7210_PLL_DIV2 val - * DA7210_PLL_DIV3 val - * DA7210_PLL_DIV3 :: DA7210_MCLK_RANGExxx - */ + da7210->mclk_rate = 0; /* This will be set from set_sysclk() */ + da7210->master = 0; /* This will be set from set_fmt() */ - /* - * make sure that DA7210 use bypass mode before start up - */ - snd_soc_write(codec, DA7210_STARTUP1, 0); - snd_soc_write(codec, DA7210_PLL_DIV3, - DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP); + /* Enable internal regulator & bias current */ + snd_soc_write(codec, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN); /* * ADC settings @@ -1007,34 +1161,13 @@ static int da7210_probe(struct snd_soc_codec *codec) /* Enable Aux2 */ snd_soc_write(codec, DA7210_AUX2, DA7210_AUX2_EN); + /* Set PLL Master clock range 10-20 MHz, enable PLL bypass */ + snd_soc_write(codec, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ | + DA7210_PLL_BYP); + /* Diable PLL and bypass it */ snd_soc_write(codec, DA7210_PLL, DA7210_PLL_FS_48000); - /* - * If 48kHz sound came, it use bypass mode, - * and when it is 44.1kHz, it use PLL. - * - * This time, this driver sets PLL always ON - * and controls bypass/PLL mode by switching - * DA7210_PLL_DIV3 :: DA7210_PLL_BYP bit. - * see da7210_hw_params - */ - snd_soc_write(codec, DA7210_PLL_DIV1, 0xE5); /* MCLK = 12.288MHz */ - snd_soc_write(codec, DA7210_PLL_DIV2, 0x99); - snd_soc_write(codec, DA7210_PLL_DIV3, 0x0A | - DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP); - snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN); - - /* As suggested by Dialog */ - /* unlock */ - regmap_write(da7210->regmap, DA7210_A_HID_UNLOCK, 0x8B); - regmap_write(da7210->regmap, DA7210_A_TEST_UNLOCK, 0xB4); - regmap_write(da7210->regmap, DA7210_A_PLL1, 0x01); - regmap_write(da7210->regmap, DA7210_A_CP_MODE, 0x7C); - /* re-lock */ - regmap_write(da7210->regmap, DA7210_A_HID_UNLOCK, 0x00); - regmap_write(da7210->regmap, DA7210_A_TEST_UNLOCK, 0x00); - /* Activate all enabled subsystem */ snd_soc_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN); @@ -1055,7 +1188,26 @@ static struct snd_soc_codec_driver soc_codec_dev_da7210 = { .num_dapm_routes = ARRAY_SIZE(da7210_audio_map), }; -static struct regmap_config da7210_regmap = { +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + +static struct reg_default da7210_regmap_i2c_patch[] = { + + /* System controller master disable */ + { DA7210_STARTUP1, 0x00 }, + /* Set PLL Master clock range 10-20 MHz */ + { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ }, + + /* to unlock */ + { DA7210_A_HID_UNLOCK, 0x8B}, + { DA7210_A_TEST_UNLOCK, 0xB4}, + { DA7210_A_PLL1, 0x01}, + { DA7210_A_CP_MODE, 0x7C}, + /* to re-lock */ + { DA7210_A_HID_UNLOCK, 0x00}, + { DA7210_A_TEST_UNLOCK, 0x00}, +}; + +static const struct regmap_config da7210_regmap_config_i2c = { .reg_bits = 8, .val_bits = 8, @@ -1066,7 +1218,6 @@ static struct regmap_config da7210_regmap = { .cache_type = REGCACHE_RBTREE, }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static int __devinit da7210_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1080,13 +1231,18 @@ static int __devinit da7210_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, da7210); - da7210->regmap = regmap_init_i2c(i2c, &da7210_regmap); + da7210->regmap = regmap_init_i2c(i2c, &da7210_regmap_config_i2c); if (IS_ERR(da7210->regmap)) { ret = PTR_ERR(da7210->regmap); dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); return ret; } + ret = regmap_register_patch(da7210->regmap, da7210_regmap_i2c_patch, + ARRAY_SIZE(da7210_regmap_i2c_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7210, &da7210_dai, 1); if (ret < 0) { @@ -1119,7 +1275,7 @@ MODULE_DEVICE_TABLE(i2c, da7210_i2c_id); /* I2C codec control layer */ static struct i2c_driver da7210_i2c_driver = { .driver = { - .name = "da7210-codec", + .name = "da7210", .owner = THIS_MODULE, }, .probe = da7210_i2c_probe, @@ -1128,12 +1284,112 @@ static struct i2c_driver da7210_i2c_driver = { }; #endif +#if defined(CONFIG_SPI_MASTER) + +static struct reg_default da7210_regmap_spi_patch[] = { + /* Dummy read to give two pulses over nCS for SPI */ + { DA7210_AUX2, 0x00 }, + { DA7210_AUX2, 0x00 }, + + /* System controller master disable */ + { DA7210_STARTUP1, 0x00 }, + /* Set PLL Master clock range 10-20 MHz */ + { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ }, + + /* to set PAGE1 of SPI register space */ + { DA7210_PAGE_CONTROL, 0x80 }, + /* to unlock */ + { DA7210_A_HID_UNLOCK, 0x8B}, + { DA7210_A_TEST_UNLOCK, 0xB4}, + { DA7210_A_PLL1, 0x01}, + { DA7210_A_CP_MODE, 0x7C}, + /* to re-lock */ + { DA7210_A_HID_UNLOCK, 0x00}, + { DA7210_A_TEST_UNLOCK, 0x00}, + /* to set back PAGE0 of SPI register space */ + { DA7210_PAGE_CONTROL, 0x00 }, +}; + +static const struct regmap_config da7210_regmap_config_spi = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0x01, + .write_flag_mask = 0x00, + + .reg_defaults = da7210_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults), + .volatile_reg = da7210_volatile_register, + .readable_reg = da7210_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit da7210_spi_probe(struct spi_device *spi) +{ + struct da7210_priv *da7210; + int ret; + + da7210 = devm_kzalloc(&spi->dev, sizeof(struct da7210_priv), + GFP_KERNEL); + if (!da7210) + return -ENOMEM; + + spi_set_drvdata(spi, da7210); + da7210->regmap = devm_regmap_init_spi(spi, &da7210_regmap_config_spi); + if (IS_ERR(da7210->regmap)) { + ret = PTR_ERR(da7210->regmap); + dev_err(&spi->dev, "Failed to register regmap: %d\n", ret); + return ret; + } + + ret = regmap_register_patch(da7210->regmap, da7210_regmap_spi_patch, + ARRAY_SIZE(da7210_regmap_spi_patch)); + if (ret != 0) + dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret); + + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_da7210, &da7210_dai, 1); + if (ret < 0) + goto err_regmap; + + return ret; + +err_regmap: + regmap_exit(da7210->regmap); + + return ret; +} + +static int __devexit da7210_spi_remove(struct spi_device *spi) +{ + struct da7210_priv *da7210 = spi_get_drvdata(spi); + snd_soc_unregister_codec(&spi->dev); + regmap_exit(da7210->regmap); + return 0; +} + +static struct spi_driver da7210_spi_driver = { + .driver = { + .name = "da7210", + .owner = THIS_MODULE, + }, + .probe = da7210_spi_probe, + .remove = __devexit_p(da7210_spi_remove) +}; +#endif + static int __init da7210_modinit(void) { int ret = 0; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) ret = i2c_add_driver(&da7210_i2c_driver); #endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&da7210_spi_driver); + if (ret) { + printk(KERN_ERR "Failed to register da7210 SPI driver: %d\n", + ret); + } +#endif return ret; } module_init(da7210_modinit); @@ -1143,6 +1399,9 @@ static void __exit da7210_exit(void) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) i2c_del_driver(&da7210_i2c_driver); #endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&da7210_spi_driver); +#endif } module_exit(da7210_exit); diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 4624e752a188..85d9cabe6d55 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -164,8 +164,7 @@ static int jz4740_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { uint32_t val; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec =rtd->codec; + struct snd_soc_codec *codec = dai->codec; switch (params_rate(params)) { case 8000: diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c new file mode 100644 index 000000000000..802b9f176b16 --- /dev/null +++ b/sound/soc/codecs/lm49453.c @@ -0,0 +1,1550 @@ +/* + * lm49453.c - LM49453 ALSA Soc Audio driver + * + * Copyright (c) 2012 Texas Instruments, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * Initially based on sound/soc/codecs/wm8350.c + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <asm/div64.h> +#include "lm49453.h" + +static struct reg_default lm49453_reg_defs[] = { + { 0, 0x00 }, + { 1, 0x00 }, + { 2, 0x00 }, + { 3, 0x00 }, + { 4, 0x00 }, + { 5, 0x00 }, + { 6, 0x00 }, + { 7, 0x00 }, + { 8, 0x00 }, + { 9, 0x00 }, + { 10, 0x00 }, + { 11, 0x00 }, + { 12, 0x00 }, + { 13, 0x00 }, + { 14, 0x00 }, + { 15, 0x00 }, + { 16, 0x00 }, + { 17, 0x00 }, + { 18, 0x00 }, + { 19, 0x00 }, + { 20, 0x00 }, + { 21, 0x00 }, + { 22, 0x00 }, + { 23, 0x00 }, + { 32, 0x00 }, + { 33, 0x00 }, + { 35, 0x00 }, + { 36, 0x00 }, + { 37, 0x00 }, + { 46, 0x00 }, + { 48, 0x00 }, + { 49, 0x00 }, + { 51, 0x00 }, + { 56, 0x00 }, + { 58, 0x00 }, + { 59, 0x00 }, + { 60, 0x00 }, + { 61, 0x00 }, + { 62, 0x00 }, + { 63, 0x00 }, + { 64, 0x00 }, + { 65, 0x00 }, + { 66, 0x00 }, + { 67, 0x00 }, + { 68, 0x00 }, + { 69, 0x00 }, + { 70, 0x00 }, + { 71, 0x00 }, + { 72, 0x00 }, + { 73, 0x00 }, + { 74, 0x00 }, + { 75, 0x00 }, + { 76, 0x00 }, + { 77, 0x00 }, + { 78, 0x00 }, + { 79, 0x00 }, + { 80, 0x00 }, + { 81, 0x00 }, + { 82, 0x00 }, + { 83, 0x00 }, + { 85, 0x00 }, + { 85, 0x00 }, + { 86, 0x00 }, + { 87, 0x00 }, + { 88, 0x00 }, + { 89, 0x00 }, + { 90, 0x00 }, + { 91, 0x00 }, + { 92, 0x00 }, + { 93, 0x00 }, + { 94, 0x00 }, + { 95, 0x00 }, + { 96, 0x01 }, + { 97, 0x00 }, + { 98, 0x00 }, + { 99, 0x00 }, + { 100, 0x00 }, + { 101, 0x00 }, + { 102, 0x00 }, + { 103, 0x01 }, + { 105, 0x01 }, + { 106, 0x00 }, + { 107, 0x01 }, + { 107, 0x00 }, + { 108, 0x00 }, + { 109, 0x00 }, + { 110, 0x00 }, + { 111, 0x02 }, + { 112, 0x02 }, + { 113, 0x00 }, + { 121, 0x80 }, + { 122, 0xBB }, + { 123, 0x80 }, + { 124, 0xBB }, + { 128, 0x00 }, + { 130, 0x00 }, + { 131, 0x00 }, + { 132, 0x00 }, + { 133, 0x0A }, + { 134, 0x0A }, + { 135, 0x0A }, + { 136, 0x0F }, + { 137, 0x00 }, + { 138, 0x73 }, + { 139, 0x33 }, + { 140, 0x73 }, + { 141, 0x33 }, + { 142, 0x73 }, + { 143, 0x33 }, + { 144, 0x73 }, + { 145, 0x33 }, + { 146, 0x73 }, + { 147, 0x33 }, + { 148, 0x73 }, + { 149, 0x33 }, + { 150, 0x73 }, + { 151, 0x33 }, + { 152, 0x00 }, + { 153, 0x00 }, + { 154, 0x00 }, + { 155, 0x00 }, + { 176, 0x00 }, + { 177, 0x00 }, + { 178, 0x00 }, + { 179, 0x00 }, + { 180, 0x00 }, + { 181, 0x00 }, + { 182, 0x00 }, + { 183, 0x00 }, + { 184, 0x00 }, + { 185, 0x00 }, + { 186, 0x00 }, + { 189, 0x00 }, + { 188, 0x00 }, + { 194, 0x00 }, + { 195, 0x00 }, + { 196, 0x00 }, + { 197, 0x00 }, + { 200, 0x00 }, + { 201, 0x00 }, + { 202, 0x00 }, + { 203, 0x00 }, + { 204, 0x00 }, + { 205, 0x00 }, + { 208, 0x00 }, + { 209, 0x00 }, + { 210, 0x00 }, + { 211, 0x00 }, + { 213, 0x00 }, + { 214, 0x00 }, + { 215, 0x00 }, + { 216, 0x00 }, + { 217, 0x00 }, + { 218, 0x00 }, + { 219, 0x00 }, + { 221, 0x00 }, + { 222, 0x00 }, + { 224, 0x00 }, + { 225, 0x00 }, + { 226, 0x00 }, + { 227, 0x00 }, + { 228, 0x00 }, + { 229, 0x00 }, + { 230, 0x13 }, + { 231, 0x00 }, + { 232, 0x80 }, + { 233, 0x0C }, + { 234, 0xDD }, + { 235, 0x00 }, + { 236, 0x04 }, + { 237, 0x00 }, + { 238, 0x00 }, + { 239, 0x00 }, + { 240, 0x00 }, + { 241, 0x00 }, + { 242, 0x00 }, + { 243, 0x00 }, + { 244, 0x00 }, + { 245, 0x00 }, + { 248, 0x00 }, + { 249, 0x00 }, + { 254, 0x00 }, + { 255, 0x00 }, +}; + +/* codec private data */ +struct lm49453_priv { + struct regmap *regmap; + int fs_rate; +}; + +/* capture path controls */ + +static const char *lm49453_mic2mode_text[] = {"Single Ended", "Differential"}; + +static const SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5, + lm49453_mic2mode_text); + +static const char *lm49453_dmic_cfg_text[] = {"DMICDAT1", "DMICDAT2"}; + +static const SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum, + LM49453_P0_DIGITAL_MIC1_CONFIG_REG, + 7, lm49453_dmic_cfg_text); + +static const SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum, + LM49453_P0_DIGITAL_MIC2_CONFIG_REG, + 7, lm49453_dmic_cfg_text); + +/* MUX Controls */ +static const char *lm49453_adcl_mux_text[] = { "MIC1", "Aux_L" }; + +static const char *lm49453_adcr_mux_text[] = { "MIC2", "Aux_R" }; + +static const struct soc_enum lm49453_adcl_enum = + SOC_ENUM_SINGLE(LM49453_P0_ANALOG_MIXER_ADC_REG, 0, + ARRAY_SIZE(lm49453_adcl_mux_text), + lm49453_adcl_mux_text); + +static const struct soc_enum lm49453_adcr_enum = + SOC_ENUM_SINGLE(LM49453_P0_ANALOG_MIXER_ADC_REG, 1, + ARRAY_SIZE(lm49453_adcr_mux_text), + lm49453_adcr_mux_text); + +static const struct snd_kcontrol_new lm49453_adcl_mux_control = + SOC_DAPM_ENUM("ADC Left Mux", lm49453_adcl_enum); + +static const struct snd_kcontrol_new lm49453_adcr_mux_control = + SOC_DAPM_ENUM("ADC Right Mux", lm49453_adcr_enum); + +static const struct snd_kcontrol_new lm49453_headset_left_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPL1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPL1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPL1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPL1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPL1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPL1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPL1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPL1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPL2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPL2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPL2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPL2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPL2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPL2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPL2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPL2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 0, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_headset_right_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPR1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPR1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPR1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPR1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPR1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPR1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPR1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPR1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPR2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPR2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPR2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPR2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPR2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPR2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPR2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPR2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 1, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_speaker_left_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSL1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSL1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSL1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSL1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSL1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSL1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSL1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSL1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSL2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSL2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSL2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSL2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSL2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSL2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSL2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSL2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 2, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_speaker_right_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSR1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSR1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSR1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSR1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSR1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSR1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSR1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSR1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSR2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSR2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSR2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSR2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSR2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSR2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSR2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSR2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 3, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_haptic_left_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAL1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAL1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAL1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAL1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAL1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAL1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAL1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAL1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAL2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAL2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAL2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAL2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAL2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAL2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAL2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAL2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 4, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_haptic_right_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAR1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAR1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAR1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAR1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAR1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAR1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAR1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAR1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAR2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAR2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAR2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAR2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAR2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAR2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAR2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAR2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 5, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_lineout_left_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOL1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOL1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOL1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOL1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOL1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOL1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOL1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOL1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOL2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOL2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOL2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOL2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOL2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOL2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOL2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOL2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 6, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_lineout_right_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOR1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOR1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOR1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOR1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOR1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOR1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOR1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOR1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOR2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOR2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOR2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOR2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOR2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOR2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOR2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOR2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 7, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx1_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT1_TX1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT1_TX1_REG, 7, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx2_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT1_TX2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT1_TX2_REG, 7, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx3_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX3_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX3_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX3_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX3_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX3_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX3_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_PORT1_TX3_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx4_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX4_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX4_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX4_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX4_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX4_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX4_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_PORT1_TX4_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx5_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX5_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX5_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX5_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX5_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX5_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX5_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_PORT1_TX5_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx6_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX6_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX6_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX6_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX6_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX6_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX6_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_PORT1_TX6_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx7_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX7_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX7_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX7_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX7_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX7_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX7_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_PORT1_TX7_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx8_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX8_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX8_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX8_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX8_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX8_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX8_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_PORT1_TX8_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port2_tx1_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT2_TX1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT2_TX1_REG, 7, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port2_tx2_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT2_TX2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0), +}; + +/* TLV Declarations */ +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7650, 150, 1); +static const DECLARE_TLV_DB_SCALE(port_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = { +/* Sidetone supports mono only */ +SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG, + 0, 0x3F, 0, digital_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG, + 0, 0x3F, 0, digital_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG, + 0, 0x3F, 0, digital_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG, + 0, 0x3F, 0, digital_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG, + 0, 0x3F, 0, digital_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG, + 0, 0x3F, 0, digital_tlv), +}; + +static const struct snd_kcontrol_new lm49453_snd_controls[] = { + /* mic1 and mic2 supports mono only */ + SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_ADC_LEVELL_REG, 0, 6, + 0, digital_tlv), + SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_ADC_LEVELR_REG, 0, 6, + 0, digital_tlv), + + SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG, + LM49453_P0_DMIC1_LEVELR_REG, 0, 6, 0, digital_tlv), + SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG, + LM49453_P0_DMIC2_LEVELR_REG, 0, 6, 0, digital_tlv), + + SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum), + SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum), + SOC_DAPM_ENUM("DMIC34 SRC", lm49453_dmic34_cfg_enum), + + /* Capture path filter enable */ + SOC_SINGLE("DMIC1 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG, + 0, 1, 0), + SOC_SINGLE("DMIC2 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG, + 1, 1, 0), + SOC_SINGLE("ADC HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG, + 2, 1, 0), + + SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG, + LM49453_P0_DAC_HP_LEVELR_REG, 0, 6, 0, digital_tlv), + SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG, + LM49453_P0_DAC_LO_LEVELR_REG, 0, 6, 0, digital_tlv), + SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG, + LM49453_P0_DAC_LS_LEVELR_REG, 0, 6, 0, digital_tlv), + SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG, + LM49453_P0_DAC_HA_LEVELR_REG, 0, 6, 0, digital_tlv), + + SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG, + 0, 6, 0, digital_tlv), + + SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, + 0, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_2_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, + 2, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_3_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, + 4, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_4_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, + 6, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_5_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG, + 0, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_6_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG, + 2, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_7_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG, + 4, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_8_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG, + 6, 3, 0, port_tlv), + + SOC_SINGLE_TLV("PORT2_1_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG, + 0, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT2_2_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG, + 2, 3, 0, port_tlv), + + SOC_SINGLE("Port1 Playback Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG, + 1, 1, 0), + SOC_SINGLE("Port2 Playback Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG, + 1, 1, 0), + SOC_SINGLE("Port1 Capture Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG, + 2, 1, 0), + SOC_SINGLE("Port2 Capture Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG, + 2, 1, 0) + +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget lm49453_dapm_widgets[] = { + + /* All end points HP,EP, LS, Lineout and Haptic */ + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + SND_SOC_DAPM_OUTPUT("EPOUT"), + SND_SOC_DAPM_OUTPUT("LSOUTL"), + SND_SOC_DAPM_OUTPUT("LSOUTR"), + SND_SOC_DAPM_OUTPUT("LOOUTR"), + SND_SOC_DAPM_OUTPUT("LOOUTL"), + SND_SOC_DAPM_OUTPUT("HAOUTL"), + SND_SOC_DAPM_OUTPUT("HAOUTR"), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("DMIC1DAT"), + SND_SOC_DAPM_INPUT("DMIC2DAT"), + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("AUXR"), + + SND_SOC_DAPM_PGA("PORT1_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_3_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_4_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_5_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_6_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_7_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_8_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT2_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT2_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("AMIC1Bias", LM49453_P0_MICL_REG, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AMIC2Bias", LM49453_P0_MICR_REG, 6, 0, NULL, 0), + + /* playback path driver enables */ + SND_SOC_DAPM_OUT_DRV("Headset Switch", + LM49453_P0_PMC_SETUP_REG, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Earpiece Switch", + LM49453_P0_EP_REG, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker Left Switch", + LM49453_P0_DIS_PKVL_FB_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker Right Switch", + LM49453_P0_DIS_PKVL_FB_REG, 1, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Haptic Left Switch", + LM49453_P0_DIS_PKVL_FB_REG, 2, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Haptic Right Switch", + LM49453_P0_DIS_PKVL_FB_REG, 3, 1, NULL, 0), + + /* DAC */ + SND_SOC_DAPM_DAC("HPL DAC", "Headset", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("HPR DAC", "Headset", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("LSL DAC", "Speaker", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("LSR DAC", "Speaker", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("HAL DAC", "Haptic", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("HAR DAC", "Haptic", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("LOL DAC", "Lineout", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("LOR DAC", "Lineout", SND_SOC_NOPM, 0, 0), + + + SND_SOC_DAPM_PGA("AUXL Input", + LM49453_P0_ANALOG_MIXER_ADC_REG, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("AUXR Input", + LM49453_P0_ANALOG_MIXER_ADC_REG, 3, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Sidetone", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("DMIC1 Left", "Capture", SND_SOC_NOPM, 1, 0), + SND_SOC_DAPM_ADC("DMIC1 Right", "Capture", SND_SOC_NOPM, 1, 0), + SND_SOC_DAPM_ADC("DMIC2 Left", "Capture", SND_SOC_NOPM, 1, 0), + SND_SOC_DAPM_ADC("DMIC2 Right", "Capture", SND_SOC_NOPM, 1, 0), + + SND_SOC_DAPM_ADC("ADC Left", "Capture", SND_SOC_NOPM, 1, 0), + SND_SOC_DAPM_ADC("ADC Right", "Capture", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 0, 0, + &lm49453_adcl_mux_control), + SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, + &lm49453_adcr_mux_control), + + SND_SOC_DAPM_MUX("Mic1 Input", + SND_SOC_NOPM, 0, 0, &lm49453_adcl_mux_control), + + SND_SOC_DAPM_MUX("Mic2 Input", + SND_SOC_NOPM, 0, 0, &lm49453_adcr_mux_control), + + /* AIF */ + SND_SOC_DAPM_AIF_IN("PORT1_SDI", NULL, 0, + LM49453_P0_PULL_CONFIG1_REG, 2, 0), + SND_SOC_DAPM_AIF_IN("PORT2_SDI", NULL, 0, + LM49453_P0_PULL_CONFIG1_REG, 6, 0), + + SND_SOC_DAPM_AIF_OUT("PORT1_SDO", NULL, 0, + LM49453_P0_PULL_CONFIG1_REG, 3, 0), + SND_SOC_DAPM_AIF_OUT("PORT2_SDO", NULL, 0, + LM49453_P0_PULL_CONFIG1_REG, 7, 0), + + /* Port1 TX controls */ + SND_SOC_DAPM_OUT_DRV("P1_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_3_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_4_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_5_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_6_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_7_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_8_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Port2 TX controls */ + SND_SOC_DAPM_OUT_DRV("P2_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P2_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Sidetone Mixer */ + SND_SOC_DAPM_MIXER("Sidetone Mixer", SND_SOC_NOPM, 0, 0, + lm49453_sidetone_mixer_controls, + ARRAY_SIZE(lm49453_sidetone_mixer_controls)), + + /* DAC MIXERS */ + SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0, + lm49453_headset_left_mixer, + ARRAY_SIZE(lm49453_headset_left_mixer)), + SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0, + lm49453_headset_right_mixer, + ARRAY_SIZE(lm49453_headset_right_mixer)), + SND_SOC_DAPM_MIXER("LOL Mixer", SND_SOC_NOPM, 0, 0, + lm49453_lineout_left_mixer, + ARRAY_SIZE(lm49453_lineout_left_mixer)), + SND_SOC_DAPM_MIXER("LOR Mixer", SND_SOC_NOPM, 0, 0, + lm49453_lineout_right_mixer, + ARRAY_SIZE(lm49453_lineout_right_mixer)), + SND_SOC_DAPM_MIXER("LSL Mixer", SND_SOC_NOPM, 0, 0, + lm49453_speaker_left_mixer, + ARRAY_SIZE(lm49453_speaker_left_mixer)), + SND_SOC_DAPM_MIXER("LSR Mixer", SND_SOC_NOPM, 0, 0, + lm49453_speaker_right_mixer, + ARRAY_SIZE(lm49453_speaker_right_mixer)), + SND_SOC_DAPM_MIXER("HAL Mixer", SND_SOC_NOPM, 0, 0, + lm49453_haptic_left_mixer, + ARRAY_SIZE(lm49453_haptic_left_mixer)), + SND_SOC_DAPM_MIXER("HAR Mixer", SND_SOC_NOPM, 0, 0, + lm49453_haptic_right_mixer, + ARRAY_SIZE(lm49453_haptic_right_mixer)), + + /* Capture Mixer */ + SND_SOC_DAPM_MIXER("Port1_1 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx1_mixer, + ARRAY_SIZE(lm49453_port1_tx1_mixer)), + SND_SOC_DAPM_MIXER("Port1_2 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx2_mixer, + ARRAY_SIZE(lm49453_port1_tx2_mixer)), + SND_SOC_DAPM_MIXER("Port1_3 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx3_mixer, + ARRAY_SIZE(lm49453_port1_tx3_mixer)), + SND_SOC_DAPM_MIXER("Port1_4 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx4_mixer, + ARRAY_SIZE(lm49453_port1_tx4_mixer)), + SND_SOC_DAPM_MIXER("Port1_5 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx5_mixer, + ARRAY_SIZE(lm49453_port1_tx5_mixer)), + SND_SOC_DAPM_MIXER("Port1_6 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx6_mixer, + ARRAY_SIZE(lm49453_port1_tx6_mixer)), + SND_SOC_DAPM_MIXER("Port1_7 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx7_mixer, + ARRAY_SIZE(lm49453_port1_tx7_mixer)), + SND_SOC_DAPM_MIXER("Port1_8 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx8_mixer, + ARRAY_SIZE(lm49453_port1_tx8_mixer)), + + SND_SOC_DAPM_MIXER("Port2_1 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port2_tx1_mixer, + ARRAY_SIZE(lm49453_port2_tx1_mixer)), + SND_SOC_DAPM_MIXER("Port2_2 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port2_tx2_mixer, + ARRAY_SIZE(lm49453_port2_tx2_mixer)), +}; + +static const struct snd_soc_dapm_route lm49453_audio_map[] = { + /* Port SDI mapping */ + { "PORT1_1_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_2_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_3_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_4_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_5_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_6_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_7_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_8_RX", "Port1 Playback Switch", "PORT1_SDI" }, + + { "PORT2_1_RX", "Port2 Playback Switch", "PORT2_SDI" }, + { "PORT2_2_RX", "Port2 Playback Switch", "PORT2_SDI" }, + + /* HP mapping */ + { "HPL Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "HPL Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "HPL Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "HPL Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "HPL Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "HPL Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "HPL Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "HPL Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + { "HPL Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "HPL Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "HPL Mixer", "ADCL Switch", "ADC Left" }, + { "HPL Mixer", "ADCR Switch", "ADC Right" }, + { "HPL Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "HPL Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "HPL Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "HPL Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "HPL Mixer", "Sidetone Switch", "Sidetone" }, + + { "HPL DAC", NULL, "HPL Mixer" }, + + { "HPR Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "HPR Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "HPR Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "HPR Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "HPR Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "HPR Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "HPR Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "HPR Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "HPR Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "HPR Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "HPR Mixer", "ADCL Switch", "ADC Left" }, + { "HPR Mixer", "ADCR Switch", "ADC Right" }, + { "HPR Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "HPR Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "HPR Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "HPR Mixer", "DMIC2L Switch", "DMIC2 Right" }, + { "HPR Mixer", "Sidetone Switch", "Sidetone" }, + + { "HPR DAC", NULL, "HPR Mixer" }, + + { "HPOUTL", "Headset Switch", "HPL DAC"}, + { "HPOUTR", "Headset Switch", "HPR DAC"}, + + /* EP map */ + { "EPOUT", "Earpiece Switch", "HPL DAC" }, + + /* Speaker map */ + { "LSL Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "LSL Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "LSL Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "LSL Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "LSL Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "LSL Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "LSL Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "LSL Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "LSL Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "LSL Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "LSL Mixer", "ADCL Switch", "ADC Left" }, + { "LSL Mixer", "ADCR Switch", "ADC Right" }, + { "LSL Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "LSL Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "LSL Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "LSL Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "LSL Mixer", "Sidetone Switch", "Sidetone" }, + + { "LSL DAC", NULL, "LSL Mixer" }, + + { "LSR Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "LSR Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "LSR Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "LSR Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "LSR Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "LSR Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "LSR Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "LSR Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "LSR Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "LSR Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "LSR Mixer", "ADCL Switch", "ADC Left" }, + { "LSR Mixer", "ADCR Switch", "ADC Right" }, + { "LSR Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "LSR Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "LSR Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "LSR Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "LSR Mixer", "Sidetone Switch", "Sidetone" }, + + { "LSR DAC", NULL, "LSR Mixer" }, + + { "LSOUTL", "Speaker Left Switch", "LSL DAC"}, + { "LSOUTR", "Speaker Left Switch", "LSR DAC"}, + + /* Haptic map */ + { "HAL Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "HAL Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "HAL Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "HAL Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "HAL Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "HAL Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "HAL Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "HAL Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "HAL Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "HAL Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "HAL Mixer", "ADCL Switch", "ADC Left" }, + { "HAL Mixer", "ADCR Switch", "ADC Right" }, + { "HAL Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "HAL Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "HAL Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "HAL Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "HAL Mixer", "Sidetone Switch", "Sidetone" }, + + { "HAL DAC", NULL, "HAL Mixer" }, + + { "HAR Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "HAR Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "HAR Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "HAR Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "HAR Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "HAR Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "HAR Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "HAR Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "HAR Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "HAR Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "HAR Mixer", "ADCL Switch", "ADC Left" }, + { "HAR Mixer", "ADCR Switch", "ADC Right" }, + { "HAR Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "HAR Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "HAR Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "HAR Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "HAR Mixer", "Sideton Switch", "Sidetone" }, + + { "HAR DAC", NULL, "HAR Mixer" }, + + { "HAOUTL", "Haptic Left Switch", "HAL DAC" }, + { "HAOUTR", "Haptic Right Switch", "HAR DAC" }, + + /* Lineout map */ + { "LOL Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "LOL Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "LOL Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "LOL Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "LOL Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "LOL Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "LOL Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "LOL Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "LOL Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "LOL Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "LOL Mixer", "ADCL Switch", "ADC Left" }, + { "LOL Mixer", "ADCR Switch", "ADC Right" }, + { "LOL Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "LOL Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "LOL Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "LOL Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "LOL Mixer", "Sidetone Switch", "Sidetone" }, + + { "LOL DAC", NULL, "LOL Mixer" }, + + { "LOR Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "LOR Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "LOR Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "LOR Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "LOR Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "LOR Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "LOR Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "LOR Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "LOR Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "LOR Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "LOR Mixer", "ADCL Switch", "ADC Left" }, + { "LOR Mixer", "ADCR Switch", "ADC Right" }, + { "LOR Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "LOR Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "LOR Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "LOR Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "LOR Mixer", "Sidetone Switch", "Sidetone" }, + + { "LOR DAC", NULL, "LOR Mixer" }, + + { "LOOUTL", NULL, "LOL DAC" }, + { "LOOUTR", NULL, "LOR DAC" }, + + /* TX map */ + /* Port1 mappings */ + { "Port1_1 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_1 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_1 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_1 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_1 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_1 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_2 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_2 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_2 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_2 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_2 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_2 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_3 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_3 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_3 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_3 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_3 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_3 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_4 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_4 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_4 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_4 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_4 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_4 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_5 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_5 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_5 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_5 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_5 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_5 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_6 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_6 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_6 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_6 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_6 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_6 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_7 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_7 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_7 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_7 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_7 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_7 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_8 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_8 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_8 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_8 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_8 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_8 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port2_1 Mixer", "ADCL Switch", "ADC Left" }, + { "Port2_1 Mixer", "ADCR Switch", "ADC Right" }, + { "Port2_1 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port2_1 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port2_1 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port2_1 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port2_2 Mixer", "ADCL Switch", "ADC Left" }, + { "Port2_2 Mixer", "ADCR Switch", "ADC Right" }, + { "Port2_2 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port2_2 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port2_2 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port2_2 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "P1_1_TX", NULL, "Port1_1 Mixer" }, + { "P1_2_TX", NULL, "Port1_2 Mixer" }, + { "P1_3_TX", NULL, "Port1_3 Mixer" }, + { "P1_4_TX", NULL, "Port1_4 Mixer" }, + { "P1_5_TX", NULL, "Port1_5 Mixer" }, + { "P1_6_TX", NULL, "Port1_6 Mixer" }, + { "P1_7_TX", NULL, "Port1_7 Mixer" }, + { "P1_8_TX", NULL, "Port1_8 Mixer" }, + + { "P2_1_TX", NULL, "Port2_1 Mixer" }, + { "P2_2_TX", NULL, "Port2_2 Mixer" }, + + { "PORT1_SDO", "Port1 Capture Switch", "P1_1_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_2_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_3_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_4_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_5_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_6_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_7_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_8_TX"}, + + { "PORT2_SDO", "Port2 Capture Switch", "P2_1_TX"}, + { "PORT2_SDO", "Port2 Capture Switch", "P2_2_TX"}, + + { "Mic1 Input", NULL, "AMIC1" }, + { "Mic2 Input", NULL, "AMIC2" }, + + { "AUXL Input", NULL, "AUXL" }, + { "AUXR Input", NULL, "AUXR" }, + + /* AUX connections */ + { "ADCL Mux", "Aux_L", "AUXL Input" }, + { "ADCL Mux", "MIC1", "Mic1 Input" }, + + { "ADCR Mux", "Aux_R", "AUXR Input" }, + { "ADCR Mux", "MIC2", "Mic2 Input" }, + + /* ADC connection */ + { "ADC Left", NULL, "ADCL Mux"}, + { "ADC Right", NULL, "ADCR Mux"}, + + { "DMIC1 Left", NULL, "DMIC1DAT"}, + { "DMIC1 Right", NULL, "DMIC1DAT"}, + { "DMIC2 Left", NULL, "DMIC2DAT"}, + { "DMIC2 Right", NULL, "DMIC2DAT"}, + + /* Sidetone map */ + { "Sidetone Mixer", NULL, "ADC Left" }, + { "Sidetone Mixer", NULL, "ADC Right" }, + { "Sidetone Mixer", NULL, "DMIC1 Left" }, + { "Sidetone Mixer", NULL, "DMIC1 Right" }, + { "Sidetone Mixer", NULL, "DMIC2 Left" }, + { "Sidetone Mixer", NULL, "DMIC2 Right" }, + + { "Sidetone", "Sidetone Switch", "Sidetone Mixer" }, +}; + +static int lm49453_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec); + u16 clk_div = 0; + + lm49453->fs_rate = params_rate(params); + + /* Setting DAC clock dividers based on substream sample rate. */ + switch (lm49453->fs_rate) { + case 8000: + case 16000: + case 32000: + case 24000: + case 48000: + clk_div = 256; + break; + case 11025: + case 22050: + case 44100: + clk_div = 216; + break; + case 96000: + clk_div = 127; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, LM49453_P0_ADC_CLK_DIV_REG, clk_div); + snd_soc_write(codec, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div); + + return 0; +} + +static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + u16 aif_val; + int mode = 0; + int clk_phase = 0; + int clk_shift = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + aif_val = 0; + break; + case SND_SOC_DAIFMT_CBS_CFM: + aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS; + break; + case SND_SOC_DAIFMT_CBM_CFS: + aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS | + LM49453_AUDIO_PORT1_BASIC_SYNC_MS; + break; + default: + return -EINVAL; + } + + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + mode = 1; + clk_phase = (1 << 5); + clk_shift = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + mode = 1; + clk_phase = (1 << 5); + clk_shift = 0; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG, + LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(1)|BIT(5), + (aif_val | mode | clk_phase)); + + snd_soc_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift); + + return 0; +} + +static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + u16 pll_clk = 0; + + switch (freq) { + case 12288000: + case 26000000: + case 19200000: + /* pll clk slection */ + pll_clk = 0; + break; + case 48000: + case 32576: + /* fll clk slection */ + pll_clk = BIT(4); + return 0; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk); + + return 0; +} + +static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0), + (mute ? (BIT(1)|BIT(0)) : 0)); + return 0; +} + +static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2), + (mute ? (BIT(3)|BIT(2)) : 0)); + return 0; +} + +static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4), + (mute ? (BIT(5)|BIT(4)) : 0)); + return 0; +} + +static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(4), + (mute ? BIT(4) : 0)); + return 0; +} + +static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6), + (mute ? (BIT(7)|BIT(6)) : 0)); + return 0; +} + +static int lm49453_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + regcache_sync(lm49453->regmap); + + snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, + LM49453_PMC_SETUP_CHIP_EN, LM49453_CHIP_EN); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, + LM49453_PMC_SETUP_CHIP_EN, 0); + break; + } + + codec->dapm.bias_level = level; + + return 0; +} + +/* Formates supported by LM49453 driver. */ +#define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops lm49453_headset_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .digital_mute = lm49453_hp_mute, +}; + +static struct snd_soc_dai_ops lm49453_speaker_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .digital_mute = lm49453_ls_mute, +}; + +static struct snd_soc_dai_ops lm49453_haptic_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .digital_mute = lm49453_ha_mute, +}; + +static struct snd_soc_dai_ops lm49453_ep_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .digital_mute = lm49453_ep_mute, +}; + +static struct snd_soc_dai_ops lm49453_lineout_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .digital_mute = lm49453_lo_mute, +}; + +/* LM49453 dai structure. */ +static const struct snd_soc_dai_driver lm49453_dai[] = { + { + .name = "LM49453 Headset", + .playback = { + .stream_name = "Headset", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 5, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_headset_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "LM49453 Speaker", + .playback = { + .stream_name = "Speaker", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_speaker_dai_ops, + }, + { + .name = "LM49453 Haptic", + .playback = { + .stream_name = "Haptic", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_haptic_dai_ops, + }, + { + .name = "LM49453 Earpiece", + .playback = { + .stream_name = "Earpiece", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_ep_dai_ops, + }, + { + .name = "LM49453 line out", + .playback = { + .stream_name = "Lineout", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_lineout_dai_ops, + }, +}; + +static int lm49453_suspend(struct snd_soc_codec *codec) +{ + lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int lm49453_resume(struct snd_soc_codec *codec) +{ + lm49453_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static int lm49453_probe(struct snd_soc_codec *codec) +{ + struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + codec->control_data = lm49453->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + return 0; +} + +/* power down chip */ +static int lm49453_remove(struct snd_soc_codec *codec) +{ + lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_lm49453 = { + .probe = lm49453_probe, + .remove = lm49453_remove, + .suspend = lm49453_suspend, + .resume = lm49453_resume, + .set_bias_level = lm49453_set_bias_level, + .controls = lm49453_snd_controls, + .num_controls = ARRAY_SIZE(lm49453_snd_controls), + .dapm_widgets = lm49453_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets), + .dapm_routes = lm49453_audio_map, + .num_dapm_routes = ARRAY_SIZE(lm49453_audio_map), + .idle_bias_off = true, +}; + +static const struct regmap_config lm49453_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = LM49453_MAX_REGISTER, + .reg_defaults = lm49453_reg_defs, + .num_reg_defaults = ARRAY_SIZE(lm49453_reg_defs), + .cache_type = REGCACHE_RBTREE, +}; + +static __devinit int lm49453_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lm49453_priv *lm49453; + int ret = 0; + + lm49453 = devm_kzalloc(&i2c->dev, sizeof(struct lm49453_priv), + GFP_KERNEL); + + if (lm49453 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, lm49453); + + lm49453->regmap = regmap_init_i2c(i2c, &lm49453_regmap_config); + if (IS_ERR(lm49453->regmap)) { + ret = PTR_ERR(lm49453->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_lm49453, + lm49453_dai, ARRAY_SIZE(lm49453_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + regmap_exit(lm49453->regmap); + return ret; + } + + return ret; +} + +static int __devexit lm49453_i2c_remove(struct i2c_client *client) +{ + struct lm49453_priv *lm49453 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + regmap_exit(lm49453->regmap); + return 0; +} + +static const struct i2c_device_id lm49453_i2c_id[] = { + { "lm49453", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id); + +static struct i2c_driver lm49453_i2c_driver = { + .driver = { + .name = "lm49453", + .owner = THIS_MODULE, + }, + .probe = lm49453_i2c_probe, + .remove = __devexit_p(lm49453_i2c_remove), + .id_table = lm49453_i2c_id, +}; + +module_i2c_driver(lm49453_i2c_driver); + +MODULE_DESCRIPTION("ASoC LM49453 driver"); +MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/lm49453.h b/sound/soc/codecs/lm49453.h new file mode 100644 index 000000000000..a63cfa5c0883 --- /dev/null +++ b/sound/soc/codecs/lm49453.h @@ -0,0 +1,380 @@ +/* + * lm49453.h - LM49453 ALSA Soc Audio drive + * + * Copyright (c) 2012 Texas Instruments, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + */ + +#ifndef _LM49453_H +#define _LM49453_H + +#include <linux/bitops.h> + +/* LM49453_P0 register space for page0 */ +#define LM49453_P0_PMC_SETUP_REG 0x00 +#define LM49453_P0_PLL_CLK_SEL1_REG 0x01 +#define LM49453_P0_PLL_CLK_SEL2_REG 0x02 +#define LM49453_P0_PMC_CLK_DIV_REG 0x03 +#define LM49453_P0_HSDET_CLK_DIV_REG 0x04 +#define LM49453_P0_DMIC_CLK_DIV_REG 0x05 +#define LM49453_P0_ADC_CLK_DIV_REG 0x06 +#define LM49453_P0_DAC_OT_CLK_DIV_REG 0x07 +#define LM49453_P0_PLL_HF_M_REG 0x08 +#define LM49453_P0_PLL_LF_M_REG 0x09 +#define LM49453_P0_PLL_NL_REG 0x0A +#define LM49453_P0_PLL_N_MODL_REG 0x0B +#define LM49453_P0_PLL_N_MODH_REG 0x0C +#define LM49453_P0_PLL_P1_REG 0x0D +#define LM49453_P0_PLL_P2_REG 0x0E +#define LM49453_P0_FLL_REF_FREQL_REG 0x0F +#define LM49453_P0_FLL_REF_FREQH_REG 0x10 +#define LM49453_P0_VCO_TARGETLL_REG 0x11 +#define LM49453_P0_VCO_TARGETLH_REG 0x12 +#define LM49453_P0_VCO_TARGETHL_REG 0x13 +#define LM49453_P0_VCO_TARGETHH_REG 0x14 +#define LM49453_P0_PLL_CONFIG_REG 0x15 +#define LM49453_P0_DAC_CLK_SEL_REG 0x16 +#define LM49453_P0_DAC_HP_CLK_DIV_REG 0x17 + +/* Analog Mixer Input Stages */ +#define LM49453_P0_MICL_REG 0x20 +#define LM49453_P0_MICR_REG 0x21 +#define LM49453_P0_EP_REG 0x24 +#define LM49453_P0_DIS_PKVL_FB_REG 0x25 + +/* Analog Mixer Output Stages */ +#define LM49453_P0_ANALOG_MIXER_ADC_REG 0x2E + +/*ADC or DAC */ +#define LM49453_P0_ADC_DSP_REG 0x30 +#define LM49453_P0_DAC_DSP_REG 0x31 + +/* EFFECTS ENABLES */ +#define LM49453_P0_ADC_FX_ENABLES_REG 0x33 + +/* GPIO */ +#define LM49453_P0_GPIO1_REG 0x38 +#define LM49453_P0_GPIO2_REG 0x39 +#define LM49453_P0_GPIO3_REG 0x3A +#define LM49453_P0_HAP_CTL_REG 0x3B +#define LM49453_P0_HAP_FREQ_PROG_LEFTL_REG 0x3C +#define LM49453_P0_HAP_FREQ_PROG_LEFTH_REG 0x3D +#define LM49453_P0_HAP_FREQ_PROG_RIGHTL_REG 0x3E +#define LM49453_P0_HAP_FREQ_PROG_RIGHTH_REG 0x3F + +/* DIGITAL MIXER */ +#define LM49453_P0_DMIX_CLK_SEL_REG 0x40 +#define LM49453_P0_PORT1_RX_LVL1_REG 0x41 +#define LM49453_P0_PORT1_RX_LVL2_REG 0x42 +#define LM49453_P0_PORT2_RX_LVL_REG 0x43 +#define LM49453_P0_PORT1_TX1_REG 0x44 +#define LM49453_P0_PORT1_TX2_REG 0x45 +#define LM49453_P0_PORT1_TX3_REG 0x46 +#define LM49453_P0_PORT1_TX4_REG 0x47 +#define LM49453_P0_PORT1_TX5_REG 0x48 +#define LM49453_P0_PORT1_TX6_REG 0x49 +#define LM49453_P0_PORT1_TX7_REG 0x4A +#define LM49453_P0_PORT1_TX8_REG 0x4B +#define LM49453_P0_PORT2_TX1_REG 0x4C +#define LM49453_P0_PORT2_TX2_REG 0x4D +#define LM49453_P0_STN_SEL_REG 0x4F +#define LM49453_P0_DACHPL1_REG 0x50 +#define LM49453_P0_DACHPL2_REG 0x51 +#define LM49453_P0_DACHPR1_REG 0x52 +#define LM49453_P0_DACHPR2_REG 0x53 +#define LM49453_P0_DACLOL1_REG 0x54 +#define LM49453_P0_DACLOL2_REG 0x55 +#define LM49453_P0_DACLOR1_REG 0x56 +#define LM49453_P0_DACLOR2_REG 0x57 +#define LM49453_P0_DACLSL1_REG 0x58 +#define LM49453_P0_DACLSL2_REG 0x59 +#define LM49453_P0_DACLSR1_REG 0x5A +#define LM49453_P0_DACLSR2_REG 0x5B +#define LM49453_P0_DACHAL1_REG 0x5C +#define LM49453_P0_DACHAL2_REG 0x5D +#define LM49453_P0_DACHAR1_REG 0x5E +#define LM49453_P0_DACHAR2_REG 0x5F + +/* AUDIO PORT 1 (TDM) */ +#define LM49453_P0_AUDIO_PORT1_BASIC_REG 0x60 +#define LM49453_P0_AUDIO_PORT1_CLK_GEN1_REG 0x61 +#define LM49453_P0_AUDIO_PORT1_CLK_GEN2_REG 0x62 +#define LM49453_P0_AUDIO_PORT1_CLK_GEN3_REG 0x63 +#define LM49453_P0_AUDIO_PORT1_SYNC_RATE_REG 0x64 +#define LM49453_P0_AUDIO_PORT1_SYNC_SDO_SETUP_REG 0x65 +#define LM49453_P0_AUDIO_PORT1_DATA_WIDTH_REG 0x66 +#define LM49453_P0_AUDIO_PORT1_RX_MSB_REG 0x67 +#define LM49453_P0_AUDIO_PORT1_TX_MSB_REG 0x68 +#define LM49453_P0_AUDIO_PORT1_TDM_CHANNELS_REG 0x69 + +/* AUDIO PORT 2 */ +#define LM49453_P0_AUDIO_PORT2_BASIC_REG 0x6A +#define LM49453_P0_AUDIO_PORT2_CLK_GEN1_REG 0x6B +#define LM49453_P0_AUDIO_PORT2_CLK_GEN2_REG 0x6C +#define LM49453_P0_AUDIO_PORT2_SYNC_GEN_REG 0x6D +#define LM49453_P0_AUDIO_PORT2_DATA_WIDTH_REG 0x6E +#define LM49453_P0_AUDIO_PORT2_RX_MODE_REG 0x6F +#define LM49453_P0_AUDIO_PORT2_TX_MODE_REG 0x70 + +/* SAMPLE RATE */ +#define LM49453_P0_PORT1_SR_LSB_REG 0x79 +#define LM49453_P0_PORT1_SR_MSB_REG 0x7A +#define LM49453_P0_PORT2_SR_LSB_REG 0x7B +#define LM49453_P0_PORT2_SR_MSB_REG 0x7C + +/* EFFECTS - HPFs */ +#define LM49453_P0_HPF_REG 0x80 + +/* EFFECTS ADC ALC */ +#define LM49453_P0_ADC_ALC1_REG 0x82 +#define LM49453_P0_ADC_ALC2_REG 0x83 +#define LM49453_P0_ADC_ALC3_REG 0x84 +#define LM49453_P0_ADC_ALC4_REG 0x85 +#define LM49453_P0_ADC_ALC5_REG 0x86 +#define LM49453_P0_ADC_ALC6_REG 0x87 +#define LM49453_P0_ADC_ALC7_REG 0x88 +#define LM49453_P0_ADC_ALC8_REG 0x89 +#define LM49453_P0_DMIC1_LEVELL_REG 0x8A +#define LM49453_P0_DMIC1_LEVELR_REG 0x8B +#define LM49453_P0_DMIC2_LEVELL_REG 0x8C +#define LM49453_P0_DMIC2_LEVELR_REG 0x8D +#define LM49453_P0_ADC_LEVELL_REG 0x8E +#define LM49453_P0_ADC_LEVELR_REG 0x8F +#define LM49453_P0_DAC_HP_LEVELL_REG 0x90 +#define LM49453_P0_DAC_HP_LEVELR_REG 0x91 +#define LM49453_P0_DAC_LO_LEVELL_REG 0x92 +#define LM49453_P0_DAC_LO_LEVELR_REG 0x93 +#define LM49453_P0_DAC_LS_LEVELL_REG 0x94 +#define LM49453_P0_DAC_LS_LEVELR_REG 0x95 +#define LM49453_P0_DAC_HA_LEVELL_REG 0x96 +#define LM49453_P0_DAC_HA_LEVELR_REG 0x97 +#define LM49453_P0_SOFT_MUTE_REG 0x98 +#define LM49453_P0_DMIC_MUTE_CFG_REG 0x99 +#define LM49453_P0_ADC_MUTE_CFG_REG 0x9A +#define LM49453_P0_DAC_MUTE_CFG_REG 0x9B + +/*DIGITAL MIC1 */ +#define LM49453_P0_DIGITAL_MIC1_CONFIG_REG 0xB0 +#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYL_REG 0xB1 +#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYR_REG 0xB2 + +/*DIGITAL MIC2 */ +#define LM49453_P0_DIGITAL_MIC2_CONFIG_REG 0xB3 +#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYL_REG 0xB4 +#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYR_REG 0xB5 + +/* ADC DECIMATOR */ +#define LM49453_P0_ADC_DECIMATOR_REG 0xB6 + +/* DAC CONFIGURE */ +#define LM49453_P0_DAC_CONFIG_REG 0xB7 + +/* SIDETONE */ +#define LM49453_P0_STN_VOL_ADCL_REG 0xB8 +#define LM49453_P0_STN_VOL_ADCR_REG 0xB9 +#define LM49453_P0_STN_VOL_DMIC1L_REG 0xBA +#define LM49453_P0_STN_VOL_DMIC1R_REG 0xBB +#define LM49453_P0_STN_VOL_DMIC2L_REG 0xBC +#define LM49453_P0_STN_VOL_DMIC2R_REG 0xBD + +/* ADC/DAC CLIPPING MONITORS (Read Only/Write to Clear) */ +#define LM49453_P0_ADC_DEC_CLIP_REG 0xC2 +#define LM49453_P0_ADC_HPF_CLIP_REG 0xC3 +#define LM49453_P0_ADC_LVL_CLIP_REG 0xC4 +#define LM49453_P0_DAC_LVL_CLIP_REG 0xC5 + +/* ADC ALC EFFECT MONITORS (Read Only) */ +#define LM49453_P0_ADC_LVLMONL_REG 0xC8 +#define LM49453_P0_ADC_LVLMONR_REG 0xC9 +#define LM49453_P0_ADC_ALCMONL_REG 0xCA +#define LM49453_P0_ADC_ALCMONR_REG 0xCB +#define LM49453_P0_ADC_MUTED_REG 0xCC +#define LM49453_P0_DAC_MUTED_REG 0xCD + +/* HEADSET DETECT */ +#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITL_REG 0xD0 +#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITR_REG 0xD1 +#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITL_REG 0xD2 +#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITH_REG 0xD3 +#define LM49453_P0_HSD_TIMEOUT1_REG 0xD4 +#define LM49453_P0_HSD_TIMEOUT2_REG 0xD5 +#define LM49453_P0_HSD_TIMEOUT3_REG 0xD6 +#define LM49453_P0_HSD_PIN3_4_CFG_REG 0xD7 +#define LM49453_P0_HSD_IRQ1_REG 0xD8 +#define LM49453_P0_HSD_IRQ2_REG 0xD9 +#define LM49453_P0_HSD_IRQ3_REG 0xDA +#define LM49453_P0_HSD_IRQ4_REG 0xDB +#define LM49453_P0_HSD_IRQ_MASK1_REG 0xDC +#define LM49453_P0_HSD_IRQ_MASK2_REG 0xDD +#define LM49453_P0_HSD_IRQ_MASK3_REG 0xDE +#define LM49453_P0_HSD_R_HPLL_REG 0xE0 +#define LM49453_P0_HSD_R_HPLH_REG 0xE1 +#define LM49453_P0_HSD_R_HPLU_REG 0xE2 +#define LM49453_P0_HSD_R_HPRL_REG 0xE3 +#define LM49453_P0_HSD_R_HPRH_REG 0xE4 +#define LM49453_P0_HSD_R_HPRU_REG 0xE5 +#define LM49453_P0_HSD_VEL_L_FINALL_REG 0xE6 +#define LM49453_P0_HSD_VEL_L_FINALH_REG 0xE7 +#define LM49453_P0_HSD_VEL_L_FINALU_REG 0xE8 +#define LM49453_P0_HSD_RO_FINALL_REG 0xE9 +#define LM49453_P0_HSD_RO_FINALH_REG 0xEA +#define LM49453_P0_HSD_RO_FINALU_REG 0xEB +#define LM49453_P0_HSD_VMIC_BIAS_FINALL_REG 0xEC +#define LM49453_P0_HSD_VMIC_BIAS_FINALH_REG 0xED +#define LM49453_P0_HSD_VMIC_BIAS_FINALU_REG 0xEE +#define LM49453_P0_HSD_PIN_CONFIG_REG 0xEF +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS1_REG 0xF1 +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS2_REG 0xF2 +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS3_REG 0xF3 +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEL_REG 0xF4 +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEH_REG 0xF5 + +/* I/O PULLDOWN CONFIG */ +#define LM49453_P0_PULL_CONFIG1_REG 0xF8 +#define LM49453_P0_PULL_CONFIG2_REG 0xF9 +#define LM49453_P0_PULL_CONFIG3_REG 0xFA + +/* RESET */ +#define LM49453_P0_RESET_REG 0xFE + +/* PAGE */ +#define LM49453_PAGE_REG 0xFF + +#define LM49453_MAX_REGISTER (0xFF+1) + +/* LM49453_P0_PMC_SETUP_REG (0x00h) */ +#define LM49453_PMC_SETUP_CHIP_EN (BIT(1)|BIT(0)) +#define LM49453_PMC_SETUP_PLL_EN BIT(2) +#define LM49453_PMC_SETUP_PLL_P2_EN BIT(3) +#define LM49453_PMC_SETUP_PLL_FLL BIT(4) +#define LM49453_PMC_SETUP_MCLK_OVER BIT(5) +#define LM49453_PMC_SETUP_RTC_CLK_OVER BIT(6) +#define LM49453_PMC_SETUP_CHIP_ACTIVE BIT(7) + +/* Chip Enable bits */ +#define LM49453_CHIP_EN_SHUTDOWN 0x00 +#define LM49453_CHIP_EN 0x01 +#define LM49453_CHIP_EN_HSD_DETECT 0x02 +#define LM49453_CHIP_EN_INVALID_HSD 0x03 + +/* LM49453_P0_PLL_CLK_SEL1_REG (0x01h) */ +#define LM49453_CLK_SEL1_MCLK_SEL 0x11 +#define LM49453_CLK_SEL1_RTC_SEL 0x11 +#define LM49453_CLK_SEL1_PORT1_SEL 0x10 +#define LM49453_CLK_SEL1_PORT2_SEL 0x11 + +/* LM49453_P0_PLL_CLK_SEL2_REG (0x02h) */ +#define LM49453_CLK_SEL2_ADC_CLK_SEL 0x38 + +/* LM49453_P0_FLL_REF_FREQL_REG (0x0F) */ +#define LM49453_FLL_REF_FREQ_VAL 0x8ca0001 + +/* LM49453_P0_VCO_TARGETLL_REG (0x11) */ +#define LM49453_VCO_TARGET_VAL 0x8ca0001 + +/* LM49453_P0_ADC_DSP_REG (0x30h) */ +#define LM49453_ADC_DSP_ADC_MUTEL BIT(0) +#define LM49453_ADC_DSP_ADC_MUTER BIT(1) +#define LM49453_ADC_DSP_DMIC1_MUTEL BIT(2) +#define LM49453_ADC_DSP_DMIC1_MUTER BIT(3) +#define LM49453_ADC_DSP_DMIC2_MUTEL BIT(4) +#define LM49453_ADC_DSP_DMIC2_MUTER BIT(5) +#define LM49453_ADC_DSP_MUTE_ALL 0x3F + +/* LM49453_P0_DAC_DSP_REG (0x31h) */ +#define LM49453_DAC_DSP_MUTE_ALL 0xFF + +/* LM49453_P0_AUDIO_PORT1_BASIC_REG (0x60h) */ +#define LM49453_AUDIO_PORT1_BASIC_FMT_MASK (BIT(4)|BIT(3)) +#define LM49453_AUDIO_PORT1_BASIC_CLK_MS BIT(3) +#define LM49453_AUDIO_PORT1_BASIC_SYNC_MS BIT(4) + +/* LM49453_P0_RESET_REG (0xFEh) */ +#define LM49453_RESET_REG_RST BIT(0) + +/* Page select register bits (0xFF) */ +#define LM49453_PAGE0_SELECT 0x0 +#define LM49453_PAGE1_SELECT 0x1 + +/* LM49453_P0_HSD_PIN3_4_CFG_REG (Jack Pin config - 0xD7) */ +#define LM49453_JACK_DISABLE 0x00 +#define LM49453_JACK_CONFIG1 0x01 +#define LM49453_JACK_CONFIG2 0x02 +#define LM49453_JACK_CONFIG3 0x03 +#define LM49453_JACK_CONFIG4 0x04 +#define LM49453_JACK_CONFIG5 0x05 + +/* Page 1 REGISTERS */ + +/* SIDETONE */ +#define LM49453_P1_SIDETONE_SA0L_REG 0x80 +#define LM49453_P1_SIDETONE_SA0H_REG 0x81 +#define LM49453_P1_SIDETONE_SAB0U_REG 0x82 +#define LM49453_P1_SIDETONE_SB0L_REG 0x83 +#define LM49453_P1_SIDETONE_SB0H_REG 0x84 +#define LM49453_P1_SIDETONE_SH0L_REG 0x85 +#define LM49453_P1_SIDETONE_SH0H_REG 0x86 +#define LM49453_P1_SIDETONE_SH0U_REG 0x87 +#define LM49453_P1_SIDETONE_SA1L_REG 0x88 +#define LM49453_P1_SIDETONE_SA1H_REG 0x89 +#define LM49453_P1_SIDETONE_SAB1U_REG 0x8A +#define LM49453_P1_SIDETONE_SB1L_REG 0x8B +#define LM49453_P1_SIDETONE_SB1H_REG 0x8C +#define LM49453_P1_SIDETONE_SH1L_REG 0x8D +#define LM49453_P1_SIDETONE_SH1H_REG 0x8E +#define LM49453_P1_SIDETONE_SH1U_REG 0x8F +#define LM49453_P1_SIDETONE_SA2L_REG 0x90 +#define LM49453_P1_SIDETONE_SA2H_REG 0x91 +#define LM49453_P1_SIDETONE_SAB2U_REG 0x92 +#define LM49453_P1_SIDETONE_SB2L_REG 0x93 +#define LM49453_P1_SIDETONE_SB2H_REG 0x94 +#define LM49453_P1_SIDETONE_SH2L_REG 0x95 +#define LM49453_P1_SIDETONE_SH2H_REG 0x96 +#define LM49453_P1_SIDETONE_SH2U_REG 0x97 +#define LM49453_P1_SIDETONE_SA3L_REG 0x98 +#define LM49453_P1_SIDETONE_SA3H_REG 0x99 +#define LM49453_P1_SIDETONE_SAB3U_REG 0x9A +#define LM49453_P1_SIDETONE_SB3L_REG 0x9B +#define LM49453_P1_SIDETONE_SB3H_REG 0x9C +#define LM49453_P1_SIDETONE_SH3L_REG 0x9D +#define LM49453_P1_SIDETONE_SH3H_REG 0x9E +#define LM49453_P1_SIDETONE_SH3U_REG 0x9F +#define LM49453_P1_SIDETONE_SA4L_REG 0xA0 +#define LM49453_P1_SIDETONE_SA4H_REG 0xA1 +#define LM49453_P1_SIDETONE_SAB4U_REG 0xA2 +#define LM49453_P1_SIDETONE_SB4L_REG 0xA3 +#define LM49453_P1_SIDETONE_SB4H_REG 0xA4 +#define LM49453_P1_SIDETONE_SH4L_REG 0xA5 +#define LM49453_P1_SIDETONE_SH4H_REG 0xA6 +#define LM49453_P1_SIDETONE_SH4U_REG 0xA7 +#define LM49453_P1_SIDETONE_SA5L_REG 0xA8 +#define LM49453_P1_SIDETONE_SA5H_REG 0xA9 +#define LM49453_P1_SIDETONE_SAB5U_REG 0xAA +#define LM49453_P1_SIDETONE_SB5L_REG 0xAB +#define LM49453_P1_SIDETONE_SB5H_REG 0xAC +#define LM49453_P1_SIDETONE_SH5L_REG 0xAD +#define LM49453_P1_SIDETONE_SH5H_REG 0xAE +#define LM49453_P1_SIDETONE_SH5U_REG 0xAF + +/* CHARGE PUMP CONFIG */ +#define LM49453_P1_CP_CONFIG1_REG 0xB0 +#define LM49453_P1_CP_CONFIG2_REG 0xB1 +#define LM49453_P1_CP_CONFIG3_REG 0xB2 +#define LM49453_P1_CP_CONFIG4_REG 0xB3 +#define LM49453_P1_CP_LA_VTH1L_REG 0xB4 +#define LM49453_P1_CP_LA_VTH1M_REG 0xB5 +#define LM49453_P1_CP_LA_VTH2L_REG 0xB6 +#define LM49453_P1_CP_LA_VTH2M_REG 0xB7 +#define LM49453_P1_CP_LA_VTH3L_REG 0xB8 +#define LM49453_P1_CP_LA_VTH3H_REG 0xB9 +#define LM49453_P1_CP_CLK_DIV_REG 0xBA + +/* DAC */ +#define LM49453_P1_DAC_CHOP_REG 0xC0 + +#define LM49453_CLK_SRC_MCLK 1 +#endif diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 0bb511a0388d..35179e2c23c9 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <asm/div64.h> #include <sound/max98095.h> +#include <sound/jack.h> #include "max98095.h" enum max98095_type { @@ -51,6 +52,8 @@ struct max98095_priv { u8 lin_state; unsigned int mic1pre; unsigned int mic2pre; + struct snd_soc_jack *headphone_jack; + struct snd_soc_jack *mic_jack; }; static const u8 max98095_reg_def[M98095_REG_CNT] = { @@ -2173,9 +2176,125 @@ static void max98095_handle_pdata(struct snd_soc_codec *codec) max98095_handle_bq_pdata(codec); } +static irqreturn_t max98095_report_jack(int irq, void *data) +{ + struct snd_soc_codec *codec = data; + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + unsigned int value; + int hp_report = 0; + int mic_report = 0; + + /* Read the Jack Status Register */ + value = snd_soc_read(codec, M98095_007_JACK_AUTO_STS); + + /* If ddone is not set, then detection isn't finished yet */ + if ((value & M98095_DDONE) == 0) + return IRQ_NONE; + + /* if hp, check its bit, and if set, clear it */ + if ((value & M98095_HP_IN || value & M98095_LO_IN) && + max98095->headphone_jack) + hp_report |= SND_JACK_HEADPHONE; + + /* if mic, check its bit, and if set, clear it */ + if ((value & M98095_MIC_IN) && max98095->mic_jack) + mic_report |= SND_JACK_MICROPHONE; + + if (max98095->headphone_jack == max98095->mic_jack) { + snd_soc_jack_report(max98095->headphone_jack, + hp_report | mic_report, + SND_JACK_HEADSET); + } else { + if (max98095->headphone_jack) + snd_soc_jack_report(max98095->headphone_jack, + hp_report, SND_JACK_HEADPHONE); + if (max98095->mic_jack) + snd_soc_jack_report(max98095->mic_jack, + mic_report, SND_JACK_MICROPHONE); + } + + return IRQ_HANDLED; +} + +int max98095_jack_detect_enable(struct snd_soc_codec *codec) +{ + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + int detect_enable = M98095_JDEN; + unsigned int slew = M98095_DEFAULT_SLEW_DELAY; + + if (max98095->pdata->jack_detect_pin5en) + detect_enable |= M98095_PIN5EN; + + if (max98095->pdata->jack_detect_delay) + slew = max98095->pdata->jack_detect_delay; + + ret = snd_soc_write(codec, M98095_08E_JACK_DC_SLEW, slew); + if (ret < 0) { + dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret); + return ret; + } + + /* configure auto detection to be enabled */ + ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, detect_enable); + if (ret < 0) { + dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret); + return ret; + } + + return ret; +} + +int max98095_jack_detect_disable(struct snd_soc_codec *codec) +{ + int ret = 0; + + /* configure auto detection to be disabled */ + ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, 0x0); + if (ret < 0) { + dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret); + return ret; + } + + return ret; +} + +int max98095_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack) +{ + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct i2c_client *client = to_i2c_client(codec->dev); + int ret = 0; + + max98095->headphone_jack = hp_jack; + max98095->mic_jack = mic_jack; + + /* only progress if we have at least 1 jack pointer */ + if (!hp_jack && !mic_jack) + return -EINVAL; + + max98095_jack_detect_enable(codec); + + /* enable interrupts for headphone jack detection */ + ret = snd_soc_update_bits(codec, M98095_013_JACK_INT_EN, + M98095_IDDONE, M98095_IDDONE); + if (ret < 0) { + dev_err(codec->dev, "Failed to cfg jack irqs %d\n", ret); + return ret; + } + + max98095_report_jack(client->irq, codec); + return 0; +} + #ifdef CONFIG_PM static int max98095_suspend(struct snd_soc_codec *codec) { + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + + if (max98095->headphone_jack || max98095->mic_jack) + max98095_jack_detect_disable(codec); + max98095_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; @@ -2183,8 +2302,16 @@ static int max98095_suspend(struct snd_soc_codec *codec) static int max98095_resume(struct snd_soc_codec *codec) { + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct i2c_client *client = to_i2c_client(codec->dev); + max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (max98095->headphone_jack || max98095->mic_jack) { + max98095_jack_detect_enable(codec); + max98095_report_jack(client->irq, codec); + } + return 0; } #else @@ -2227,6 +2354,7 @@ static int max98095_probe(struct snd_soc_codec *codec) { struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); struct max98095_cdata *cdata; + struct i2c_client *client; int ret = 0; ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); @@ -2238,6 +2366,8 @@ static int max98095_probe(struct snd_soc_codec *codec) /* reset the codec, the DSP core, and disable all interrupts */ max98095_reset(codec); + client = to_i2c_client(codec->dev); + /* initialize private data */ max98095->sysclk = (unsigned)-1; @@ -2266,11 +2396,23 @@ static int max98095_probe(struct snd_soc_codec *codec) max98095->mic1pre = 0; max98095->mic2pre = 0; + if (client->irq) { + /* register an audio interrupt */ + ret = request_threaded_irq(client->irq, NULL, + max98095_report_jack, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "max98095", codec); + if (ret) { + dev_err(codec->dev, "Failed to request IRQ: %d\n", ret); + goto err_access; + } + } + ret = snd_soc_read(codec, M98095_0FF_REV_ID); if (ret < 0) { dev_err(codec->dev, "Failure reading hardware revision: %d\n", ret); - goto err_access; + goto err_irq; } dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A'); @@ -2306,14 +2448,28 @@ static int max98095_probe(struct snd_soc_codec *codec) max98095_add_widgets(codec); + return 0; + +err_irq: + if (client->irq) + free_irq(client->irq, codec); err_access: return ret; } static int max98095_remove(struct snd_soc_codec *codec) { + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct i2c_client *client = to_i2c_client(codec->dev); + max98095_set_bias_level(codec, SND_SOC_BIAS_OFF); + if (max98095->headphone_jack || max98095->mic_jack) + max98095_jack_detect_disable(codec); + + if (client->irq) + free_irq(client->irq, codec); + return 0; } diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h index 891584a0eb03..2ebbe4e894bf 100644 --- a/sound/soc/codecs/max98095.h +++ b/sound/soc/codecs/max98095.h @@ -175,11 +175,23 @@ /* MAX98095 Registers Bit Fields */ +/* M98095_007_JACK_AUTO_STS */ + #define M98095_MIC_IN (1<<3) + #define M98095_LO_IN (1<<5) + #define M98095_HP_IN (1<<6) + #define M98095_DDONE (1<<7) + /* M98095_00F_HOST_CFG */ #define M98095_SEG (1<<0) #define M98095_XTEN (1<<1) #define M98095_MDLLEN (1<<2) +/* M98095_013_JACK_INT_EN */ + #define M98095_IMIC_IN (1<<3) + #define M98095_ILO_IN (1<<5) + #define M98095_IHP_IN (1<<6) + #define M98095_IDDONE (1<<7) + /* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */ #define M98095_CLKMODE_MASK 0xFF @@ -255,6 +267,10 @@ #define M98095_EQ2EN (1<<1) #define M98095_EQ1EN (1<<0) +/* M98095_089_JACK_DET_AUTO */ + #define M98095_PIN5EN (1<<2) + #define M98095_JDEN (1<<7) + /* M98095_090_PWR_EN_IN */ #define M98095_INEN (1<<7) #define M98095_MB2EN (1<<3) @@ -296,4 +312,10 @@ #define M98095_174_DAI1_BQ_BASE 0x74 #define M98095_17E_DAI2_BQ_BASE 0x7E +/* Default Delay used in Slew Rate Calculation for Jack detection */ +#define M98095_DEFAULT_SLEW_DELAY 0x18 + +extern int max98095_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack); + #endif diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c new file mode 100644 index 000000000000..6276e352125f --- /dev/null +++ b/sound/soc/codecs/mc13783.c @@ -0,0 +1,786 @@ +/* + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de + * Copyright 2012 Philippe Retornaz, philippe.retornaz@epfl.ch + * + * Initial development of this code was funded by + * Phytec Messtechnik GmbH, http://www.phytec.de + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include <linux/module.h> +#include <linux/device.h> +#include <linux/mfd/mc13xxx.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/soc-dapm.h> + +#include "mc13783.h" + +#define MC13783_AUDIO_RX0 36 +#define MC13783_AUDIO_RX1 37 +#define MC13783_AUDIO_TX 38 +#define MC13783_SSI_NETWORK 39 +#define MC13783_AUDIO_CODEC 40 +#define MC13783_AUDIO_DAC 41 + +#define AUDIO_RX0_ALSPEN (1 << 5) +#define AUDIO_RX0_ALSPSEL (1 << 7) +#define AUDIO_RX0_ADDCDC (1 << 21) +#define AUDIO_RX0_ADDSTDC (1 << 22) +#define AUDIO_RX0_ADDRXIN (1 << 23) + +#define AUDIO_RX1_PGARXEN (1 << 0); +#define AUDIO_RX1_PGASTEN (1 << 5) +#define AUDIO_RX1_ARXINEN (1 << 10) + +#define AUDIO_TX_AMC1REN (1 << 5) +#define AUDIO_TX_AMC1LEN (1 << 7) +#define AUDIO_TX_AMC2EN (1 << 9) +#define AUDIO_TX_ATXINEN (1 << 11) +#define AUDIO_TX_RXINREC (1 << 13) + +#define SSI_NETWORK_CDCTXRXSLOT(x) (((x) & 0x3) << 2) +#define SSI_NETWORK_CDCTXSECSLOT(x) (((x) & 0x3) << 4) +#define SSI_NETWORK_CDCRXSECSLOT(x) (((x) & 0x3) << 6) +#define SSI_NETWORK_CDCRXSECGAIN(x) (((x) & 0x3) << 8) +#define SSI_NETWORK_CDCSUMGAIN(x) (1 << 10) +#define SSI_NETWORK_CDCFSDLY(x) (1 << 11) +#define SSI_NETWORK_DAC_SLOTS_8 (1 << 12) +#define SSI_NETWORK_DAC_SLOTS_4 (2 << 12) +#define SSI_NETWORK_DAC_SLOTS_2 (3 << 12) +#define SSI_NETWORK_DAC_SLOT_MASK (3 << 12) +#define SSI_NETWORK_DAC_RXSLOT_0_1 (0 << 14) +#define SSI_NETWORK_DAC_RXSLOT_2_3 (1 << 14) +#define SSI_NETWORK_DAC_RXSLOT_4_5 (2 << 14) +#define SSI_NETWORK_DAC_RXSLOT_6_7 (3 << 14) +#define SSI_NETWORK_DAC_RXSLOT_MASK (3 << 14) +#define SSI_NETWORK_STDCRXSECSLOT(x) (((x) & 0x3) << 16) +#define SSI_NETWORK_STDCRXSECGAIN(x) (((x) & 0x3) << 18) +#define SSI_NETWORK_STDCSUMGAIN (1 << 20) + +/* + * MC13783_AUDIO_CODEC and MC13783_AUDIO_DAC mostly share the same + * register layout + */ +#define AUDIO_SSI_SEL (1 << 0) +#define AUDIO_CLK_SEL (1 << 1) +#define AUDIO_CSM (1 << 2) +#define AUDIO_BCL_INV (1 << 3) +#define AUDIO_CFS_INV (1 << 4) +#define AUDIO_CFS(x) (((x) & 0x3) << 5) +#define AUDIO_CLK(x) (((x) & 0x7) << 7) +#define AUDIO_C_EN (1 << 11) +#define AUDIO_C_CLK_EN (1 << 12) +#define AUDIO_C_RESET (1 << 15) + +#define AUDIO_CODEC_CDCFS8K16K (1 << 10) +#define AUDIO_DAC_CFS_DLY_B (1 << 10) + +struct mc13783_priv { + struct snd_soc_codec codec; + struct mc13xxx *mc13xxx; + + enum mc13783_ssi_port adc_ssi_port; + enum mc13783_ssi_port dac_ssi_port; +}; + +static unsigned int mc13783_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); + unsigned int value = 0; + + mc13xxx_lock(priv->mc13xxx); + + mc13xxx_reg_read(priv->mc13xxx, reg, &value); + + mc13xxx_unlock(priv->mc13xxx); + + return value; +} + +static int mc13783_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + mc13xxx_lock(priv->mc13xxx); + + ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); + + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +/* Mapping between sample rates and register value */ +static unsigned int mc13783_rates[] = { + 8000, 11025, 12000, 16000, + 22050, 24000, 32000, 44100, + 48000, 64000, 96000 +}; + +static int mc13783_pcm_hw_params_dac(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + unsigned int rate = params_rate(params); + int i; + + for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) { + if (rate == mc13783_rates[i]) { + snd_soc_update_bits(codec, MC13783_AUDIO_DAC, + 0xf << 17, i << 17); + return 0; + } + } + + return -EINVAL; +} + +static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + unsigned int rate = params_rate(params); + unsigned int val; + + switch (rate) { + case 8000: + val = 0; + break; + case 16000: + val = AUDIO_CODEC_CDCFS8K16K; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K, + val); + + return 0; +} + +static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return mc13783_pcm_hw_params_dac(substream, params, dai); + else + return mc13783_pcm_hw_params_codec(substream, params, dai); +} + +static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt, + unsigned int reg) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val = 0; + unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV | + AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET; + + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val |= AUDIO_CFS(2); + break; + case SND_SOC_DAIFMT_DSP_A: + val |= AUDIO_CFS(1); + break; + default: + return -EINVAL; + } + + /* DAI clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val |= AUDIO_BCL_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + val |= AUDIO_BCL_INV | AUDIO_CFS_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + val |= AUDIO_CFS_INV; + break; + } + + /* DAI clock master masks */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + val |= AUDIO_C_CLK_EN; + break; + case SND_SOC_DAIFMT_CBS_CFS: + val |= AUDIO_CSM; + break; + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + return -EINVAL; + } + + val |= AUDIO_C_RESET; + + snd_soc_update_bits(codec, reg, mask, val); + + return 0; +} + +static int mc13783_set_fmt_async(struct snd_soc_dai *dai, unsigned int fmt) +{ + if (dai->id == MC13783_ID_STEREO_DAC) + return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC); + else + return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC); +} + +static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt) +{ + int ret; + + ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC); + if (ret) + return ret; + + /* + * In synchronous mode force the voice codec into slave mode + * so that the clock / framesync from the stereo DAC is used + */ + fmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + fmt |= SND_SOC_DAIFMT_CBS_CFS; + ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC); + + return ret; +} + +static int mc13783_sysclk[] = { + 13000000, + 15360000, + 16800000, + -1, + 26000000, + -1, /* 12000000, invalid for voice codec */ + -1, /* 3686400, invalid for voice codec */ + 33600000, +}; + +static int mc13783_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir, + unsigned int reg) +{ + struct snd_soc_codec *codec = dai->codec; + int clk; + unsigned int val = 0; + unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL; + + for (clk = 0; clk < ARRAY_SIZE(mc13783_sysclk); clk++) { + if (mc13783_sysclk[clk] < 0) + continue; + if (mc13783_sysclk[clk] == freq) + break; + } + + if (clk == ARRAY_SIZE(mc13783_sysclk)) + return -EINVAL; + + if (clk_id == MC13783_CLK_CLIB) + val |= AUDIO_CLK_SEL; + + val |= AUDIO_CLK(clk); + + snd_soc_update_bits(codec, reg, mask, val); + + return 0; +} + +static int mc13783_set_sysclk_dac(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC); +} + +static int mc13783_set_sysclk_codec(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC); +} + +static int mc13783_set_sysclk_sync(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + int ret; + + ret = mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC); + if (ret) + return ret; + + return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC); +} + +static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, + int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val = 0; + unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK | + SSI_NETWORK_DAC_RXSLOT_MASK; + + switch (slots) { + case 2: + val |= SSI_NETWORK_DAC_SLOTS_2; + break; + case 4: + val |= SSI_NETWORK_DAC_SLOTS_4; + break; + case 8: + val |= SSI_NETWORK_DAC_SLOTS_8; + break; + default: + return -EINVAL; + } + + switch (rx_mask) { + case 0xfffffffc: + val |= SSI_NETWORK_DAC_RXSLOT_0_1; + break; + case 0xfffffff3: + val |= SSI_NETWORK_DAC_RXSLOT_2_3; + break; + case 0xffffffcf: + val |= SSI_NETWORK_DAC_RXSLOT_4_5; + break; + case 0xffffff3f: + val |= SSI_NETWORK_DAC_RXSLOT_6_7; + break; + default: + return -EINVAL; + }; + + snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val); + + return 0; +} + +static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, + int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val = 0; + unsigned int mask = 0x3f; + + if (slots != 4) + return -EINVAL; + + if (tx_mask != 0xfffffffc) + return -EINVAL; + + val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */ + val |= (0x01 << 4); /* secondary timeslot TX is 1 */ + + snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val); + + return 0; +} + +static int mc13783_set_tdm_slot_sync(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, + int slot_width) +{ + int ret; + + ret = mc13783_set_tdm_slot_dac(dai, tx_mask, rx_mask, slots, + slot_width); + if (ret) + return ret; + + ret = mc13783_set_tdm_slot_codec(dai, tx_mask, rx_mask, slots, + slot_width); + + return ret; +} + +static const struct snd_kcontrol_new mc1l_amp_ctl = + SOC_DAPM_SINGLE("Switch", 38, 7, 1, 0); + +static const struct snd_kcontrol_new mc1r_amp_ctl = + SOC_DAPM_SINGLE("Switch", 38, 5, 1, 0); + +static const struct snd_kcontrol_new mc2_amp_ctl = + SOC_DAPM_SINGLE("Switch", 38, 9, 1, 0); + +static const struct snd_kcontrol_new atx_amp_ctl = + SOC_DAPM_SINGLE("Switch", 38, 11, 1, 0); + + +/* Virtual mux. The chip does the input selection automatically + * as soon as we enable one input. */ +static const char * const adcl_enum_text[] = { + "MC1L", "RXINL", +}; + +static const struct soc_enum adcl_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcl_enum_text), adcl_enum_text); + +static const struct snd_kcontrol_new left_input_mux = + SOC_DAPM_ENUM_VIRT("Route", adcl_enum); + +static const char * const adcr_enum_text[] = { + "MC1R", "MC2", "RXINR", "TXIN", +}; + +static const struct soc_enum adcr_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcr_enum_text), adcr_enum_text); + +static const struct snd_kcontrol_new right_input_mux = + SOC_DAPM_ENUM_VIRT("Route", adcr_enum); + +static const struct snd_kcontrol_new samp_ctl = + SOC_DAPM_SINGLE("Switch", 36, 3, 1, 0); + +static const struct snd_kcontrol_new lamp_ctl = + SOC_DAPM_SINGLE("Switch", 36, 5, 1, 0); + +static const struct snd_kcontrol_new hlamp_ctl = + SOC_DAPM_SINGLE("Switch", 36, 10, 1, 0); + +static const struct snd_kcontrol_new hramp_ctl = + SOC_DAPM_SINGLE("Switch", 36, 9, 1, 0); + +static const struct snd_kcontrol_new llamp_ctl = + SOC_DAPM_SINGLE("Switch", 36, 16, 1, 0); + +static const struct snd_kcontrol_new lramp_ctl = + SOC_DAPM_SINGLE("Switch", 36, 15, 1, 0); + +static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = { +/* Input */ + SND_SOC_DAPM_INPUT("MC1LIN"), + SND_SOC_DAPM_INPUT("MC1RIN"), + SND_SOC_DAPM_INPUT("MC2IN"), + SND_SOC_DAPM_INPUT("RXINR"), + SND_SOC_DAPM_INPUT("RXINL"), + SND_SOC_DAPM_INPUT("TXIN"), + + SND_SOC_DAPM_SUPPLY("MC1 Bias", 38, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MC2 Bias", 38, 1, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("MC1L Amp", 38, 7, 0, &mc1l_amp_ctl), + SND_SOC_DAPM_SWITCH("MC1R Amp", 38, 5, 0, &mc1r_amp_ctl), + SND_SOC_DAPM_SWITCH("MC2 Amp", 38, 9, 0, &mc2_amp_ctl), + SND_SOC_DAPM_SWITCH("TXIN Amp", 38, 11, 0, &atx_amp_ctl), + + SND_SOC_DAPM_VIRT_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0, + &left_input_mux), + SND_SOC_DAPM_VIRT_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0, + &right_input_mux), + + SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", 40, 11, 0), + SND_SOC_DAPM_SUPPLY("ADC_Reset", 40, 15, 0, NULL, 0), + +/* Output */ + SND_SOC_DAPM_SUPPLY("DAC_E", 41, 11, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC_Reset", 41, 15, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("RXOUTL"), + SND_SOC_DAPM_OUTPUT("RXOUTR"), + SND_SOC_DAPM_OUTPUT("HSL"), + SND_SOC_DAPM_OUTPUT("HSR"), + SND_SOC_DAPM_OUTPUT("LSP"), + SND_SOC_DAPM_OUTPUT("SP"), + + SND_SOC_DAPM_SWITCH("Speaker Amp", 36, 3, 0, &samp_ctl), + SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl), + SND_SOC_DAPM_SWITCH("Headset Amp Left", 36, 10, 0, &hlamp_ctl), + SND_SOC_DAPM_SWITCH("Headset Amp Right", 36, 9, 0, &hramp_ctl), + SND_SOC_DAPM_SWITCH("Line out Amp Left", 36, 16, 0, &llamp_ctl), + SND_SOC_DAPM_SWITCH("Line out Amp Right", 36, 15, 0, &lramp_ctl), + SND_SOC_DAPM_DAC("DAC", "Playback", 36, 22, 0), + SND_SOC_DAPM_PGA("DAC PGA", 37, 5, 0, NULL, 0), +}; + +static struct snd_soc_dapm_route mc13783_routes[] = { +/* Input */ + { "MC1L Amp", NULL, "MC1LIN"}, + { "MC1R Amp", NULL, "MC1RIN" }, + { "MC2 Amp", NULL, "MC2IN" }, + { "TXIN Amp", NULL, "TXIN"}, + + { "PGA Left Input Mux", "MC1L", "MC1L Amp" }, + { "PGA Left Input Mux", "RXINL", "RXINL"}, + { "PGA Right Input Mux", "MC1R", "MC1R Amp" }, + { "PGA Right Input Mux", "MC2", "MC2 Amp"}, + { "PGA Right Input Mux", "TXIN", "TXIN Amp"}, + { "PGA Right Input Mux", "RXINR", "RXINR"}, + + { "PGA Left Input", NULL, "PGA Left Input Mux"}, + { "PGA Right Input", NULL, "PGA Right Input Mux"}, + + { "ADC", NULL, "PGA Left Input"}, + { "ADC", NULL, "PGA Right Input"}, + { "ADC", NULL, "ADC_Reset"}, + +/* Output */ + { "HSL", NULL, "Headset Amp Left" }, + { "HSR", NULL, "Headset Amp Right"}, + { "RXOUTL", NULL, "Line out Amp Left"}, + { "RXOUTR", NULL, "Line out Amp Right"}, + { "SP", NULL, "Speaker Amp"}, + { "Speaker Amp", NULL, "DAC PGA"}, + { "LSP", NULL, "DAC PGA"}, + { "Headset Amp Left", NULL, "DAC PGA"}, + { "Headset Amp Right", NULL, "DAC PGA"}, + { "Line out Amp Left", NULL, "DAC PGA"}, + { "Line out Amp Right", NULL, "DAC PGA"}, + { "DAC PGA", NULL, "DAC"}, + { "DAC", NULL, "DAC_E"}, +}; + +static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix", + "Mono", "Mono Mix"}; + +static const struct soc_enum mc13783_enum_3d_mixer = + SOC_ENUM_SINGLE(MC13783_AUDIO_RX1, 16, ARRAY_SIZE(mc13783_3d_mixer), + mc13783_3d_mixer); + +static struct snd_kcontrol_new mc13783_control_list[] = { + SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0), + SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0), + SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0), + SOC_ENUM("3D Control", mc13783_enum_3d_mixer), +}; + +static int mc13783_probe(struct snd_soc_codec *codec) +{ + struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc13xxx_lock(priv->mc13xxx); + + /* these are the reset values */ + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893); + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A); + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_TX, 0x420000); + mc13xxx_reg_write(priv->mc13xxx, MC13783_SSI_NETWORK, 0x013060); + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_CODEC, 0x180027); + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_DAC, 0x0e0004); + + if (priv->adc_ssi_port == MC13783_SSI1_PORT) + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC, + AUDIO_SSI_SEL, 0); + else + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC, + 0, AUDIO_SSI_SEL); + + if (priv->dac_ssi_port == MC13783_SSI1_PORT) + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, + AUDIO_SSI_SEL, 0); + else + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, + 0, AUDIO_SSI_SEL); + + mc13xxx_unlock(priv->mc13xxx); + + return 0; +} + +static int mc13783_remove(struct snd_soc_codec *codec) +{ + struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc13xxx_lock(priv->mc13xxx); + + /* Make sure VAUDIOON is off */ + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0); + + mc13xxx_unlock(priv->mc13xxx); + + return 0; +} + +#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) + +#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops mc13783_ops_dac = { + .hw_params = mc13783_pcm_hw_params_dac, + .set_fmt = mc13783_set_fmt_async, + .set_sysclk = mc13783_set_sysclk_dac, + .set_tdm_slot = mc13783_set_tdm_slot_dac, +}; + +static struct snd_soc_dai_ops mc13783_ops_codec = { + .hw_params = mc13783_pcm_hw_params_codec, + .set_fmt = mc13783_set_fmt_async, + .set_sysclk = mc13783_set_sysclk_codec, + .set_tdm_slot = mc13783_set_tdm_slot_codec, +}; + +/* + * The mc13783 has two SSI ports, both of them can be routed either + * to the voice codec or the stereo DAC. When two different SSI ports + * are used for the voice codec and the stereo DAC we can do different + * formats and sysclock settings for playback and capture + * (mc13783-hifi-playback and mc13783-hifi-capture). Using the same port + * forces us to use symmetric rates (mc13783-hifi). + */ +static struct snd_soc_dai_driver mc13783_dai_async[] = { + { + .name = "mc13783-hifi-playback", + .id = MC13783_ID_STEREO_DAC, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = MC13783_FORMATS, + }, + .ops = &mc13783_ops_dac, + }, { + .name = "mc13783-hifi-capture", + .id = MC13783_ID_STEREO_CODEC, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MC13783_RATES_RECORD, + .formats = MC13783_FORMATS, + }, + .ops = &mc13783_ops_codec, + }, +}; + +static struct snd_soc_dai_ops mc13783_ops_sync = { + .hw_params = mc13783_pcm_hw_params_sync, + .set_fmt = mc13783_set_fmt_sync, + .set_sysclk = mc13783_set_sysclk_sync, + .set_tdm_slot = mc13783_set_tdm_slot_sync, +}; + +static struct snd_soc_dai_driver mc13783_dai_sync[] = { + { + .name = "mc13783-hifi", + .id = MC13783_ID_SYNC, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = MC13783_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MC13783_RATES_RECORD, + .formats = MC13783_FORMATS, + }, + .ops = &mc13783_ops_sync, + .symmetric_rates = 1, + } +}; + +static struct snd_soc_codec_driver soc_codec_dev_mc13783 = { + .probe = mc13783_probe, + .remove = mc13783_remove, + .read = mc13783_read, + .write = mc13783_write, + .controls = mc13783_control_list, + .num_controls = ARRAY_SIZE(mc13783_control_list), + .dapm_widgets = mc13783_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets), + .dapm_routes = mc13783_routes, + .num_dapm_routes = ARRAY_SIZE(mc13783_routes), +}; + +static int mc13783_codec_probe(struct platform_device *pdev) +{ + struct mc13xxx *mc13xxx; + struct mc13783_priv *priv; + struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data; + int ret; + + mc13xxx = dev_get_drvdata(pdev->dev.parent); + + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, priv); + priv->mc13xxx = mc13xxx; + if (pdata) { + priv->adc_ssi_port = pdata->adc_ssi_port; + priv->dac_ssi_port = pdata->dac_ssi_port; + } else { + priv->adc_ssi_port = MC13783_SSI1_PORT; + priv->dac_ssi_port = MC13783_SSI2_PORT; + } + + if (priv->adc_ssi_port == priv->dac_ssi_port) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783, + mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync)); + else + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783, + mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async)); + + if (ret) + goto err_register_codec; + + return 0; + +err_register_codec: + dev_err(&pdev->dev, "register codec failed with %d\n", ret); + + return ret; +} + +static int mc13783_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver mc13783_codec_driver = { + .driver = { + .name = "mc13783-codec", + .owner = THIS_MODULE, + }, + .probe = mc13783_codec_probe, + .remove = __devexit_p(mc13783_codec_remove), +}; + +module_platform_driver(mc13783_codec_driver); + +MODULE_DESCRIPTION("ASoC MC13783 driver"); +MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>"); +MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h new file mode 100644 index 000000000000..3a6d1993a217 --- /dev/null +++ b/sound/soc/codecs/mc13783.h @@ -0,0 +1,28 @@ +/* + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef MC13783_MIXER_H +#define MC13783_MIXER_H + +#define MC13783_CLK_CLIA 1 +#define MC13783_CLK_CLIB 2 + +#define MC13783_ID_STEREO_DAC 1 +#define MC13783_ID_STEREO_CODEC 2 +#define MC13783_ID_SYNC 3 + +#endif /* MC13783_MIXER_H */ diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c new file mode 100644 index 000000000000..22cb5bf59273 --- /dev/null +++ b/sound/soc/codecs/ml26124.c @@ -0,0 +1,681 @@ +/* + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "ml26124.h" + +#define DVOL_CTL_DVMUTE_ON BIT(4) /* Digital volume MUTE On */ +#define DVOL_CTL_DVMUTE_OFF 0 /* Digital volume MUTE Off */ +#define ML26124_SAI_NO_DELAY BIT(1) +#define ML26124_SAI_FRAME_SYNC (BIT(5) | BIT(0)) /* For mono (Telecodec) */ +#define ML26134_CACHESIZE 212 +#define ML26124_VMID BIT(1) +#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000) +#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) +#define ML26124_NUM_REGISTER ML26134_CACHESIZE + +struct ml26124_priv { + u32 mclk; + u32 rate; + struct regmap *regmap; + int clk_in; + struct snd_pcm_substream *substream; +}; + +struct clk_coeff { + u32 mclk; + u32 rate; + u8 pllnl; + u8 pllnh; + u8 pllml; + u8 pllmh; + u8 plldiv; +}; + +/* ML26124 configuration */ +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0); + +static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0); + +static const char * const ml26124_companding[] = {"16bit PCM", "u-law", + "A-law"}; + +static const struct soc_enum ml26124_adc_companding_enum + = SOC_ENUM_SINGLE(ML26124_SAI_TRANS_CTL, 6, 3, ml26124_companding); + +static const struct soc_enum ml26124_dac_companding_enum + = SOC_ENUM_SINGLE(ML26124_SAI_RCV_CTL, 6, 3, ml26124_companding); + +static const struct snd_kcontrol_new ml26124_snd_controls[] = { + SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0, + 0x3f, 0, boost_vol), + SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("ALC Target Level", ML26124_ALC_TARGET_LEV, 0, + 0xf, 1, alclvl), + SOC_SINGLE_TLV("ALC Min Input Volume", ML26124_ALC_MAXMIN_GAIN, 0, + 7, 0, mingain), + SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4, + 7, 1, maxgain), + SOC_SINGLE_TLV("Playback Limiter Min Input Volume", + ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain), + SOC_SINGLE_TLV("Playback Limiter Max Input Volume", + ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain), + SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0, + 0x3f, 0, boost_vol), + SOC_SINGLE("DC High Pass Filter Switch", ML26124_FILTER_EN, 0, 1, 0), + SOC_SINGLE("Noise High Pass Filter Switch", ML26124_FILTER_EN, 1, 1, 0), + SOC_SINGLE("ZC Switch", ML26124_PW_ZCCMP_PW_MNG, 1, + 1, 0), + SOC_SINGLE("EQ Band0 Switch", ML26124_FILTER_EN, 2, 1, 0), + SOC_SINGLE("EQ Band1 Switch", ML26124_FILTER_EN, 3, 1, 0), + SOC_SINGLE("EQ Band2 Switch", ML26124_FILTER_EN, 4, 1, 0), + SOC_SINGLE("EQ Band3 Switch", ML26124_FILTER_EN, 5, 1, 0), + SOC_SINGLE("EQ Band4 Switch", ML26124_FILTER_EN, 6, 1, 0), + SOC_SINGLE("Play Limiter", ML26124_DVOL_CTL, 0, 1, 0), + SOC_SINGLE("Capture Limiter", ML26124_DVOL_CTL, 1, 1, 0), + SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0), + SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0), + SOC_ENUM("DAC Companding", ml26124_dac_companding_enum), + SOC_ENUM("ADC Companding", ml26124_adc_companding_enum), +}; + +static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC Switch", ML26124_SPK_AMP_OUT, 1, 1, 0), + SOC_DAPM_SINGLE("Line in loopback Switch", ML26124_SPK_AMP_OUT, 3, 1, + 0), + SOC_DAPM_SINGLE("PGA Switch", ML26124_SPK_AMP_OUT, 5, 1, 0), +}; + +/* Input mux */ +static const char * const ml26124_input_select[] = {"Analog MIC SingleEnded in", + "Digital MIC in", "Analog MIC Differential in"}; + +static const struct soc_enum ml26124_insel_enum = + SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 3, ml26124_input_select); + +static const struct snd_kcontrol_new ml26124_input_mux_controls = + SOC_DAPM_ENUM("Input Select", ml26124_insel_enum); + +static const struct snd_kcontrol_new ml26124_line_control = + SOC_DAPM_SINGLE("Switch", ML26124_PW_LOUT_PW_MNG, 1, 1, 0); + +static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("MCLKEN", ML26124_CLK_EN, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLLEN", ML26124_CLK_EN, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLLOE", ML26124_CLK_EN, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, + &ml26124_output_mixer_controls[0], + ARRAY_SIZE(ml26124_output_mixer_controls)), + SND_SOC_DAPM_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", ML26124_PW_IN_PW_MNG, 1, 0), + SND_SOC_DAPM_PGA("PGA", ML26124_PW_IN_PW_MNG, 3, 0, NULL, 0), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &ml26124_input_mux_controls), + SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0, + &ml26124_line_control), + SND_SOC_DAPM_INPUT("MDIN"), + SND_SOC_DAPM_INPUT("MIN"), + SND_SOC_DAPM_INPUT("LIN"), + SND_SOC_DAPM_OUTPUT("SPOUT"), + SND_SOC_DAPM_OUTPUT("LOUT"), +}; + +static const struct snd_soc_dapm_route ml26124_intercon[] = { + /* Supply */ + {"DAC", NULL, "MCLKEN"}, + {"ADC", NULL, "MCLKEN"}, + {"DAC", NULL, "PLLEN"}, + {"ADC", NULL, "PLLEN"}, + {"DAC", NULL, "PLLOE"}, + {"ADC", NULL, "PLLOE"}, + + /* output mixer */ + {"Output Mixer", "DAC Switch", "DAC"}, + {"Output Mixer", "Line in loopback Switch", "LIN"}, + + /* outputs */ + {"LOUT", NULL, "Output Mixer"}, + {"SPOUT", NULL, "Output Mixer"}, + {"Line Out Enable", NULL, "LOUT"}, + + /* input */ + {"ADC", NULL, "Input Mux"}, + {"Input Mux", "Analog MIC SingleEnded in", "PGA"}, + {"Input Mux", "Analog MIC Differential in", "PGA"}, + {"PGA", NULL, "MIN"}, +}; + +/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */ +static const struct clk_coeff coeff_div[] = { + {12288000, 16000, 0xc, 0x0, 0x20, 0x0, 0x4}, + {12288000, 32000, 0xc, 0x0, 0x20, 0x0, 0x4}, + {12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4}, +}; + +static struct reg_default ml26124_reg[] = { + /* CLOCK control Register */ + {0x00, 0x00 }, /* Sampling Rate */ + {0x02, 0x00}, /* PLL NL */ + {0x04, 0x00}, /* PLLNH */ + {0x06, 0x00}, /* PLLML */ + {0x08, 0x00}, /* MLLMH */ + {0x0a, 0x00}, /* PLLDIV */ + {0x0c, 0x00}, /* Clock Enable */ + {0x0e, 0x00}, /* CLK Input/Output Control */ + + /* System Control Register */ + {0x10, 0x00}, /* Software RESET */ + {0x12, 0x00}, /* Record/Playback Run */ + {0x14, 0x00}, /* Mic Input/Output control */ + + /* Power Management Register */ + {0x20, 0x00}, /* Reference Power Management */ + {0x22, 0x00}, /* Input Power Management */ + {0x24, 0x00}, /* DAC Power Management */ + {0x26, 0x00}, /* SP-AMP Power Management */ + {0x28, 0x00}, /* LINEOUT Power Management */ + {0x2a, 0x00}, /* VIDEO Power Management */ + {0x2e, 0x00}, /* AC-CMP Power Management */ + + /* Analog reference Control Register */ + {0x30, 0x04}, /* MICBIAS Voltage Control */ + + /* Input/Output Amplifier Control Register */ + {0x32, 0x10}, /* MIC Input Volume */ + {0x38, 0x00}, /* Mic Boost Volume */ + {0x3a, 0x33}, /* Speaker AMP Volume */ + {0x48, 0x00}, /* AMP Volume Control Function Enable */ + {0x4a, 0x00}, /* Amplifier Volume Fader Control */ + + /* Analog Path Control Register */ + {0x54, 0x00}, /* Speaker AMP Output Control */ + {0x5a, 0x00}, /* Mic IF Control */ + {0xe8, 0x01}, /* Mic Select Control */ + + /* Audio Interface Control Register */ + {0x60, 0x00}, /* SAI-Trans Control */ + {0x62, 0x00}, /* SAI-Receive Control */ + {0x64, 0x00}, /* SAI Mode select */ + + /* DSP Control Register */ + {0x66, 0x01}, /* Filter Func Enable */ + {0x68, 0x00}, /* Volume Control Func Enable */ + {0x6A, 0x00}, /* Mixer & Volume Control*/ + {0x6C, 0xff}, /* Record Digital Volume */ + {0x70, 0xff}, /* Playback Digital Volume */ + {0x72, 0x10}, /* Digital Boost Volume */ + {0x74, 0xe7}, /* EQ gain Band0 */ + {0x76, 0xe7}, /* EQ gain Band1 */ + {0x78, 0xe7}, /* EQ gain Band2 */ + {0x7A, 0xe7}, /* EQ gain Band3 */ + {0x7C, 0xe7}, /* EQ gain Band4 */ + {0x7E, 0x00}, /* HPF2 CutOff*/ + {0x80, 0x00}, /* EQ Band0 Coef0L */ + {0x82, 0x00}, /* EQ Band0 Coef0H */ + {0x84, 0x00}, /* EQ Band0 Coef0L */ + {0x86, 0x00}, /* EQ Band0 Coef0H */ + {0x88, 0x00}, /* EQ Band1 Coef0L */ + {0x8A, 0x00}, /* EQ Band1 Coef0H */ + {0x8C, 0x00}, /* EQ Band1 Coef0L */ + {0x8E, 0x00}, /* EQ Band1 Coef0H */ + {0x90, 0x00}, /* EQ Band2 Coef0L */ + {0x92, 0x00}, /* EQ Band2 Coef0H */ + {0x94, 0x00}, /* EQ Band2 Coef0L */ + {0x96, 0x00}, /* EQ Band2 Coef0H */ + {0x98, 0x00}, /* EQ Band3 Coef0L */ + {0x9A, 0x00}, /* EQ Band3 Coef0H */ + {0x9C, 0x00}, /* EQ Band3 Coef0L */ + {0x9E, 0x00}, /* EQ Band3 Coef0H */ + {0xA0, 0x00}, /* EQ Band4 Coef0L */ + {0xA2, 0x00}, /* EQ Band4 Coef0H */ + {0xA4, 0x00}, /* EQ Band4 Coef0L */ + {0xA6, 0x00}, /* EQ Band4 Coef0H */ + + /* ALC Control Register */ + {0xb0, 0x00}, /* ALC Mode */ + {0xb2, 0x02}, /* ALC Attack Time */ + {0xb4, 0x03}, /* ALC Decay Time */ + {0xb6, 0x00}, /* ALC Hold Time */ + {0xb8, 0x0b}, /* ALC Target Level */ + {0xba, 0x70}, /* ALC Max/Min Gain */ + {0xbc, 0x00}, /* Noise Gate Threshold */ + {0xbe, 0x00}, /* ALC ZeroCross TimeOut */ + + /* Playback Limiter Control Register */ + {0xc0, 0x04}, /* PL Attack Time */ + {0xc2, 0x05}, /* PL Decay Time */ + {0xc4, 0x0d}, /* PL Target Level */ + {0xc6, 0x70}, /* PL Max/Min Gain */ + {0xc8, 0x10}, /* Playback Boost Volume */ + {0xca, 0x00}, /* PL ZeroCross TimeOut */ + + /* Video Amplifier Control Register */ + {0xd0, 0x01}, /* VIDEO AMP Gain Control */ + {0xd2, 0x01}, /* VIDEO AMP Setup 1 */ + {0xd4, 0x01}, /* VIDEO AMP Control2 */ +}; + +/* Get sampling rate value of sampling rate setting register (0x0) */ +static inline int get_srate(int rate) +{ + int srate; + + switch (rate) { + case 16000: + srate = 3; + break; + case 32000: + srate = 6; + break; + case 48000: + srate = 8; + break; + default: + return -EINVAL; + } + return srate; +} + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -EINVAL; +} + +static int ml26124_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); + int i = get_coeff(priv->mclk, params_rate(hw_params)); + + priv->substream = substream; + priv->rate = params_rate(hw_params); + + if (priv->clk_in) { + switch (priv->mclk / params_rate(hw_params)) { + case 256: + snd_soc_update_bits(codec, ML26124_CLK_CTL, + BIT(0) | BIT(1), 1); + break; + case 512: + snd_soc_update_bits(codec, ML26124_CLK_CTL, + BIT(0) | BIT(1), 2); + break; + case 1024: + snd_soc_update_bits(codec, ML26124_CLK_CTL, + BIT(0) | BIT(1), 3); + break; + default: + dev_err(codec->dev, "Unsupported MCLKI\n"); + break; + } + } else { + snd_soc_update_bits(codec, ML26124_CLK_CTL, + BIT(0) | BIT(1), 0); + } + + switch (params_rate(hw_params)) { + case 16000: + snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, + get_srate(params_rate(hw_params))); + snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, + coeff_div[i].pllnl); + snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, + coeff_div[i].pllnh); + snd_soc_update_bits(codec, ML26124_PLLML, 0xff, + coeff_div[i].pllml); + snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, + coeff_div[i].pllmh); + snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, + coeff_div[i].plldiv); + break; + case 32000: + snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, + get_srate(params_rate(hw_params))); + snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, + coeff_div[i].pllnl); + snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, + coeff_div[i].pllnh); + snd_soc_update_bits(codec, ML26124_PLLML, 0xff, + coeff_div[i].pllml); + snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, + coeff_div[i].pllmh); + snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, + coeff_div[i].plldiv); + break; + case 48000: + snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, + get_srate(params_rate(hw_params))); + snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, + coeff_div[i].pllnl); + snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, + coeff_div[i].pllnh); + snd_soc_update_bits(codec, ML26124_PLLML, 0xff, + coeff_div[i].pllml); + snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, + coeff_div[i].pllmh); + snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, + coeff_div[i].plldiv); + break; + default: + pr_err("%s:this rate is no support for ml26124\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int ml26124_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (priv->substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(0), 1); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(1), 2); + break; + } + + if (mute) + snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4), + DVOL_CTL_DVMUTE_ON); + else + snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4), + DVOL_CTL_DVMUTE_OFF); + + return 0; +} + +static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + unsigned char mode; + struct snd_soc_codec *codec = codec_dai->codec; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + mode = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + mode = 0; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, ML26124_SAI_MODE_SEL, BIT(0), mode); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case ML26124_USE_PLLOUT: + priv->clk_in = ML26124_USE_PLLOUT; + break; + case ML26124_USE_MCLKI: + priv->clk_in = ML26124_USE_MCLKI; + break; + default: + return -EINVAL; + } + + priv->mclk = freq; + + return 0; +} + +static int ml26124_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG, + ML26124_R26_MASK, ML26124_BLT_PREAMP_ON); + msleep(100); + snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG, + ML26124_R26_MASK, + ML26124_MICBEN_ON | ML26124_BLT_ALL_ON); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* VMID ON */ + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG, + ML26124_VMID, ML26124_VMID); + msleep(500); + regcache_sync(priv->regmap); + } + break; + case SND_SOC_BIAS_OFF: + /* VMID OFF */ + snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG, + ML26124_VMID, 0); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +static const struct snd_soc_dai_ops ml26124_dai_ops = { + .hw_params = ml26124_hw_params, + .digital_mute = ml26124_mute, + .set_fmt = ml26124_set_dai_fmt, + .set_sysclk = ml26124_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver ml26124_dai = { + .name = "ml26124-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ML26124_RATES, + .formats = ML26124_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ML26124_RATES, + .formats = ML26124_FORMATS,}, + .ops = &ml26124_dai_ops, + .symmetric_rates = 1, +}; + +#ifdef CONFIG_PM +static int ml26124_suspend(struct snd_soc_codec *codec) +{ + ml26124_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int ml26124_resume(struct snd_soc_codec *codec) +{ + ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define ml26124_suspend NULL +#define ml26124_resume NULL +#endif + +static int ml26124_probe(struct snd_soc_codec *codec) +{ + int ret; + struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); + codec->control_data = priv->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + /* Software Reset */ + snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1); + snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0); + + ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_ml26124 = { + .probe = ml26124_probe, + .suspend = ml26124_suspend, + .resume = ml26124_resume, + .set_bias_level = ml26124_set_bias_level, + .dapm_widgets = ml26124_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets), + .dapm_routes = ml26124_intercon, + .num_dapm_routes = ARRAY_SIZE(ml26124_intercon), + .controls = ml26124_snd_controls, + .num_controls = ARRAY_SIZE(ml26124_snd_controls), +}; + +static const struct regmap_config ml26124_i2c_regmap = { + .val_bits = 8, + .reg_bits = 8, + .max_register = ML26124_NUM_REGISTER, + .reg_defaults = ml26124_reg, + .num_reg_defaults = ARRAY_SIZE(ml26124_reg), + .cache_type = REGCACHE_RBTREE, + .write_flag_mask = 0x01, +}; + +static __devinit int ml26124_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct ml26124_priv *priv; + int ret; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(i2c, priv); + + priv->regmap = regmap_init_i2c(i2c, &ml26124_i2c_regmap); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret); + return ret; + } + + return snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_ml26124, &ml26124_dai, 1); +} + +static __devexit int ml26124_i2c_remove(struct i2c_client *client) +{ + struct ml26124_priv *priv = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + regmap_exit(priv->regmap); + return 0; +} + +static const struct i2c_device_id ml26124_i2c_id[] = { + { "ml26124", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id); + +static struct i2c_driver ml26124_i2c_driver = { + .driver = { + .name = "ml26124", + .owner = THIS_MODULE, + }, + .probe = ml26124_i2c_probe, + .remove = __devexit_p(ml26124_i2c_remove), + .id_table = ml26124_i2c_id, +}; + +module_i2c_driver(ml26124_i2c_driver); + +MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>"); +MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h new file mode 100644 index 000000000000..5ea0cbb8c46c --- /dev/null +++ b/sound/soc/codecs/ml26124.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ML26124_H +#define ML26124_H + +/* Clock Control Register */ +#define ML26124_SMPLING_RATE 0x00 +#define ML26124_PLLNL 0x02 +#define ML26124_PLLNH 0x04 +#define ML26124_PLLML 0x06 +#define ML26124_PLLMH 0x08 +#define ML26124_PLLDIV 0x0a +#define ML26124_CLK_EN 0x0c +#define ML26124_CLK_CTL 0x0e + +/* System Control Register */ +#define ML26124_SW_RST 0x10 +#define ML26124_REC_PLYBAK_RUN 0x12 +#define ML26124_MIC_TIM 0x14 + +/* Power Mnagement Register */ +#define ML26124_PW_REF_PW_MNG 0x20 +#define ML26124_PW_IN_PW_MNG 0x22 +#define ML26124_PW_DAC_PW_MNG 0x24 +#define ML26124_PW_SPAMP_PW_MNG 0x26 +#define ML26124_PW_LOUT_PW_MNG 0x28 +#define ML26124_PW_VOUT_PW_MNG 0x2a +#define ML26124_PW_ZCCMP_PW_MNG 0x2e + +/* Analog Reference Control Register */ +#define ML26124_PW_MICBIAS_VOL 0x30 + +/* Input/Output Amplifier Control Register */ +#define ML26124_PW_MIC_IN_VOL 0x32 +#define ML26124_PW_MIC_BOST_VOL 0x38 +#define ML26124_PW_SPK_AMP_VOL 0x3a +#define ML26124_PW_AMP_VOL_FUNC 0x48 +#define ML26124_PW_AMP_VOL_FADE 0x4a + +/* Analog Path Control Register */ +#define ML26124_SPK_AMP_OUT 0x54 +#define ML26124_MIC_IF_CTL 0x5a +#define ML26124_MIC_SELECT 0xe8 + +/* Audio Interface Control Register */ +#define ML26124_SAI_TRANS_CTL 0x60 +#define ML26124_SAI_RCV_CTL 0x62 +#define ML26124_SAI_MODE_SEL 0x64 + +/* DSP Control Register */ +#define ML26124_FILTER_EN 0x66 +#define ML26124_DVOL_CTL 0x68 +#define ML26124_MIXER_VOL_CTL 0x6a +#define ML26124_RECORD_DIG_VOL 0x6c +#define ML26124_PLBAK_DIG_VOL 0x70 +#define ML26124_DIGI_BOOST_VOL 0x72 +#define ML26124_EQ_GAIN_BRAND0 0x74 +#define ML26124_EQ_GAIN_BRAND1 0x76 +#define ML26124_EQ_GAIN_BRAND2 0x78 +#define ML26124_EQ_GAIN_BRAND3 0x7a +#define ML26124_EQ_GAIN_BRAND4 0x7c +#define ML26124_HPF2_CUTOFF 0x7e +#define ML26124_EQBRAND0_F0L 0x80 +#define ML26124_EQBRAND0_F0H 0x82 +#define ML26124_EQBRAND0_F1L 0x84 +#define ML26124_EQBRAND0_F1H 0x86 +#define ML26124_EQBRAND1_F0L 0x88 +#define ML26124_EQBRAND1_F0H 0x8a +#define ML26124_EQBRAND1_F1L 0x8c +#define ML26124_EQBRAND1_F1H 0x8e +#define ML26124_EQBRAND2_F0L 0x90 +#define ML26124_EQBRAND2_F0H 0x92 +#define ML26124_EQBRAND2_F1L 0x94 +#define ML26124_EQBRAND2_F1H 0x96 +#define ML26124_EQBRAND3_F0L 0x98 +#define ML26124_EQBRAND3_F0H 0x9a +#define ML26124_EQBRAND3_F1L 0x9c +#define ML26124_EQBRAND3_F1H 0x9e +#define ML26124_EQBRAND4_F0L 0xa0 +#define ML26124_EQBRAND4_F0H 0xa2 +#define ML26124_EQBRAND4_F1L 0xa4 +#define ML26124_EQBRAND4_F1H 0xa6 + +/* ALC Control Register */ +#define ML26124_ALC_MODE 0xb0 +#define ML26124_ALC_ATTACK_TIM 0xb2 +#define ML26124_ALC_DECAY_TIM 0xb4 +#define ML26124_ALC_HOLD_TIM 0xb6 +#define ML26124_ALC_TARGET_LEV 0xb8 +#define ML26124_ALC_MAXMIN_GAIN 0xba +#define ML26124_NOIS_GATE_THRSH 0xbc +#define ML26124_ALC_ZERO_TIMOUT 0xbe + +/* Playback Limiter Control Register */ +#define ML26124_PL_ATTACKTIME 0xc0 +#define ML26124_PL_DECAYTIME 0xc2 +#define ML26124_PL_TARGETTIME 0xc4 +#define ML26124_PL_MAXMIN_GAIN 0xc6 +#define ML26124_PLYBAK_BOST_VOL 0xc8 +#define ML26124_PL_0CROSS_TIMOUT 0xca + +/* Video Amplifer Control Register */ +#define ML26124_VIDEO_AMP_GAIN_CTL 0xd0 +#define ML26124_VIDEO_AMP_SETUP1 0xd2 +#define ML26124_VIDEO_AMP_CTL2 0xd4 + +/* Clock select for machine driver */ +#define ML26124_USE_PLL 0 +#define ML26124_USE_MCLKI_256FS 1 +#define ML26124_USE_MCLKI_512FS 2 +#define ML26124_USE_MCLKI_1024FS 3 + +/* Register Mask */ +#define ML26124_R0_MASK 0xf +#define ML26124_R2_MASK 0xff +#define ML26124_R4_MASK 0x1 +#define ML26124_R6_MASK 0xf +#define ML26124_R8_MASK 0x3f +#define ML26124_Ra_MASK 0x1f +#define ML26124_Rc_MASK 0x1f +#define ML26124_Re_MASK 0x7 +#define ML26124_R10_MASK 0x1 +#define ML26124_R12_MASK 0x17 +#define ML26124_R14_MASK 0x3f +#define ML26124_R20_MASK 0x47 +#define ML26124_R22_MASK 0xa +#define ML26124_R24_MASK 0x2 +#define ML26124_R26_MASK 0x1f +#define ML26124_R28_MASK 0x2 +#define ML26124_R2a_MASK 0x2 +#define ML26124_R2e_MASK 0x2 +#define ML26124_R30_MASK 0x7 +#define ML26124_R32_MASK 0x3f +#define ML26124_R38_MASK 0x38 +#define ML26124_R3a_MASK 0x3f +#define ML26124_R48_MASK 0x3 +#define ML26124_R4a_MASK 0x7 +#define ML26124_R54_MASK 0x2a +#define ML26124_R5a_MASK 0x3 +#define ML26124_Re8_MASK 0x3 +#define ML26124_R60_MASK 0xff +#define ML26124_R62_MASK 0xff +#define ML26124_R64_MASK 0x1 +#define ML26124_R66_MASK 0xff +#define ML26124_R68_MASK 0x3b +#define ML26124_R6a_MASK 0xf3 +#define ML26124_R6c_MASK 0xff +#define ML26124_R70_MASK 0xff + +#define ML26124_MCLKEN BIT(0) +#define ML26124_PLLEN BIT(1) +#define ML26124_PLLOE BIT(2) +#define ML26124_MCLKOE BIT(3) + +#define ML26124_BLT_ALL_ON 0x1f +#define ML26124_BLT_PREAMP_ON 0x13 + +#define ML26124_MICBEN_ON BIT(2) + +enum ml26124_regs { + ML26124_MCLK = 0, +}; + +enum ml26124_clk_in { + ML26124_USE_PLLOUT = 0, + ML26124_USE_MCLKI, +}; + +#endif diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 20c324c7c349..960d0e93cce9 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -18,7 +18,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/spi/spi.h> +#include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -30,6 +30,7 @@ #include "rt5631.h" struct rt5631_priv { + struct regmap *regmap; int codec_version; int master; int sysclk; @@ -38,33 +39,33 @@ struct rt5631_priv { int dmic_used_flag; }; -static const u16 rt5631_reg[RT5631_VENDOR_ID2 + 1] = { - [RT5631_SPK_OUT_VOL] = 0x8888, - [RT5631_HP_OUT_VOL] = 0x8080, - [RT5631_MONO_AXO_1_2_VOL] = 0xa080, - [RT5631_AUX_IN_VOL] = 0x0808, - [RT5631_ADC_REC_MIXER] = 0xf0f0, - [RT5631_VDAC_DIG_VOL] = 0x0010, - [RT5631_OUTMIXER_L_CTRL] = 0xffc0, - [RT5631_OUTMIXER_R_CTRL] = 0xffc0, - [RT5631_AXO1MIXER_CTRL] = 0x88c0, - [RT5631_AXO2MIXER_CTRL] = 0x88c0, - [RT5631_DIG_MIC_CTRL] = 0x3000, - [RT5631_MONO_INPUT_VOL] = 0x8808, - [RT5631_SPK_MIXER_CTRL] = 0xf8f8, - [RT5631_SPK_MONO_OUT_CTRL] = 0xfc00, - [RT5631_SPK_MONO_HP_OUT_CTRL] = 0x4440, - [RT5631_SDP_CTRL] = 0x8000, - [RT5631_MONO_SDP_CTRL] = 0x8000, - [RT5631_STEREO_AD_DA_CLK_CTRL] = 0x2010, - [RT5631_GEN_PUR_CTRL_REG] = 0x0e00, - [RT5631_INT_ST_IRQ_CTRL_2] = 0x071a, - [RT5631_MISC_CTRL] = 0x2040, - [RT5631_DEPOP_FUN_CTRL_2] = 0x8000, - [RT5631_SOFT_VOL_CTRL] = 0x07e0, - [RT5631_ALC_CTRL_1] = 0x0206, - [RT5631_ALC_CTRL_3] = 0x2000, - [RT5631_PSEUDO_SPATL_CTRL] = 0x0553, +static const struct reg_default rt5631_reg[] = { + { RT5631_SPK_OUT_VOL, 0x8888 }, + { RT5631_HP_OUT_VOL, 0x8080 }, + { RT5631_MONO_AXO_1_2_VOL, 0xa080 }, + { RT5631_AUX_IN_VOL, 0x0808 }, + { RT5631_ADC_REC_MIXER, 0xf0f0 }, + { RT5631_VDAC_DIG_VOL, 0x0010 }, + { RT5631_OUTMIXER_L_CTRL, 0xffc0 }, + { RT5631_OUTMIXER_R_CTRL, 0xffc0 }, + { RT5631_AXO1MIXER_CTRL, 0x88c0 }, + { RT5631_AXO2MIXER_CTRL, 0x88c0 }, + { RT5631_DIG_MIC_CTRL, 0x3000 }, + { RT5631_MONO_INPUT_VOL, 0x8808 }, + { RT5631_SPK_MIXER_CTRL, 0xf8f8 }, + { RT5631_SPK_MONO_OUT_CTRL, 0xfc00 }, + { RT5631_SPK_MONO_HP_OUT_CTRL, 0x4440 }, + { RT5631_SDP_CTRL, 0x8000 }, + { RT5631_MONO_SDP_CTRL, 0x8000 }, + { RT5631_STEREO_AD_DA_CLK_CTRL, 0x2010 }, + { RT5631_GEN_PUR_CTRL_REG, 0x0e00 }, + { RT5631_INT_ST_IRQ_CTRL_2, 0x071a }, + { RT5631_MISC_CTRL, 0x2040 }, + { RT5631_DEPOP_FUN_CTRL_2, 0x8000 }, + { RT5631_SOFT_VOL_CTRL, 0x07e0 }, + { RT5631_ALC_CTRL_1, 0x0206 }, + { RT5631_ALC_CTRL_3, 0x2000 }, + { RT5631_PSEUDO_SPATL_CTRL, 0x0553 }, }; /** @@ -96,8 +97,7 @@ static int rt5631_reset(struct snd_soc_codec *codec) return snd_soc_write(codec, RT5631_RESET, 0); } -static int rt5631_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) +static bool rt5631_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case RT5631_RESET: @@ -111,8 +111,7 @@ static int rt5631_volatile_register(struct snd_soc_codec *codec, } } -static int rt5631_readable_register(struct snd_soc_codec *codec, - unsigned int reg) +static bool rt5631_readable_register(struct device *dev, unsigned int reg) { switch (reg) { case RT5631_RESET: @@ -1361,8 +1360,7 @@ static int get_coeff(int mclk, int rate, int timesofbclk) static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec); int timesofbclk = 32, coeff; unsigned int iface = 0; @@ -1544,6 +1542,8 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, static int rt5631_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: @@ -1561,8 +1561,8 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3, RT5631_PWR_FAST_VREF_CTRL, RT5631_PWR_FAST_VREF_CTRL); - codec->cache_only = false; - snd_soc_cache_sync(codec); + regcache_cache_only(rt5631->regmap, false); + regcache_sync(rt5631->regmap); } break; @@ -1587,7 +1587,9 @@ static int rt5631_probe(struct snd_soc_codec *codec) unsigned int val; int ret; - ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); + codec->control_data = rt5631->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); if (ret != 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -1698,12 +1700,6 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = { .suspend = rt5631_suspend, .resume = rt5631_resume, .set_bias_level = rt5631_set_bias_level, - .reg_cache_size = RT5631_VENDOR_ID2 + 1, - .reg_word_size = sizeof(u16), - .reg_cache_default = rt5631_reg, - .volatile_register = rt5631_volatile_register, - .readable_register = rt5631_readable_register, - .reg_cache_step = 1, .controls = rt5631_snd_controls, .num_controls = ARRAY_SIZE(rt5631_snd_controls), .dapm_widgets = rt5631_dapm_widgets, @@ -1718,6 +1714,18 @@ static const struct i2c_device_id rt5631_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id); +static const struct regmap_config rt5631_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + + .readable_reg = rt5631_readable_register, + .volatile_reg = rt5631_volatile_register, + .max_register = RT5631_VENDOR_ID2, + .reg_defaults = rt5631_reg, + .num_reg_defaults = ARRAY_SIZE(rt5631_reg), + .cache_type = REGCACHE_RBTREE, +}; + static int rt5631_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1731,6 +1739,10 @@ static int rt5631_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, rt5631); + rt5631->regmap = devm_regmap_init_i2c(i2c, &rt5631_regmap_config); + if (IS_ERR(rt5631->regmap)) + return PTR_ERR(rt5631->regmap); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5631, rt5631_dai, ARRAY_SIZE(rt5631_dai)); return ret; @@ -1752,17 +1764,7 @@ static struct i2c_driver rt5631_i2c_driver = { .id_table = rt5631_i2c_id, }; -static int __init rt5631_modinit(void) -{ - return i2c_add_driver(&rt5631_i2c_driver); -} -module_init(rt5631_modinit); - -static void __exit rt5631_modexit(void) -{ - i2c_del_driver(&rt5631_i2c_driver); -} -module_exit(rt5631_modexit); +module_i2c_driver(rt5631_i2c_driver); MODULE_DESCRIPTION("ASoC RT5631 driver"); MODULE_AUTHOR("flove <flove@realtek.com>"); diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 8e92fb88ed09..8005fe6db66f 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -84,8 +84,8 @@ static struct regulator_consumer_supply ldo_consumer[] = { static struct regulator_init_data ldo_init_data = { .constraints = { - .min_uV = 850000, - .max_uV = 1600000, + .min_uV = 1200000, + .max_uV = 1200000, .valid_modes_mask = REGULATOR_MODE_NORMAL, .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, @@ -197,9 +197,9 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("HP_OUT"), SND_SOC_DAPM_OUTPUT("LINE_OUT"), - SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0, - mic_bias_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0, + mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0), SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0), @@ -665,8 +665,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); int channels = params_channels(params); int i2s_ctl = 0; @@ -1451,17 +1450,7 @@ static struct i2c_driver sgtl5000_i2c_driver = { .id_table = sgtl5000_id, }; -static int __init sgtl5000_modinit(void) -{ - return i2c_add_driver(&sgtl5000_i2c_driver); -} -module_init(sgtl5000_modinit); - -static void __exit sgtl5000_exit(void) -{ - i2c_del_driver(&sgtl5000_i2c_driver); -} -module_exit(sgtl5000_exit); +module_i2c_driver(sgtl5000_i2c_driver); MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver"); MODULE_AUTHOR("Zeng Zhaoming <zengzm.kernel@gmail.com>"); diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index de2b20544ceb..079066fef425 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -33,6 +33,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/spi/spi.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -43,8 +44,6 @@ #include "ssm2602.h" -#define SSM2602_VERSION "0.1" - enum ssm2602_type { SSM2602, SSM2604, @@ -53,10 +52,12 @@ enum ssm2602_type { /* codec private data */ struct ssm2602_priv { unsigned int sysclk; - enum snd_soc_control_type control_type; + struct snd_pcm_hw_constraint_list *sysclk_constraints; struct snd_pcm_substream *master_substream; struct snd_pcm_substream *slave_substream; + struct regmap *regmap; + enum ssm2602_type type; unsigned int clk_out_pwr; }; @@ -73,7 +74,6 @@ static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { 0x0000, 0x0000 }; -#define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0) /*Appending several "None"s just for OSS mixer use*/ static const char *ssm2602_input_select[] = { @@ -195,6 +195,24 @@ static const struct snd_soc_dapm_route ssm2604_routes[] = { {"ADC", NULL, "Line Input"}, }; +static const unsigned int ssm2602_rates_12288000[] = { + 8000, 32000, 48000, 96000, +}; + +static struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = { + .list = ssm2602_rates_12288000, + .count = ARRAY_SIZE(ssm2602_rates_12288000), +}; + +static const unsigned int ssm2602_rates_11289600[] = { + 8000, 44100, 88200, +}; + +static struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = { + .list = ssm2602_rates_11289600, + .count = ARRAY_SIZE(ssm2602_rates_11289600), +}; + struct ssm2602_coeff { u32 mclk; u32 rate; @@ -254,11 +272,10 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); - u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3; int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params)); + unsigned int iface; if (substream == ssm2602->slave_substream) { dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n"); @@ -268,31 +285,34 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, if (srate < 0) return srate; - snd_soc_write(codec, SSM2602_SRATE, srate); + regmap_write(ssm2602->regmap, SSM2602_SRATE, srate); /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: + iface = 0x0; break; case SNDRV_PCM_FORMAT_S20_3LE: - iface |= 0x0004; + iface = 0x4; break; case SNDRV_PCM_FORMAT_S24_LE: - iface |= 0x0008; + iface = 0x8; break; case SNDRV_PCM_FORMAT_S32_LE: - iface |= 0x000c; + iface = 0xc; break; + default: + return -EINVAL; } - snd_soc_write(codec, SSM2602_IFACE, iface); + regmap_update_bits(ssm2602->regmap, SSM2602_IFACE, + IFACE_AUDIO_DATA_LEN, iface); return 0; } static int ssm2602_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct snd_pcm_runtime *master_runtime; @@ -322,14 +342,19 @@ static int ssm2602_startup(struct snd_pcm_substream *substream, } else ssm2602->master_substream = substream; + if (ssm2602->sysclk_constraints) { + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + ssm2602->sysclk_constraints); + } + return 0; } static void ssm2602_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); if (ssm2602->master_substream == substream) @@ -341,14 +366,14 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream, static int ssm2602_mute(struct snd_soc_dai *dai, int mute) { - struct snd_soc_codec *codec = dai->codec; + struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec); if (mute) - snd_soc_update_bits(codec, SSM2602_APDIGI, + regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI, APDIGI_ENABLE_DAC_MUTE, APDIGI_ENABLE_DAC_MUTE); else - snd_soc_update_bits(codec, SSM2602_APDIGI, + regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI, APDIGI_ENABLE_DAC_MUTE, 0); return 0; } @@ -364,16 +389,21 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; switch (freq) { - case 11289600: - case 12000000: case 12288000: - case 16934400: case 18432000: - ssm2602->sysclk = freq; + ssm2602->sysclk_constraints = &ssm2602_constraints_12288000; + break; + case 11289600: + case 16934400: + ssm2602->sysclk_constraints = &ssm2602_constraints_11289600; + break; + case 12000000: + ssm2602->sysclk_constraints = NULL; break; default: return -EINVAL; } + ssm2602->sysclk = freq; } else { unsigned int mask; @@ -393,7 +423,7 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai, else ssm2602->clk_out_pwr &= ~mask; - snd_soc_update_bits(codec, SSM2602_PWR, + regmap_update_bits(ssm2602->regmap, SSM2602_PWR, PWR_CLK_OUT_PDN | PWR_OSC_PDN, ssm2602->clk_out_pwr); } @@ -403,8 +433,8 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; - u16 iface = 0; + struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec_dai->codec); + unsigned int iface = 0; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -455,7 +485,7 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai, } /* set iface */ - snd_soc_write(codec, SSM2602_IFACE, iface); + regmap_write(ssm2602->regmap, SSM2602_IFACE, iface); return 0; } @@ -467,7 +497,7 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: /* vref/mid on, osc and clkout on if enabled */ - snd_soc_update_bits(codec, SSM2602_PWR, + regmap_update_bits(ssm2602->regmap, SSM2602_PWR, PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN, ssm2602->clk_out_pwr); break; @@ -475,13 +505,13 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: /* everything off except vref/vmid, */ - snd_soc_update_bits(codec, SSM2602_PWR, + regmap_update_bits(ssm2602->regmap, SSM2602_PWR, PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN, PWR_CLK_OUT_PDN | PWR_OSC_PDN); break; case SND_SOC_BIAS_OFF: /* everything off */ - snd_soc_update_bits(codec, SSM2602_PWR, + regmap_update_bits(ssm2602->regmap, SSM2602_PWR, PWR_POWER_OFF, PWR_POWER_OFF); break; @@ -540,12 +570,13 @@ static int ssm2602_resume(struct snd_soc_codec *codec) static int ssm2602_probe(struct snd_soc_codec *codec) { + struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - snd_soc_update_bits(codec, SSM2602_LOUT1V, + regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V, LOUT1V_LRHP_BOTH, LOUT1V_LRHP_BOTH); - snd_soc_update_bits(codec, SSM2602_ROUT1V, + regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V, ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH); ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls, @@ -581,27 +612,26 @@ static int ssm260x_probe(struct snd_soc_codec *codec) struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); int ret; - pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION); - - ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type); + codec->control_data = ssm2602->regmap; + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; } - ret = ssm2602_reset(codec); + ret = regmap_write(ssm2602->regmap, SSM2602_RESET, 0); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset: %d\n", ret); return ret; } /* set the update bits */ - snd_soc_update_bits(codec, SSM2602_LINVOL, + regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL, LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH); - snd_soc_update_bits(codec, SSM2602_RINVOL, + regmap_update_bits(ssm2602->regmap, SSM2602_RINVOL, RINVOL_RLIN_BOTH, RINVOL_RLIN_BOTH); /*select Line in as default input*/ - snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | + regmap_write(ssm2602->regmap, SSM2602_APANA, APANA_SELECT_DAC | APANA_ENABLE_MIC_BOOST); switch (ssm2602->type) { @@ -634,9 +664,6 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { .suspend = ssm2602_suspend, .resume = ssm2602_resume, .set_bias_level = ssm2602_set_bias_level, - .reg_cache_size = ARRAY_SIZE(ssm2602_reg), - .reg_word_size = sizeof(u16), - .reg_cache_default = ssm2602_reg, .controls = ssm260x_snd_controls, .num_controls = ARRAY_SIZE(ssm260x_snd_controls), @@ -646,6 +673,23 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { .num_dapm_routes = ARRAY_SIZE(ssm260x_routes), }; +static bool ssm2602_register_volatile(struct device *dev, unsigned int reg) +{ + return reg == SSM2602_RESET; +} + +static const struct regmap_config ssm2602_regmap_config = { + .val_bits = 9, + .reg_bits = 7, + + .max_register = SSM2602_RESET, + .volatile_reg = ssm2602_register_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults_raw = ssm2602_reg, + .num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg), +}; + #if defined(CONFIG_SPI_MASTER) static int __devinit ssm2602_spi_probe(struct spi_device *spi) { @@ -658,9 +702,12 @@ static int __devinit ssm2602_spi_probe(struct spi_device *spi) return -ENOMEM; spi_set_drvdata(spi, ssm2602); - ssm2602->control_type = SND_SOC_SPI; ssm2602->type = SSM2602; + ssm2602->regmap = devm_regmap_init_spi(spi, &ssm2602_regmap_config); + if (IS_ERR(ssm2602->regmap)) + return PTR_ERR(ssm2602->regmap); + ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_ssm2602, &ssm2602_dai, 1); return ret; @@ -701,9 +748,12 @@ static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c, return -ENOMEM; i2c_set_clientdata(i2c, ssm2602); - ssm2602->control_type = SND_SOC_I2C; ssm2602->type = id->driver_data; + ssm2602->regmap = devm_regmap_init_i2c(i2c, &ssm2602_regmap_config); + if (IS_ERR(ssm2602->regmap)) + return PTR_ERR(ssm2602->regmap); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ssm2602, &ssm2602_dai, 1); return ret; diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 7db6fa515028..8d717f4b5a87 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -609,8 +609,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); unsigned int rate; int i, mcs = -1, ir = -1; diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index df1e07ffac32..31762ebdd774 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -34,8 +34,6 @@ #include "tlv320aic23.h" -#define AIC23_VERSION "0.1" - /* * AIC23 register cache */ @@ -325,8 +323,7 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 iface_reg; int ret; struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec); @@ -371,8 +368,7 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream, static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; /* set active */ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0001); @@ -383,8 +379,7 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream, static void tlv320aic23_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec); /* deactivate */ @@ -548,8 +543,6 @@ static int tlv320aic23_probe(struct snd_soc_codec *codec) struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec); int ret; - printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION); - ret = snd_soc_codec_set_cache_io(codec, 7, 9, aic23->control_type); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 802064b5030d..85944e953578 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -126,8 +126,7 @@ static int aic26_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec); int fsref, divisor, wlen, pval, jval, dval, qval; u16 reg; diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 8d20f6ec20f3..64d2a4fa34b2 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -802,8 +802,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec =rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; @@ -1161,24 +1160,6 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, return 0; } -void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect, - int headset_debounce, int button_debounce) -{ - u8 val; - - val = ((detect & AIC3X_HEADSET_DETECT_MASK) - << AIC3X_HEADSET_DETECT_SHIFT) | - ((headset_debounce & AIC3X_HEADSET_DEBOUNCE_MASK) - << AIC3X_HEADSET_DEBOUNCE_SHIFT) | - ((button_debounce & AIC3X_BUTTON_DEBOUNCE_MASK) - << AIC3X_BUTTON_DEBOUNCE_SHIFT); - - if (detect & AIC3X_HEADSET_DETECT_MASK) - val |= AIC3X_HEADSET_DETECT_ENABLED; - - snd_soc_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val); -} - #define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 #define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 4587ddd0fbf8..0dd41077ab79 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -62,8 +62,10 @@ #define UTHR_FROM_PERIOD_SIZE(samples, playrate, burstrate) \ (((samples)*5000) / (((burstrate)*5000) / ((burstrate) - (playrate)))) -static void dac33_calculate_times(struct snd_pcm_substream *substream); -static int dac33_prepare_chip(struct snd_pcm_substream *substream); +static void dac33_calculate_times(struct snd_pcm_substream *substream, + struct snd_soc_codec *codec); +static int dac33_prepare_chip(struct snd_pcm_substream *substream, + struct snd_soc_codec *codec); enum dac33_state { DAC33_IDLE = 0, @@ -427,8 +429,8 @@ static int dac33_playback_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: if (likely(dac33->substream)) { - dac33_calculate_times(dac33->substream); - dac33_prepare_chip(dac33->substream); + dac33_calculate_times(dac33->substream, w->codec); + dac33_prepare_chip(dac33->substream, w->codec); } break; case SND_SOC_DAPM_POST_PMD: @@ -799,8 +801,7 @@ static void dac33_oscwait(struct snd_soc_codec *codec) static int dac33_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); /* Stream started, save the substream pointer */ @@ -812,8 +813,7 @@ static int dac33_startup(struct snd_pcm_substream *substream, static void dac33_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); dac33->substream = NULL; @@ -825,8 +825,7 @@ static int dac33_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); /* Check parameters for validity */ @@ -868,10 +867,9 @@ static int dac33_hw_params(struct snd_pcm_substream *substream, * writes happens in different order, than dac33 might end up in unknown state. * Use the known, working sequence of register writes to initialize the dac33. */ -static int dac33_prepare_chip(struct snd_pcm_substream *substream) +static int dac33_prepare_chip(struct snd_pcm_substream *substream, + struct snd_soc_codec *codec) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); unsigned int oscset, ratioset, pwr_ctrl, reg_tmp; u8 aictrl_a, aictrl_b, fifoctrl_a; @@ -1067,10 +1065,9 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) return 0; } -static void dac33_calculate_times(struct snd_pcm_substream *substream) +static void dac33_calculate_times(struct snd_pcm_substream *substream, + struct snd_soc_codec *codec) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); unsigned int period_size = substream->runtime->period_size; unsigned int rate = substream->runtime->rate; @@ -1128,8 +1125,7 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream) static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); int ret = 0; @@ -1161,8 +1157,7 @@ static snd_pcm_sframes_t dac33_dai_delay( struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); unsigned long long t0, t1, t_now; unsigned int time_delta, uthr; diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 170cf9a8fc79..391fcfc7b63b 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1685,8 +1685,7 @@ static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction, static int twl4030_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); if (twl4030->master_substream) { @@ -1715,8 +1714,7 @@ static int twl4030_startup(struct snd_pcm_substream *substream, static void twl4030_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); if (twl4030->master_substream == substream) @@ -1740,8 +1738,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 mode, old_mode, format, old_format; @@ -1974,8 +1971,7 @@ static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction, static int twl4030_voice_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 mode; @@ -2007,8 +2003,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream, static void twl4030_voice_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; /* Enable voice digital filters */ twl4030_voice_enable(codec, substream->stream, 0); @@ -2017,8 +2012,7 @@ static void twl4030_voice_shutdown(struct snd_pcm_substream *substream, static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 old_mode, mode; diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 2d8c6b825e57..a36e9fcdf184 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -26,7 +26,6 @@ #include <linux/pm.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/i2c/twl.h> #include <linux/mfd/twl6040.h> #include <sound/core.h> @@ -47,17 +46,6 @@ #define TWL6040_OUTHF_0dB 0x03 #define TWL6040_OUTHF_M52dB 0x1D -#define TWL6040_RAMP_NONE 0 -#define TWL6040_RAMP_UP 1 -#define TWL6040_RAMP_DOWN 2 - -#define TWL6040_HSL_VOL_MASK 0x0F -#define TWL6040_HSL_VOL_SHIFT 0 -#define TWL6040_HSR_VOL_MASK 0xF0 -#define TWL6040_HSR_VOL_SHIFT 4 -#define TWL6040_HF_VOL_MASK 0x1F -#define TWL6040_HF_VOL_SHIFT 0 - /* Shadow register used by the driver */ #define TWL6040_REG_SW_SHADOW 0x2F #define TWL6040_CACHEREGNUM (TWL6040_REG_SW_SHADOW + 1) @@ -65,18 +53,6 @@ /* TWL6040_REG_SW_SHADOW (0x2F) fields */ #define TWL6040_EAR_PATH_ENABLE 0x01 -struct twl6040_output { - u16 active; - u16 left_vol; - u16 right_vol; - u16 left_step; - u16 right_step; - unsigned int step_delay; - u16 ramp; - struct delayed_work work; - struct completion ramp_done; -}; - struct twl6040_jack_data { struct snd_soc_jack *jack; struct delayed_work work; @@ -101,8 +77,6 @@ struct twl6040_data { struct snd_soc_codec *codec; struct workqueue_struct *workqueue; struct mutex mutex; - struct twl6040_output headset; - struct twl6040_output handsfree; }; /* @@ -312,318 +286,6 @@ static void twl6040_restore_regs(struct snd_soc_codec *codec) } } -/* - * Ramp HS PGA volume to minimise pops at stream startup and shutdown. - */ -static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, - unsigned int left_step, unsigned int right_step) -{ - - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - struct twl6040_output *headset = &priv->headset; - int left_complete = 0, right_complete = 0; - u8 reg, val; - - /* left channel */ - left_step = (left_step > 0xF) ? 0xF : left_step; - reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); - val = (~reg & TWL6040_HSL_VOL_MASK); - - if (headset->ramp == TWL6040_RAMP_UP) { - /* ramp step up */ - if (val < headset->left_vol) { - if (val + left_step > headset->left_vol) - val = headset->left_vol; - else - val += left_step; - - reg &= ~TWL6040_HSL_VOL_MASK; - twl6040_write(codec, TWL6040_REG_HSGAIN, - (reg | (~val & TWL6040_HSL_VOL_MASK))); - } else { - left_complete = 1; - } - } else if (headset->ramp == TWL6040_RAMP_DOWN) { - /* ramp step down */ - if (val > 0x0) { - if ((int)val - (int)left_step < 0) - val = 0; - else - val -= left_step; - - reg &= ~TWL6040_HSL_VOL_MASK; - twl6040_write(codec, TWL6040_REG_HSGAIN, reg | - (~val & TWL6040_HSL_VOL_MASK)); - } else { - left_complete = 1; - } - } - - /* right channel */ - right_step = (right_step > 0xF) ? 0xF : right_step; - reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); - val = (~reg & TWL6040_HSR_VOL_MASK) >> TWL6040_HSR_VOL_SHIFT; - - if (headset->ramp == TWL6040_RAMP_UP) { - /* ramp step up */ - if (val < headset->right_vol) { - if (val + right_step > headset->right_vol) - val = headset->right_vol; - else - val += right_step; - - reg &= ~TWL6040_HSR_VOL_MASK; - twl6040_write(codec, TWL6040_REG_HSGAIN, - (reg | (~val << TWL6040_HSR_VOL_SHIFT))); - } else { - right_complete = 1; - } - } else if (headset->ramp == TWL6040_RAMP_DOWN) { - /* ramp step down */ - if (val > 0x0) { - if ((int)val - (int)right_step < 0) - val = 0; - else - val -= right_step; - - reg &= ~TWL6040_HSR_VOL_MASK; - twl6040_write(codec, TWL6040_REG_HSGAIN, - reg | (~val << TWL6040_HSR_VOL_SHIFT)); - } else { - right_complete = 1; - } - } - - return left_complete & right_complete; -} - -/* - * Ramp HF PGA volume to minimise pops at stream startup and shutdown. - */ -static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, - unsigned int left_step, unsigned int right_step) -{ - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - struct twl6040_output *handsfree = &priv->handsfree; - int left_complete = 0, right_complete = 0; - u16 reg, val; - - /* left channel */ - left_step = (left_step > 0x1D) ? 0x1D : left_step; - reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN); - reg = 0x1D - reg; - val = (reg & TWL6040_HF_VOL_MASK); - if (handsfree->ramp == TWL6040_RAMP_UP) { - /* ramp step up */ - if (val < handsfree->left_vol) { - if (val + left_step > handsfree->left_vol) - val = handsfree->left_vol; - else - val += left_step; - - reg &= ~TWL6040_HF_VOL_MASK; - twl6040_write(codec, TWL6040_REG_HFLGAIN, - reg | (0x1D - val)); - } else { - left_complete = 1; - } - } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { - /* ramp step down */ - if (val > 0) { - if ((int)val - (int)left_step < 0) - val = 0; - else - val -= left_step; - - reg &= ~TWL6040_HF_VOL_MASK; - twl6040_write(codec, TWL6040_REG_HFLGAIN, - reg | (0x1D - val)); - } else { - left_complete = 1; - } - } - - /* right channel */ - right_step = (right_step > 0x1D) ? 0x1D : right_step; - reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN); - reg = 0x1D - reg; - val = (reg & TWL6040_HF_VOL_MASK); - if (handsfree->ramp == TWL6040_RAMP_UP) { - /* ramp step up */ - if (val < handsfree->right_vol) { - if (val + right_step > handsfree->right_vol) - val = handsfree->right_vol; - else - val += right_step; - - reg &= ~TWL6040_HF_VOL_MASK; - twl6040_write(codec, TWL6040_REG_HFRGAIN, - reg | (0x1D - val)); - } else { - right_complete = 1; - } - } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { - /* ramp step down */ - if (val > 0) { - if ((int)val - (int)right_step < 0) - val = 0; - else - val -= right_step; - - reg &= ~TWL6040_HF_VOL_MASK; - twl6040_write(codec, TWL6040_REG_HFRGAIN, - reg | (0x1D - val)); - } - } - - return left_complete & right_complete; -} - -/* - * This work ramps both output PGAs at stream start/stop time to - * minimise pop associated with DAPM power switching. - */ -static void twl6040_pga_hs_work(struct work_struct *work) -{ - struct twl6040_data *priv = - container_of(work, struct twl6040_data, headset.work.work); - struct snd_soc_codec *codec = priv->codec; - struct twl6040_output *headset = &priv->headset; - int i, headset_complete; - - /* do we need to ramp at all ? */ - if (headset->ramp == TWL6040_RAMP_NONE) - return; - - /* HS PGA gain range: 0x0 - 0xf (0 - 15) */ - for (i = 0; i < 16; i++) { - headset_complete = twl6040_hs_ramp_step(codec, - headset->left_step, - headset->right_step); - - /* ramp finished ? */ - if (headset_complete) - break; - - schedule_timeout_interruptible( - msecs_to_jiffies(headset->step_delay)); - } - - if (headset->ramp == TWL6040_RAMP_DOWN) { - headset->active = 0; - complete(&headset->ramp_done); - } else { - headset->active = 1; - } - headset->ramp = TWL6040_RAMP_NONE; -} - -static void twl6040_pga_hf_work(struct work_struct *work) -{ - struct twl6040_data *priv = - container_of(work, struct twl6040_data, handsfree.work.work); - struct snd_soc_codec *codec = priv->codec; - struct twl6040_output *handsfree = &priv->handsfree; - int i, handsfree_complete; - - /* do we need to ramp at all ? */ - if (handsfree->ramp == TWL6040_RAMP_NONE) - return; - - /* - * HF PGA gain range: 0x00 - 0x1d (0 - 29) */ - for (i = 0; i < 30; i++) { - handsfree_complete = twl6040_hf_ramp_step(codec, - handsfree->left_step, - handsfree->right_step); - - /* ramp finished ? */ - if (handsfree_complete) - break; - - schedule_timeout_interruptible( - msecs_to_jiffies(handsfree->step_delay)); - } - - - if (handsfree->ramp == TWL6040_RAMP_DOWN) { - handsfree->active = 0; - complete(&handsfree->ramp_done); - } else - handsfree->active = 1; - handsfree->ramp = TWL6040_RAMP_NONE; -} - -static int out_drv_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - struct twl6040_output *out; - struct delayed_work *work; - - switch (w->shift) { - case 2: /* Headset output driver */ - out = &priv->headset; - work = &out->work; - /* - * Make sure, that we do not mess up variables for already - * executing work. - */ - cancel_delayed_work_sync(work); - - out->left_step = priv->hs_left_step; - out->right_step = priv->hs_right_step; - out->step_delay = 5; /* 5 ms between volume ramp steps */ - break; - case 4: /* Handsfree output driver */ - out = &priv->handsfree; - work = &out->work; - /* - * Make sure, that we do not mess up variables for already - * executing work. - */ - cancel_delayed_work_sync(work); - - out->left_step = priv->hf_left_step; - out->right_step = priv->hf_right_step; - out->step_delay = 5; /* 5 ms between volume ramp steps */ - break; - default: - return -1; - } - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - if (out->active) - break; - - /* don't use volume ramp for power-up */ - out->ramp = TWL6040_RAMP_UP; - out->left_step = out->left_vol; - out->right_step = out->right_vol; - - queue_delayed_work(priv->workqueue, work, msecs_to_jiffies(1)); - break; - - case SND_SOC_DAPM_PRE_PMD: - if (!out->active) - break; - - /* use volume ramp for power-down */ - out->ramp = TWL6040_RAMP_DOWN; - INIT_COMPLETION(out->ramp_done); - - queue_delayed_work(priv->workqueue, work, msecs_to_jiffies(1)); - - wait_for_completion_timeout(&out->ramp_done, - msecs_to_jiffies(2000)); - break; - } - - return 0; -} - /* set headset dac and driver power mode */ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) { @@ -748,71 +410,6 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) return IRQ_HANDLED; } -static int twl6040_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); - struct twl6040_output *out = NULL; - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int ret; - - /* For HS and HF we shadow the values and only actually write - * them out when active in order to ensure the amplifier comes on - * as quietly as possible. */ - switch (mc->reg) { - case TWL6040_REG_HSGAIN: - out = &twl6040_priv->headset; - break; - case TWL6040_REG_HFLGAIN: - out = &twl6040_priv->handsfree; - break; - default: - dev_warn(codec->dev, "%s: Unexpected register: 0x%02x\n", - __func__, mc->reg); - return -EINVAL; - } - - out->left_vol = ucontrol->value.integer.value[0]; - out->right_vol = ucontrol->value.integer.value[1]; - if (!out->active) - return 1; - - ret = snd_soc_put_volsw(kcontrol, ucontrol); - if (ret < 0) - return ret; - - return 1; -} - -static int twl6040_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); - struct twl6040_output *out = &twl6040_priv->headset; - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - - switch (mc->reg) { - case TWL6040_REG_HSGAIN: - out = &twl6040_priv->headset; - break; - case TWL6040_REG_HFLGAIN: - out = &twl6040_priv->handsfree; - break; - default: - dev_warn(codec->dev, "%s: Unexpected register: 0x%02x\n", - __func__, mc->reg); - return -EINVAL; - } - - ucontrol->value.integer.value[0] = out->left_vol; - ucontrol->value.integer.value[1] = out->right_vol; - return 0; -} - static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1077,12 +674,10 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv), /* Playback gains */ - SOC_DOUBLE_EXT_TLV("Headset Playback Volume", - TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, twl6040_get_volsw, - twl6040_put_volsw, hs_tlv), - SOC_DOUBLE_R_EXT_TLV("Handsfree Playback Volume", - TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, - twl6040_get_volsw, twl6040_put_volsw, hf_tlv), + SOC_DOUBLE_TLV("Headset Playback Volume", + TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv), + SOC_DOUBLE_R_TLV("Handsfree Playback Volume", + TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), SOC_SINGLE_TLV("Earphone Playback Volume", TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), @@ -1181,22 +776,14 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { &auxr_switch_control), /* Analog playback drivers */ - SND_SOC_DAPM_OUT_DRV_E("HF Left Driver", - TWL6040_REG_HFLCTL, 4, 0, NULL, 0, - out_drv_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_OUT_DRV_E("HF Right Driver", - TWL6040_REG_HFRCTL, 4, 0, NULL, 0, - out_drv_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_OUT_DRV_E("HS Left Driver", - TWL6040_REG_HSLCTL, 2, 0, NULL, 0, - out_drv_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_OUT_DRV_E("HS Right Driver", - TWL6040_REG_HSRCTL, 2, 0, NULL, 0, - out_drv_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV("HF Left Driver", + TWL6040_REG_HFLCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HF Right Driver", + TWL6040_REG_HFRCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HS Left Driver", + TWL6040_REG_HSLCTL, 2, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HS Right Driver", + TWL6040_REG_HSRCTL, 2, 0, NULL, 0), SND_SOC_DAPM_OUT_DRV_E("Earphone Driver", TWL6040_REG_EARCTL, 0, 0, NULL, 0, twl6040_ep_drv_event, @@ -1340,8 +927,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, static int twl6040_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); snd_pcm_hw_constraint_list(substream->runtime, 0, @@ -1355,8 +941,7 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int rate; @@ -1392,8 +977,7 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, static int twl6040_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int ret; @@ -1528,7 +1112,7 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { struct twl6040_data *priv; - struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); + struct twl6040_codec_data *pdata = dev_get_platdata(codec->dev); struct platform_device *pdev = container_of(codec->dev, struct platform_device, dev); int ret = 0; @@ -1571,14 +1155,9 @@ static int twl6040_probe(struct snd_soc_codec *codec) } INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work); - INIT_DELAYED_WORK(&priv->headset.work, twl6040_pga_hs_work); - INIT_DELAYED_WORK(&priv->handsfree.work, twl6040_pga_hf_work); mutex_init(&priv->mutex); - init_completion(&priv->headset.ramp_done); - init_completion(&priv->handsfree.ramp_done); - ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, 0, "twl6040_irq_plug", codec); if (ret) { diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 797b0dde2c68..6c3d43b8ee85 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -159,8 +159,7 @@ static int uda134x_mute(struct snd_soc_dai *dai, int mute) static int uda134x_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec =rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec); struct snd_pcm_runtime *master_runtime; @@ -191,8 +190,7 @@ static int uda134x_startup(struct snd_pcm_substream *substream, static void uda134x_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec); if (uda134x->master_substream == substream) diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 4f1b23d7e404..2502214b84ab 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -502,8 +502,7 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai, static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER); @@ -528,8 +527,7 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK); /* set WSPLL power and divider if running from this clock */ diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 3d868dc40092..7b24d6d192e1 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -293,8 +293,7 @@ static const struct snd_kcontrol_new wl1273_controls[] = { static int wl1273_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); switch (wl1273->mode) { @@ -329,8 +328,7 @@ static int wl1273_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(rtd->codec); + struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(dai->codec); struct wl1273_core *core = wl1273->core; unsigned int rate, width, r; diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index aefb4f89be0e..e0b51e9f8b12 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -79,22 +79,65 @@ static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = { { "WM1250 Output", NULL, "DAC" }, }; +static int wm1250_ev1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wm1250_priv *wm1250 = snd_soc_codec_get_drvdata(dai->codec); + + switch (params_rate(params)) { + case 8000: + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, + 1); + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, + 1); + break; + case 16000: + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, + 0); + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, + 1); + break; + case 32000: + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, + 1); + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, + 0); + break; + case 64000: + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, + 0); + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, + 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops wm1250_ev1_ops = { + .hw_params = wm1250_ev1_hw_params, +}; + static struct snd_soc_dai_driver wm1250_ev1_dai = { .name = "wm1250-ev1", .playback = { .stream_name = "Playback", .channels_min = 1, - .channels_max = 1, + .channels_max = 2, .rates = SNDRV_PCM_RATE_8000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .stream_name = "Capture", .channels_min = 1, - .channels_max = 1, + .channels_max = 2, .rates = SNDRV_PCM_RATE_8000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, + .ops = &wm1250_ev1_ops, }; static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = { @@ -215,23 +258,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = { .id_table = wm1250_ev1_i2c_id, }; -static int __init wm1250_ev1_modinit(void) -{ - int ret = 0; - - ret = i2c_add_driver(&wm1250_ev1_i2c_driver); - if (ret != 0) - pr_err("Failed to register WM1250-EV1 I2C driver: %d\n", ret); - - return ret; -} -module_init(wm1250_ev1_modinit); - -static void __exit wm1250_ev1_exit(void) -{ - i2c_del_driver(&wm1250_ev1_i2c_driver); -} -module_exit(wm1250_ev1_exit); +module_i2c_driver(wm1250_ev1_i2c_driver); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver"); diff --git a/sound/soc/codecs/wm5100-tables.c b/sound/soc/codecs/wm5100-tables.c index 9a18fae68204..e167207a19cc 100644 --- a/sound/soc/codecs/wm5100-tables.c +++ b/sound/soc/codecs/wm5100-tables.c @@ -32,7 +32,18 @@ bool wm5100_volatile_register(struct device *dev, unsigned int reg) case WM5100_MIC_DETECT_3: return 1; default: - return 0; + if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) || + (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) || + (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) || + (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) || + (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) || + (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) || + (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) || + (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) || + (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511)) + return 1; + else + return 0; } } @@ -697,9 +708,110 @@ bool wm5100_readable_register(struct device *dev, unsigned int reg) case WM5100_HPLPF3_2: case WM5100_HPLPF4_1: case WM5100_HPLPF4_2: + case WM5100_DSP1_CONTROL_1: + case WM5100_DSP1_CONTROL_2: + case WM5100_DSP1_CONTROL_3: + case WM5100_DSP1_CONTROL_4: + case WM5100_DSP1_CONTROL_5: + case WM5100_DSP1_CONTROL_6: + case WM5100_DSP1_CONTROL_7: + case WM5100_DSP1_CONTROL_8: + case WM5100_DSP1_CONTROL_9: + case WM5100_DSP1_CONTROL_10: + case WM5100_DSP1_CONTROL_11: + case WM5100_DSP1_CONTROL_12: + case WM5100_DSP1_CONTROL_13: + case WM5100_DSP1_CONTROL_14: + case WM5100_DSP1_CONTROL_15: + case WM5100_DSP1_CONTROL_16: + case WM5100_DSP1_CONTROL_17: + case WM5100_DSP1_CONTROL_18: + case WM5100_DSP1_CONTROL_19: + case WM5100_DSP1_CONTROL_20: + case WM5100_DSP1_CONTROL_21: + case WM5100_DSP1_CONTROL_22: + case WM5100_DSP1_CONTROL_23: + case WM5100_DSP1_CONTROL_24: + case WM5100_DSP1_CONTROL_25: + case WM5100_DSP1_CONTROL_26: + case WM5100_DSP1_CONTROL_27: + case WM5100_DSP1_CONTROL_28: + case WM5100_DSP1_CONTROL_29: + case WM5100_DSP1_CONTROL_30: + case WM5100_DSP2_CONTROL_1: + case WM5100_DSP2_CONTROL_2: + case WM5100_DSP2_CONTROL_3: + case WM5100_DSP2_CONTROL_4: + case WM5100_DSP2_CONTROL_5: + case WM5100_DSP2_CONTROL_6: + case WM5100_DSP2_CONTROL_7: + case WM5100_DSP2_CONTROL_8: + case WM5100_DSP2_CONTROL_9: + case WM5100_DSP2_CONTROL_10: + case WM5100_DSP2_CONTROL_11: + case WM5100_DSP2_CONTROL_12: + case WM5100_DSP2_CONTROL_13: + case WM5100_DSP2_CONTROL_14: + case WM5100_DSP2_CONTROL_15: + case WM5100_DSP2_CONTROL_16: + case WM5100_DSP2_CONTROL_17: + case WM5100_DSP2_CONTROL_18: + case WM5100_DSP2_CONTROL_19: + case WM5100_DSP2_CONTROL_20: + case WM5100_DSP2_CONTROL_21: + case WM5100_DSP2_CONTROL_22: + case WM5100_DSP2_CONTROL_23: + case WM5100_DSP2_CONTROL_24: + case WM5100_DSP2_CONTROL_25: + case WM5100_DSP2_CONTROL_26: + case WM5100_DSP2_CONTROL_27: + case WM5100_DSP2_CONTROL_28: + case WM5100_DSP2_CONTROL_29: + case WM5100_DSP2_CONTROL_30: + case WM5100_DSP3_CONTROL_1: + case WM5100_DSP3_CONTROL_2: + case WM5100_DSP3_CONTROL_3: + case WM5100_DSP3_CONTROL_4: + case WM5100_DSP3_CONTROL_5: + case WM5100_DSP3_CONTROL_6: + case WM5100_DSP3_CONTROL_7: + case WM5100_DSP3_CONTROL_8: + case WM5100_DSP3_CONTROL_9: + case WM5100_DSP3_CONTROL_10: + case WM5100_DSP3_CONTROL_11: + case WM5100_DSP3_CONTROL_12: + case WM5100_DSP3_CONTROL_13: + case WM5100_DSP3_CONTROL_14: + case WM5100_DSP3_CONTROL_15: + case WM5100_DSP3_CONTROL_16: + case WM5100_DSP3_CONTROL_17: + case WM5100_DSP3_CONTROL_18: + case WM5100_DSP3_CONTROL_19: + case WM5100_DSP3_CONTROL_20: + case WM5100_DSP3_CONTROL_21: + case WM5100_DSP3_CONTROL_22: + case WM5100_DSP3_CONTROL_23: + case WM5100_DSP3_CONTROL_24: + case WM5100_DSP3_CONTROL_25: + case WM5100_DSP3_CONTROL_26: + case WM5100_DSP3_CONTROL_27: + case WM5100_DSP3_CONTROL_28: + case WM5100_DSP3_CONTROL_29: + case WM5100_DSP3_CONTROL_30: return 1; default: - return 0; + if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) || + (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) || + (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) || + (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) || + (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) || + (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) || + (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) || + (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) || + (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511)) + return 1; + else + return 0; } } @@ -1361,4 +1473,13 @@ struct reg_default wm5100_reg_defaults[WM5100_REGISTER_COUNT] = { { 0x0EC9, 0x0000 }, /* R3785 - HPLPF3_2 */ { 0x0ECC, 0x0000 }, /* R3788 - HPLPF4_1 */ { 0x0ECD, 0x0000 }, /* R3789 - HPLPF4_2 */ + { 0x0F02, 0x0000 }, /* R3842 - DSP1 Control 2 */ + { 0x0F03, 0x0000 }, /* R3843 - DSP1 Control 3 */ + { 0x0F04, 0x0000 }, /* R3844 - DSP1 Control 4 */ + { 0x1002, 0x0000 }, /* R4098 - DSP2 Control 2 */ + { 0x1003, 0x0000 }, /* R4099 - DSP2 Control 3 */ + { 0x1004, 0x0000 }, /* R4100 - DSP2 Control 4 */ + { 0x1102, 0x0000 }, /* R4354 - DSP3 Control 2 */ + { 0x1103, 0x0000 }, /* R4355 - DSP3 Control 3 */ + { 0x1104, 0x0000 }, /* R4356 - DSP3 Control 4 */ }; diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index b9c185ce64e4..cb6d5372103a 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1265,29 +1265,12 @@ static const __devinitdata struct reg_default wm5100_reva_patches[] = { { WM5100_AUDIO_IF_3_19, 1 }, }; -static int wm5100_dai_to_base(struct snd_soc_dai *dai) -{ - switch (dai->id) { - case 0: - return WM5100_AUDIO_IF_1_1 - 1; - case 1: - return WM5100_AUDIO_IF_2_1 - 1; - case 2: - return WM5100_AUDIO_IF_3_1 - 1; - default: - BUG(); - return -EINVAL; - } -} - static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; int lrclk, bclk, mask, base; - base = wm5100_dai_to_base(dai); - if (base < 0) - return base; + base = dai->driver->base; lrclk = 0; bclk = 0; @@ -1414,9 +1397,7 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream, int i, base, bclk, aif_rate, lrclk, wl, fl, sr; int *bclk_rates; - base = wm5100_dai_to_base(dai); - if (base < 0) - return base; + base = dai->driver->base; /* Data sizes if not using TDM */ wl = snd_pcm_format_width(params_format(params)); @@ -1897,6 +1878,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, static struct snd_soc_dai_driver wm5100_dai[] = { { .name = "wm5100-aif1", + .base = WM5100_AUDIO_IF_1_1 - 1, .playback = { .stream_name = "AIF1 Playback", .channels_min = 2, @@ -1916,6 +1898,7 @@ static struct snd_soc_dai_driver wm5100_dai[] = { { .name = "wm5100-aif2", .id = 1, + .base = WM5100_AUDIO_IF_2_1 - 1, .playback = { .stream_name = "AIF2 Playback", .channels_min = 2, @@ -1935,6 +1918,7 @@ static struct snd_soc_dai_driver wm5100_dai[] = { { .name = "wm5100-aif3", .id = 2, + .base = WM5100_AUDIO_IF_3_1 - 1, .playback = { .stream_name = "AIF3 Playback", .channels_min = 2, @@ -2454,7 +2438,7 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, wm5100->dev = &i2c->dev; - wm5100->regmap = regmap_init_i2c(i2c, &wm5100_regmap); + wm5100->regmap = devm_regmap_init_i2c(i2c, &wm5100_regmap); if (IS_ERR(wm5100->regmap)) { ret = PTR_ERR(wm5100->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", @@ -2479,7 +2463,7 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, if (ret != 0) { dev_err(&i2c->dev, "Failed to request core supplies: %d\n", ret); - goto err_regmap; + goto err; } ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies), @@ -2487,7 +2471,7 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, if (ret != 0) { dev_err(&i2c->dev, "Failed to enable core supplies: %d\n", ret); - goto err_regmap; + goto err; } if (wm5100->pdata.ldo_ena) { @@ -2660,8 +2644,6 @@ err_ldo: err_enable: regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), wm5100->core_supplies); -err_regmap: - regmap_exit(wm5100->regmap); err: return ret; } @@ -2682,7 +2664,6 @@ static __devexit int wm5100_i2c_remove(struct i2c_client *i2c) gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); gpio_free(wm5100->pdata.ldo_ena); } - regmap_exit(wm5100->regmap); return 0; } @@ -2749,17 +2730,7 @@ static struct i2c_driver wm5100_i2c_driver = { .id_table = wm5100_i2c_id, }; -static int __init wm5100_modinit(void) -{ - return i2c_add_driver(&wm5100_i2c_driver); -} -module_init(wm5100_modinit); - -static void __exit wm5100_exit(void) -{ - i2c_del_driver(&wm5100_i2c_driver); -} -module_exit(wm5100_exit); +module_i2c_driver(wm5100_i2c_driver); MODULE_DESCRIPTION("ASoC WM5100 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); diff --git a/sound/soc/codecs/wm5100.h b/sound/soc/codecs/wm5100.h index 25cb6016f9d7..935a9b7fb274 100644 --- a/sound/soc/codecs/wm5100.h +++ b/sound/soc/codecs/wm5100.h @@ -709,6 +709,96 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack); #define WM5100_HPLPF3_2 0xEC9 #define WM5100_HPLPF4_1 0xECC #define WM5100_HPLPF4_2 0xECD +#define WM5100_DSP1_CONTROL_1 0xF00 +#define WM5100_DSP1_CONTROL_2 0xF02 +#define WM5100_DSP1_CONTROL_3 0xF03 +#define WM5100_DSP1_CONTROL_4 0xF04 +#define WM5100_DSP1_CONTROL_5 0xF06 +#define WM5100_DSP1_CONTROL_6 0xF07 +#define WM5100_DSP1_CONTROL_7 0xF08 +#define WM5100_DSP1_CONTROL_8 0xF09 +#define WM5100_DSP1_CONTROL_9 0xF0A +#define WM5100_DSP1_CONTROL_10 0xF0B +#define WM5100_DSP1_CONTROL_11 0xF0C +#define WM5100_DSP1_CONTROL_12 0xF0D +#define WM5100_DSP1_CONTROL_13 0xF0F +#define WM5100_DSP1_CONTROL_14 0xF10 +#define WM5100_DSP1_CONTROL_15 0xF11 +#define WM5100_DSP1_CONTROL_16 0xF12 +#define WM5100_DSP1_CONTROL_17 0xF13 +#define WM5100_DSP1_CONTROL_18 0xF14 +#define WM5100_DSP1_CONTROL_19 0xF16 +#define WM5100_DSP1_CONTROL_20 0xF17 +#define WM5100_DSP1_CONTROL_21 0xF18 +#define WM5100_DSP1_CONTROL_22 0xF1A +#define WM5100_DSP1_CONTROL_23 0xF1B +#define WM5100_DSP1_CONTROL_24 0xF1C +#define WM5100_DSP1_CONTROL_25 0xF1E +#define WM5100_DSP1_CONTROL_26 0xF20 +#define WM5100_DSP1_CONTROL_27 0xF21 +#define WM5100_DSP1_CONTROL_28 0xF22 +#define WM5100_DSP1_CONTROL_29 0xF23 +#define WM5100_DSP1_CONTROL_30 0xF24 +#define WM5100_DSP2_CONTROL_1 0x1000 +#define WM5100_DSP2_CONTROL_2 0x1002 +#define WM5100_DSP2_CONTROL_3 0x1003 +#define WM5100_DSP2_CONTROL_4 0x1004 +#define WM5100_DSP2_CONTROL_5 0x1006 +#define WM5100_DSP2_CONTROL_6 0x1007 +#define WM5100_DSP2_CONTROL_7 0x1008 +#define WM5100_DSP2_CONTROL_8 0x1009 +#define WM5100_DSP2_CONTROL_9 0x100A +#define WM5100_DSP2_CONTROL_10 0x100B +#define WM5100_DSP2_CONTROL_11 0x100C +#define WM5100_DSP2_CONTROL_12 0x100D +#define WM5100_DSP2_CONTROL_13 0x100F +#define WM5100_DSP2_CONTROL_14 0x1010 +#define WM5100_DSP2_CONTROL_15 0x1011 +#define WM5100_DSP2_CONTROL_16 0x1012 +#define WM5100_DSP2_CONTROL_17 0x1013 +#define WM5100_DSP2_CONTROL_18 0x1014 +#define WM5100_DSP2_CONTROL_19 0x1016 +#define WM5100_DSP2_CONTROL_20 0x1017 +#define WM5100_DSP2_CONTROL_21 0x1018 +#define WM5100_DSP2_CONTROL_22 0x101A +#define WM5100_DSP2_CONTROL_23 0x101B +#define WM5100_DSP2_CONTROL_24 0x101C +#define WM5100_DSP2_CONTROL_25 0x101E +#define WM5100_DSP2_CONTROL_26 0x1020 +#define WM5100_DSP2_CONTROL_27 0x1021 +#define WM5100_DSP2_CONTROL_28 0x1022 +#define WM5100_DSP2_CONTROL_29 0x1023 +#define WM5100_DSP2_CONTROL_30 0x1024 +#define WM5100_DSP3_CONTROL_1 0x1100 +#define WM5100_DSP3_CONTROL_2 0x1102 +#define WM5100_DSP3_CONTROL_3 0x1103 +#define WM5100_DSP3_CONTROL_4 0x1104 +#define WM5100_DSP3_CONTROL_5 0x1106 +#define WM5100_DSP3_CONTROL_6 0x1107 +#define WM5100_DSP3_CONTROL_7 0x1108 +#define WM5100_DSP3_CONTROL_8 0x1109 +#define WM5100_DSP3_CONTROL_9 0x110A +#define WM5100_DSP3_CONTROL_10 0x110B +#define WM5100_DSP3_CONTROL_11 0x110C +#define WM5100_DSP3_CONTROL_12 0x110D +#define WM5100_DSP3_CONTROL_13 0x110F +#define WM5100_DSP3_CONTROL_14 0x1110 +#define WM5100_DSP3_CONTROL_15 0x1111 +#define WM5100_DSP3_CONTROL_16 0x1112 +#define WM5100_DSP3_CONTROL_17 0x1113 +#define WM5100_DSP3_CONTROL_18 0x1114 +#define WM5100_DSP3_CONTROL_19 0x1116 +#define WM5100_DSP3_CONTROL_20 0x1117 +#define WM5100_DSP3_CONTROL_21 0x1118 +#define WM5100_DSP3_CONTROL_22 0x111A +#define WM5100_DSP3_CONTROL_23 0x111B +#define WM5100_DSP3_CONTROL_24 0x111C +#define WM5100_DSP3_CONTROL_25 0x111E +#define WM5100_DSP3_CONTROL_26 0x1120 +#define WM5100_DSP3_CONTROL_27 0x1121 +#define WM5100_DSP3_CONTROL_28 0x1122 +#define WM5100_DSP3_CONTROL_29 0x1123 +#define WM5100_DSP3_CONTROL_30 0x1124 #define WM5100_DSP1_DM_0 0x4000 #define WM5100_DSP1_DM_1 0x4001 #define WM5100_DSP1_DM_2 0x4002 @@ -4561,6 +4651,75 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack); #define WM5100_LHPF4_COEFF_WIDTH 16 /* LHPF4_COEFF - [15:0] */ /* + * R4132 (0x1024) - DSP2 Control 30 + */ +#define WM5100_DSP2_RATE_MASK 0xC000 /* DSP2_RATE - [15:14] */ +#define WM5100_DSP2_RATE_SHIFT 14 /* DSP2_RATE - [15:14] */ +#define WM5100_DSP2_RATE_WIDTH 2 /* DSP2_RATE - [15:14] */ +#define WM5100_DSP2_DBG_CLK_ENA 0x0008 /* DSP2_DBG_CLK_ENA */ +#define WM5100_DSP2_DBG_CLK_ENA_MASK 0x0008 /* DSP2_DBG_CLK_ENA */ +#define WM5100_DSP2_DBG_CLK_ENA_SHIFT 3 /* DSP2_DBG_CLK_ENA */ +#define WM5100_DSP2_DBG_CLK_ENA_WIDTH 1 /* DSP2_DBG_CLK_ENA */ +#define WM5100_DSP2_SYS_ENA 0x0004 /* DSP2_SYS_ENA */ +#define WM5100_DSP2_SYS_ENA_MASK 0x0004 /* DSP2_SYS_ENA */ +#define WM5100_DSP2_SYS_ENA_SHIFT 2 /* DSP2_SYS_ENA */ +#define WM5100_DSP2_SYS_ENA_WIDTH 1 /* DSP2_SYS_ENA */ +#define WM5100_DSP2_CORE_ENA 0x0002 /* DSP2_CORE_ENA */ +#define WM5100_DSP2_CORE_ENA_MASK 0x0002 /* DSP2_CORE_ENA */ +#define WM5100_DSP2_CORE_ENA_SHIFT 1 /* DSP2_CORE_ENA */ +#define WM5100_DSP2_CORE_ENA_WIDTH 1 /* DSP2_CORE_ENA */ +#define WM5100_DSP2_START 0x0001 /* DSP2_START */ +#define WM5100_DSP2_START_MASK 0x0001 /* DSP2_START */ +#define WM5100_DSP2_START_SHIFT 0 /* DSP2_START */ +#define WM5100_DSP2_START_WIDTH 1 /* DSP2_START */ + +/* + * R3876 (0xF24) - DSP1 Control 30 + */ +#define WM5100_DSP1_RATE_MASK 0xC000 /* DSP1_RATE - [15:14] */ +#define WM5100_DSP1_RATE_SHIFT 14 /* DSP1_RATE - [15:14] */ +#define WM5100_DSP1_RATE_WIDTH 2 /* DSP1_RATE - [15:14] */ +#define WM5100_DSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ +#define WM5100_DSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ +#define WM5100_DSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ +#define WM5100_DSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ +#define WM5100_DSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ +#define WM5100_DSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ +#define WM5100_DSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ +#define WM5100_DSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ +#define WM5100_DSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ +#define WM5100_DSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ +#define WM5100_DSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ +#define WM5100_DSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ +#define WM5100_DSP1_START 0x0001 /* DSP1_START */ +#define WM5100_DSP1_START_MASK 0x0001 /* DSP1_START */ +#define WM5100_DSP1_START_SHIFT 0 /* DSP1_START */ +#define WM5100_DSP1_START_WIDTH 1 /* DSP1_START */ + +/* + * R4388 (0x1124) - DSP3 Control 30 + */ +#define WM5100_DSP3_RATE_MASK 0xC000 /* DSP3_RATE - [15:14] */ +#define WM5100_DSP3_RATE_SHIFT 14 /* DSP3_RATE - [15:14] */ +#define WM5100_DSP3_RATE_WIDTH 2 /* DSP3_RATE - [15:14] */ +#define WM5100_DSP3_DBG_CLK_ENA 0x0008 /* DSP3_DBG_CLK_ENA */ +#define WM5100_DSP3_DBG_CLK_ENA_MASK 0x0008 /* DSP3_DBG_CLK_ENA */ +#define WM5100_DSP3_DBG_CLK_ENA_SHIFT 3 /* DSP3_DBG_CLK_ENA */ +#define WM5100_DSP3_DBG_CLK_ENA_WIDTH 1 /* DSP3_DBG_CLK_ENA */ +#define WM5100_DSP3_SYS_ENA 0x0004 /* DSP3_SYS_ENA */ +#define WM5100_DSP3_SYS_ENA_MASK 0x0004 /* DSP3_SYS_ENA */ +#define WM5100_DSP3_SYS_ENA_SHIFT 2 /* DSP3_SYS_ENA */ +#define WM5100_DSP3_SYS_ENA_WIDTH 1 /* DSP3_SYS_ENA */ +#define WM5100_DSP3_CORE_ENA 0x0002 /* DSP3_CORE_ENA */ +#define WM5100_DSP3_CORE_ENA_MASK 0x0002 /* DSP3_CORE_ENA */ +#define WM5100_DSP3_CORE_ENA_SHIFT 1 /* DSP3_CORE_ENA */ +#define WM5100_DSP3_CORE_ENA_WIDTH 1 /* DSP3_CORE_ENA */ +#define WM5100_DSP3_START 0x0001 /* DSP3_START */ +#define WM5100_DSP3_START_MASK 0x0001 /* DSP3_START */ +#define WM5100_DSP3_START_SHIFT 0 /* DSP3_START */ +#define WM5100_DSP3_START_WIDTH 1 /* DSP3_START */ + +/* * R16384 (0x4000) - DSP1 DM 0 */ #define WM5100_DSP1_DM_START_1_MASK 0x00FF /* DSP1_DM_START - [7:0] */ diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index aa12c6b6beeb..555ee146ae0d 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -71,13 +71,6 @@ struct wm8350_data { int fll_freq_in; }; -static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct wm8350 *wm8350 = codec->control_data; - return wm8350->reg_cache[reg]; -} - static unsigned int wm8350_codec_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -99,7 +92,7 @@ static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec) { struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_data->out1; - struct wm8350 *wm8350 = codec->control_data; + struct wm8350 *wm8350 = wm8350_data->wm8350; int left_complete = 0, right_complete = 0; u16 reg, val; @@ -165,7 +158,7 @@ static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec) { struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out2 = &wm8350_data->out2; - struct wm8350 *wm8350 = codec->control_data; + struct wm8350 *wm8350 = wm8350_data->wm8350; int left_complete = 0, right_complete = 0; u16 reg, val; @@ -360,8 +353,8 @@ static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, return ret; /* now hit the volume update bits (always bit 8) */ - val = wm8350_codec_read(codec, reg); - wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU); + val = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, val | WM8350_OUT1_VU); return 1; } @@ -781,7 +774,8 @@ static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8350 *wm8350 = codec->control_data; + struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); + struct wm8350 *wm8350 = wm8350_data->wm8350; u16 fll_4; switch (clk_id) { @@ -795,9 +789,9 @@ static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai, case WM8350_MCLK_SEL_PLL_32K: wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1, WM8350_MCLK_SEL); - fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & + fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) & ~WM8350_FLL_CLK_SRC_MASK; - wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id); + snd_soc_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id); break; } @@ -819,39 +813,39 @@ static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) switch (div_id) { case WM8350_ADC_CLKDIV: - val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) & + val = snd_soc_read(codec, WM8350_ADC_DIVIDER) & ~WM8350_ADC_CLKDIV_MASK; - wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div); + snd_soc_write(codec, WM8350_ADC_DIVIDER, val | div); break; case WM8350_DAC_CLKDIV: - val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) & + val = snd_soc_read(codec, WM8350_DAC_CLOCK_CONTROL) & ~WM8350_DAC_CLKDIV_MASK; - wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div); + snd_soc_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div); break; case WM8350_BCLK_CLKDIV: - val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & + val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_BCLK_DIV_MASK; - wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); + snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_OPCLK_CLKDIV: - val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & + val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_OPCLK_DIV_MASK; - wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); + snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_SYS_CLKDIV: - val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & + val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_MCLK_DIV_MASK; - wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); + snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_DACLR_CLKDIV: - val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) & + val = snd_soc_read(codec, WM8350_DAC_LR_RATE) & ~WM8350_DACLRC_RATE_MASK; - wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div); + snd_soc_write(codec, WM8350_DAC_LR_RATE, val | div); break; case WM8350_ADCLR_CLKDIV: - val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) & + val = snd_soc_read(codec, WM8350_ADC_LR_RATE) & ~WM8350_ADCLRC_RATE_MASK; - wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div); + snd_soc_write(codec, WM8350_ADC_LR_RATE, val | div); break; default: return -EINVAL; @@ -863,13 +857,13 @@ static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & + u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) & ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK); - u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) & + u16 master = snd_soc_read(codec, WM8350_AI_DAC_CONTROL) & ~WM8350_BCLK_MSTR; - u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) & + u16 dac_lrc = snd_soc_read(codec, WM8350_DAC_LR_RATE) & ~WM8350_DACLRC_ENA; - u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) & + u16 adc_lrc = snd_soc_read(codec, WM8350_ADC_LR_RATE) & ~WM8350_ADCLRC_ENA; /* set master/slave audio interface */ @@ -922,42 +916,10 @@ static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); - wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master); - wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc); - wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc); - return 0; -} - -static int wm8350_pcm_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *codec_dai) -{ - struct snd_soc_codec *codec = codec_dai->codec; - int master = wm8350_codec_cache_read(codec, WM8350_AI_DAC_CONTROL) & - WM8350_BCLK_MSTR; - int enabled = 0; - - /* Check that the DACs or ADCs are enabled since they are - * required for LRC in master mode. The DACs or ADCs need a - * valid audio path i.e. pin -> ADC or DAC -> pin before - * the LRC will be enabled in master mode. */ - if (!master || cmd != SNDRV_PCM_TRIGGER_START) - return 0; - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) & - (WM8350_ADCR_ENA | WM8350_ADCL_ENA); - } else { - enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) & - (WM8350_DACR_ENA | WM8350_DACL_ENA); - } - - if (!enabled) { - dev_err(codec->dev, - "%s: invalid audio path - no clocks available\n", - __func__); - return -EINVAL; - } + snd_soc_write(codec, WM8350_AI_FORMATING, iface); + snd_soc_write(codec, WM8350_AI_DAC_CONTROL, master); + snd_soc_write(codec, WM8350_DAC_LR_RATE, dac_lrc); + snd_soc_write(codec, WM8350_ADC_LR_RATE, adc_lrc); return 0; } @@ -966,8 +928,9 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8350 *wm8350 = codec->control_data; - u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & + struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); + struct wm8350 *wm8350 = wm8350_data->wm8350; + u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) & ~WM8350_AIF_WL_MASK; /* bit size */ @@ -985,7 +948,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, break; } - wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); + snd_soc_write(codec, WM8350_AI_FORMATING, iface); /* The sloping stopband filter is recommended for use with * lower sample rates to improve performance. @@ -1005,12 +968,15 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, static int wm8350_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - struct wm8350 *wm8350 = codec->control_data; + unsigned int val; if (mute) - wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); + val = WM8350_DAC_MUTE_ENA; else - wm8350_clear_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); + val = 0; + + snd_soc_update_bits(codec, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val); + return 0; } @@ -1079,8 +1045,8 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8350 *wm8350 = codec->control_data; struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); + struct wm8350 *wm8350 = priv->wm8350; struct _fll_div fll_div; int ret = 0; u16 fll_1, fll_4; @@ -1104,17 +1070,17 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai, fll_div.ratio); /* set up N.K & dividers */ - fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) & + fll_1 = snd_soc_read(codec, WM8350_FLL_CONTROL_1) & ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000); - wm8350_codec_write(codec, WM8350_FLL_CONTROL_1, + snd_soc_write(codec, WM8350_FLL_CONTROL_1, fll_1 | (fll_div.div << 8) | 0x50); - wm8350_codec_write(codec, WM8350_FLL_CONTROL_2, + snd_soc_write(codec, WM8350_FLL_CONTROL_2, (fll_div.ratio << 11) | (fll_div. n & WM8350_FLL_N_MASK)); - wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k); - fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & + snd_soc_write(codec, WM8350_FLL_CONTROL_3, fll_div.k); + fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) & ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF); - wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, + snd_soc_write(codec, WM8350_FLL_CONTROL_4, fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) | (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0)); @@ -1131,8 +1097,8 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai, static int wm8350_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct wm8350 *wm8350 = codec->control_data; struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); + struct wm8350 *wm8350 = priv->wm8350; struct wm8350_audio_platform_data *platform = wm8350->codec.platform_data; u16 pm1; @@ -1339,35 +1305,36 @@ static void wm8350_hpr_work(struct work_struct *work) wm8350_hp_work(priv, &priv->hpr, WM8350_JACK_R_LVL); } -static irqreturn_t wm8350_hp_jack_handler(int irq, void *data) +static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data) { struct wm8350_data *priv = data; struct wm8350 *wm8350 = priv->wm8350; - struct wm8350_jack_data *jack = NULL; - switch (irq - wm8350->irq_base) { - case WM8350_IRQ_CODEC_JCK_DET_L: #ifndef CONFIG_SND_SOC_WM8350_MODULE - trace_snd_soc_jack_irq("WM8350 HPL"); + trace_snd_soc_jack_irq("WM8350 HPL"); #endif - jack = &priv->hpl; - break; - case WM8350_IRQ_CODEC_JCK_DET_R: + if (device_may_wakeup(wm8350->dev)) + pm_wakeup_event(wm8350->dev, 250); + + schedule_delayed_work(&priv->hpl.work, 200); + + return IRQ_HANDLED; +} + +static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data) +{ + struct wm8350_data *priv = data; + struct wm8350 *wm8350 = priv->wm8350; + #ifndef CONFIG_SND_SOC_WM8350_MODULE - trace_snd_soc_jack_irq("WM8350 HPR"); + trace_snd_soc_jack_irq("WM8350 HPR"); #endif - jack = &priv->hpr; - break; - - default: - BUG(); - } if (device_may_wakeup(wm8350->dev)) pm_wakeup_event(wm8350->dev, 250); - schedule_delayed_work(&jack->work, 200); + schedule_delayed_work(&priv->hpr.work, 200); return IRQ_HANDLED; } @@ -1387,7 +1354,7 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which, struct snd_soc_jack *jack, int report) { struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); - struct wm8350 *wm8350 = codec->control_data; + struct wm8350 *wm8350 = priv->wm8350; int irq; int ena; @@ -1418,7 +1385,14 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which, } /* Sync status */ - wm8350_hp_jack_handler(irq + wm8350->irq_base, priv); + switch (which) { + case WM8350_JDL: + wm8350_hpl_jack_handler(0, priv); + break; + case WM8350_JDR: + wm8350_hpr_jack_handler(0, priv); + break; + } return 0; } @@ -1463,7 +1437,7 @@ int wm8350_mic_jack_detect(struct snd_soc_codec *codec, int detect_report, int short_report) { struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); - struct wm8350 *wm8350 = codec->control_data; + struct wm8350 *wm8350 = priv->wm8350; priv->mic.jack = jack; priv->mic.report = detect_report; @@ -1491,7 +1465,6 @@ EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect); static const struct snd_soc_dai_ops wm8350_dai_ops = { .hw_params = wm8350_pcm_hw_params, .digital_mute = wm8350_mute, - .trigger = wm8350_pcm_trigger, .set_fmt = wm8350_set_dai_fmt, .set_sysclk = wm8350_set_dai_sysclk, .set_pll = wm8350_set_fll, @@ -1559,9 +1532,9 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); /* Enable robust clocking mode in ADC */ - wm8350_codec_write(codec, WM8350_SECURITY, 0xa7); - wm8350_codec_write(codec, 0xde, 0x13); - wm8350_codec_write(codec, WM8350_SECURITY, 0); + snd_soc_write(codec, WM8350_SECURITY, 0xa7); + snd_soc_write(codec, 0xde, 0x13); + snd_soc_write(codec, WM8350_SECURITY, 0); /* read OUT1 & OUT2 volumes */ out1 = &priv->out1; @@ -1601,10 +1574,10 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) WM8350_JDL_ENA | WM8350_JDR_ENA); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, - wm8350_hp_jack_handler, 0, "Left jack detect", + wm8350_hpl_jack_handler, 0, "Left jack detect", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, - wm8350_hp_jack_handler, 0, "Right jack detect", + wm8350_hpr_jack_handler, 0, "Right jack detect", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, wm8350_mic_handler, 0, "Microphone short", priv); diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 898979d23010..5dc31ebcd0e7 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -138,8 +138,8 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, return ret; /* now hit the volume update bits (always bit 8) */ - val = wm8400_read(codec, reg); - return wm8400_write(codec, reg, val | 0x0100); + val = snd_soc_read(codec, reg); + return snd_soc_write(codec, reg, val | 0x0100); } #define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \ @@ -362,8 +362,8 @@ static int inmixer_event (struct snd_soc_dapm_widget *w, { u16 reg, fakepower; - reg = wm8400_read(w->codec, WM8400_POWER_MANAGEMENT_2); - fakepower = wm8400_read(w->codec, WM8400_INTDRIVBITS); + reg = snd_soc_read(w->codec, WM8400_POWER_MANAGEMENT_2); + fakepower = snd_soc_read(w->codec, WM8400_INTDRIVBITS); if (fakepower & ((1 << WM8400_INMIXL_PWR) | (1 << WM8400_AINLMUX_PWR))) { @@ -378,7 +378,7 @@ static int inmixer_event (struct snd_soc_dapm_widget *w, } else { reg &= ~WM8400_AINR_ENA; } - wm8400_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg); + snd_soc_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg); return 0; } @@ -394,7 +394,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, switch (reg_shift) { case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) : - reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER1); + reg = snd_soc_read(w->codec, WM8400_OUTPUT_MIXER1); if (reg & WM8400_LDLO) { printk(KERN_WARNING "Cannot set as Output Mixer 1 LDLO Set\n"); @@ -402,7 +402,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, } break; case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8): - reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER2); + reg = snd_soc_read(w->codec, WM8400_OUTPUT_MIXER2); if (reg & WM8400_RDRO) { printk(KERN_WARNING "Cannot set as Output Mixer 2 RDRO Set\n"); @@ -410,7 +410,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, } break; case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8): - reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER); + reg = snd_soc_read(w->codec, WM8400_SPEAKER_MIXER); if (reg & WM8400_LDSPK) { printk(KERN_WARNING "Cannot set as Speaker Mixer LDSPK Set\n"); @@ -418,7 +418,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, } break; case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8): - reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER); + reg = snd_soc_read(w->codec, WM8400_SPEAKER_MIXER); if (reg & WM8400_RDSPK) { printk(KERN_WARNING "Cannot set as Speaker Mixer RDSPK Set\n"); @@ -1021,13 +1021,13 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, wm8400->fll_in = freq_in; /* We *must* disable the FLL before any changes */ - reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_2); + reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_2); reg &= ~WM8400_FLL_ENA; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_2, reg); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_2, reg); - reg = wm8400_read(codec, WM8400_FLL_CONTROL_1); + reg = snd_soc_read(codec, WM8400_FLL_CONTROL_1); reg &= ~WM8400_FLL_OSC_ENA; - wm8400_write(codec, WM8400_FLL_CONTROL_1, reg); + snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg); if (!freq_out) return 0; @@ -1035,15 +1035,15 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK); reg |= WM8400_FLL_FRAC | factors.fratio; reg |= factors.freq_ref << WM8400_FLL_REF_FREQ_SHIFT; - wm8400_write(codec, WM8400_FLL_CONTROL_1, reg); + snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg); - wm8400_write(codec, WM8400_FLL_CONTROL_2, factors.k); - wm8400_write(codec, WM8400_FLL_CONTROL_3, factors.n); + snd_soc_write(codec, WM8400_FLL_CONTROL_2, factors.k); + snd_soc_write(codec, WM8400_FLL_CONTROL_3, factors.n); - reg = wm8400_read(codec, WM8400_FLL_CONTROL_4); + reg = snd_soc_read(codec, WM8400_FLL_CONTROL_4); reg &= ~WM8400_FLL_OUTDIV_MASK; reg |= factors.outdiv; - wm8400_write(codec, WM8400_FLL_CONTROL_4, reg); + snd_soc_write(codec, WM8400_FLL_CONTROL_4, reg); return 0; } @@ -1057,8 +1057,8 @@ static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_codec *codec = codec_dai->codec; u16 audio1, audio3; - audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1); - audio3 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_3); + audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1); + audio3 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_3); /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -1099,8 +1099,8 @@ static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); - wm8400_write(codec, WM8400_AUDIO_INTERFACE_3, audio3); + snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); + snd_soc_write(codec, WM8400_AUDIO_INTERFACE_3, audio3); return 0; } @@ -1112,24 +1112,24 @@ static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai, switch (div_id) { case WM8400_MCLK_DIV: - reg = wm8400_read(codec, WM8400_CLOCKING_2) & + reg = snd_soc_read(codec, WM8400_CLOCKING_2) & ~WM8400_MCLK_DIV_MASK; - wm8400_write(codec, WM8400_CLOCKING_2, reg | div); + snd_soc_write(codec, WM8400_CLOCKING_2, reg | div); break; case WM8400_DACCLK_DIV: - reg = wm8400_read(codec, WM8400_CLOCKING_2) & + reg = snd_soc_read(codec, WM8400_CLOCKING_2) & ~WM8400_DAC_CLKDIV_MASK; - wm8400_write(codec, WM8400_CLOCKING_2, reg | div); + snd_soc_write(codec, WM8400_CLOCKING_2, reg | div); break; case WM8400_ADCCLK_DIV: - reg = wm8400_read(codec, WM8400_CLOCKING_2) & + reg = snd_soc_read(codec, WM8400_CLOCKING_2) & ~WM8400_ADC_CLKDIV_MASK; - wm8400_write(codec, WM8400_CLOCKING_2, reg | div); + snd_soc_write(codec, WM8400_CLOCKING_2, reg | div); break; case WM8400_BCLK_DIV: - reg = wm8400_read(codec, WM8400_CLOCKING_1) & + reg = snd_soc_read(codec, WM8400_CLOCKING_1) & ~WM8400_BCLK_DIV_MASK; - wm8400_write(codec, WM8400_CLOCKING_1, reg | div); + snd_soc_write(codec, WM8400_CLOCKING_1, reg | div); break; default: return -EINVAL; @@ -1145,9 +1145,8 @@ static int wm8400_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1); + struct snd_soc_codec *codec = dai->codec; + u16 audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1); audio1 &= ~WM8400_AIF_WL_MASK; /* bit size */ @@ -1165,19 +1164,19 @@ static int wm8400_hw_params(struct snd_pcm_substream *substream, break; } - wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); + snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); return 0; } static int wm8400_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 val = wm8400_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE; + u16 val = snd_soc_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE; if (mute) - wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); + snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); else - wm8400_write(codec, WM8400_DAC_CTRL, val); + snd_soc_write(codec, WM8400_DAC_CTRL, val); return 0; } @@ -1196,9 +1195,9 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: /* VMID=2*50k */ - val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) & + val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) & ~WM8400_VMID_MODE_MASK; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2); break; case SND_SOC_BIAS_STANDBY: @@ -1212,74 +1211,74 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, return ret; } - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, WM8400_CODEC_ENA | WM8400_SYSCLK_ENA); /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | WM8400_BUFDCOPEN | WM8400_POBCTRL); msleep(50); /* Enable VREF & VMID at 2x50k */ - val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); + val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); val |= 0x2 | WM8400_VREF_ENA; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val); /* Enable BUFIOEN */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | WM8400_BUFDCOPEN | WM8400_POBCTRL | WM8400_BUFIOEN); /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN); + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN); } /* VMID=2*300k */ - val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) & + val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) & ~WM8400_VMID_MODE_MASK; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4); break; case SND_SOC_BIAS_OFF: /* Enable POBCTRL and SOFT_ST */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | WM8400_POBCTRL | WM8400_BUFIOEN); /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | WM8400_BUFDCOPEN | WM8400_POBCTRL | WM8400_BUFIOEN); /* mute DAC */ - val = wm8400_read(codec, WM8400_DAC_CTRL); - wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); + val = snd_soc_read(codec, WM8400_DAC_CTRL); + snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); /* Enable any disabled outputs */ - val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); + val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); val |= WM8400_SPK_ENA | WM8400_OUT3_ENA | WM8400_OUT4_ENA | WM8400_LOUT_ENA | WM8400_ROUT_ENA; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val); /* Disable VMID */ val &= ~WM8400_VMID_MODE_MASK; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val); msleep(300); /* Enable all output discharge bits */ - wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE | + snd_soc_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE | WM8400_DIS_RLINE | WM8400_DIS_OUT3 | WM8400_DIS_OUT4 | WM8400_DIS_LOUT | WM8400_DIS_ROUT); /* Disable VREF */ val &= ~WM8400_VREF_ENA; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val); /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ - wm8400_write(codec, WM8400_ANTIPOP2, 0x0); + snd_soc_write(codec, WM8400_ANTIPOP2, 0x0); ret = regulator_bulk_disable(ARRAY_SIZE(power), &power[0]); @@ -1385,19 +1384,19 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec) wm8400_codec_reset(codec); - reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA); + reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA); /* Latch volume update bits */ - reg = wm8400_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME); - wm8400_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME, + reg = snd_soc_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME); + snd_soc_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME, reg & WM8400_IPVU); - reg = wm8400_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME); - wm8400_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, + reg = snd_soc_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME); + snd_soc_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, reg & WM8400_IPVU); - wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); - wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); + snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); + snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); if (!schedule_work(&priv->work)) { ret = -EINVAL; @@ -1414,8 +1413,8 @@ static int wm8400_codec_remove(struct snd_soc_codec *codec) { u16 reg; - reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, + reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, reg & (~WM8400_CODEC_ENA)); regulator_bulk_free(ARRAY_SIZE(power), power); @@ -1428,7 +1427,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8400 = { .remove = wm8400_codec_remove, .suspend = wm8400_suspend, .resume = wm8400_resume, - .read = wm8400_read, + .read = snd_soc_read, .write = wm8400_write, .set_bias_level = wm8400_set_bias_level, diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 9166126bd312..56a049555e2c 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -392,8 +392,7 @@ static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 iface = snd_soc_read(codec, WM8510_IFACE) & 0x19f; u16 adn = snd_soc_read(codec, WM8510_ADD) & 0x1f1; diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 7fea2c3bf7e7..1c3ffb290cdc 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -145,8 +145,7 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec); int i; u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1); diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index fc3d59e49084..1467f97dce21 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -88,8 +88,7 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 dac = snd_soc_read(codec, WM8728_DACCTL); dac &= ~0x18; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index a32caa72bd7d..9d1b9b0271f1 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -635,16 +635,17 @@ static int __devinit wm8731_spi_probe(struct spi_device *spi) struct wm8731_priv *wm8731; int ret; - wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); + wm8731 = devm_kzalloc(&spi->dev, sizeof(struct wm8731_priv), + GFP_KERNEL); if (wm8731 == NULL) return -ENOMEM; - wm8731->regmap = regmap_init_spi(spi, &wm8731_regmap); + wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); dev_err(&spi->dev, "Failed to allocate register map: %d\n", ret); - goto err; + return ret; } spi_set_drvdata(spi, wm8731); @@ -653,25 +654,15 @@ static int __devinit wm8731_spi_probe(struct spi_device *spi) &soc_codec_dev_wm8731, &wm8731_dai, 1); if (ret != 0) { dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret); - goto err_regmap; + return ret; } return 0; - -err_regmap: - regmap_exit(wm8731->regmap); -err: - kfree(wm8731); - return ret; } static int __devexit wm8731_spi_remove(struct spi_device *spi) { - struct wm8731_priv *wm8731 = spi_get_drvdata(spi); - snd_soc_unregister_codec(&spi->dev); - regmap_exit(wm8731->regmap); - kfree(wm8731); return 0; } @@ -693,16 +684,17 @@ static __devinit int wm8731_i2c_probe(struct i2c_client *i2c, struct wm8731_priv *wm8731; int ret; - wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); + wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv), + GFP_KERNEL); if (wm8731 == NULL) return -ENOMEM; - wm8731->regmap = regmap_init_i2c(i2c, &wm8731_regmap); + wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - goto err; + return ret; } i2c_set_clientdata(i2c, wm8731); @@ -711,24 +703,15 @@ static __devinit int wm8731_i2c_probe(struct i2c_client *i2c, &soc_codec_dev_wm8731, &wm8731_dai, 1); if (ret != 0) { dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); - goto err_regmap; + return ret; } return 0; - -err_regmap: - regmap_exit(wm8731->regmap); -err: - kfree(wm8731); - return ret; } static __devexit int wm8731_i2c_remove(struct i2c_client *client) { - struct wm8731_priv *wm8731 = i2c_get_clientdata(client); snd_soc_unregister_codec(&client->dev); - regmap_exit(wm8731->regmap); - kfree(wm8731); return 0; } diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 4fe9d191e277..d0520124616d 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -329,8 +329,7 @@ static int wm8737_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); int i; u16 clocking = 0; diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 3941f50bf187..6e849cb04243 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -203,8 +203,7 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC; int i; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index e4c50ce7d9c0..89151ca5e776 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -547,8 +547,7 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3; u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index e27e7b62b365..a26482cd7654 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -931,8 +931,7 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01f3; u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x017f; @@ -1161,8 +1160,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x01c0; u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01f3; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index f18c554efc98..077c9628c70d 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -610,8 +610,7 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 reg; reg = snd_soc_read(codec, WM8900_REG_AUDIO1) & ~0x60; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index c91fb2f99c13..86b8a2926591 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1432,8 +1432,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec =rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); int fs = params_rate(params); int bclk; diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index d2883affea3b..481a3d9cfe48 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -371,8 +371,7 @@ static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F; u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1; u16 companding = snd_soc_read(codec, diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 840d72086d04..8bc659d8dd2e 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -505,8 +505,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; int i; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 15d467ff91b4..0cfce9999c89 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1478,7 +1478,8 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static int wm8962_dsp2_write_config(struct snd_soc_codec *codec) { - return 0; + return regcache_sync_region(codec->control_data, + WM8962_HDBASS_AI_1, WM8962_MAX_REGISTER); } static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val) @@ -1755,10 +1756,22 @@ SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23, SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23, WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0), +SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA), + +SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0), +SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA), + +SOC_SINGLE("DRC Switch", WM8962_DRC_1, 0, 1, 0), +SND_SOC_BYTES_MASK("DRC Coefficients", WM8962_DRC_1, 5, WM8962_DRC_ENA), + WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT), +SND_SOC_BYTES("VSS Coefficients", WM8962_VSS_XHD2_1, 148), WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT), WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT), +SND_SOC_BYTES("HPF Coefficients", WM8962_LHPF2, 1), WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT), +SND_SOC_BYTES("HD Bass Coefficients", WM8962_HDBASS_AI_1, 30), }; static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = { @@ -2519,8 +2532,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int i; int aif0 = 0; diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 28fe59e3ce01..eef783f6b6d6 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -478,8 +478,7 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3; u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0; diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 72d5fdcd3cc2..a5be3adecf75 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -723,8 +723,7 @@ static int wm8978_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec); /* Word length mask = 0x60 */ u16 iface_ctl = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x60; diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 6cdf6a2bc283..1d4c5cf47b06 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -668,8 +668,7 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3; u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180; diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 9d242351e6e8..db63c97ddf51 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1112,8 +1112,7 @@ static int wm8990_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1); audio1 &= ~WM8990_AIF_WL_MASK; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index d256a9340644..36acfccab999 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -218,7 +218,6 @@ struct wm8993_priv { unsigned int sysclk_rate; unsigned int fs; unsigned int bclk; - int class_w_users; unsigned int fll_fref; unsigned int fll_fout; int fll_src; @@ -824,84 +823,6 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w, return 0; } -/* - * When used with DAC outputs only the WM8993 charge pump supports - * operation in class W mode, providing very low power consumption - * when used with digital sources. Enable and disable this mode - * automatically depending on the mixer configuration. - * - * Currently the only supported paths are the direct DAC->headphone - * paths (which provide minimum power consumption anyway). - */ -static int class_w_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; - struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); - int ret; - - /* Turn it off if we're using the main output mixer */ - if (ucontrol->value.integer.value[0] == 0) { - if (wm8993->class_w_users == 0) { - dev_dbg(codec->dev, "Disabling Class W\n"); - snd_soc_update_bits(codec, WM8993_CLASS_W_0, - WM8993_CP_DYN_FREQ | - WM8993_CP_DYN_V, - 0); - } - wm8993->class_w_users++; - wm8993->hubs_data.class_w = true; - } - - /* Implement the change */ - ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); - - /* Enable it if we're using the direct DAC path */ - if (ucontrol->value.integer.value[0] == 1) { - if (wm8993->class_w_users == 1) { - dev_dbg(codec->dev, "Enabling Class W\n"); - snd_soc_update_bits(codec, WM8993_CLASS_W_0, - WM8993_CP_DYN_FREQ | - WM8993_CP_DYN_V, - WM8993_CP_DYN_FREQ | - WM8993_CP_DYN_V); - } - wm8993->class_w_users--; - wm8993->hubs_data.class_w = false; - } - - dev_dbg(codec->dev, "Indirect DAC use count now %d\n", - wm8993->class_w_users); - - return ret; -} - -#define SOC_DAPM_ENUM_W(xname, xenum) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_double, \ - .get = snd_soc_dapm_get_enum_double, \ - .put = class_w_put, \ - .private_value = (unsigned long)&xenum } - -static const char *hp_mux_text[] = { - "Mixer", - "DAC", -}; - -static const struct soc_enum hpl_enum = - SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text); - -static const struct snd_kcontrol_new hpl_mux = - SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum); - -static const struct soc_enum hpr_enum = - SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text); - -static const struct snd_kcontrol_new hpr_mux = - SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum); - static const struct snd_kcontrol_new left_speaker_mixer[] = { SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0), SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0), @@ -988,8 +909,8 @@ SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &sidetoner_mux), SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0), SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0), -SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), -SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux), SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0, left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), @@ -1579,9 +1500,6 @@ static int wm8993_probe(struct snd_soc_codec *codec) return ret; } - /* By default we're using the output mixers */ - wm8993->class_w_users = 2; - /* Latch volume update bits and default ZC on */ snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME, WM8993_DAC_VU, WM8993_DAC_VU); diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 2de12ebe43b5..993639d694ce 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -70,8 +70,8 @@ static const struct wm8958_micd_rate micdet_rates[] = { static const struct wm8958_micd_rate jackdet_rates[] = { { 32768, true, 0, 1 }, { 32768, false, 0, 1 }, - { 44100 * 256, true, 7, 10 }, - { 44100 * 256, false, 7, 10 }, + { 44100 * 256, true, 10, 10 }, + { 44100 * 256, false, 7, 8 }, }; static void wm8958_micd_set_rate(struct snd_soc_codec *codec) @@ -82,7 +82,8 @@ static void wm8958_micd_set_rate(struct snd_soc_codec *codec) const struct wm8958_micd_rate *rates; int num_rates; - if (wm8994->jack_cb != wm8958_default_micdet) + if (!(wm8994->pdata && wm8994->pdata->micd_rates) && + wm8994->jack_cb != wm8958_default_micdet) return; idle = !wm8994->jack_mic; @@ -118,6 +119,10 @@ static void wm8958_micd_set_rate(struct snd_soc_codec *codec) val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT | rates[best].rate << WM8958_MICD_RATE_SHIFT; + dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n", + rates[best].start, rates[best].rate, sysclk, + idle ? "idle" : "active"); + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_BIAS_STARTTIME_MASK | WM8958_MICD_RATE_MASK, val); @@ -398,7 +403,7 @@ static void wm8994_set_retune_mobile(struct snd_soc_codec *codec, int block) wm8994->dac_rates[iface]); /* The EQ will be disabled while reconfiguring it, remember the - * current configuration. + * current configuration. */ save = snd_soc_read(codec, base); save &= WM8994_AIF1DAC1_EQ_ENA; @@ -689,6 +694,9 @@ static void wm1811_jackdet_set_mode(struct snd_soc_codec *codec, u16 mode) if (!wm8994->jackdet || !wm8994->jack_cb) return; + if (!wm8994->jackdet || !wm8994->jack_cb) + return; + if (wm8994->active_refcount) mode = WM1811_JACKDET_MODE_AUDIO; @@ -784,7 +792,7 @@ static void vmid_reference(struct snd_soc_codec *codec) switch (wm8994->vmid_mode) { default: - WARN_ON(0 == "Invalid VMID mode"); + WARN_ON(NULL == "Invalid VMID mode"); case WM8994_VMID_NORMAL: /* Startup bias, VMID ramp & buffer */ snd_soc_update_bits(codec, WM8994_ANTIPOP_2, @@ -937,27 +945,12 @@ static int vmid_event(struct snd_soc_dapm_widget *w, return 0; } -static void wm8994_update_class_w(struct snd_soc_codec *codec) +static bool wm8994_check_class_w_digital(struct snd_soc_codec *codec) { - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - int enable = 1; int source = 0; /* GCC flow analysis can't track enable */ int reg, reg_r; - /* Only support direct DAC->headphone paths */ - reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_1); - if (!(reg & WM8994_DAC1L_TO_HPOUT1L)) { - dev_vdbg(codec->dev, "HPL connected to output mixer\n"); - enable = 0; - } - - reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_2); - if (!(reg & WM8994_DAC1R_TO_HPOUT1R)) { - dev_vdbg(codec->dev, "HPR connected to output mixer\n"); - enable = 0; - } - - /* We also need the same setting for L/R and only one path */ + /* We also need the same AIF source for L/R and only one path */ reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING); switch (reg) { case WM8994_AIF2DACL_TO_DAC1L: @@ -974,30 +967,20 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec) break; default: dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg); - enable = 0; - break; + return false; } reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING); if (reg_r != reg) { dev_vdbg(codec->dev, "Left and right DAC mixers different\n"); - enable = 0; + return false; } - if (enable) { - dev_dbg(codec->dev, "Class W enabled\n"); - snd_soc_update_bits(codec, WM8994_CLASS_W_1, - WM8994_CP_DYN_PWR | - WM8994_CP_DYN_SRC_SEL_MASK, - source | WM8994_CP_DYN_PWR); - wm8994->hubs.class_w = true; - - } else { - dev_dbg(codec->dev, "Class W disabled\n"); - snd_soc_update_bits(codec, WM8994_CLASS_W_1, - WM8994_CP_DYN_PWR, 0); - wm8994->hubs.class_w = false; - } + /* Set the source up */ + snd_soc_update_bits(codec, WM8994_CLASS_W_1, + WM8994_CP_DYN_SRC_SEL_MASK, source); + + return true; } static int aif1clk_ev(struct snd_soc_dapm_widget *w, @@ -1280,45 +1263,6 @@ static int dac_ev(struct snd_soc_dapm_widget *w, return 0; } -static const char *hp_mux_text[] = { - "Mixer", - "DAC", -}; - -#define WM8994_HP_ENUM(xname, xenum) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_double, \ - .get = snd_soc_dapm_get_enum_double, \ - .put = wm8994_put_hp_enum, \ - .private_value = (unsigned long)&xenum } - -static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *w = wlist->widgets[0]; - struct snd_soc_codec *codec = w->codec; - int ret; - - ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); - - wm8994_update_class_w(codec); - - return ret; -} - -static const struct soc_enum hpl_enum = - SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_1, 8, 2, hp_mux_text); - -static const struct snd_kcontrol_new hpl_mux = - WM8994_HP_ENUM("Left Headphone Mux", hpl_enum); - -static const struct soc_enum hpr_enum = - SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_2, 8, 2, hp_mux_text); - -static const struct snd_kcontrol_new hpr_mux = - WM8994_HP_ENUM("Right Headphone Mux", hpr_enum); - static const char *adc_mux_text[] = { "ADC", "DMIC", @@ -1430,7 +1374,7 @@ static int wm8994_put_class_w(struct snd_kcontrol *kcontrol, ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); - wm8994_update_class_w(codec); + wm_hubs_update_class_w(codec); return ret; } @@ -1524,7 +1468,7 @@ static const struct snd_kcontrol_new wm8958_aif3adc_mux = SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum); static const char *mono_pcm_out_text[] = { - "None", "AIF2ADCL", "AIF2ADCR", + "None", "AIF2ADCL", "AIF2ADCR", }; static const struct soc_enum mono_pcm_out_enum = @@ -1573,9 +1517,9 @@ SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer), late_enable_ev, SND_SOC_DAPM_PRE_PMU), -SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux, +SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux, late_enable_ev, SND_SOC_DAPM_PRE_PMU), -SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux, +SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux, late_enable_ev, SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) @@ -1591,8 +1535,8 @@ SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), -SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), -SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux), }; static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { @@ -1732,6 +1676,7 @@ SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux), }; static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF3", WM8994_POWER_MANAGEMENT_6, 5, 1, NULL, 0), SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux), SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux), SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux), @@ -1972,6 +1917,9 @@ static const struct snd_soc_dapm_route wm8958_intercon[] = { { "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" }, { "AIF2DACR Mux", "AIF3", "AIF3DACDAT" }, + { "AIF3DACDAT", NULL, "AIF3" }, + { "AIF3ADCDAT", NULL, "AIF3" }, + { "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" }, { "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" }, @@ -2068,24 +2016,20 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, struct wm8994 *control = wm8994->wm8994; int reg_offset, ret; struct fll_div fll; - u16 reg, aif1, aif2; + u16 reg, clk1, aif_reg, aif_src; unsigned long timeout; bool was_enabled; - aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1) - & WM8994_AIF1CLK_ENA; - - aif2 = snd_soc_read(codec, WM8994_AIF2_CLOCKING_1) - & WM8994_AIF2CLK_ENA; - switch (id) { case WM8994_FLL1: reg_offset = 0; id = 0; + aif_src = 0x10; break; case WM8994_FLL2: reg_offset = 0x20; id = 1; + aif_src = 0x18; break; default: return -EINVAL; @@ -2127,16 +2071,33 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, if (ret < 0) return ret; - /* Gate the AIF clocks while we reclock */ - snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, - WM8994_AIF1CLK_ENA, 0); - snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, - WM8994_AIF2CLK_ENA, 0); + /* Make sure that we're not providing SYSCLK right now */ + clk1 = snd_soc_read(codec, WM8994_CLOCKING_1); + if (clk1 & WM8994_SYSCLK_SRC) + aif_reg = WM8994_AIF2_CLOCKING_1; + else + aif_reg = WM8994_AIF1_CLOCKING_1; + reg = snd_soc_read(codec, aif_reg); + + if ((reg & WM8994_AIF1CLK_ENA) && + (reg & WM8994_AIF1CLK_SRC_MASK) == aif_src) { + dev_err(codec->dev, "FLL%d is currently providing SYSCLK\n", + id + 1); + return -EBUSY; + } /* We always need to disable the FLL while reconfiguring */ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset, WM8994_FLL1_ENA, 0); + if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK && + freq_in == freq_out && freq_out) { + dev_dbg(codec->dev, "Bypassing FLL%d\n", id + 1); + snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset, + WM8958_FLL1_BYP, WM8958_FLL1_BYP); + goto out; + } + reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) | (fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT); snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_2 + reg_offset, @@ -2151,6 +2112,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, fll.n << WM8994_FLL1_N_SHIFT); snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset, + WM8958_FLL1_BYP | WM8994_FLL1_REFCLK_DIV_MASK | WM8994_FLL1_REFCLK_SRC_MASK, (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) | @@ -2213,16 +2175,11 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, } } +out: wm8994->fll[id].in = freq_in; wm8994->fll[id].out = freq_out; wm8994->fll[id].src = src; - /* Enable any gated AIF clocks */ - snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, - WM8994_AIF1CLK_ENA, aif1); - snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, - WM8994_AIF2CLK_ENA, aif2); - configure_clock(codec); return 0; @@ -2290,7 +2247,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, case WM8994_SYSCLK_OPCLK: /* Special case - a division (times 10) is given and - * no effect on main clocking. + * no effect on main clocking. */ if (freq) { for (i = 0; i < ARRAY_SIZE(opclk_divs); i++) @@ -2792,33 +2749,6 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream, return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1); } -static void wm8994_aif_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_codec *codec = dai->codec; - int rate_reg = 0; - - switch (dai->id) { - case 1: - rate_reg = WM8994_AIF1_RATE; - break; - case 2: - rate_reg = WM8994_AIF2_RATE; - break; - default: - break; - } - - /* If the DAI is idle then configure the divider tree for the - * lowest output rate to save a little power if the clock is - * still active (eg, because it is system clock). - */ - if (rate_reg && !dai->playback_active && !dai->capture_active) - snd_soc_update_bits(codec, rate_reg, - WM8994_AIF1_SR_MASK | - WM8994_AIF1CLK_RATE_MASK, 0x9); -} - static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; @@ -2860,10 +2790,6 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate) reg = WM8994_AIF2_MASTER_SLAVE; mask = WM8994_AIF2_TRI; break; - case 3: - reg = WM8994_POWER_MANAGEMENT_6; - mask = WM8994_AIF3_TRI; - break; default: return -EINVAL; } @@ -2900,7 +2826,6 @@ static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = { .set_sysclk = wm8994_set_dai_sysclk, .set_fmt = wm8994_set_dai_fmt, .hw_params = wm8994_hw_params, - .shutdown = wm8994_aif_shutdown, .digital_mute = wm8994_aif_mute, .set_pll = wm8994_set_fll, .set_tristate = wm8994_set_tristate, @@ -2910,7 +2835,6 @@ static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = { .set_sysclk = wm8994_set_dai_sysclk, .set_fmt = wm8994_set_dai_fmt, .hw_params = wm8994_hw_params, - .shutdown = wm8994_aif_shutdown, .digital_mute = wm8994_aif_mute, .set_pll = wm8994_set_fll, .set_tristate = wm8994_set_tristate, @@ -2918,7 +2842,6 @@ static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = { static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = { .hw_params = wm8994_aif3_hw_params, - .set_tristate = wm8994_set_tristate, }; static struct snd_soc_dai_driver wm8994_dai[] = { @@ -3126,14 +3049,14 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994) /* Expand the array... */ t = krealloc(wm8994->retune_mobile_texts, - sizeof(char *) * + sizeof(char *) * (wm8994->num_retune_mobile_texts + 1), GFP_KERNEL); if (t == NULL) continue; /* ...store the new entry... */ - t[wm8994->num_retune_mobile_texts] = + t[wm8994->num_retune_mobile_texts] = pdata->retune_mobile_cfgs[i].name; /* ...and remember the new version. */ @@ -3304,25 +3227,25 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, } EXPORT_SYMBOL_GPL(wm8994_mic_detect); -static irqreturn_t wm8994_mic_irq(int irq, void *data) +static void wm8994_mic_work(struct work_struct *work) { - struct wm8994_priv *priv = data; - struct snd_soc_codec *codec = priv->codec; - int reg; + struct wm8994_priv *priv = container_of(work, + struct wm8994_priv, + mic_work.work); + struct regmap *regmap = priv->wm8994->regmap; + struct device *dev = priv->wm8994->dev; + unsigned int reg; + int ret; int report; -#ifndef CONFIG_SND_SOC_WM8994_MODULE - trace_snd_soc_jack_irq(dev_name(codec->dev)); -#endif - - reg = snd_soc_read(codec, WM8994_INTERRUPT_RAW_STATUS_2); - if (reg < 0) { - dev_err(codec->dev, "Failed to read microphone status: %d\n", - reg); - return IRQ_HANDLED; + ret = regmap_read(regmap, WM8994_INTERRUPT_RAW_STATUS_2, ®); + if (ret < 0) { + dev_err(dev, "Failed to read microphone status: %d\n", + ret); + return; } - dev_dbg(codec->dev, "Microphone status: %x\n", reg); + dev_dbg(dev, "Microphone status: %x\n", reg); report = 0; if (reg & WM8994_MIC1_DET_STS) { @@ -3361,6 +3284,20 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) snd_soc_jack_report(priv->micdet[1].jack, report, SND_JACK_HEADSET | SND_JACK_BTN_0); +} + +static irqreturn_t wm8994_mic_irq(int irq, void *data) +{ + struct wm8994_priv *priv = data; + struct snd_soc_codec *codec = priv->codec; + +#ifndef CONFIG_SND_SOC_WM8994_MODULE + trace_snd_soc_jack_irq(dev_name(codec->dev)); +#endif + + pm_wakeup_event(codec->dev, 300); + + schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250)); return IRQ_HANDLED; } @@ -3415,9 +3352,6 @@ static void wm8958_default_micdet(u16 status, void *data) wm8958_micd_set_rate(codec); - snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, - SND_JACK_HEADSET); - /* If we have jackdet that will detect removal */ if (wm8994->jackdet) { mutex_lock(&wm8994->accdet_lock); @@ -3430,14 +3364,13 @@ static void wm8958_default_micdet(u16 status, void *data) mutex_unlock(&wm8994->accdet_lock); - if (wm8994->pdata->jd_ext_cap) { - mutex_lock(&codec->mutex); + if (wm8994->pdata->jd_ext_cap) snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2"); - snd_soc_dapm_sync(&codec->dapm); - mutex_unlock(&codec->mutex); - } } + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, + SND_JACK_HEADSET); } /* Report short circuit as a button */ @@ -3489,6 +3422,8 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) if (present) { dev_dbg(codec->dev, "Jack detected\n"); + wm8958_micd_set_rate(codec); + snd_soc_update_bits(codec, WM8958_MICBIAS2, WM8958_MICB2_DISCH, 0); @@ -3526,16 +3461,11 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) /* If required for an external cap force MICBIAS on */ if (wm8994->pdata->jd_ext_cap) { - mutex_lock(&codec->mutex); - if (present) snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS2"); else snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2"); - - snd_soc_dapm_sync(&codec->dapm); - mutex_unlock(&codec->mutex); } if (present) @@ -3740,6 +3670,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->codec = codec; mutex_init(&wm8994->accdet_lock); + INIT_DELAYED_WORK(&wm8994->mic_work, wm8994_mic_work); for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) init_completion(&wm8994->fll_locked[i]); @@ -3783,13 +3714,22 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) case WM8958: wm8994->hubs.dcs_readback_mode = 1; wm8994->hubs.hp_startup_mode = 1; + + switch (wm8994->revision) { + case 0: + break; + default: + wm8994->fll_byp = true; + break; + } break; case WM1811: wm8994->hubs.dcs_readback_mode = 2; wm8994->hubs.no_series_update = 1; wm8994->hubs.hp_startup_mode = 1; - wm8994->hubs.no_cache_class_w = true; + wm8994->hubs.no_cache_dac_hp_direct = true; + wm8994->fll_byp = true; switch (wm8994->revision) { case 0: @@ -4010,7 +3950,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; } - wm8994_update_class_w(codec); + wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital; + wm_hubs_update_class_w(codec); wm8994_handle_pdata(wm8994); @@ -4075,7 +4016,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) ARRAY_SIZE(wm8994_dac_widgets)); break; } - wm_hubs_add_analogue_routes(codec, 0, 0); snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); @@ -4140,7 +4080,7 @@ err_irq: return ret; } -static int wm8994_codec_remove(struct snd_soc_codec *codec) +static int wm8994_codec_remove(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; @@ -4181,14 +4121,10 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) free_irq(wm8994->micdet_irq, wm8994); break; } - if (wm8994->mbc) - release_firmware(wm8994->mbc); - if (wm8994->mbc_vss) - release_firmware(wm8994->mbc_vss); - if (wm8994->enh_eq) - release_firmware(wm8994->enh_eq); + release_firmware(wm8994->mbc); + release_firmware(wm8994->mbc_vss); + release_firmware(wm8994->enh_eq); kfree(wm8994->retune_mobile_texts); - return 0; } diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index c724112998d8..d77e06f0a675 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -12,6 +12,7 @@ #include <sound/soc.h> #include <linux/firmware.h> #include <linux/completion.h> +#include <linux/workqueue.h> #include "wm_hubs.h" @@ -79,6 +80,7 @@ struct wm8994_priv { struct wm8994_fll_config fll[2], fll_suspend[2]; struct completion fll_locked[2]; bool fll_locked_irq; + bool fll_byp; int vmid_refcount; int active_refcount; @@ -126,6 +128,7 @@ struct wm8994_priv { struct mutex accdet_lock; struct wm8994_micdet micdet[2]; + struct delayed_work mic_work; bool mic_detecting; bool jack_mic; int btn_mask; diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 1fd635494045..8af422e38fd0 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -1770,7 +1770,13 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: + break; case SND_SOC_BIAS_PREPARE: + /* Put the MICBIASes into regulating mode */ + snd_soc_update_bits(codec, WM8996_MICBIAS_1, + WM8996_MICB1_MODE, 0); + snd_soc_update_bits(codec, WM8996_MICBIAS_2, + WM8996_MICB2_MODE, 0); break; case SND_SOC_BIAS_STANDBY: @@ -1793,6 +1799,12 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, regcache_cache_only(codec->control_data, false); regcache_sync(codec->control_data); } + + /* Bypass the MICBIASes for lowest power */ + snd_soc_update_bits(codec, WM8996_MICBIAS_1, + WM8996_MICB1_MODE, WM8996_MICB1_MODE); + snd_soc_update_bits(codec, WM8996_MICBIAS_2, + WM8996_MICB2_MODE, WM8996_MICB2_MODE); break; case SND_SOC_BIAS_OFF: diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 076c126ed9b1..9328270df16c 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -774,7 +774,7 @@ static const struct snd_soc_dapm_widget wm9081_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN1"), SND_SOC_DAPM_INPUT("IN2"), -SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM9081_POWER_MANAGEMENT, 0, 0), +SND_SOC_DAPM_DAC("DAC", NULL, WM9081_POWER_MANAGEMENT, 0, 0), SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0, mixer, ARRAY_SIZE(mixer)), @@ -799,6 +799,7 @@ SND_SOC_DAPM_SUPPLY("TSENSE", WM9081_POWER_MANAGEMENT, 7, 0, NULL, 0), static const struct snd_soc_dapm_route wm9081_audio_paths[] = { { "DAC", NULL, "CLK_SYS" }, { "DAC", NULL, "CLK_DSP" }, + { "DAC", NULL, "AIF" }, { "Mixer", "IN1 Switch", "IN1" }, { "Mixer", "IN2 Switch", "IN2" }, @@ -1252,7 +1253,7 @@ static const struct snd_soc_dai_ops wm9081_dai_ops = { static struct snd_soc_dai_driver wm9081_dai = { .name = "wm9081-hifi", .playback = { - .stream_name = "HiFi Playback", + .stream_name = "AIF", .channels_min = 1, .channels_max = 2, .rates = WM9081_RATES, diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index cacc6a86b46f..e8e782a0c78d 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -236,9 +236,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, static int ac97_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; int reg; u16 vra; @@ -250,7 +248,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream, else reg = AC97_PCM_LR_ADC_RATE; - return ac97_write(codec, reg, runtime->rate); + return ac97_write(codec, reg, substream->runtime->rate); } #define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \ diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index b342ae50bcd6..a1541414d904 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -467,11 +467,10 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, static int ac97_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec =rtd->codec; + struct snd_soc_codec *codec = dai->codec; int reg; u16 vra; + struct snd_pcm_runtime *runtime = substream->runtime; vra = ac97_read(codec, AC97_EXTENDED_STATUS); ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); @@ -487,10 +486,9 @@ static int ac97_prepare(struct snd_pcm_substream *substream, static int ac97_aux_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 vra, xsle; + struct snd_pcm_runtime *runtime = substream->runtime; vra = ac97_read(codec, AC97_EXTENDED_STATUS); ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 6c028c470601..dfe957a47f29 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -109,12 +109,103 @@ irqreturn_t wm_hubs_dcs_done(int irq, void *data) } EXPORT_SYMBOL_GPL(wm_hubs_dcs_done); +static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec) +{ + int reg; + + /* If we're going via the mixer we'll need to do additional checks */ + reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER1); + if (!(reg & WM8993_DACL_TO_HPOUT1L)) { + if (reg & ~WM8993_DACL_TO_MIXOUTL) { + dev_vdbg(codec->dev, "Analogue paths connected: %x\n", + reg & ~WM8993_DACL_TO_HPOUT1L); + return false; + } else { + dev_vdbg(codec->dev, "HPL connected to mixer\n"); + } + } else { + dev_vdbg(codec->dev, "HPL connected to DAC\n"); + } + + reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER2); + if (!(reg & WM8993_DACR_TO_HPOUT1R)) { + if (reg & ~WM8993_DACR_TO_MIXOUTR) { + dev_vdbg(codec->dev, "Analogue paths connected: %x\n", + reg & ~WM8993_DACR_TO_HPOUT1R); + return false; + } else { + dev_vdbg(codec->dev, "HPR connected to mixer\n"); + } + } else { + dev_vdbg(codec->dev, "HPR connected to DAC\n"); + } + + return true; +} + +struct wm_hubs_dcs_cache { + struct list_head list; + unsigned int left; + unsigned int right; + u16 dcs_cfg; +}; + +static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec, + struct wm_hubs_dcs_cache **entry) +{ + struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); + struct wm_hubs_dcs_cache *cache; + unsigned int left, right; + + left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME); + left &= WM8993_HPOUT1L_VOL_MASK; + + right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME); + right &= WM8993_HPOUT1R_VOL_MASK; + + list_for_each_entry(cache, &hubs->dcs_cache, list) { + if (cache->left != left || cache->right != right) + continue; + + *entry = cache; + return true; + } + + return false; +} + +static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg) +{ + struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); + struct wm_hubs_dcs_cache *cache; + + if (hubs->no_cache_dac_hp_direct) + return; + + cache = devm_kzalloc(codec->dev, sizeof(*cache), GFP_KERNEL); + if (!cache) { + dev_err(codec->dev, "Failed to allocate DCS cache entry\n"); + return; + } + + cache->left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME); + cache->left &= WM8993_HPOUT1L_VOL_MASK; + + cache->right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME); + cache->right &= WM8993_HPOUT1R_VOL_MASK; + + cache->dcs_cfg = dcs_cfg; + + list_add_tail(&cache->list, &hubs->dcs_cache); +} + /* * Startup calibration of the DC servo */ static void calibrate_dc_servo(struct snd_soc_codec *codec) { struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); + struct wm_hubs_dcs_cache *cache; s8 offset; u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg; @@ -129,10 +220,11 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) /* If we're using a digital only path and have a previously * callibrated DC servo offset stored then use that. */ - if (hubs->class_w && hubs->class_w_dcs) { - dev_dbg(codec->dev, "Using cached DC servo offset %x\n", - hubs->class_w_dcs); - snd_soc_write(codec, dcs_reg, hubs->class_w_dcs); + if (wm_hubs_dac_hp_direct(codec) && + wm_hubs_dcs_cache_get(codec, &cache)) { + dev_dbg(codec->dev, "Using cached DCS offset %x for %d,%d\n", + cache->dcs_cfg, cache->left, cache->right); + snd_soc_write(codec, dcs_reg, cache->dcs_cfg); wait_for_dc_servo(codec, WM8993_DCS_TRIG_DAC_WR_0 | WM8993_DCS_TRIG_DAC_WR_1); @@ -207,8 +299,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) /* Save the callibrated offset if we're in class W mode and * therefore don't have any analogue signal mixed in. */ - if (hubs->class_w && !hubs->no_cache_class_w) - hubs->class_w_dcs = dcs_cfg; + if (wm_hubs_dac_hp_direct(codec)) + wm_hubs_dcs_cache_set(codec, dcs_cfg); } /* @@ -223,9 +315,6 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, ret = snd_soc_put_volsw(kcontrol, ucontrol); - /* Updating the analogue gains invalidates the DC servo cache */ - hubs->class_w_dcs = 0; - /* If we're applying an offset correction then updating the * callibration would be likely to introduce further offsets. */ if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update) @@ -530,6 +619,86 @@ static int lineout_event(struct snd_soc_dapm_widget *w, return 0; } +void wm_hubs_update_class_w(struct snd_soc_codec *codec) +{ + struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); + int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ; + + if (!wm_hubs_dac_hp_direct(codec)) + enable = false; + + if (hubs->check_class_w_digital && !hubs->check_class_w_digital(codec)) + enable = false; + + dev_vdbg(codec->dev, "Class W %s\n", enable ? "enabled" : "disabled"); + + snd_soc_update_bits(codec, WM8993_CLASS_W_0, + WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable); +} +EXPORT_SYMBOL_GPL(wm_hubs_update_class_w); + +#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = class_w_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + +static int class_w_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; + int ret; + + ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); + + wm_hubs_update_class_w(codec); + + return ret; +} + +#define WM_HUBS_ENUM_W(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = class_w_put_double, \ + .private_value = (unsigned long)&xenum } + +static int class_w_put_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; + int ret; + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + + wm_hubs_update_class_w(codec); + + return ret; +} + +static const char *hp_mux_text[] = { + "Mixer", + "DAC", +}; + +static const struct soc_enum hpl_enum = + SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text); + +const struct snd_kcontrol_new wm_hubs_hpl_mux = + WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum); +EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux); + +static const struct soc_enum hpr_enum = + SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text); + +const struct snd_kcontrol_new wm_hubs_hpr_mux = + WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum); +EXPORT_SYMBOL_GPL(wm_hubs_hpr_mux); + static const struct snd_kcontrol_new in1l_pga[] = { SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0), SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0), @@ -561,25 +730,25 @@ SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0), }; static const struct snd_kcontrol_new left_output_mixer[] = { -SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0), -SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0), -SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0), -SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0), -SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0), -SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0), -SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0), -SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0), +WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0), +WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0), +WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0), +WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0), +WM_HUBS_SINGLE_W("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0), +WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0), +WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0), +WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0), }; static const struct snd_kcontrol_new right_output_mixer[] = { -SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0), -SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0), -SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0), -SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0), -SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0), -SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0), -SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0), -SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0), +WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0), +WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0), +WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0), +WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0), +WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0), +WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0), +WM_HUBS_SINGLE_W("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0), +WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0), }; static const struct snd_kcontrol_new earpiece_mixer[] = { @@ -943,6 +1112,7 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec, struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; + INIT_LIST_HEAD(&hubs->dcs_cache); init_completion(&hubs->dcs_done); snd_soc_dapm_add_routes(dapm, analogue_routes, diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index 5705276f4943..da2dc899ce6d 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h @@ -16,6 +16,8 @@ #include <linux/completion.h> #include <linux/interrupt.h> +#include <linux/list.h> +#include <sound/control.h> struct snd_soc_codec; @@ -30,9 +32,9 @@ struct wm_hubs_data { int series_startup; int no_series_update; - bool no_cache_class_w; - bool class_w; - u16 class_w_dcs; + bool no_cache_dac_hp_direct; + struct list_head dcs_cache; + bool (*check_class_w_digital)(struct snd_soc_codec *); bool lineout1_se; bool lineout1n_ena; @@ -58,5 +60,9 @@ extern irqreturn_t wm_hubs_dcs_done(int irq, void *data); extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec); extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level); +extern void wm_hubs_update_class_w(struct snd_soc_codec *codec); + +extern const struct snd_kcontrol_new wm_hubs_hpl_mux; +extern const struct snd_kcontrol_new wm_hubs_hpr_mux; #endif diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c index 0678637abd66..bdffab33e160 100644 --- a/sound/soc/ep93xx/ep93xx-ac97.c +++ b/sound/soc/ep93xx/ep93xx-ac97.c @@ -87,17 +87,13 @@ * struct ep93xx_ac97_info - EP93xx AC97 controller info structure * @lock: mutex serializing access to the bus (slot 1 & 2 ops) * @dev: pointer to the platform device dev structure - * @mem: physical memory resource for the registers * @regs: mapped AC97 controller registers - * @irq: AC97 interrupt number * @done: bus ops wait here for an interrupt */ struct ep93xx_ac97_info { struct mutex lock; struct device *dev; - struct resource *mem; void __iomem *regs; - int irq; struct completion done; }; @@ -359,66 +355,50 @@ static struct snd_soc_dai_driver ep93xx_ac97_dai = { static int __devinit ep93xx_ac97_probe(struct platform_device *pdev) { struct ep93xx_ac97_info *info; + struct resource *res; + unsigned int irq; int ret; - info = kzalloc(sizeof(struct ep93xx_ac97_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - dev_set_drvdata(&pdev->dev, info); - - mutex_init(&info->lock); - init_completion(&info->done); - info->dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; - info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!info->mem) { - ret = -ENXIO; - goto fail_free_info; - } + info->regs = devm_request_and_ioremap(&pdev->dev, res); + if (!info->regs) + return -ENXIO; - info->irq = platform_get_irq(pdev, 0); - if (!info->irq) { - ret = -ENXIO; - goto fail_free_info; - } + irq = platform_get_irq(pdev, 0); + if (!irq) + return -ENODEV; - if (!request_mem_region(info->mem->start, resource_size(info->mem), - pdev->name)) { - ret = -EBUSY; - goto fail_free_info; - } + ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt, + IRQF_TRIGGER_HIGH, pdev->name, info); + if (ret) + goto fail; - info->regs = ioremap(info->mem->start, resource_size(info->mem)); - if (!info->regs) { - ret = -ENOMEM; - goto fail_release_mem; - } + dev_set_drvdata(&pdev->dev, info); - ret = request_irq(info->irq, ep93xx_ac97_interrupt, IRQF_TRIGGER_HIGH, - pdev->name, info); - if (ret) - goto fail_unmap_mem; + mutex_init(&info->lock); + init_completion(&info->done); + info->dev = &pdev->dev; ep93xx_ac97_info = info; platform_set_drvdata(pdev, info); ret = snd_soc_register_dai(&pdev->dev, &ep93xx_ac97_dai); if (ret) - goto fail_free_irq; + goto fail; return 0; -fail_free_irq: +fail: platform_set_drvdata(pdev, NULL); - free_irq(info->irq, info); -fail_unmap_mem: - iounmap(info->regs); -fail_release_mem: - release_mem_region(info->mem->start, resource_size(info->mem)); -fail_free_info: - kfree(info); - + ep93xx_ac97_info = NULL; + dev_set_drvdata(&pdev->dev, NULL); return ret; } @@ -431,11 +411,9 @@ static int __devexit ep93xx_ac97_remove(struct platform_device *pdev) /* disable the AC97 controller */ ep93xx_ac97_write_reg(info, AC97GCR, 0); - free_irq(info->irq, info); - iounmap(info->regs); - release_mem_region(info->mem->start, resource_size(info->mem)); platform_set_drvdata(pdev, NULL); - kfree(info); + ep93xx_ac97_info = NULL; + dev_set_drvdata(&pdev->dev, NULL); return 0; } diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c index f7a62348e3fe..8df8f6dc474f 100644 --- a/sound/soc/ep93xx/ep93xx-i2s.c +++ b/sound/soc/ep93xx/ep93xx-i2s.c @@ -63,7 +63,6 @@ struct ep93xx_i2s_info { struct clk *sclk; struct clk *lrclk; struct ep93xx_pcm_dma_params *dma_params; - struct resource *mem; void __iomem *regs; }; @@ -373,38 +372,22 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) struct resource *res; int err; - info = kzalloc(sizeof(struct ep93xx_i2s_info), GFP_KERNEL); - if (!info) { - err = -ENOMEM; - goto fail; - } - - dev_set_drvdata(&pdev->dev, info); - info->dma_params = ep93xx_i2s_dma_params; + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - err = -ENODEV; - goto fail_free_info; - } + if (!res) + return -ENODEV; - info->mem = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!info->mem) { - err = -EBUSY; - goto fail_free_info; - } - - info->regs = ioremap(info->mem->start, resource_size(info->mem)); - if (!info->regs) { - err = -ENXIO; - goto fail_release_mem; - } + info->regs = devm_request_and_ioremap(&pdev->dev, res); + if (!info->regs) + return -ENXIO; info->mclk = clk_get(&pdev->dev, "mclk"); if (IS_ERR(info->mclk)) { err = PTR_ERR(info->mclk); - goto fail_unmap_mem; + goto fail; } info->sclk = clk_get(&pdev->dev, "sclk"); @@ -419,6 +402,9 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) goto fail_put_sclk; } + dev_set_drvdata(&pdev->dev, info); + info->dma_params = ep93xx_i2s_dma_params; + err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai); if (err) goto fail_put_lrclk; @@ -426,17 +412,12 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) return 0; fail_put_lrclk: + dev_set_drvdata(&pdev->dev, NULL); clk_put(info->lrclk); fail_put_sclk: clk_put(info->sclk); fail_put_mclk: clk_put(info->mclk); -fail_unmap_mem: - iounmap(info->regs); -fail_release_mem: - release_mem_region(info->mem->start, resource_size(info->mem)); -fail_free_info: - kfree(info); fail: return err; } @@ -446,12 +427,10 @@ static int __devexit ep93xx_i2s_remove(struct platform_device *pdev) struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev); snd_soc_unregister_dai(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); clk_put(info->lrclk); clk_put(info->sclk); clk_put(info->mclk); - iounmap(info->regs); - release_mem_region(info->mem->start, resource_size(info->mem)); - kfree(info); return 0; } diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index d754d34d68a6..3f2dd3a0377a 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,18 +1,31 @@ -config SND_MPC52xx_DMA +config SND_SOC_FSL_SSI tristate -# ASoC platform support for the Freescale PowerPC SOCs that have an SSI and -# an Elo DMA controller, such as the MPC8610 and P1022. You will still need to -# select a platform driver and a codec driver. -config SND_SOC_POWERPC_SSI +config SND_SOC_FSL_UTILS tristate + +menuconfig SND_POWERPC_SOC + tristate "SoC Audio for Freescale PowerPC CPUs" depends on FSL_SOC + help + Say Y or M if you want to add support for codecs attached to + the PowerPC CPUs. + +if SND_POWERPC_SOC + +config SND_MPC52xx_DMA + tristate + +config SND_SOC_POWERPC_DMA + tristate config SND_SOC_MPC8610_HPCD tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" # I2C is necessary for the CS4270 driver depends on MPC8610_HPCD && I2C - select SND_SOC_POWERPC_SSI + select SND_SOC_FSL_SSI + select SND_SOC_FSL_UTILS + select SND_SOC_POWERPC_DMA select SND_SOC_CS4270 select SND_SOC_CS4270_VD33_ERRATA default y if MPC8610_HPCD @@ -23,7 +36,9 @@ config SND_SOC_P1022_DS tristate "ALSA SoC support for the Freescale P1022 DS board" # I2C is necessary for the WM8776 driver depends on P1022_DS && I2C - select SND_SOC_POWERPC_SSI + select SND_SOC_FSL_SSI + select SND_SOC_FSL_UTILS + select SND_SOC_POWERPC_DMA select SND_SOC_WM8776 default y if P1022_DS help @@ -65,3 +80,103 @@ config SND_MPC52xx_SOC_EFIKA help Say Y if you want to add support for sound on the Efika. +endif # SND_POWERPC_SOC + +menuconfig SND_IMX_SOC + tristate "SoC Audio for Freescale i.MX CPUs" + depends on ARCH_MXC + help + Say Y or M if you want to add support for codecs attached to + the i.MX CPUs. + +if SND_IMX_SOC + +config SND_SOC_IMX_SSI + tristate + +config SND_SOC_IMX_PCM + tristate + +config SND_SOC_IMX_PCM_FIQ + tristate + select FIQ + select SND_SOC_IMX_PCM + +config SND_SOC_IMX_PCM_DMA + tristate + select SND_SOC_DMAENGINE_PCM + select SND_SOC_IMX_PCM + +config SND_SOC_IMX_AUDMUX + tristate + +config SND_MXC_SOC_WM1133_EV1 + tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted" + depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL + select SND_SOC_WM8350 + select SND_SOC_IMX_PCM_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI + help + Enable support for audio on the i.MX31ADS with the WM1133-EV1 + PMIC board with WM8835x fitted. + +config SND_SOC_MX27VIS_AIC32X4 + tristate "SoC audio support for Visstrim M10 boards" + depends on MACH_IMX27_VISSTRIM_M10 && I2C + select SND_SOC_TLV320AIC32X4 + select SND_SOC_IMX_PCM_DMA + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI + help + Say Y if you want to add support for SoC audio on Visstrim SM10 + board with TLV320AIC32X4 codec. + +config SND_SOC_PHYCORE_AC97 + tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" + depends on MACH_PCM043 || MACH_PCA100 + select SND_SOC_AC97_BUS + select SND_SOC_WM9712 + select SND_SOC_IMX_PCM_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI + help + Say Y if you want to add support for SoC audio on Phytec phyCORE + and phyCARD boards in AC97 mode + +config SND_SOC_EUKREA_TLV320 + tristate "Eukrea TLV320" + depends on MACH_EUKREA_MBIMX27_BASEBOARD \ + || MACH_EUKREA_MBIMXSD25_BASEBOARD \ + || MACH_EUKREA_MBIMXSD35_BASEBOARD \ + || MACH_EUKREA_MBIMXSD51_BASEBOARD + depends on I2C + select SND_SOC_TLV320AIC23 + select SND_SOC_IMX_PCM_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI + help + Enable I2S based access to the TLV320AIC23B codec attached + to the SSI interface + +config SND_SOC_IMX_SGTL5000 + tristate "SoC Audio support for i.MX boards with sgtl5000" + depends on OF && I2C + select SND_SOC_SGTL5000 + select SND_SOC_IMX_PCM_DMA + select SND_SOC_IMX_AUDMUX + select SND_SOC_FSL_SSI + select SND_SOC_FSL_UTILS + help + Say Y if you want to add support for SoC audio on an i.MX board with + a sgtl5000 codec. + +config SND_SOC_IMX_MC13783 + tristate "SoC Audio support for I.MX boards with mc13783" + depends on MFD_MC13783 + select SND_SOC_IMX_SSI + select SND_SOC_IMX_AUDMUX + select SND_SOC_MC13783 + select SND_SOC_IMX_PCM_DMA + +endif # SND_IMX_SOC diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index b4a38c0ac58c..5f3cf3f52ea0 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -8,8 +8,11 @@ obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o # Freescale PowerPC SSI/DMA Platform Support snd-soc-fsl-ssi-objs := fsl_ssi.o +snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o -obj-$(CONFIG_SND_SOC_POWERPC_SSI) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o +obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o +obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o +obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o # MPC5200 Platform Support obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o @@ -20,3 +23,29 @@ obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o +# i.MX Platform Support +snd-soc-imx-ssi-objs := imx-ssi.o +snd-soc-imx-audmux-objs := imx-audmux.o + +obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o +obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o + +obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o +snd-soc-imx-pcm-y := imx-pcm.o +snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o +snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o + +# i.MX Machine Support +snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o +snd-soc-phycore-ac97-objs := phycore-ac97.o +snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o +snd-soc-wm1133-ev1-objs := wm1133-ev1.o +snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o +snd-soc-imx-mc13783-objs := imx-mc13783.o + +obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o +obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o +obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o +obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o +obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o +obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 7d4475cfdb24..efb9ede01208 100644 --- a/sound/soc/imx/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -7,7 +7,7 @@ * which is Copyright 2009 Simtec Electronics * and on sound/soc/imx/phycore-ac97.c which is * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> - * + * * 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 diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 2eb407fa3b48..4ed2afd47782 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -11,11 +11,15 @@ */ #include <linux/init.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <sound/core.h> @@ -25,6 +29,26 @@ #include <sound/soc.h> #include "fsl_ssi.h" +#include "imx-pcm.h" + +#ifdef PPC +#define read_ssi(addr) in_be32(addr) +#define write_ssi(val, addr) out_be32(addr, val) +#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set) +#elif defined ARM +#define read_ssi(addr) readl(addr) +#define write_ssi(val, addr) writel(val, addr) +/* + * FIXME: Proper locking should be added at write_ssi_mask caller level + * to ensure this register read/modify/write sequence is race free. + */ +static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set) +{ + u32 val = readl(addr); + val = (val & ~clear) | set; + writel(val, addr); +} +#endif /** * FSLSSI_I2S_RATES: sample rates supported by the I2S @@ -94,6 +118,13 @@ struct fsl_ssi_private { struct device_attribute dev_attr; struct platform_device *pdev; + bool new_binding; + bool ssi_on_imx; + struct clk *clk; + struct platform_device *imx_pcm_pdev; + struct imx_pcm_dma_params dma_params_tx; + struct imx_pcm_dma_params dma_params_rx; + struct { unsigned int rfrc; unsigned int tfrc; @@ -145,7 +176,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) were interrupted for. We mask it with the Interrupt Enable register so that we only check for events that we're interested in. */ - sisr = in_be32(&ssi->sisr) & SIER_FLAGS; + sisr = read_ssi(&ssi->sisr) & SIER_FLAGS; if (sisr & CCSR_SSI_SISR_RFRC) { ssi_private->stats.rfrc++; @@ -260,7 +291,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) /* Clear the bits that we set */ if (sisr2) - out_be32(&ssi->sisr, sisr2); + write_ssi(sisr2, &ssi->sisr); return ret; } @@ -295,7 +326,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, * SSI needs to be disabled before updating the registers we set * here. */ - clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); /* * Program the SSI into I2S Slave Non-Network Synchronous mode. @@ -303,20 +334,18 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, * * FIXME: Little-endian samples require a different shift dir */ - clrsetbits_be32(&ssi->scr, + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE | (synchronous ? CCSR_SSI_SCR_SYN : 0)); - out_be32(&ssi->stcr, - CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | + write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | - CCSR_SSI_STCR_TSCKP); + CCSR_SSI_STCR_TSCKP, &ssi->stcr); - out_be32(&ssi->srcr, - CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | + write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | - CCSR_SSI_SRCR_RSCKP); + CCSR_SSI_SRCR_RSCKP, &ssi->srcr); /* * The DC and PM bits are only used if the SSI is the clock @@ -324,7 +353,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, */ /* Enable the interrupts and DMA requests */ - out_be32(&ssi->sier, SIER_FLAGS); + write_ssi(SIER_FLAGS, &ssi->sier); /* * Set the watermark for transmit FIFI 0 and receive FIFO 0. We @@ -339,9 +368,9 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, * make this value larger (and maybe we should), but this way * data will be written to memory as soon as it's available. */ - out_be32(&ssi->sfcsr, - CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | - CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2)); + write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | + CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2), + &ssi->sfcsr); /* * We keep the SSI disabled because if we enable it, then the @@ -393,6 +422,12 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, ssi_private->second_stream = substream; } + if (ssi_private->ssi_on_imx) + snd_soc_dai_set_dma_data(dai, substream, + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &ssi_private->dma_params_tx : + &ssi_private->dma_params_rx); + return 0; } @@ -417,7 +452,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, unsigned int sample_size = snd_pcm_format_width(params_format(hw_params)); u32 wl = CCSR_SSI_SxCCR_WL(sample_size); - int enabled = in_be32(&ssi->scr) & CCSR_SSI_SCR_SSIEN; + int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN; /* * If we're in synchronous mode, and the SSI is already enabled, @@ -439,9 +474,9 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, /* In synchronous mode, the SSI uses STCCR for capture */ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) || ssi_private->cpu_dai_drv.symmetric_rates) - clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); + write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); else - clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); + write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); return 0; } @@ -466,19 +501,19 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - setbits32(&ssi->scr, + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); else - setbits32(&ssi->scr, + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0); else - clrbits32(&ssi->scr, CCSR_SSI_SCR_RE); + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0); break; default: @@ -510,7 +545,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, if (!ssi_private->first_stream) { struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); } } @@ -622,12 +657,6 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev) if (!of_device_is_available(np)) return -ENODEV; - /* Check for a codec-handle property. */ - if (!of_get_property(np, "codec-handle", NULL)) { - dev_err(&pdev->dev, "missing codec-handle property\n"); - return -ENODEV; - } - /* We only support the SSI in "I2S Slave" mode */ sprop = of_get_property(np, "fsl,mode", NULL); if (!sprop || strcmp(sprop, "i2s-slave")) { @@ -692,6 +721,50 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev) /* Older 8610 DTs didn't have the fifo-depth property */ ssi_private->fifo_depth = 8; + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) { + u32 dma_events[2]; + ssi_private->ssi_on_imx = true; + + ssi_private->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(ssi_private->clk)) { + ret = PTR_ERR(ssi_private->clk); + dev_err(&pdev->dev, "could not get clock: %d\n", ret); + goto error_irq; + } + clk_prepare_enable(ssi_private->clk); + + /* + * We have burstsize be "fifo_depth - 2" to match the SSI + * watermark setting in fsl_ssi_startup(). + */ + ssi_private->dma_params_tx.burstsize = + ssi_private->fifo_depth - 2; + ssi_private->dma_params_rx.burstsize = + ssi_private->fifo_depth - 2; + ssi_private->dma_params_tx.dma_addr = + ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0); + ssi_private->dma_params_rx.dma_addr = + ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0); + /* + * TODO: This is a temporary solution and should be changed + * to use generic DMA binding later when the helplers get in. + */ + ret = of_property_read_u32_array(pdev->dev.of_node, + "fsl,ssi-dma-events", dma_events, 2); + if (ret) { + dev_err(&pdev->dev, "could not get dma events\n"); + goto error_clk; + } + ssi_private->dma_params_tx.dma = dma_events[0]; + ssi_private->dma_params_rx.dma = dma_events[1]; + + ssi_private->dma_params_tx.shared_peripheral = + of_device_is_compatible(of_get_parent(np), + "fsl,spba-bus"); + ssi_private->dma_params_rx.shared_peripheral = + ssi_private->dma_params_tx.shared_peripheral; + } + /* Initialize the the device_attribute structure */ dev_attr = &ssi_private->dev_attr; sysfs_attr_init(&dev_attr->attr); @@ -715,6 +788,26 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev) goto error_dev; } + if (ssi_private->ssi_on_imx) { + ssi_private->imx_pcm_pdev = + platform_device_register_simple("imx-pcm-audio", + -1, NULL, 0); + if (IS_ERR(ssi_private->imx_pcm_pdev)) { + ret = PTR_ERR(ssi_private->imx_pcm_pdev); + goto error_dev; + } + } + + /* + * If codec-handle property is missing from SSI node, we assume + * that the machine driver uses new binding which does not require + * SSI driver to trigger machine driver's probe. + */ + if (!of_get_property(np, "codec-handle", NULL)) { + ssi_private->new_binding = true; + goto done; + } + /* Trigger the machine driver's probe function. The platform driver * name of the machine driver is taken from /compatible property of the * device tree. We also pass the address of the CPU DAI driver @@ -736,15 +829,24 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev) goto error_dai; } +done: return 0; error_dai: + if (ssi_private->ssi_on_imx) + platform_device_unregister(ssi_private->imx_pcm_pdev); snd_soc_unregister_dai(&pdev->dev); error_dev: dev_set_drvdata(&pdev->dev, NULL); device_remove_file(&pdev->dev, dev_attr); +error_clk: + if (ssi_private->ssi_on_imx) { + clk_disable_unprepare(ssi_private->clk); + clk_put(ssi_private->clk); + } + error_irq: free_irq(ssi_private->irq, ssi_private); @@ -764,7 +866,13 @@ static int fsl_ssi_remove(struct platform_device *pdev) { struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); - platform_device_unregister(ssi_private->pdev); + if (!ssi_private->new_binding) + platform_device_unregister(ssi_private->pdev); + if (ssi_private->ssi_on_imx) { + platform_device_unregister(ssi_private->imx_pcm_pdev); + clk_disable_unprepare(ssi_private->clk); + clk_put(ssi_private->clk); + } snd_soc_unregister_dai(&pdev->dev); device_remove_file(&pdev->dev, &ssi_private->dev_attr); @@ -779,6 +887,7 @@ static int fsl_ssi_remove(struct platform_device *pdev) static const struct of_device_id fsl_ssi_ids[] = { { .compatible = "fsl,mpc8610-ssi", }, + { .compatible = "fsl,imx21-ssi", }, {} }; MODULE_DEVICE_TABLE(of, fsl_ssi_ids); diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c new file mode 100644 index 000000000000..b9e42b503a37 --- /dev/null +++ b/sound/soc/fsl/fsl_utils.c @@ -0,0 +1,91 @@ +/** + * Freescale ALSA SoC Machine driver utility + * + * Author: Timur Tabi <timur@freescale.com> + * + * Copyright 2010 Freescale Semiconductor, 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/module.h> +#include <linux/of_address.h> +#include <sound/soc.h> + +#include "fsl_utils.h" + +/** + * fsl_asoc_get_dma_channel - determine the dma channel for a SSI node + * + * @ssi_np: pointer to the SSI device tree node + * @name: name of the phandle pointing to the dma channel + * @dai: ASoC DAI link pointer to be filled with platform_name + * @dma_channel_id: dma channel id to be returned + * @dma_id: dma id to be returned + * + * This function determines the dma and channel id for given SSI node. It + * also discovers the platform_name for the ASoC DAI link. + */ +int fsl_asoc_get_dma_channel(struct device_node *ssi_np, + const char *name, + struct snd_soc_dai_link *dai, + unsigned int *dma_channel_id, + unsigned int *dma_id) +{ + struct resource res; + struct device_node *dma_channel_np, *dma_np; + const u32 *iprop; + int ret; + + dma_channel_np = of_parse_phandle(ssi_np, name, 0); + if (!dma_channel_np) + return -EINVAL; + + if (!of_device_is_compatible(dma_channel_np, "fsl,ssi-dma-channel")) { + of_node_put(dma_channel_np); + return -EINVAL; + } + + /* Determine the dev_name for the device_node. This code mimics the + * behavior of of_device_make_bus_id(). We need this because ASoC uses + * the dev_name() of the device to match the platform (DMA) device with + * the CPU (SSI) device. It's all ugly and hackish, but it works (for + * now). + * + * dai->platform name should already point to an allocated buffer. + */ + ret = of_address_to_resource(dma_channel_np, 0, &res); + if (ret) { + of_node_put(dma_channel_np); + return ret; + } + snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", + (unsigned long long) res.start, dma_channel_np->name); + + iprop = of_get_property(dma_channel_np, "cell-index", NULL); + if (!iprop) { + of_node_put(dma_channel_np); + return -EINVAL; + } + *dma_channel_id = be32_to_cpup(iprop); + + dma_np = of_get_parent(dma_channel_np); + iprop = of_get_property(dma_np, "cell-index", NULL); + if (!iprop) { + of_node_put(dma_np); + return -EINVAL; + } + *dma_id = be32_to_cpup(iprop); + + of_node_put(dma_np); + of_node_put(dma_channel_np); + + return 0; +} +EXPORT_SYMBOL(fsl_asoc_get_dma_channel); + +MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); +MODULE_DESCRIPTION("Freescale ASoC utility code"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h new file mode 100644 index 000000000000..b2951126527c --- /dev/null +++ b/sound/soc/fsl/fsl_utils.h @@ -0,0 +1,26 @@ +/** + * Freescale ALSA SoC Machine driver utility + * + * Author: Timur Tabi <timur@freescale.com> + * + * Copyright 2010 Freescale Semiconductor, 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. + */ + +#ifndef _FSL_UTILS_H +#define _FSL_UTILS_H + +#define DAI_NAME_SIZE 32 + +struct snd_soc_dai_link; +struct device_node; + +int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name, + struct snd_soc_dai_link *dai, + unsigned int *dma_channel_id, + unsigned int *dma_id); + +#endif /* _FSL_UTILS_H */ diff --git a/sound/soc/imx/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 0fe66c3dde12..f23700359c67 100644 --- a/sound/soc/imx/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -40,12 +40,6 @@ static void __iomem *audmux_base; #ifdef CONFIG_DEBUG_FS static struct dentry *audmux_debugfs_root; -static int audmux_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - /* There is an annoying discontinuity in the SSI numbering with regard * to the Linux number of the devices */ static const char *audmux_port_string(int port) @@ -145,7 +139,7 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf, } static const struct file_operations audmux_debugfs_fops = { - .open = audmux_open_file, + .open = simple_open, .read = audmux_read_file, .llseek = default_llseek, }; diff --git a/sound/soc/imx/imx-audmux.h b/sound/soc/fsl/imx-audmux.h index 04ebbab8d7b9..04ebbab8d7b9 100644 --- a/sound/soc/imx/imx-audmux.h +++ b/sound/soc/fsl/imx-audmux.h diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c new file mode 100644 index 000000000000..f59c34943662 --- /dev/null +++ b/sound/soc/fsl/imx-mc13783.c @@ -0,0 +1,156 @@ +/* + * imx-mc13783.c -- SoC audio for imx based boards with mc13783 codec + * + * Copyright 2012 Philippe Retornaz, <philippe.retornaz@epfl.ch> + * + * Heavly based on phycore-mc13783: + * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <asm/mach-types.h> + +#include "../codecs/mc13783.h" +#include "imx-ssi.h" +#include "imx-audmux.h" + +#define FMT_SSI (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBM_CFM) + +static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xfffffffc, 0xfffffffc, + 4, 16); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, MC13783_CLK_CLIA, 26000000, 0); + if (ret) + return ret; + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x0, 0xfffffffc, 2, 16); + if (ret) + return ret; + + return 0; +} + +static struct snd_soc_ops imx_mc13783_hifi_ops = { + .hw_params = imx_mc13783_hifi_hw_params, +}; + +static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = { + { + .name = "MC13783", + .stream_name = "Sound", + .codec_dai_name = "mc13783-hifi", + .codec_name = "mc13783-codec", + .cpu_dai_name = "imx-ssi.0", + .platform_name = "imx-pcm-audio.0", + .ops = &imx_mc13783_hifi_ops, + .symmetric_rates = 1, + .dai_fmt = FMT_SSI, + }, +}; + +static const struct snd_soc_dapm_widget imx_mc13783_widget[] = { + SND_SOC_DAPM_MIC("Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route imx_mc13783_routes[] = { + {"Speaker", NULL, "LSP"}, + {"Headphone", NULL, "HSL"}, + {"Headphone", NULL, "HSR"}, + + {"MC1LIN", NULL, "MC1 Bias"}, + {"MC2IN", NULL, "MC2 Bias"}, + {"MC1 Bias", NULL, "Mic"}, + {"MC2 Bias", NULL, "Mic"}, +}; + +static struct snd_soc_card imx_mc13783 = { + .name = "imx_mc13783", + .dai_link = imx_mc13783_dai_mc13783, + .num_links = ARRAY_SIZE(imx_mc13783_dai_mc13783), + .dapm_widgets = imx_mc13783_widget, + .num_dapm_widgets = ARRAY_SIZE(imx_mc13783_widget), + .dapm_routes = imx_mc13783_routes, + .num_dapm_routes = ARRAY_SIZE(imx_mc13783_routes), +}; + +static int __devinit imx_mc13783_probe(struct platform_device *pdev) +{ + int ret; + + imx_mc13783.dev = &pdev->dev; + + ret = snd_soc_register_card(&imx_mc13783); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + return ret; + } + + imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4, + IMX_AUDMUX_V2_PTCR_SYN, + IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) | + IMX_AUDMUX_V2_PDCR_MODE(1) | + IMX_AUDMUX_V2_PDCR_INMMASK(0xfc)); + imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, + IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) | + IMX_AUDMUX_V2_PTCR_RFSDIR | + IMX_AUDMUX_V2_PTCR_RFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) | + IMX_AUDMUX_V2_PTCR_RCLKDIR | + IMX_AUDMUX_V2_PTCR_RCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4), + IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT4_SSI_PINS_4)); + + return ret; +} + +static int __devexit imx_mc13783_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&imx_mc13783); + + return 0; +} + +static struct platform_driver imx_mc13783_audio_driver = { + .driver = { + .name = "imx_mc13783", + .owner = THIS_MODULE, + }, + .probe = imx_mc13783_probe, + .remove = __devexit_p(imx_mc13783_remove) +}; + +module_platform_driver(imx_mc13783_audio_driver); + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch"); +MODULE_DESCRIPTION("imx with mc13783 codec ALSA SoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx_mc13783"); diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/fsl/imx-pcm-dma.c index 6b818de2fc03..f3c0a5ef35c8 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -109,7 +109,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream) dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL); - dma_data->peripheral_type = IMX_DMATYPE_SSI; + dma_data->peripheral_type = dma_params->shared_peripheral ? + IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI; dma_data->priority = DMA_PRIO_HIGH; dma_data->dma_request = dma_params->dma; diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 456b7d723d66..456b7d723d66 100644 --- a/sound/soc/imx/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/fsl/imx-pcm.c index 93dc360b1777..93dc360b1777 100644 --- a/sound/soc/imx/imx-pcm.c +++ b/sound/soc/fsl/imx-pcm.c diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index b5f5c3acf34d..83c0ed7d55c9 100644 --- a/sound/soc/imx/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -22,6 +22,7 @@ struct imx_pcm_dma_params { int dma; unsigned long dma_addr; int burstsize; + bool shared_peripheral; /* The peripheral is on SPBA bus */ }; int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c new file mode 100644 index 000000000000..3a729caeb8c8 --- /dev/null +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -0,0 +1,221 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_i2c.h> +#include <linux/clk.h> +#include <sound/soc.h> + +#include "../codecs/sgtl5000.h" +#include "imx-audmux.h" + +#define DAI_NAME_SIZE 32 + +struct imx_sgtl5000_data { + struct snd_soc_dai_link dai; + struct snd_soc_card card; + char codec_dai_name[DAI_NAME_SIZE]; + char platform_name[DAI_NAME_SIZE]; + struct clk *codec_clk; + unsigned int clk_frequency; +}; + +static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct imx_sgtl5000_data *data = container_of(rtd->card, + struct imx_sgtl5000_data, card); + struct device *dev = rtd->card->dev; + int ret; + + ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, + data->clk_frequency, SND_SOC_CLOCK_IN); + if (ret) { + dev_err(dev, "could not set codec driver clock params\n"); + return ret; + } + + return 0; +} + +static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Line Out Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static int __devinit imx_sgtl5000_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *ssi_np, *codec_np; + struct platform_device *ssi_pdev; + struct i2c_client *codec_dev; + struct imx_sgtl5000_data *data; + int int_port, ext_port; + int ret; + + ret = of_property_read_u32(np, "mux-int-port", &int_port); + if (ret) { + dev_err(&pdev->dev, "mux-int-port missing or invalid\n"); + return ret; + } + ret = of_property_read_u32(np, "mux-ext-port", &ext_port); + if (ret) { + dev_err(&pdev->dev, "mux-ext-port missing or invalid\n"); + return ret; + } + + /* + * The port numbering in the hardware manual starts at 1, while + * the audmux API expects it starts at 0. + */ + int_port--; + ext_port--; + ret = imx_audmux_v2_configure_port(int_port, + IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TCLKDIR, + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); + if (ret) { + dev_err(&pdev->dev, "audmux internal port setup failed\n"); + return ret; + } + imx_audmux_v2_configure_port(ext_port, + IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TCSEL(int_port), + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); + if (ret) { + dev_err(&pdev->dev, "audmux external port setup failed\n"); + return ret; + } + + ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); + codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); + if (!ssi_np || !codec_np) { + dev_err(&pdev->dev, "phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + ssi_pdev = of_find_device_by_node(ssi_np); + if (!ssi_pdev) { + dev_err(&pdev->dev, "failed to find SSI platform device\n"); + ret = -EINVAL; + goto fail; + } + codec_dev = of_find_i2c_device_by_node(codec_np); + if (!codec_dev) { + dev_err(&pdev->dev, "failed to find codec platform device\n"); + return -EINVAL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto fail; + } + + data->codec_clk = clk_get(&codec_dev->dev, NULL); + if (IS_ERR(data->codec_clk)) { + /* assuming clock enabled by default */ + data->codec_clk = NULL; + ret = of_property_read_u32(codec_np, "clock-frequency", + &data->clk_frequency); + if (ret) { + dev_err(&codec_dev->dev, + "clock-frequency missing or invalid\n"); + goto fail; + } + } else { + data->clk_frequency = clk_get_rate(data->codec_clk); + clk_prepare_enable(data->codec_clk); + } + + data->dai.name = "HiFi"; + data->dai.stream_name = "HiFi"; + data->dai.codec_dai_name = "sgtl5000"; + data->dai.codec_of_node = codec_np; + data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev); + data->dai.platform_name = "imx-pcm-audio"; + data->dai.init = &imx_sgtl5000_dai_init; + data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + data->card.dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto clk_fail; + ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); + if (ret) + goto clk_fail; + data->card.num_links = 1; + data->card.dai_link = &data->dai; + data->card.dapm_widgets = imx_sgtl5000_dapm_widgets; + data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets); + + ret = snd_soc_register_card(&data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + goto clk_fail; + } + + platform_set_drvdata(pdev, data); +clk_fail: + clk_put(data->codec_clk); +fail: + if (ssi_np) + of_node_put(ssi_np); + if (codec_np) + of_node_put(codec_np); + + return ret; +} + +static int __devexit imx_sgtl5000_remove(struct platform_device *pdev) +{ + struct imx_sgtl5000_data *data = platform_get_drvdata(pdev); + + if (data->codec_clk) { + clk_disable_unprepare(data->codec_clk); + clk_put(data->codec_clk); + } + snd_soc_unregister_card(&data->card); + + return 0; +} + +static const struct of_device_id imx_sgtl5000_dt_ids[] = { + { .compatible = "fsl,imx-audio-sgtl5000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_sgtl5000_dt_ids); + +static struct platform_driver imx_sgtl5000_driver = { + .driver = { + .name = "imx-sgtl5000", + .owner = THIS_MODULE, + .of_match_table = imx_sgtl5000_dt_ids, + }, + .probe = imx_sgtl5000_probe, + .remove = __devexit_p(imx_sgtl5000_remove), +}; +module_platform_driver(imx_sgtl5000_driver); + +MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); +MODULE_DESCRIPTION("Freescale i.MX SGTL5000 ASoC machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-sgtl5000"); diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 4f81ed456325..cf3ed0362c9c 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -28,7 +28,7 @@ * value. When we read the same register two times (and the register still * contains the same value) these status bits are not set. We work * around this by not polling these bits but only wait a fixed delay. - * + * */ #include <linux/clk.h> diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/fsl/imx-ssi.h index 5744e86ca878..5744e86ca878 100644 --- a/sound/soc/imx/imx-ssi.h +++ b/sound/soc/fsl/imx-ssi.h diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index afbabf427f27..60bcba1bc30e 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -14,18 +14,16 @@ #include <linux/interrupt.h> #include <linux/of_device.h> #include <linux/slab.h> -#include <linux/of_i2c.h> #include <sound/soc.h> #include <asm/fsl_guts.h> #include "fsl_dma.h" #include "fsl_ssi.h" +#include "fsl_utils.h" /* There's only one global utilities register */ static phys_addr_t guts_phys; -#define DAI_NAME_SIZE 32 - /** * mpc8610_hpcd_data: machine-specific ASoC device data * @@ -43,7 +41,6 @@ struct mpc8610_hpcd_data { unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ char codec_dai_name[DAI_NAME_SIZE]; - char codec_name[DAI_NAME_SIZE]; char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ }; @@ -58,9 +55,9 @@ static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card) { struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); - struct ccsr_guts_86xx __iomem *guts; + struct ccsr_guts __iomem *guts; - guts = ioremap(guts_phys, sizeof(struct ccsr_guts_86xx)); + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); if (!guts) { dev_err(card->dev, "could not map global utilities\n"); return -ENOMEM; @@ -142,9 +139,9 @@ static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card) { struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); - struct ccsr_guts_86xx __iomem *guts; + struct ccsr_guts __iomem *guts; - guts = ioremap(guts_phys, sizeof(struct ccsr_guts_86xx)); + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); if (!guts) { dev_err(card->dev, "could not map global utilities\n"); return -ENOMEM; @@ -181,141 +178,6 @@ static struct snd_soc_ops mpc8610_hpcd_ops = { }; /** - * get_node_by_phandle_name - get a node by its phandle name - * - * This function takes a node, the name of a property in that node, and a - * compatible string. Assuming the property is a phandle to another node, - * it returns that node, (optionally) if that node is compatible. - * - * If the property is not a phandle, or the node it points to is not compatible - * with the specific string, then NULL is returned. - */ -static struct device_node *get_node_by_phandle_name(struct device_node *np, - const char *name, - const char *compatible) -{ - const phandle *ph; - int len; - - ph = of_get_property(np, name, &len); - if (!ph || (len != sizeof(phandle))) - return NULL; - - np = of_find_node_by_phandle(*ph); - if (!np) - return NULL; - - if (compatible && !of_device_is_compatible(np, compatible)) { - of_node_put(np); - return NULL; - } - - return np; -} - -/** - * get_parent_cell_index -- return the cell-index of the parent of a node - * - * Return the value of the cell-index property of the parent of the given - * node. This is used for DMA channel nodes that need to know the DMA ID - * of the controller they are on. - */ -static int get_parent_cell_index(struct device_node *np) -{ - struct device_node *parent = of_get_parent(np); - const u32 *iprop; - - if (!parent) - return -1; - - iprop = of_get_property(parent, "cell-index", NULL); - of_node_put(parent); - - if (!iprop) - return -1; - - return be32_to_cpup(iprop); -} - -/** - * codec_node_dev_name - determine the dev_name for a codec node - * - * This function determines the dev_name for an I2C node. This is the name - * that would be returned by dev_name() if this device_node were part of a - * 'struct device' It's ugly and hackish, but it works. - * - * The dev_name for such devices include the bus number and I2C address. For - * example, "cs4270.0-004f". - */ -static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) -{ - const u32 *iprop; - int addr; - char temp[DAI_NAME_SIZE]; - struct i2c_client *i2c; - - of_modalias_node(np, temp, DAI_NAME_SIZE); - - iprop = of_get_property(np, "reg", NULL); - if (!iprop) - return -EINVAL; - - addr = be32_to_cpup(iprop); - - /* We need the adapter number */ - i2c = of_find_i2c_device_by_node(np); - if (!i2c) - return -ENODEV; - - snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr); - - return 0; -} - -static int get_dma_channel(struct device_node *ssi_np, - const char *name, - struct snd_soc_dai_link *dai, - unsigned int *dma_channel_id, - unsigned int *dma_id) -{ - struct resource res; - struct device_node *dma_channel_np; - const u32 *iprop; - int ret; - - dma_channel_np = get_node_by_phandle_name(ssi_np, name, - "fsl,ssi-dma-channel"); - if (!dma_channel_np) - return -EINVAL; - - /* Determine the dev_name for the device_node. This code mimics the - * behavior of of_device_make_bus_id(). We need this because ASoC uses - * the dev_name() of the device to match the platform (DMA) device with - * the CPU (SSI) device. It's all ugly and hackish, but it works (for - * now). - * - * dai->platform name should already point to an allocated buffer. - */ - ret = of_address_to_resource(dma_channel_np, 0, &res); - if (ret) - return ret; - snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", - (unsigned long long) res.start, dma_channel_np->name); - - iprop = of_get_property(dma_channel_np, "cell-index", NULL); - if (!iprop) { - of_node_put(dma_channel_np); - return -EINVAL; - } - - *dma_channel_id = be32_to_cpup(iprop); - *dma_id = get_parent_cell_index(dma_channel_np); - of_node_put(dma_channel_np); - - return 0; -} - -/** * mpc8610_hpcd_probe: platform probe function for the machine driver * * Although this is a machine driver, the SSI node is the "master" node with @@ -352,16 +214,8 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); machine_data->dai[0].ops = &mpc8610_hpcd_ops; - /* Determine the codec name, it will be used as the codec DAI name */ - ret = codec_node_dev_name(codec_np, machine_data->codec_name, - DAI_NAME_SIZE); - if (ret) { - dev_err(&pdev->dev, "invalid codec node %s\n", - codec_np->full_name); - ret = -EINVAL; - goto error; - } - machine_data->dai[0].codec_name = machine_data->codec_name; + /* ASoC core can match codec with device node */ + machine_data->dai[0].codec_of_node = codec_np; /* The DAI name from the codec (snd_soc_dai_driver.name) */ machine_data->dai[0].codec_dai_name = "cs4270-hifi"; @@ -458,9 +312,10 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) /* Find the playback DMA channel to use. */ machine_data->dai[0].platform_name = machine_data->platform_name[0]; - ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0], - &machine_data->dma_channel_id[0], - &machine_data->dma_id[0]); + ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", + &machine_data->dai[0], + &machine_data->dma_channel_id[0], + &machine_data->dma_id[0]); if (ret) { dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); goto error; @@ -468,9 +323,10 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) /* Find the capture DMA channel to use. */ machine_data->dai[1].platform_name = machine_data->platform_name[1]; - ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1], - &machine_data->dma_channel_id[1], - &machine_data->dma_id[1]); + ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", + &machine_data->dai[1], + &machine_data->dma_channel_id[1], + &machine_data->dma_id[1]); if (ret) { dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); goto error; diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c index f6d04ad4bb39..f6d04ad4bb39 100644 --- a/sound/soc/imx/mx27vis-aic32x4.c +++ b/sound/soc/fsl/mx27vis-aic32x4.c diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 46623405a2ce..50adf4032bcc 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -14,12 +14,12 @@ #include <linux/interrupt.h> #include <linux/of_device.h> #include <linux/slab.h> -#include <linux/of_i2c.h> #include <sound/soc.h> #include <asm/fsl_guts.h> #include "fsl_dma.h" #include "fsl_ssi.h" +#include "fsl_utils.h" /* P1022-specific PMUXCR and DMUXCR bit definitions */ @@ -46,7 +46,7 @@ * ch: The channel on the DMA controller (0, 1, 2, or 3) * device: The device to set as the target (CCSR_GUTS_DMUXCR_xxx) */ -static inline void guts_set_dmuxcr(struct ccsr_guts_85xx __iomem *guts, +static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts, unsigned int co, unsigned int ch, unsigned int device) { unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch)); @@ -57,8 +57,6 @@ static inline void guts_set_dmuxcr(struct ccsr_guts_85xx __iomem *guts, /* There's only one global utilities register */ static phys_addr_t guts_phys; -#define DAI_NAME_SIZE 32 - /** * machine_data: machine-specific ASoC device data * @@ -75,7 +73,6 @@ struct machine_data { unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ - char codec_name[DAI_NAME_SIZE]; char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ }; @@ -90,9 +87,9 @@ static int p1022_ds_machine_probe(struct snd_soc_card *card) { struct machine_data *mdata = container_of(card, struct machine_data, card); - struct ccsr_guts_85xx __iomem *guts; + struct ccsr_guts __iomem *guts; - guts = ioremap(guts_phys, sizeof(struct ccsr_guts_85xx)); + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); if (!guts) { dev_err(card->dev, "could not map global utilities\n"); return -ENOMEM; @@ -164,9 +161,9 @@ static int p1022_ds_machine_remove(struct snd_soc_card *card) { struct machine_data *mdata = container_of(card, struct machine_data, card); - struct ccsr_guts_85xx __iomem *guts; + struct ccsr_guts __iomem *guts; - guts = ioremap(guts_phys, sizeof(struct ccsr_guts_85xx)); + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); if (!guts) { dev_err(card->dev, "could not map global utilities\n"); return -ENOMEM; @@ -191,136 +188,6 @@ static struct snd_soc_ops p1022_ds_ops = { }; /** - * get_node_by_phandle_name - get a node by its phandle name - * - * This function takes a node, the name of a property in that node, and a - * compatible string. Assuming the property is a phandle to another node, - * it returns that node, (optionally) if that node is compatible. - * - * If the property is not a phandle, or the node it points to is not compatible - * with the specific string, then NULL is returned. - */ -static struct device_node *get_node_by_phandle_name(struct device_node *np, - const char *name, const char *compatible) -{ - np = of_parse_phandle(np, name, 0); - if (!np) - return NULL; - - if (!of_device_is_compatible(np, compatible)) { - of_node_put(np); - return NULL; - } - - return np; -} - -/** - * get_parent_cell_index -- return the cell-index of the parent of a node - * - * Return the value of the cell-index property of the parent of the given - * node. This is used for DMA channel nodes that need to know the DMA ID - * of the controller they are on. - */ -static int get_parent_cell_index(struct device_node *np) -{ - struct device_node *parent = of_get_parent(np); - const u32 *iprop; - int ret = -1; - - if (!parent) - return -1; - - iprop = of_get_property(parent, "cell-index", NULL); - if (iprop) - ret = be32_to_cpup(iprop); - - of_node_put(parent); - - return ret; -} - -/** - * codec_node_dev_name - determine the dev_name for a codec node - * - * This function determines the dev_name for an I2C node. This is the name - * that would be returned by dev_name() if this device_node were part of a - * 'struct device' It's ugly and hackish, but it works. - * - * The dev_name for such devices include the bus number and I2C address. For - * example, "cs4270-codec.0-004f". - */ -static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) -{ - const u32 *iprop; - int addr; - char temp[DAI_NAME_SIZE]; - struct i2c_client *i2c; - - of_modalias_node(np, temp, DAI_NAME_SIZE); - - iprop = of_get_property(np, "reg", NULL); - if (!iprop) - return -EINVAL; - - addr = be32_to_cpup(iprop); - - /* We need the adapter number */ - i2c = of_find_i2c_device_by_node(np); - if (!i2c) - return -ENODEV; - - snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr); - - return 0; -} - -static int get_dma_channel(struct device_node *ssi_np, - const char *name, - struct snd_soc_dai_link *dai, - unsigned int *dma_channel_id, - unsigned int *dma_id) -{ - struct resource res; - struct device_node *dma_channel_np; - const u32 *iprop; - int ret; - - dma_channel_np = get_node_by_phandle_name(ssi_np, name, - "fsl,ssi-dma-channel"); - if (!dma_channel_np) - return -EINVAL; - - /* Determine the dev_name for the device_node. This code mimics the - * behavior of of_device_make_bus_id(). We need this because ASoC uses - * the dev_name() of the device to match the platform (DMA) device with - * the CPU (SSI) device. It's all ugly and hackish, but it works (for - * now). - * - * dai->platform name should already point to an allocated buffer. - */ - ret = of_address_to_resource(dma_channel_np, 0, &res); - if (ret) { - of_node_put(dma_channel_np); - return ret; - } - snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", - (unsigned long long) res.start, dma_channel_np->name); - - iprop = of_get_property(dma_channel_np, "cell-index", NULL); - if (!iprop) { - of_node_put(dma_channel_np); - return -EINVAL; - } - - *dma_channel_id = be32_to_cpup(iprop); - *dma_id = get_parent_cell_index(dma_channel_np); - of_node_put(dma_channel_np); - - return 0; -} - -/** * p1022_ds_probe: platform probe function for the machine driver * * Although this is a machine driver, the SSI node is the "master" node with @@ -357,15 +224,8 @@ static int p1022_ds_probe(struct platform_device *pdev) mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); mdata->dai[0].ops = &p1022_ds_ops; - /* Determine the codec name, it will be used as the codec DAI name */ - ret = codec_node_dev_name(codec_np, mdata->codec_name, DAI_NAME_SIZE); - if (ret) { - dev_err(&pdev->dev, "invalid codec node %s\n", - codec_np->full_name); - ret = -EINVAL; - goto error; - } - mdata->dai[0].codec_name = mdata->codec_name; + /* ASoC core can match codec with device node */ + mdata->dai[0].codec_of_node = codec_np; /* We register two DAIs per SSI, one for playback and the other for * capture. We support codecs that have separate DAIs for both playback @@ -462,9 +322,9 @@ static int p1022_ds_probe(struct platform_device *pdev) /* Find the playback DMA channel to use. */ mdata->dai[0].platform_name = mdata->platform_name[0]; - ret = get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0], - &mdata->dma_channel_id[0], - &mdata->dma_id[0]); + ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0], + &mdata->dma_channel_id[0], + &mdata->dma_id[0]); if (ret) { dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); goto error; @@ -472,9 +332,9 @@ static int p1022_ds_probe(struct platform_device *pdev) /* Find the capture DMA channel to use. */ mdata->dai[1].platform_name = mdata->platform_name[1]; - ret = get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1], - &mdata->dma_channel_id[1], - &mdata->dma_id[1]); + ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1], + &mdata->dma_channel_id[1], + &mdata->dma_id[1]); if (ret) { dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); goto error; diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c index f8da6dd115ed..f8da6dd115ed 100644 --- a/sound/soc/imx/phycore-ac97.c +++ b/sound/soc/fsl/phycore-ac97.c diff --git a/sound/soc/imx/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c index fe54a69073e5..fe54a69073e5 100644 --- a/sound/soc/imx/wm1133-ev1.c +++ b/sound/soc/fsl/wm1133-ev1.c diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig new file mode 100644 index 000000000000..610f61251640 --- /dev/null +++ b/sound/soc/generic/Kconfig @@ -0,0 +1,4 @@ +config SND_SIMPLE_CARD + tristate "ASoC Simple sound card support" + help + This option enables generic simple sound card support diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile new file mode 100644 index 000000000000..9c3b246792bf --- /dev/null +++ b/sound/soc/generic/Makefile @@ -0,0 +1,3 @@ +snd-soc-simple-card-objs := simple-card.o + +obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c new file mode 100644 index 000000000000..b4b4cab30232 --- /dev/null +++ b/sound/soc/generic/simple-card.c @@ -0,0 +1,114 @@ +/* + * ASoC simple sound card support + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <sound/simple_card.h> + +#define asoc_simple_get_card_info(p) \ + container_of(p->dai_link, struct asoc_simple_card_info, snd_link) + +static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct asoc_simple_card_info *cinfo = asoc_simple_get_card_info(rtd); + struct asoc_simple_dai_init_info *iinfo = cinfo->init; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + unsigned int cpu_daifmt = iinfo->fmt | iinfo->cpu_daifmt; + unsigned int codec_daifmt = iinfo->fmt | iinfo->codec_daifmt; + int ret; + + if (codec_daifmt) { + ret = snd_soc_dai_set_fmt(codec, codec_daifmt); + if (ret < 0) + return ret; + } + + if (iinfo->sysclk) { + ret = snd_soc_dai_set_sysclk(codec, 0, iinfo->sysclk, 0); + if (ret < 0) + return ret; + } + + if (cpu_daifmt) { + ret = snd_soc_dai_set_fmt(cpu, cpu_daifmt); + if (ret < 0) + return ret; + } + + return 0; +} + +static int asoc_simple_card_probe(struct platform_device *pdev) +{ + struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; + + if (!cinfo) { + dev_err(&pdev->dev, "no info for asoc-simple-card\n"); + return -EINVAL; + } + + if (!cinfo->name || + !cinfo->card || + !cinfo->cpu_dai || + !cinfo->codec || + !cinfo->platform || + !cinfo->codec_dai) { + dev_err(&pdev->dev, "insufficient asoc_simple_card_info settings\n"); + return -EINVAL; + } + + /* + * init snd_soc_dai_link + */ + cinfo->snd_link.name = cinfo->name; + cinfo->snd_link.stream_name = cinfo->name; + cinfo->snd_link.cpu_dai_name = cinfo->cpu_dai; + cinfo->snd_link.platform_name = cinfo->platform; + cinfo->snd_link.codec_name = cinfo->codec; + cinfo->snd_link.codec_dai_name = cinfo->codec_dai; + + /* enable snd_link.init if cinfo has settings */ + if (cinfo->init) + cinfo->snd_link.init = asoc_simple_card_dai_init; + + /* + * init snd_soc_card + */ + cinfo->snd_card.name = cinfo->card; + cinfo->snd_card.owner = THIS_MODULE; + cinfo->snd_card.dai_link = &cinfo->snd_link; + cinfo->snd_card.num_links = 1; + cinfo->snd_card.dev = &pdev->dev; + + return snd_soc_register_card(&cinfo->snd_card); +} + +static int asoc_simple_card_remove(struct platform_device *pdev) +{ + struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; + + return snd_soc_unregister_card(&cinfo->snd_card); +} + +static struct platform_driver asoc_simple_card = { + .driver = { + .name = "asoc-simple-card", + }, + .probe = asoc_simple_card_probe, + .remove = asoc_simple_card_remove, +}; + +module_platform_driver(asoc_simple_card); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ASoC Simple Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig deleted file mode 100644 index 810acaa09009..000000000000 --- a/sound/soc/imx/Kconfig +++ /dev/null @@ -1,79 +0,0 @@ -menuconfig SND_IMX_SOC - tristate "SoC Audio for Freescale i.MX CPUs" - depends on ARCH_MXC - help - Say Y or M if you want to add support for codecs attached to - the i.MX SSI interface. - - -if SND_IMX_SOC - -config SND_SOC_IMX_SSI - tristate - -config SND_SOC_IMX_PCM - tristate - -config SND_MXC_SOC_FIQ - tristate - select FIQ - select SND_SOC_IMX_PCM - -config SND_MXC_SOC_MX2 - select SND_SOC_DMAENGINE_PCM - tristate - select SND_SOC_IMX_PCM - -config SND_SOC_IMX_AUDMUX - tristate - -config SND_MXC_SOC_WM1133_EV1 - tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted" - depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL - select SND_SOC_WM8350 - select SND_MXC_SOC_FIQ - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Enable support for audio on the i.MX31ADS with the WM1133-EV1 - PMIC board with WM8835x fitted. - -config SND_SOC_MX27VIS_AIC32X4 - tristate "SoC audio support for Visstrim M10 boards" - depends on MACH_IMX27_VISSTRIM_M10 && I2C - select SND_SOC_TLV320AIC32X4 - select SND_MXC_SOC_MX2 - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Say Y if you want to add support for SoC audio on Visstrim SM10 - board with TLV320AIC32X4 codec. - -config SND_SOC_PHYCORE_AC97 - tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" - depends on MACH_PCM043 || MACH_PCA100 - select SND_SOC_AC97_BUS - select SND_SOC_WM9712 - select SND_MXC_SOC_FIQ - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Say Y if you want to add support for SoC audio on Phytec phyCORE - and phyCARD boards in AC97 mode - -config SND_SOC_EUKREA_TLV320 - tristate "Eukrea TLV320" - depends on MACH_EUKREA_MBIMX27_BASEBOARD \ - || MACH_EUKREA_MBIMXSD25_BASEBOARD \ - || MACH_EUKREA_MBIMXSD35_BASEBOARD \ - || MACH_EUKREA_MBIMXSD51_BASEBOARD - depends on I2C - select SND_SOC_TLV320AIC23 - select SND_MXC_SOC_FIQ - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Enable I2S based access to the TLV320AIC23B codec attached - to the SSI interface - -endif # SND_IMX_SOC diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile deleted file mode 100644 index f5db3e92d0d1..000000000000 --- a/sound/soc/imx/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# i.MX Platform Support -snd-soc-imx-ssi-objs := imx-ssi.o -snd-soc-imx-audmux-objs := imx-audmux.o - -obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o -obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o - -obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o -snd-soc-imx-pcm-y := imx-pcm.o -snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_FIQ) += imx-pcm-fiq.o -snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_MX2) += imx-pcm-dma-mx2.o - -# i.MX Machine Support -snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o -snd-soc-phycore-ac97-objs := phycore-ac97.o -snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o -snd-soc-wm1133-ev1-objs := wm1133-ev1.o - -obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o -obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o -obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o -obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index a5af7c42e62b..41349670adab 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -346,7 +346,7 @@ static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s) /* Playback */ dma_config = &i2s->pcm_config_playback.dma_config; - dma_config->src_width = JZ4740_DMA_WIDTH_32BIT, + dma_config->src_width = JZ4740_DMA_WIDTH_32BIT; dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE; dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT; dma_config->flags = JZ4740_DMA_SRC_AUTOINC; @@ -355,7 +355,7 @@ static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s) /* Capture */ dma_config = &i2s->pcm_config_capture.dma_config; - dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT, + dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT; dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE; dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE; dma_config->flags = JZ4740_DMA_DST_AUTOINC; diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index e373fbbc97a0..373dec90579f 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -220,28 +220,16 @@ static struct snd_soc_platform_driver mxs_soc_platform = { .pcm_free = mxs_pcm_free, }; -static int __devinit mxs_soc_platform_probe(struct platform_device *pdev) +int __devinit mxs_pcm_platform_register(struct device *dev) { - return snd_soc_register_platform(&pdev->dev, &mxs_soc_platform); + return snd_soc_register_platform(dev, &mxs_soc_platform); } +EXPORT_SYMBOL_GPL(mxs_pcm_platform_register); -static int __devexit mxs_soc_platform_remove(struct platform_device *pdev) +void __devexit mxs_pcm_platform_unregister(struct device *dev) { - snd_soc_unregister_platform(&pdev->dev); - - return 0; + snd_soc_unregister_platform(dev); } - -static struct platform_driver mxs_pcm_driver = { - .driver = { - .name = "mxs-pcm-audio", - .owner = THIS_MODULE, - }, - .probe = mxs_soc_platform_probe, - .remove = __devexit_p(mxs_soc_platform_remove), -}; - -module_platform_driver(mxs_pcm_driver); +EXPORT_SYMBOL_GPL(mxs_pcm_platform_unregister); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:mxs-pcm-audio"); diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h index 5f01a9124b3d..35ba2ca42384 100644 --- a/sound/soc/mxs/mxs-pcm.h +++ b/sound/soc/mxs/mxs-pcm.h @@ -24,4 +24,7 @@ struct mxs_pcm_dma_params { int chan_num; }; +int mxs_pcm_platform_register(struct device *dev); +void mxs_pcm_platform_unregister(struct device *dev); + #endif diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 53f4fd8feced..064274880673 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -18,6 +18,8 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/dma-mapping.h> @@ -620,36 +622,56 @@ static irqreturn_t mxs_saif_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int mxs_saif_probe(struct platform_device *pdev) +static int __devinit mxs_saif_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct resource *iores, *dmares; struct mxs_saif *saif; struct mxs_saif_platform_data *pdata; int ret = 0; - if (pdev->id >= ARRAY_SIZE(mxs_saif)) + + if (!np && pdev->id >= ARRAY_SIZE(mxs_saif)) return -EINVAL; saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL); if (!saif) return -ENOMEM; - mxs_saif[pdev->id] = saif; - saif->id = pdev->id; - - pdata = pdev->dev.platform_data; - if (pdata && !pdata->master_mode) { - saif->master_id = pdata->master_id; - if (saif->master_id < 0 || - saif->master_id >= ARRAY_SIZE(mxs_saif) || - saif->master_id == saif->id) { - dev_err(&pdev->dev, "get wrong master id\n"); - return -EINVAL; + if (np) { + struct device_node *master; + saif->id = of_alias_get_id(np, "saif"); + if (saif->id < 0) + return saif->id; + /* + * If there is no "fsl,saif-master" phandle, it's a saif + * master. Otherwise, it's a slave and its phandle points + * to the master. + */ + master = of_parse_phandle(np, "fsl,saif-master", 0); + if (!master) { + saif->master_id = saif->id; + } else { + saif->master_id = of_alias_get_id(master, "saif"); + if (saif->master_id < 0) + return saif->master_id; } } else { - saif->master_id = saif->id; + saif->id = pdev->id; + pdata = pdev->dev.platform_data; + if (pdata && !pdata->master_mode) + saif->master_id = pdata->master_id; + else + saif->master_id = saif->id; + } + + if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) { + dev_err(&pdev->dev, "get wrong master id\n"); + return -EINVAL; } + mxs_saif[saif->id] = saif; + saif->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(saif->clk)) { ret = PTR_ERR(saif->clk); @@ -669,12 +691,19 @@ static int mxs_saif_probe(struct platform_device *pdev) dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!dmares) { - ret = -ENODEV; - dev_err(&pdev->dev, "failed to get dma resource: %d\n", - ret); - goto failed_get_resource; + /* + * TODO: This is a temporary solution and should be changed + * to use generic DMA binding later when the helplers get in. + */ + ret = of_property_read_u32(np, "fsl,saif-dma-channel", + &saif->dma_param.chan_num); + if (ret) { + dev_err(&pdev->dev, "failed to get dma channel\n"); + goto failed_get_resource; + } + } else { + saif->dma_param.chan_num = dmares->start; } - saif->dma_param.chan_num = dmares->start; saif->irq = platform_get_irq(pdev, 0); if (saif->irq < 0) { @@ -708,24 +737,14 @@ static int mxs_saif_probe(struct platform_device *pdev) goto failed_get_resource; } - saif->soc_platform_pdev = platform_device_alloc( - "mxs-pcm-audio", pdev->id); - if (!saif->soc_platform_pdev) { - ret = -ENOMEM; - goto failed_pdev_alloc; - } - - platform_set_drvdata(saif->soc_platform_pdev, saif); - ret = platform_device_add(saif->soc_platform_pdev); + ret = mxs_pcm_platform_register(&pdev->dev); if (ret) { - dev_err(&pdev->dev, "failed to add soc platform device\n"); - goto failed_pdev_add; + dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + goto failed_pdev_alloc; } return 0; -failed_pdev_add: - platform_device_put(saif->soc_platform_pdev); failed_pdev_alloc: snd_soc_unregister_dai(&pdev->dev); failed_get_resource: @@ -738,13 +757,19 @@ static int __devexit mxs_saif_remove(struct platform_device *pdev) { struct mxs_saif *saif = platform_get_drvdata(pdev); - platform_device_unregister(saif->soc_platform_pdev); + mxs_pcm_platform_unregister(&pdev->dev); snd_soc_unregister_dai(&pdev->dev); clk_put(saif->clk); return 0; } +static const struct of_device_id mxs_saif_dt_ids[] = { + { .compatible = "fsl,imx28-saif", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_saif_dt_ids); + static struct platform_driver mxs_saif_driver = { .probe = mxs_saif_probe, .remove = __devexit_p(mxs_saif_remove), @@ -752,6 +777,7 @@ static struct platform_driver mxs_saif_driver = { .driver = { .name = "mxs-saif", .owner = THIS_MODULE, + .of_match_table = mxs_saif_dt_ids, }, }; diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h index 12c91e4eb941..3cb342e5bc90 100644 --- a/sound/soc/mxs/mxs-saif.h +++ b/sound/soc/mxs/mxs-saif.h @@ -123,7 +123,6 @@ struct mxs_saif { unsigned int cur_rate; unsigned int ongoing; - struct platform_device *soc_platform_pdev; u32 fifo_underrun; u32 fifo_overrun; }; diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 60f052b7cf22..3e6e8764b2e6 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -18,6 +18,8 @@ #include <linux/module.h> #include <linux/device.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -90,7 +92,7 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { .codec_dai_name = "sgtl5000", .codec_name = "sgtl5000.0-000a", .cpu_dai_name = "mxs-saif.0", - .platform_name = "mxs-pcm-audio.0", + .platform_name = "mxs-saif.0", .ops = &mxs_sgtl5000_hifi_ops, }, { .name = "HiFi Rx", @@ -98,7 +100,7 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { .codec_dai_name = "sgtl5000", .codec_name = "sgtl5000.0-000a", .cpu_dai_name = "mxs-saif.1", - .platform_name = "mxs-pcm-audio.1", + .platform_name = "mxs-saif.1", .ops = &mxs_sgtl5000_hifi_ops, }, }; @@ -110,11 +112,48 @@ static struct snd_soc_card mxs_sgtl5000 = { .num_links = ARRAY_SIZE(mxs_sgtl5000_dai), }; +static int __devinit mxs_sgtl5000_probe_dt(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *saif_np[2], *codec_np; + int i, ret = 0; + + if (!np) + return 1; /* no device tree */ + + saif_np[0] = of_parse_phandle(np, "saif-controllers", 0); + saif_np[1] = of_parse_phandle(np, "saif-controllers", 1); + codec_np = of_parse_phandle(np, "audio-codec", 0); + if (!saif_np[0] || !saif_np[1] || !codec_np) { + dev_err(&pdev->dev, "phandle missing or invalid\n"); + return -EINVAL; + } + + for (i = 0; i < 2; i++) { + mxs_sgtl5000_dai[i].codec_name = NULL; + mxs_sgtl5000_dai[i].codec_of_node = codec_np; + mxs_sgtl5000_dai[i].cpu_dai_name = NULL; + mxs_sgtl5000_dai[i].cpu_dai_of_node = saif_np[i]; + mxs_sgtl5000_dai[i].platform_name = NULL; + mxs_sgtl5000_dai[i].platform_of_node = saif_np[i]; + } + + of_node_put(codec_np); + of_node_put(saif_np[0]); + of_node_put(saif_np[1]); + + return ret; +} + static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mxs_sgtl5000; int ret; + ret = mxs_sgtl5000_probe_dt(pdev); + if (ret < 0) + return ret; + /* * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w). * The Sgtl5000 sysclk is derived from saif0 mclk and it's range @@ -148,10 +187,17 @@ static int __devexit mxs_sgtl5000_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxs_sgtl5000_dt_ids[] = { + { .compatible = "fsl,mxs-audio-sgtl5000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids); + static struct platform_driver mxs_sgtl5000_audio_driver = { .driver = { .name = "mxs-sgtl5000", .owner = THIS_MODULE, + .of_match_table = mxs_sgtl5000_dt_ids, }, .probe = mxs_sgtl5000_probe, .remove = __devexit_p(mxs_sgtl5000_remove), diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index e00dd0b1139c..deafbfaacdbf 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -97,7 +97,7 @@ config SND_OMAP_SOC_SDP3430 config SND_OMAP_SOC_OMAP_ABE_TWL6040 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL4030_CORE && SND_OMAP_SOC && ARCH_OMAP4 + depends on TWL6040_CORE && SND_OMAP_SOC && ARCH_OMAP4 select SND_OMAP_SOC_DMIC select SND_OMAP_SOC_MCPDM select SND_SOC_TWL6040 diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index fd04ce139031..1c2aa7fab3fd 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -85,14 +85,12 @@ struct pxa2xx_pcm_dma_data { char name[20]; }; -static struct pxa2xx_pcm_dma_params * -pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out) +static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4, + int out, struct pxa2xx_pcm_dma_params *dma_data) { struct pxa2xx_pcm_dma_data *dma; - dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL); - if (dma == NULL) - return NULL; + dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params); snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id, width4 ? "32-bit" : "16-bit", out ? "out" : "in"); @@ -103,8 +101,6 @@ pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out) (DCMD_INCTRGADDR | DCMD_FLOWSRC)) | (width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16; dma->params.dev_addr = ssp->phys_base + SSDR; - - return &dma->params; } static int pxa_ssp_startup(struct snd_pcm_substream *substream, @@ -112,6 +108,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, { struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; + struct pxa2xx_pcm_dma_data *dma; int ret = 0; if (!cpu_dai->active) { @@ -119,8 +116,10 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, pxa_ssp_disable(ssp); } - kfree(snd_soc_dai_get_dma_data(cpu_dai, substream)); - snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); + dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL); + if (!dma) + return -ENOMEM; + snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params); return ret; } @@ -573,18 +572,13 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); - /* generate correct DMA params */ - kfree(dma_data); - /* Network mode with one active slot (ttsa == 1) can be used * to force 16-bit frame width on the wire (for S16_LE), even * with two channels. Use 16-bit DMA transfers for this case. */ - dma_data = pxa_ssp_get_dma_params(ssp, - ((chn == 2) && (ttsa != 1)) || (width == 32), - substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - - snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); + pxa_ssp_set_dma_params(ssp, + ((chn == 2) && (ttsa != 1)) || (width == 32), + substream->stream == SNDRV_PCM_STREAM_PLAYBACK, dma_data); /* we can only change the settings if the port is not in use */ if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index d08583790d23..3075a426124c 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -166,7 +166,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, struct pxa2xx_pcm_dma_params *dma_data; BUG_ON(IS_ERR(clk_i2s)); - clk_enable(clk_i2s); + clk_prepare_enable(clk_i2s); clk_ena = 1; pxa_i2s_wait(); @@ -259,7 +259,7 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream, SACR0 &= ~SACR0_ENB; pxa_i2s_wait(); if (clk_ena) { - clk_disable(clk_i2s); + clk_disable_unprepare(clk_i2s); clk_ena = 0; } } diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index e7416851bf7d..c82c646b8a08 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -23,10 +23,10 @@ static int littlemill_set_bias_level(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; int ret; - if (dapm->dev != codec_dai->dev) + if (dapm->dev != aif1_dai->dev) return 0; switch (level) { @@ -36,7 +36,7 @@ static int littlemill_set_bias_level(struct snd_soc_card *card, * then do so now, otherwise these are noops. */ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { - ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, + ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK2, 32768, sample_rate * 512); if (ret < 0) { @@ -44,7 +44,7 @@ static int littlemill_set_bias_level(struct snd_soc_card *card, return ret; } - ret = snd_soc_dai_set_sysclk(codec_dai, + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, sample_rate * 512, SND_SOC_CLOCK_IN); @@ -66,25 +66,25 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; int ret; - if (dapm->dev != codec_dai->dev) + if (dapm->dev != aif1_dai->dev) return 0; switch (level) { case SND_SOC_BIAS_STANDBY: - ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK2, + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, 32768, SND_SOC_CLOCK_IN); if (ret < 0) { - pr_err("Failed to switch away from FLL: %d\n", ret); + pr_err("Failed to switch away from FLL1: %d\n", ret); return ret; } - ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, + ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); if (ret < 0) { - pr_err("Failed to stop FLL: %d\n", ret); + pr_err("Failed to stop FLL1: %d\n", ret); return ret; } break; @@ -131,6 +131,14 @@ static struct snd_soc_ops littlemill_ops = { .hw_params = littlemill_hw_params, }; +static const struct snd_soc_pcm_stream baseband_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 2, + .channels_max = 2, +}; + static struct snd_soc_dai_link littlemill_dai[] = { { .name = "CPU", @@ -143,13 +151,75 @@ static struct snd_soc_dai_link littlemill_dai[] = { | SND_SOC_DAIFMT_CBM_CFM, .ops = &littlemill_ops, }, + { + .name = "Baseband", + .stream_name = "Baseband", + .cpu_dai_name = "wm8994-aif2", + .codec_dai_name = "wm1250-ev1", + .codec_name = "wm1250-ev1.1-0027", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .ignore_suspend = 1, + .params = &baseband_params, + }, }; +static int bbclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, + WM8994_FLL_SRC_BCLK, 64 * 8000, + 8000 * 256); + if (ret < 0) { + pr_err("Failed to start FLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_FLL2, + 8000 * 256, + SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to set SYSCLK: %d\n", ret); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMD: + ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2, + 32768, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to switch away from FLL2: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, + 0, 0, 0); + if (ret < 0) { + pr_err("Failed to stop FLL2: %d\n", ret); + return ret; + } + break; + default: + return -EINVAL; + } + + return 0; +} + static struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), SND_SOC_DAPM_MIC("DMIC", NULL), + + SND_SOC_DAPM_SUPPLY_S("Baseband Clock", -1, SND_SOC_NOPM, 0, 0, + bbclk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; static struct snd_soc_dapm_route audio_paths[] = { @@ -162,6 +232,8 @@ static struct snd_soc_dapm_route audio_paths[] = { { "DMIC", NULL, "MICBIAS2" }, /* Default for DMICBIAS jumper */ { "DMIC1DAT", NULL, "DMIC" }, { "DMIC2DAT", NULL, "DMIC" }, + + { "AIF2CLK", NULL, "Baseband Clock" }, }; static struct snd_soc_jack littlemill_headset; @@ -169,10 +241,16 @@ static struct snd_soc_jack littlemill_headset; static int littlemill_late_probe(struct snd_soc_card *card) { struct snd_soc_codec *codec = card->rtd[0].codec; - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; + struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai; int ret; - ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK2, + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, + 32768, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2, 32768, SND_SOC_CLOCK_IN); if (ret < 0) return ret; diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 4adff934f771..6abf341c4a2a 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -21,33 +21,6 @@ #define MCLK1_RATE (44100 * 512) #define CLKOUT_RATE (44100 * 256) -static int lowland_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret; - - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops lowland_ops = { - .hw_params = lowland_hw_params, -}; - static struct snd_soc_jack lowland_headset; /* Headset jack detection DAPM pins */ @@ -101,6 +74,25 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_dapm_nc_pin(&codec->dapm, "LINEOUT"); + + /* At any time the WM9081 is active it will have this clock */ + return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0, + CLKOUT_RATE, 0); +} + +static const struct snd_soc_pcm_stream sub_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, +}; + static struct snd_soc_dai_link lowland_dai[] = { { .name = "CPU", @@ -109,7 +101,8 @@ static struct snd_soc_dai_link lowland_dai[] = { .codec_dai_name = "wm5100-aif1", .platform_name = "samsung-audio", .codec_name = "wm5100.1-001a", - .ops = &lowland_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .init = lowland_wm5100_init, }, { @@ -118,24 +111,20 @@ static struct snd_soc_dai_link lowland_dai[] = { .cpu_dai_name = "wm5100-aif2", .codec_dai_name = "wm1250-ev1", .codec_name = "wm1250-ev1.1-0027", - .ops = &lowland_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ignore_suspend = 1, }, -}; - -static int lowland_wm9081_init(struct snd_soc_dapm_context *dapm) -{ - snd_soc_dapm_nc_pin(dapm, "LINEOUT"); - - /* At any time the WM9081 is active it will have this clock */ - return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, 0, - CLKOUT_RATE, 0); -} - -static struct snd_soc_aux_dev lowland_aux_dev[] = { { - .name = "wm9081", + .name = "Sub Speaker", + .stream_name = "Sub Speaker", + .cpu_dai_name = "wm5100-aif3", + .codec_dai_name = "wm9081-hifi", .codec_name = "wm9081.1-006c", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + .ignore_suspend = 1, + .params = &sub_params, .init = lowland_wm9081_init, }, }; @@ -180,8 +169,6 @@ static struct snd_soc_card lowland = { .owner = THIS_MODULE, .dai_link = lowland_dai, .num_links = ARRAY_SIZE(lowland_dai), - .aux_dev = lowland_aux_dev, - .num_aux_devs = ARRAY_SIZE(lowland_aux_dev), .codec_conf = lowland_codec_conf, .num_configs = ARRAY_SIZE(lowland_codec_conf), diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index f9ab7707a3e4..a4a9fc7e8c76 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -92,33 +92,6 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card, return 0; } -static int speyside_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret; - - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops speyside_ops = { - .hw_params = speyside_hw_params, -}; - static struct snd_soc_jack speyside_headset; /* Headset jack detection DAPM pins */ @@ -208,7 +181,8 @@ static struct snd_soc_dai_link speyside_dai[] = { .platform_name = "samsung-audio", .codec_name = "wm8996.1-001a", .init = speyside_wm8996_init, - .ops = &speyside_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, }, { .name = "Baseband", @@ -216,7 +190,8 @@ static struct snd_soc_dai_link speyside_dai[] = { .cpu_dai_name = "wm8996-aif2", .codec_dai_name = "wm1250-ev1", .codec_name = "wm1250-ev1.1-0027", - .ops = &speyside_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, .ignore_suspend = 1, }, }; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index d8e06a607a22..6bcb1164d599 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -22,6 +22,7 @@ config SND_SOC_SH4_SSI config SND_SOC_SH4_FSI tristate "SH4 FSI support" + select SND_SIMPLE_CARD help This option enables FSI sound support @@ -46,29 +47,6 @@ config SND_SH7760_AC97 This option enables generic sound support for the first AC97 unit of the SH7760. -config SND_FSI_AK4642 - tristate "FSI-AK4642 sound support" - depends on SND_SOC_SH4_FSI && I2C - select SND_SOC_AK4642 - help - This option enables generic sound support for the - FSI - AK4642 unit - -config SND_FSI_DA7210 - tristate "FSI-DA7210 sound support" - depends on SND_SOC_SH4_FSI && I2C - select SND_SOC_DA7210 - help - This option enables generic sound support for the - FSI - DA7210 unit - -config SND_FSI_HDMI - tristate "FSI-HDMI sound support" - depends on SND_SOC_SH4_FSI && FB_SH_MOBILE_HDMI - help - This option enables generic sound support for the - FSI - HDMI unit - config SND_SIU_MIGOR tristate "SIU sound support on Migo-R" depends on SH_MIGOR diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index 94476d4c0fd5..849b387d17d9 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile @@ -14,13 +14,7 @@ obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o ## boards snd-soc-sh7760-ac97-objs := sh7760-ac97.o -snd-soc-fsi-ak4642-objs := fsi-ak4642.o -snd-soc-fsi-da7210-objs := fsi-da7210.o -snd-soc-fsi-hdmi-objs := fsi-hdmi.o snd-soc-migor-objs := migor.o obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o -obj-$(CONFIG_SND_FSI_AK4642) += snd-soc-fsi-ak4642.o -obj-$(CONFIG_SND_FSI_DA7210) += snd-soc-fsi-da7210.o -obj-$(CONFIG_SND_FSI_HDMI) += snd-soc-fsi-hdmi.o obj-$(CONFIG_SND_SIU_MIGOR) += snd-soc-migor.o diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c deleted file mode 100644 index 97f540aabbdd..000000000000 --- a/sound/soc/sh/fsi-ak4642.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * FSI-AK464x sound support for ms7724se - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * 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/platform_device.h> -#include <linux/module.h> -#include <sound/sh_fsi.h> - -struct fsi_ak4642_data { - const char *name; - const char *card; - const char *cpu_dai; - const char *codec; - const char *platform; - int id; -}; - -static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - int ret; - - ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_CBS_CFS); - - return ret; -} - -static struct snd_soc_dai_link fsi_dai_link = { - .codec_dai_name = "ak4642-hifi", - .init = fsi_ak4642_dai_init, -}; - -static struct snd_soc_card fsi_soc_card = { - .owner = THIS_MODULE, - .dai_link = &fsi_dai_link, - .num_links = 1, -}; - -static struct platform_device *fsi_snd_device; - -static int fsi_ak4642_probe(struct platform_device *pdev) -{ - int ret = -ENOMEM; - struct fsi_ak4642_info *pinfo = pdev->dev.platform_data; - - if (!pinfo) { - dev_err(&pdev->dev, "no info for fsi ak4642\n"); - goto out; - } - - fsi_snd_device = platform_device_alloc("soc-audio", pinfo->id); - if (!fsi_snd_device) - goto out; - - fsi_dai_link.name = pinfo->name; - fsi_dai_link.stream_name = pinfo->name; - fsi_dai_link.cpu_dai_name = pinfo->cpu_dai; - fsi_dai_link.platform_name = pinfo->platform; - fsi_dai_link.codec_name = pinfo->codec; - fsi_soc_card.name = pinfo->card; - - platform_set_drvdata(fsi_snd_device, &fsi_soc_card); - ret = platform_device_add(fsi_snd_device); - - if (ret) - platform_device_put(fsi_snd_device); - -out: - return ret; -} - -static int fsi_ak4642_remove(struct platform_device *pdev) -{ - platform_device_unregister(fsi_snd_device); - return 0; -} - -static struct platform_driver fsi_ak4642 = { - .driver = { - .name = "fsi-ak4642-audio", - }, - .probe = fsi_ak4642_probe, - .remove = fsi_ak4642_remove, -}; - -module_platform_driver(fsi_ak4642); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic SH4 FSI-AK4642 sound card"); -MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c deleted file mode 100644 index 1dd3354c7411..000000000000 --- a/sound/soc/sh/fsi-da7210.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * fsi-da7210.c - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto <morimoto.kuninori@renesas.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/platform_device.h> -#include <linux/module.h> -#include <sound/sh_fsi.h> - -static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - int ret; - - ret = snd_soc_dai_set_fmt(codec, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_CBS_CFS); - - return ret; -} - -static struct snd_soc_dai_link fsi_da7210_dai = { - .name = "DA7210", - .stream_name = "DA7210", - .cpu_dai_name = "fsib-dai", /* FSI B */ - .codec_dai_name = "da7210-hifi", - .platform_name = "sh_fsi.0", - .codec_name = "da7210-codec.0-001a", - .init = fsi_da7210_init, -}; - -static struct snd_soc_card fsi_soc_card = { - .name = "FSI-DA7210", - .owner = THIS_MODULE, - .dai_link = &fsi_da7210_dai, - .num_links = 1, -}; - -static struct platform_device *fsi_da7210_snd_device; - -static int __init fsi_da7210_sound_init(void) -{ - int ret; - - fsi_da7210_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B); - if (!fsi_da7210_snd_device) - return -ENOMEM; - - platform_set_drvdata(fsi_da7210_snd_device, &fsi_soc_card); - ret = platform_device_add(fsi_da7210_snd_device); - if (ret) - platform_device_put(fsi_da7210_snd_device); - - return ret; -} - -static void __exit fsi_da7210_sound_exit(void) -{ - platform_device_unregister(fsi_da7210_snd_device); -} - -module_init(fsi_da7210_sound_init); -module_exit(fsi_da7210_sound_exit); - -/* Module information */ -MODULE_DESCRIPTION("ALSA SoC FSI DA2710"); -MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c deleted file mode 100644 index 6e41908323e8..000000000000 --- a/sound/soc/sh/fsi-hdmi.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * FSI - HDMI sound support - * - * Copyright (C) 2010 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * 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/platform_device.h> -#include <linux/module.h> -#include <sound/sh_fsi.h> - -struct fsi_hdmi_data { - const char *cpu_dai; - const char *card; - int id; -}; - -static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai *cpu = rtd->cpu_dai; - int ret; - - ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM); - - return ret; -} - -static struct snd_soc_dai_link fsi_dai_link = { - .name = "HDMI", - .stream_name = "HDMI", - .codec_dai_name = "sh_mobile_hdmi-hifi", - .platform_name = "sh_fsi2", - .codec_name = "sh-mobile-hdmi", - .init = fsi_hdmi_dai_init, -}; - -static struct snd_soc_card fsi_soc_card = { - .owner = THIS_MODULE, - .dai_link = &fsi_dai_link, - .num_links = 1, -}; - -static struct platform_device *fsi_snd_device; - -static int fsi_hdmi_probe(struct platform_device *pdev) -{ - int ret = -ENOMEM; - const struct platform_device_id *id_entry; - struct fsi_hdmi_data *pdata; - - id_entry = pdev->id_entry; - if (!id_entry) { - dev_err(&pdev->dev, "unknown fsi hdmi\n"); - return -ENODEV; - } - - pdata = (struct fsi_hdmi_data *)id_entry->driver_data; - - fsi_snd_device = platform_device_alloc("soc-audio", pdata->id); - if (!fsi_snd_device) - goto out; - - fsi_dai_link.cpu_dai_name = pdata->cpu_dai; - fsi_soc_card.name = pdata->card; - - platform_set_drvdata(fsi_snd_device, &fsi_soc_card); - ret = platform_device_add(fsi_snd_device); - - if (ret) - platform_device_put(fsi_snd_device); - -out: - return ret; -} - -static int fsi_hdmi_remove(struct platform_device *pdev) -{ - platform_device_unregister(fsi_snd_device); - return 0; -} - -static struct fsi_hdmi_data fsi2_a_hdmi = { - .cpu_dai = "fsia-dai", - .card = "FSI2A-HDMI", - .id = FSI_PORT_A, -}; - -static struct fsi_hdmi_data fsi2_b_hdmi = { - .cpu_dai = "fsib-dai", - .card = "FSI2B-HDMI", - .id = FSI_PORT_B, -}; - -static struct platform_device_id fsi_id_table[] = { - /* FSI 2 */ - { "sh_fsi2_a_hdmi", (kernel_ulong_t)&fsi2_a_hdmi }, - { "sh_fsi2_b_hdmi", (kernel_ulong_t)&fsi2_b_hdmi }, - {}, -}; - -static struct platform_driver fsi_hdmi = { - .driver = { - .name = "fsi-hdmi-audio", - }, - .probe = fsi_hdmi_probe, - .remove = fsi_hdmi_remove, - .id_table = fsi_id_table, -}; - -module_platform_driver(fsi_hdmi); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic SH4 FSI-HDMI sound card"); -MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b390f00b4e99..b37ee8077ed1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -39,6 +39,7 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-dpcm.h> #include <sound/initval.h> #define CREATE_TRACE_POINTS @@ -54,7 +55,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); -static LIST_HEAD(card_list); static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); @@ -201,12 +201,6 @@ static ssize_t pmdown_time_set(struct device *dev, static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set); #ifdef CONFIG_DEBUG_FS -static int codec_reg_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -264,7 +258,7 @@ static ssize_t codec_reg_write_file(struct file *file, } static const struct file_operations codec_reg_fops = { - .open = codec_reg_open_file, + .open = simple_open, .read = codec_reg_read_file, .write = codec_reg_write_file, .llseek = default_llseek, @@ -471,6 +465,35 @@ static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) } #endif +struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, + const char *dai_link, int stream) +{ + int i; + + for (i = 0; i < card->num_links; i++) { + if (card->rtd[i].dai_link->no_pcm && + !strcmp(card->rtd[i].dai_link->name, dai_link)) + return card->rtd[i].pcm->streams[stream].substream; + } + dev_dbg(card->dev, "failed to find dai link %s\n", dai_link); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); + +struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, + const char *dai_link) +{ + int i; + + for (i = 0; i < card->num_links; i++) { + if (!strcmp(card->rtd[i].dai_link->name, dai_link)) + return &card->rtd[i]; + } + dev_dbg(card->dev, "failed to find rtd %s\n", dai_link); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); + #ifdef CONFIG_SND_SOC_AC97_BUS /* unregister ac97 codec */ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) @@ -573,19 +596,16 @@ int snd_soc_suspend(struct device *dev) } for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai; if (card->rtd[i].dai_link->ignore_suspend) continue; snd_soc_dapm_stream_event(&card->rtd[i], SNDRV_PCM_STREAM_PLAYBACK, - codec_dai, SND_SOC_DAPM_STREAM_SUSPEND); snd_soc_dapm_stream_event(&card->rtd[i], SNDRV_PCM_STREAM_CAPTURE, - codec_dai, SND_SOC_DAPM_STREAM_SUSPEND); } @@ -689,17 +709,16 @@ static void soc_resume_deferred(struct work_struct *work) } for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai; if (card->rtd[i].dai_link->ignore_suspend) continue; snd_soc_dapm_stream_event(&card->rtd[i], - SNDRV_PCM_STREAM_PLAYBACK, codec_dai, + SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_RESUME); snd_soc_dapm_stream_event(&card->rtd[i], - SNDRV_PCM_STREAM_CAPTURE, codec_dai, + SNDRV_PCM_STREAM_CAPTURE, SND_SOC_DAPM_STREAM_RESUME); } @@ -789,15 +808,9 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) struct snd_soc_dai *codec_dai, *cpu_dai; const char *platform_name; - if (rtd->complete) - return 1; dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num); - /* do we already have the CPU DAI for this link ? */ - if (rtd->cpu_dai) { - goto find_codec; - } - /* no, then find CPU DAI from registered DAIs*/ + /* Find CPU DAI from registered DAIs*/ list_for_each_entry(cpu_dai, &dai_list, list) { if (dai_link->cpu_dai_of_node) { if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node) @@ -808,18 +821,15 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) } rtd->cpu_dai = cpu_dai; - goto find_codec; } - dev_dbg(card->dev, "CPU DAI %s not registered\n", - dai_link->cpu_dai_name); -find_codec: - /* do we already have the CODEC for this link ? */ - if (rtd->codec) { - goto find_platform; + if (!rtd->cpu_dai) { + dev_dbg(card->dev, "CPU DAI %s not registered\n", + dai_link->cpu_dai_name); + return -EPROBE_DEFER; } - /* no, then find CODEC from registered CODECs*/ + /* Find CODEC from registered CODECs */ list_for_each_entry(codec, &codec_list, list) { if (dai_link->codec_of_node) { if (codec->dev->of_node != dai_link->codec_of_node) @@ -841,28 +851,28 @@ find_codec: dai_link->codec_dai_name)) { rtd->codec_dai = codec_dai; - goto find_platform; } } - dev_dbg(card->dev, "CODEC DAI %s not registered\n", - dai_link->codec_dai_name); - goto find_platform; + if (!rtd->codec_dai) { + dev_dbg(card->dev, "CODEC DAI %s not registered\n", + dai_link->codec_dai_name); + return -EPROBE_DEFER; + } } - dev_dbg(card->dev, "CODEC %s not registered\n", - dai_link->codec_name); -find_platform: - /* do we need a platform? */ - if (rtd->platform) - goto out; + if (!rtd->codec) { + dev_dbg(card->dev, "CODEC %s not registered\n", + dai_link->codec_name); + return -EPROBE_DEFER; + } /* if there's no platform we match on the empty platform */ platform_name = dai_link->platform_name; if (!platform_name && !dai_link->platform_of_node) platform_name = "snd-soc-dummy"; - /* no, then find one from the set of registered platforms */ + /* find one from the set of registered platforms */ list_for_each_entry(platform, &platform_list, list) { if (dai_link->platform_of_node) { if (platform->dev->of_node != @@ -874,20 +884,16 @@ find_platform: } rtd->platform = platform; - goto out; } - - dev_dbg(card->dev, "platform %s not registered\n", + if (!rtd->platform) { + dev_dbg(card->dev, "platform %s not registered\n", dai_link->platform_name); - return 0; - -out: - /* mark rtd as complete if we found all 4 of our client devices */ - if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) { - rtd->complete = 1; - card->num_rtd++; + return -EPROBE_DEFER; } - return 1; + + card->num_rtd++; + + return 0; } static void soc_remove_codec(struct snd_soc_codec *codec) @@ -1074,6 +1080,7 @@ static int soc_probe_platform(struct snd_soc_card *card, { int ret = 0; const struct snd_soc_platform_driver *driver = platform->driver; + struct snd_soc_dai *dai; platform->card = card; platform->dapm.card = card; @@ -1087,6 +1094,14 @@ static int soc_probe_platform(struct snd_soc_card *card, snd_soc_dapm_new_controls(&platform->dapm, driver->dapm_widgets, driver->num_dapm_widgets); + /* Create DAPM widgets for each DAI stream */ + list_for_each_entry(dai, &dai_list, list) { + if (dai->dev != platform->dev) + continue; + + snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); + } + platform->dapm.idle_bias_off = 1; if (driver->probe) { @@ -1176,6 +1191,10 @@ static int soc_post_component_init(struct snd_soc_card *card, rtd->dev->init_name = name; dev_set_drvdata(rtd->dev, rtd); mutex_init(&rtd->pcm_mutex); + INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients); + INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients); + INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients); + INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients); ret = device_add(rtd->dev); if (ret < 0) { dev_err(card->dev, @@ -1197,6 +1216,17 @@ static int soc_post_component_init(struct snd_soc_card *card, dev_err(codec->dev, "asoc: failed to add codec sysfs files: %d\n", ret); +#ifdef CONFIG_DEBUG_FS + /* add DPCM sysfs entries */ + if (!dailess && !dai_link->dynamic) + goto out; + + ret = soc_dpcm_debugfs_add(rtd); + if (ret < 0) + dev_err(rtd->dev, "asoc: failed to add dpcm sysfs entries: %d\n", ret); + +out: +#endif return 0; } @@ -1206,14 +1236,15 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dapm_widget *play_w, *capture_w; int ret; dev_dbg(card->dev, "probe %s dai link %d late %d\n", card->name, num, order); /* config components */ - codec_dai->codec = codec; cpu_dai->platform = platform; codec_dai->card = card; cpu_dai->card = card; @@ -1224,9 +1255,12 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) /* probe the cpu_dai */ if (!cpu_dai->probed && cpu_dai->driver->probe_order == order) { + cpu_dai->dapm.card = card; if (!try_module_get(cpu_dai->dev->driver->owner)) return -ENODEV; + snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); + if (cpu_dai->driver->probe) { ret = cpu_dai->driver->probe(cpu_dai); if (ret < 0) { @@ -1285,12 +1319,39 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) if (ret < 0) pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret); - /* create the pcm */ - ret = soc_new_pcm(rtd, num); - if (ret < 0) { - pr_err("asoc: can't create pcm %s :%d\n", - dai_link->stream_name, ret); - return ret; + if (!dai_link->params) { + /* create the pcm */ + ret = soc_new_pcm(rtd, num); + if (ret < 0) { + pr_err("asoc: can't create pcm %s :%d\n", + dai_link->stream_name, ret); + return ret; + } + } else { + /* link the DAI widgets */ + play_w = codec_dai->playback_widget; + capture_w = cpu_dai->capture_widget; + if (play_w && capture_w) { + ret = snd_soc_dapm_new_pcm(card, dai_link->params, + capture_w, play_w); + if (ret != 0) { + dev_err(card->dev, "Can't link %s to %s: %d\n", + play_w->name, capture_w->name, ret); + return ret; + } + } + + play_w = cpu_dai->playback_widget; + capture_w = codec_dai->capture_widget; + if (play_w && capture_w) { + ret = snd_soc_dapm_new_pcm(card, dai_link->params, + capture_w, play_w); + if (ret != 0) { + dev_err(card->dev, "Can't link %s to %s: %d\n", + play_w->name, capture_w->name, ret); + return ret; + } + } } /* add platform data for AC97 devices */ @@ -1340,6 +1401,20 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec) } #endif +static int soc_check_aux_dev(struct snd_soc_card *card, int num) +{ + struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; + struct snd_soc_codec *codec; + + /* find CODEC from registered CODECs*/ + list_for_each_entry(codec, &codec_list, list) { + if (!strcmp(codec->name, aux_dev->codec_name)) + return 0; + } + + return -EPROBE_DEFER; +} + static int soc_probe_aux_dev(struct snd_soc_card *card, int num) { struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; @@ -1360,7 +1435,7 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) } /* codec not found */ dev_err(card->dev, "asoc: codec %s not found", aux_dev->codec_name); - goto out; + return -EPROBE_DEFER; found: ret = soc_probe_codec(card, codec); @@ -1410,29 +1485,28 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, return 0; } -static void snd_soc_instantiate_card(struct snd_soc_card *card) +static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_codec *codec; struct snd_soc_codec_conf *codec_conf; enum snd_soc_compress_type compress_type; struct snd_soc_dai_link *dai_link; - int ret, i, order; - - mutex_lock(&card->mutex); + int ret, i, order, dai_fmt; - if (card->instantiated) { - mutex_unlock(&card->mutex); - return; - } + mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); /* bind DAIs */ - for (i = 0; i < card->num_links; i++) - soc_bind_dai_link(card, i); + for (i = 0; i < card->num_links; i++) { + ret = soc_bind_dai_link(card, i); + if (ret != 0) + goto base_error; + } - /* bind completed ? */ - if (card->num_rtd != card->num_links) { - mutex_unlock(&card->mutex); - return; + /* check aux_devs too */ + for (i = 0; i < card->num_aux_devs; i++) { + ret = soc_check_aux_dev(card, i); + if (ret != 0) + goto base_error; } /* initialize the register cache for each available codec */ @@ -1452,10 +1526,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } ret = snd_soc_init_codec_cache(codec, compress_type); - if (ret < 0) { - mutex_unlock(&card->mutex); - return; - } + if (ret < 0) + goto base_error; } /* card bind complete so register a sound card */ @@ -1464,8 +1536,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) if (ret < 0) { pr_err("asoc: can't create sound card for card %s: %d\n", card->name, ret); - mutex_unlock(&card->mutex); - return; + goto base_error; } card->snd_card->dev = card->dev; @@ -1529,17 +1600,47 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) for (i = 0; i < card->num_links; i++) { dai_link = &card->dai_link[i]; + dai_fmt = dai_link->dai_fmt; - if (dai_link->dai_fmt) { + if (dai_fmt) { ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai, - dai_link->dai_fmt); + dai_fmt); if (ret != 0 && ret != -ENOTSUPP) dev_warn(card->rtd[i].codec_dai->dev, "Failed to set DAI format: %d\n", ret); + } + /* If this is a regular CPU link there will be a platform */ + if (dai_fmt && + (dai_link->platform_name || dai_link->platform_of_node)) { ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, - dai_link->dai_fmt); + dai_fmt); + if (ret != 0 && ret != -ENOTSUPP) + dev_warn(card->rtd[i].cpu_dai->dev, + "Failed to set DAI format: %d\n", + ret); + } else if (dai_fmt) { + /* Flip the polarity for the "CPU" end */ + dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + switch (dai_link->dai_fmt & + SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + break; + case SND_SOC_DAIFMT_CBM_CFS: + dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + break; + case SND_SOC_DAIFMT_CBS_CFM: + dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + break; + } + + ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, + dai_fmt); if (ret != 0 && ret != -ENOTSUPP) dev_warn(card->rtd[i].cpu_dai->dev, "Failed to set DAI format: %d\n", @@ -1605,7 +1706,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) card->instantiated = 1; snd_soc_dapm_sync(&card->dapm); mutex_unlock(&card->mutex); - return; + + return 0; probe_aux_dev_err: for (i = 0; i < card->num_aux_devs; i++) @@ -1620,18 +1722,10 @@ card_probe_error: snd_card_free(card->snd_card); +base_error: mutex_unlock(&card->mutex); -} -/* - * Attempt to initialise any uninitialised cards. Must be called with - * client_mutex. - */ -static void snd_soc_instantiate_cards(void) -{ - struct snd_soc_card *card; - list_for_each_entry(card, &card_list, list) - snd_soc_instantiate_card(card); + return ret; } /* probes a new socdev */ @@ -2533,6 +2627,87 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_put_volsw); /** + * snd_soc_get_volsw_sx - single mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a single mixer control, or a double mixer + * control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int mask = (1 << (fls(min + max) - 1)) - 1; + + ucontrol->value.integer.value[0] = + ((snd_soc_read(codec, reg) >> shift) - min) & mask; + + if (snd_soc_volsw_is_stereo(mc)) + ucontrol->value.integer.value[1] = + ((snd_soc_read(codec, reg2) >> rshift) - min) & mask; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); + +/** + * snd_soc_put_volsw_sx - double mixer set callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a double mixer control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int mask = (1 << (fls(min + max) - 1)) - 1; + int err = 0; + unsigned short val, val_mask, val2 = 0; + + val_mask = mask << shift; + val = (ucontrol->value.integer.value[0] + min) & mask; + val = val << shift; + + if (snd_soc_update_bits_locked(codec, reg, val_mask, val)) + return err; + + if (snd_soc_volsw_is_stereo(mc)) { + val_mask = mask << rshift; + val2 = (ucontrol->value.integer.value[1] + min) & mask; + val2 = val2 << rshift; + + if (snd_soc_update_bits_locked(codec, reg2, val_mask, val2)) + return err; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); + +/** * snd_soc_info_volsw_s8 - signed mixer info callback * @kcontrol: mixer control * @uinfo: control element information @@ -2653,99 +2828,6 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_limit_volume); -/** - * snd_soc_info_volsw_2r_sx - double with tlv and variable data size - * mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Returns 0 for success. - */ -int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int max = mc->max; - int min = mc->min; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max-min; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r_sx); - -/** - * snd_soc_get_volsw_2r_sx - double with tlv and variable data size - * mixer get callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Returns 0 for success. - */ -int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int mask = (1<<mc->shift)-1; - int min = mc->min; - int val = snd_soc_read(codec, mc->reg) & mask; - int valr = snd_soc_read(codec, mc->rreg) & mask; - - ucontrol->value.integer.value[0] = ((val & 0xff)-min) & mask; - ucontrol->value.integer.value[1] = ((valr & 0xff)-min) & mask; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx); - -/** - * snd_soc_put_volsw_2r_sx - double with tlv and variable data size - * mixer put callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Returns 0 for success. - */ -int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int mask = (1<<mc->shift)-1; - int min = mc->min; - int ret; - unsigned int val, valr, oval, ovalr; - - val = ((ucontrol->value.integer.value[0]+min) & 0xff); - val &= mask; - valr = ((ucontrol->value.integer.value[1]+min) & 0xff); - valr &= mask; - - oval = snd_soc_read(codec, mc->reg) & mask; - ovalr = snd_soc_read(codec, mc->rreg) & mask; - - ret = 0; - if (oval != val) { - ret = snd_soc_write(codec, mc->reg, val); - if (ret < 0) - return ret; - } - if (ovalr != valr) { - ret = snd_soc_write(codec, mc->rreg, valr); - if (ret < 0) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx); - int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2856,6 +2938,186 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_bytes_put); /** + * snd_soc_info_xr_sx - signed multi register info callback + * @kcontrol: mreg control + * @uinfo: control element information + * + * Callback to provide information of a control that can + * span multiple codec registers which together + * forms a single signed value in a MSB/LSB manner. + * + * Returns 0 for success. + */ +int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = mc->min; + uinfo->value.integer.max = mc->max; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx); + +/** + * snd_soc_get_xr_sx - signed multi register get callback + * @kcontrol: mreg control + * @ucontrol: control element information + * + * Callback to get the value of a control that can span + * multiple codec registers which together forms a single + * signed value in a MSB/LSB manner. The control supports + * specifying total no of bits used to allow for bitfields + * across the multiple codec registers. + * + * Returns 0 for success. + */ +int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int regbase = mc->regbase; + unsigned int regcount = mc->regcount; + unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE; + unsigned int regwmask = (1<<regwshift)-1; + unsigned int invert = mc->invert; + unsigned long mask = (1UL<<mc->nbits)-1; + long min = mc->min; + long max = mc->max; + long val = 0; + unsigned long regval; + unsigned int i; + + for (i = 0; i < regcount; i++) { + regval = snd_soc_read(codec, regbase+i) & regwmask; + val |= regval << (regwshift*(regcount-i-1)); + } + val &= mask; + if (min < 0 && val > max) + val |= ~mask; + if (invert) + val = max - val; + ucontrol->value.integer.value[0] = val; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx); + +/** + * snd_soc_put_xr_sx - signed multi register get callback + * @kcontrol: mreg control + * @ucontrol: control element information + * + * Callback to set the value of a control that can span + * multiple codec registers which together forms a single + * signed value in a MSB/LSB manner. The control supports + * specifying total no of bits used to allow for bitfields + * across the multiple codec registers. + * + * Returns 0 for success. + */ +int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int regbase = mc->regbase; + unsigned int regcount = mc->regcount; + unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE; + unsigned int regwmask = (1<<regwshift)-1; + unsigned int invert = mc->invert; + unsigned long mask = (1UL<<mc->nbits)-1; + long max = mc->max; + long val = ucontrol->value.integer.value[0]; + unsigned int i, regval, regmask; + int err; + + if (invert) + val = max - val; + val &= mask; + for (i = 0; i < regcount; i++) { + regval = (val >> (regwshift*(regcount-i-1))) & regwmask; + regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask; + err = snd_soc_update_bits_locked(codec, regbase+i, + regmask, regval); + if (err < 0) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx); + +/** + * snd_soc_get_strobe - strobe get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback get the value of a strobe mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = 1 << shift; + unsigned int invert = mc->invert != 0; + unsigned int val = snd_soc_read(codec, reg) & mask; + + if (shift != 0 && val != 0) + val = val >> shift; + ucontrol->value.enumerated.item[0] = val ^ invert; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_strobe); + +/** + * snd_soc_put_strobe - strobe put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback strobe a register bit to high then low (or the inverse) + * in one pass of a single mixer enum control. + * + * Returns 1 for success. + */ +int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = 1 << shift; + unsigned int invert = mc->invert != 0; + unsigned int strobe = ucontrol->value.enumerated.item[0] != 0; + unsigned int val1 = (strobe ^ invert) ? mask : 0; + unsigned int val2 = (strobe ^ invert) ? 0 : mask; + int err; + + err = snd_soc_update_bits_locked(codec, reg, mask, val1); + if (err < 0) + return err; + + err = snd_soc_update_bits_locked(codec, reg, mask, val2); + return err; +} +EXPORT_SYMBOL_GPL(snd_soc_put_strobe); + +/** * snd_soc_dai_set_sysclk - configure DAI system or master clock. * @dai: DAI * @clk_id: DAI specific clock ID @@ -3054,7 +3316,7 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) if (dai->driver && dai->driver->ops->digital_mute) return dai->driver->ops->digital_mute(dai, mute); else - return -EINVAL; + return -ENOTSUPP; } EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); @@ -3066,7 +3328,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); */ int snd_soc_register_card(struct snd_soc_card *card) { - int i; + int i, ret; if (!card->name || !card->dev) return -EINVAL; @@ -3129,15 +3391,13 @@ int snd_soc_register_card(struct snd_soc_card *card) INIT_LIST_HEAD(&card->dapm_dirty); card->instantiated = 0; mutex_init(&card->mutex); + mutex_init(&card->dapm_mutex); - mutex_lock(&client_mutex); - list_add(&card->list, &card_list); - snd_soc_instantiate_cards(); - mutex_unlock(&client_mutex); - - dev_dbg(card->dev, "Registered card '%s'\n", card->name); + ret = snd_soc_instantiate_card(card); + if (ret != 0) + soc_cleanup_card_debugfs(card); - return 0; + return ret; } EXPORT_SYMBOL_GPL(snd_soc_register_card); @@ -3151,9 +3411,6 @@ int snd_soc_unregister_card(struct snd_soc_card *card) { if (card->instantiated) soc_cleanup_card_resources(card); - mutex_lock(&client_mutex); - list_del(&card->list); - mutex_unlock(&client_mutex); dev_dbg(card->dev, "Unregistered card '%s'\n", card->name); return 0; @@ -3227,6 +3484,7 @@ static inline char *fmt_multiple_name(struct device *dev, int snd_soc_register_dai(struct device *dev, struct snd_soc_dai_driver *dai_drv) { + struct snd_soc_codec *codec; struct snd_soc_dai *dai; dev_dbg(dev, "dai register %s\n", dev_name(dev)); @@ -3244,12 +3502,23 @@ int snd_soc_register_dai(struct device *dev, dai->dev = dev; dai->driver = dai_drv; + dai->dapm.dev = dev; if (!dai->driver->ops) dai->driver->ops = &null_dai_ops; mutex_lock(&client_mutex); + + list_for_each_entry(codec, &codec_list, list) { + if (codec->dev == dev) { + dev_dbg(dev, "Mapped DAI %s to CODEC %s\n", + dai->name, codec->name); + dai->codec = codec; + break; + } + } + list_add(&dai->list, &dai_list); - snd_soc_instantiate_cards(); + mutex_unlock(&client_mutex); pr_debug("Registered DAI '%s'\n", dai->name); @@ -3293,6 +3562,7 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_dai); int snd_soc_register_dais(struct device *dev, struct snd_soc_dai_driver *dai_drv, size_t count) { + struct snd_soc_codec *codec; struct snd_soc_dai *dai; int i, ret = 0; @@ -3320,19 +3590,28 @@ int snd_soc_register_dais(struct device *dev, dai->id = dai->driver->id; else dai->id = i; + dai->dapm.dev = dev; if (!dai->driver->ops) dai->driver->ops = &null_dai_ops; mutex_lock(&client_mutex); + + list_for_each_entry(codec, &codec_list, list) { + if (codec->dev == dev) { + dev_dbg(dev, "Mapped DAI %s to CODEC %s\n", + dai->name, codec->name); + dai->codec = codec; + break; + } + } + list_add(&dai->list, &dai_list); + mutex_unlock(&client_mutex); pr_debug("Registered DAI '%s'\n", dai->name); } - mutex_lock(&client_mutex); - snd_soc_instantiate_cards(); - mutex_unlock(&client_mutex); return 0; err: @@ -3390,7 +3669,6 @@ int snd_soc_register_platform(struct device *dev, mutex_lock(&client_mutex); list_add(&platform->list, &platform_list); - snd_soc_instantiate_cards(); mutex_unlock(&client_mutex); pr_debug("Registered platform '%s'\n", platform->name); @@ -3540,18 +3818,18 @@ int snd_soc_register_codec(struct device *dev, fixup_codec_formats(&dai_drv[i].capture); } + mutex_lock(&client_mutex); + list_add(&codec->list, &codec_list); + mutex_unlock(&client_mutex); + /* register any DAIs */ if (num_dai) { ret = snd_soc_register_dais(dev, dai_drv, num_dai); if (ret < 0) - goto fail; + dev_err(codec->dev, "Failed to regster DAIs: %d\n", + ret); } - mutex_lock(&client_mutex); - list_add(&codec->list, &codec_list); - snd_soc_instantiate_cards(); - mutex_unlock(&client_mutex); - pr_debug("Registered codec '%s'\n", codec->name); return 0; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index dc7dbfe61cd0..90ee77d2409d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -52,6 +52,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_supply] = 1, [snd_soc_dapm_regulator_supply] = 1, [snd_soc_dapm_micbias] = 2, + [snd_soc_dapm_dai_link] = 2, [snd_soc_dapm_dai] = 3, [snd_soc_dapm_aif_in] = 3, [snd_soc_dapm_aif_out] = 3, @@ -90,9 +91,10 @@ static int dapm_down_seq[] = { [snd_soc_dapm_aif_in] = 10, [snd_soc_dapm_aif_out] = 10, [snd_soc_dapm_dai] = 10, - [snd_soc_dapm_regulator_supply] = 11, - [snd_soc_dapm_supply] = 11, - [snd_soc_dapm_post] = 12, + [snd_soc_dapm_dai_link] = 11, + [snd_soc_dapm_regulator_supply] = 12, + [snd_soc_dapm_supply] = 12, + [snd_soc_dapm_post] = 13, }; static void pop_wait(u32 pop_time) @@ -208,7 +210,23 @@ static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val) return -1; } -static int soc_widget_update_bits(struct snd_soc_dapm_widget *w, +static inline void soc_widget_lock(struct snd_soc_dapm_widget *w) +{ + if (w->codec && !w->codec->using_regmap) + mutex_lock(&w->codec->mutex); + else if (w->platform) + mutex_lock(&w->platform->mutex); +} + +static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w) +{ + if (w->codec && !w->codec->using_regmap) + mutex_unlock(&w->codec->mutex); + else if (w->platform) + mutex_unlock(&w->platform->mutex); +} + +static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, unsigned short reg, unsigned int mask, unsigned int value) { bool change; @@ -221,18 +239,24 @@ static int soc_widget_update_bits(struct snd_soc_dapm_widget *w, if (ret != 0) return ret; } else { + soc_widget_lock(w); ret = soc_widget_read(w, reg); - if (ret < 0) + if (ret < 0) { + soc_widget_unlock(w); return ret; + } old = ret; new = (old & ~mask) | (value & mask); change = old != new; if (change) { ret = soc_widget_write(w, reg, new); - if (ret < 0) + if (ret < 0) { + soc_widget_unlock(w); return ret; + } } + soc_widget_unlock(w); } return change; @@ -374,6 +398,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_mic: case snd_soc_dapm_spk: case snd_soc_dapm_line: + case snd_soc_dapm_dai_link: p->connect = 1; break; /* does affect routing - dynamically connected */ @@ -682,11 +707,51 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) } } +/* add widget to list if it's not already in the list */ +static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list, + struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_widget_list *wlist; + int wlistsize, wlistentries, i; + + if (*list == NULL) + return -EINVAL; + + wlist = *list; + + /* is this widget already in the list */ + for (i = 0; i < wlist->num_widgets; i++) { + if (wlist->widgets[i] == w) + return 0; + } + + /* allocate some new space */ + wlistentries = wlist->num_widgets + 1; + wlistsize = sizeof(struct snd_soc_dapm_widget_list) + + wlistentries * sizeof(struct snd_soc_dapm_widget *); + *list = krealloc(wlist, wlistsize, GFP_KERNEL); + if (*list == NULL) { + dev_err(w->dapm->dev, "can't allocate widget list for %s\n", + w->name); + return -ENOMEM; + } + wlist = *list; + + /* insert the widget */ + dev_dbg(w->dapm->dev, "added %s in widget list pos %d\n", + w->name, wlist->num_widgets); + + wlist->widgets[wlist->num_widgets] = w; + wlist->num_widgets++; + return 1; +} + /* * Recursively check for a completed path to an active or physically connected * output widget. Returns number of complete paths. */ -static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) +static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, + struct snd_soc_dapm_widget_list **list) { struct snd_soc_dapm_path *path; int con = 0; @@ -742,9 +807,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) if (path->walked) continue; + trace_snd_soc_dapm_output_path(widget, path); + if (path->sink && path->connect) { path->walked = 1; - con += is_connected_output_ep(path->sink); + + /* do we need to add this widget to the list ? */ + if (list) { + int err; + err = dapm_list_add_widget(list, path->sink); + if (err < 0) { + dev_err(widget->dapm->dev, "could not add widget %s\n", + widget->name); + return con; + } + } + + con += is_connected_output_ep(path->sink, list); } } @@ -757,7 +836,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) * Recursively check for a completed path to an active or physically connected * input widget. Returns number of complete paths. */ -static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) +static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, + struct snd_soc_dapm_widget_list **list) { struct snd_soc_dapm_path *path; int con = 0; @@ -825,9 +905,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) if (path->walked) continue; + trace_snd_soc_dapm_input_path(widget, path); + if (path->source && path->connect) { path->walked = 1; - con += is_connected_input_ep(path->source); + + /* do we need to add this widget to the list ? */ + if (list) { + int err; + err = dapm_list_add_widget(list, path->sink); + if (err < 0) { + dev_err(widget->dapm->dev, "could not add widget %s\n", + widget->name); + return con; + } + } + + con += is_connected_input_ep(path->source, list); } } @@ -836,6 +930,39 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) return con; } +/** + * snd_soc_dapm_get_connected_widgets - query audio path and it's widgets. + * @dai: the soc DAI. + * @stream: stream direction. + * @list: list of active widgets for this stream. + * + * Queries DAPM graph as to whether an valid audio stream path exists for + * the initial stream specified by name. This takes into account + * current mixer and mux kcontrol settings. Creates list of valid widgets. + * + * Returns the number of valid paths or negative error. + */ +int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, + struct snd_soc_dapm_widget_list **list) +{ + struct snd_soc_card *card = dai->card; + int paths; + + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm_reset(card); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + paths = is_connected_output_ep(dai->playback_widget, list); + else + paths = is_connected_input_ep(dai->playback_widget, list); + + trace_snd_soc_dapm_connected(paths, stream); + dapm_clear_walk(&card->dapm); + mutex_unlock(&card->dapm_mutex); + + return paths; +} + /* * Handler for generic register modifier widget. */ @@ -849,7 +976,7 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, else val = w->off_val; - soc_widget_update_bits(w, -(w->reg + 1), + soc_widget_update_bits_locked(w, -(w->reg + 1), w->mask << w->shift, val << w->shift); return 0; @@ -863,9 +990,9 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { if (SND_SOC_DAPM_EVENT_ON(event)) - return regulator_enable(w->priv); + return regulator_enable(w->regulator); else - return regulator_disable_deferred(w->priv, w->shift); + return regulator_disable_deferred(w->regulator, w->shift); } EXPORT_SYMBOL_GPL(dapm_regulator_event); @@ -892,9 +1019,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) DAPM_UPDATE_STAT(w, power_checks); - in = is_connected_input_ep(w); + in = is_connected_input_ep(w, NULL); dapm_clear_walk(w->dapm); - out = is_connected_output_ep(w); + out = is_connected_output_ep(w, NULL); dapm_clear_walk(w->dapm); return out != 0 && in != 0; } @@ -903,7 +1030,10 @@ static int dapm_dai_check_power(struct snd_soc_dapm_widget *w) { DAPM_UPDATE_STAT(w, power_checks); - return w->active; + if (w->active) + return w->active; + + return dapm_generic_check_power(w); } /* Check to see if an ADC has power */ @@ -914,7 +1044,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) DAPM_UPDATE_STAT(w, power_checks); if (w->active) { - in = is_connected_input_ep(w); + in = is_connected_input_ep(w, NULL); dapm_clear_walk(w->dapm); return in != 0; } else { @@ -930,7 +1060,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) DAPM_UPDATE_STAT(w, power_checks); if (w->active) { - out = is_connected_output_ep(w); + out = is_connected_output_ep(w, NULL); dapm_clear_walk(w->dapm); return out != 0; } else { @@ -1107,7 +1237,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); pop_wait(card->pop_time); - soc_widget_update_bits(w, reg, mask, value); + soc_widget_update_bits_locked(w, reg, mask, value); } list_for_each_entry(w, pending, power_list) { @@ -1237,7 +1367,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) w->name, ret); } - ret = snd_soc_update_bits(w->codec, update->reg, update->mask, + ret = soc_widget_update_bits_locked(w, update->reg, update->mask, update->val); if (ret < 0) pr_err("%s DAPM update failed: %d\n", w->name, ret); @@ -1421,12 +1551,10 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) trace_snd_soc_dapm_start(card); list_for_each_entry(d, &card->dapm_list, list) { - if (d->n_widgets || d->codec == NULL) { - if (d->idle_bias_off) - d->target_bias_level = SND_SOC_BIAS_OFF; - else - d->target_bias_level = SND_SOC_BIAS_STANDBY; - } + if (d->idle_bias_off) + d->target_bias_level = SND_SOC_BIAS_OFF; + else + d->target_bias_level = SND_SOC_BIAS_STANDBY; } dapm_reset(card); @@ -1471,32 +1599,6 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } - /* If there are no DAPM widgets then try to figure out power from the - * event type. - */ - if (!dapm->n_widgets) { - switch (event) { - case SND_SOC_DAPM_STREAM_START: - case SND_SOC_DAPM_STREAM_RESUME: - dapm->target_bias_level = SND_SOC_BIAS_ON; - break; - case SND_SOC_DAPM_STREAM_STOP: - if (dapm->codec && dapm->codec->active) - dapm->target_bias_level = SND_SOC_BIAS_ON; - else - dapm->target_bias_level = SND_SOC_BIAS_STANDBY; - break; - case SND_SOC_DAPM_STREAM_SUSPEND: - dapm->target_bias_level = SND_SOC_BIAS_STANDBY; - break; - case SND_SOC_DAPM_STREAM_NOP: - dapm->target_bias_level = dapm->bias_level; - break; - default: - break; - } - } - /* Force all contexts in the card to the same bias state if * they're not ground referenced. */ @@ -1546,12 +1648,6 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } #ifdef CONFIG_DEBUG_FS -static int dapm_widget_power_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t dapm_widget_power_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1566,9 +1662,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (!buf) return -ENOMEM; - in = is_connected_input_ep(w); + in = is_connected_input_ep(w, NULL); dapm_clear_walk(w->dapm); - out = is_connected_output_ep(w); + out = is_connected_output_ep(w, NULL); dapm_clear_walk(w->dapm); ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", @@ -1615,17 +1711,11 @@ static ssize_t dapm_widget_power_read_file(struct file *file, } static const struct file_operations dapm_widget_power_fops = { - .open = dapm_widget_power_open_file, + .open = simple_open, .read = dapm_widget_power_read_file, .llseek = default_llseek, }; -static int dapm_bias_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1656,7 +1746,7 @@ static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, } static const struct file_operations dapm_bias_fops = { - .open = dapm_bias_open_file, + .open = simple_open, .read = dapm_bias_read_file, .llseek = default_llseek, }; @@ -1721,7 +1811,7 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif /* test and update the power status of a mux widget */ -int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, +static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; @@ -1758,12 +1848,26 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); } - return 0; + return found; +} + +int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) +{ + struct snd_soc_card *card = widget->dapm->card; + int ret; + + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e); + mutex_unlock(&card->dapm_mutex); + if (ret > 0) + soc_dpcm_runtime_update(widget); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); /* test and update the power status of a mixer or switch widget */ -int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, +static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, int connect) { struct snd_soc_dapm_path *path; @@ -1790,7 +1894,21 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); } - return 0; + return found; +} + +int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol, int connect) +{ + struct snd_soc_card *card = widget->dapm->card; + int ret; + + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + ret = soc_dapm_mixer_update_power(widget, kcontrol, connect); + mutex_unlock(&card->dapm_mutex); + if (ret > 0) + soc_dpcm_runtime_update(widget); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); @@ -1951,6 +2069,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, */ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) { + int ret; + /* * Suppress early reports (eg, jacks syncing their state) to avoid * silly DAPM runs during card startup. @@ -1958,7 +2078,10 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) if (!dapm->card || !dapm->card->instantiated) return 0; - return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&dapm->card->dapm_mutex); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); @@ -2067,6 +2190,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: case snd_soc_dapm_dai: + case snd_soc_dapm_dai_link: list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); @@ -2122,19 +2246,21 @@ err: int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num) { - int i, ret; + int i, ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { ret = snd_soc_dapm_add_route(dapm, route); if (ret < 0) { dev_err(dapm->dev, "Failed to add route %s->%s\n", route->source, route->sink); - return ret; + break; } route++; } + mutex_unlock(&dapm->card->dapm_mutex); - return 0; + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); @@ -2205,12 +2331,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, int i, err; int ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { err = snd_soc_dapm_weak_route(dapm, route); if (err) ret = err; route++; } + mutex_unlock(&dapm->card->dapm_mutex); return ret; } @@ -2229,6 +2357,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) struct snd_soc_dapm_widget *w; unsigned int val; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + list_for_each_entry(w, &dapm->card->widgets, list) { if (w->new) @@ -2238,8 +2368,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) w->kcontrols = kzalloc(w->num_kcontrols * sizeof(struct snd_kcontrol *), GFP_KERNEL); - if (!w->kcontrols) + if (!w->kcontrols) { + mutex_unlock(&dapm->card->dapm_mutex); return -ENOMEM; + } } switch(w->id) { @@ -2279,6 +2411,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) } dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&dapm->card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -2338,6 +2471,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2364,7 +2498,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, /* old connection must be powered down */ connect = invert ? 1 : 0; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { @@ -2380,13 +2514,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, update.val = val; widget->dapm->update = &update; - snd_soc_dapm_mixer_update_power(widget, kcontrol, connect); + soc_dapm_mixer_update_power(widget, kcontrol, connect); widget->dapm->update = NULL; } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -2435,6 +2569,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask, bitmask; @@ -2455,7 +2590,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mask |= (bitmask - 1) << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { @@ -2471,13 +2606,13 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.val = val; widget->dapm->update = &update; - snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e); + soc_dapm_mux_update_power(widget, kcontrol, mux, e); widget->dapm->update = NULL; } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -2514,6 +2649,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; @@ -2523,7 +2659,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); change = widget->value != ucontrol->value.enumerated.item[0]; if (change) { @@ -2532,11 +2668,11 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, widget->value = ucontrol->value.enumerated.item[0]; - snd_soc_dapm_mux_update_power(widget, kcontrol, widget->value, e); + soc_dapm_mux_update_power(widget, kcontrol, widget->value, e); } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); @@ -2601,6 +2737,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask; @@ -2619,7 +2756,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { @@ -2635,13 +2772,13 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.val = val; widget->dapm->update = &update; - snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e); + soc_dapm_mux_update_power(widget, kcontrol, mux, e); widget->dapm->update = NULL; } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -2678,12 +2815,12 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock(&card->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); ucontrol->value.integer.value[0] = snd_soc_dapm_get_pin_status(&card->dapm, pin); - mutex_unlock(&card->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } @@ -2701,17 +2838,16 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock(&card->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); if (ucontrol->value.integer.value[0]) snd_soc_dapm_enable_pin(&card->dapm, pin); else snd_soc_dapm_disable_pin(&card->dapm, pin); - snd_soc_dapm_sync(&card->dapm); - - mutex_unlock(&card->mutex); + mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_sync(&card->dapm); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); @@ -2729,9 +2865,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, switch (w->id) { case snd_soc_dapm_regulator_supply: - w->priv = devm_regulator_get(dapm->dev, w->name); - if (IS_ERR(w->priv)) { - ret = PTR_ERR(w->priv); + w->regulator = devm_regulator_get(dapm->dev, w->name); + if (IS_ERR(w->regulator)) { + ret = PTR_ERR(w->regulator); dev_err(dapm->dev, "Failed to request %s: %d\n", w->name, ret); return NULL; @@ -2783,6 +2919,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_hp: case snd_soc_dapm_mic: case snd_soc_dapm_line: + case snd_soc_dapm_dai_link: w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_supply: @@ -2828,21 +2965,177 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; int i; + int ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { w = snd_soc_dapm_new_control(dapm, widget); if (!w) { dev_err(dapm->dev, "ASoC: Failed to create DAPM control %s\n", widget->name); - return -ENOMEM; + ret = -ENOMEM; + break; } widget++; } - return 0; + mutex_unlock(&dapm->card->dapm_mutex); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); +static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_dapm_path *source_p, *sink_p; + struct snd_soc_dai *source, *sink; + const struct snd_soc_pcm_stream *config = w->params; + struct snd_pcm_substream substream; + struct snd_pcm_hw_params *params = NULL; + u64 fmt; + int ret; + + BUG_ON(!config); + BUG_ON(list_empty(&w->sources) || list_empty(&w->sinks)); + + /* We only support a single source and sink, pick the first */ + source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path, + list_sink); + sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path, + list_source); + + BUG_ON(!source_p || !sink_p); + BUG_ON(!sink_p->source || !source_p->sink); + BUG_ON(!source_p->source || !sink_p->sink); + + source = source_p->source->priv; + sink = sink_p->sink->priv; + + /* Be a little careful as we don't want to overflow the mask array */ + if (config->formats) { + fmt = ffs(config->formats) - 1; + } else { + dev_warn(w->dapm->dev, "Invalid format %llx specified\n", + config->formats); + fmt = 0; + } + + /* Currently very limited parameter selection */ + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + ret = -ENOMEM; + goto out; + } + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt); + + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min = + config->rate_min; + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max = + config->rate_max; + + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min + = config->channels_min; + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max + = config->channels_max; + + memset(&substream, 0, sizeof(substream)); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (source->driver->ops && source->driver->ops->hw_params) { + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + ret = source->driver->ops->hw_params(&substream, + params, source); + if (ret != 0) { + dev_err(source->dev, + "hw_params() failed: %d\n", ret); + goto out; + } + } + + if (sink->driver->ops && sink->driver->ops->hw_params) { + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + ret = sink->driver->ops->hw_params(&substream, params, + sink); + if (ret != 0) { + dev_err(sink->dev, + "hw_params() failed: %d\n", ret); + goto out; + } + } + break; + + case SND_SOC_DAPM_POST_PMU: + ret = snd_soc_dai_digital_mute(sink, 0); + if (ret != 0 && ret != -ENOTSUPP) + dev_warn(sink->dev, "Failed to unmute: %d\n", ret); + ret = 0; + break; + + case SND_SOC_DAPM_PRE_PMD: + ret = snd_soc_dai_digital_mute(sink, 1); + if (ret != 0 && ret != -ENOTSUPP) + dev_warn(sink->dev, "Failed to mute: %d\n", ret); + ret = 0; + break; + + default: + BUG(); + return -EINVAL; + } + +out: + kfree(params); + return ret; +} + +int snd_soc_dapm_new_pcm(struct snd_soc_card *card, + const struct snd_soc_pcm_stream *params, + struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_route routes[2]; + struct snd_soc_dapm_widget template; + struct snd_soc_dapm_widget *w; + size_t len; + char *link_name; + + len = strlen(source->name) + strlen(sink->name) + 2; + link_name = devm_kzalloc(card->dev, len, GFP_KERNEL); + if (!link_name) + return -ENOMEM; + snprintf(link_name, len, "%s-%s", source->name, sink->name); + + memset(&template, 0, sizeof(template)); + template.reg = SND_SOC_NOPM; + template.id = snd_soc_dapm_dai_link; + template.name = link_name; + template.event = snd_soc_dai_link_event; + template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD; + + dev_dbg(card->dev, "adding %s widget\n", link_name); + + w = snd_soc_dapm_new_control(&card->dapm, &template); + if (!w) { + dev_err(card->dev, "Failed to create %s widget\n", + link_name); + return -ENOMEM; + } + + w->params = params; + + memset(&routes, 0, sizeof(routes)); + + routes[0].source = source->name; + routes[0].sink = link_name; + routes[1].source = link_name; + routes[1].sink = sink->name; + + return snd_soc_dapm_add_routes(&card->dapm, routes, + ARRAY_SIZE(routes)); +} + int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, struct snd_soc_dai *dai) { @@ -2946,37 +3239,61 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) return 0; } -static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, - int stream, struct snd_soc_dai *dai, - int event) +static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, + int event) { - struct snd_soc_dapm_widget *w; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + struct snd_soc_dapm_widget *w_cpu, *w_codec; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; - if (!w) - return; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + w_cpu = cpu_dai->playback_widget; + w_codec = codec_dai->playback_widget; + } else { + w_cpu = cpu_dai->capture_widget; + w_codec = codec_dai->capture_widget; + } - dapm_mark_dirty(w, "stream event"); + if (w_cpu) { - switch (event) { - case SND_SOC_DAPM_STREAM_START: - w->active = 1; - break; - case SND_SOC_DAPM_STREAM_STOP: - w->active = 0; - break; - case SND_SOC_DAPM_STREAM_SUSPEND: - case SND_SOC_DAPM_STREAM_RESUME: - case SND_SOC_DAPM_STREAM_PAUSE_PUSH: - case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: - break; + dapm_mark_dirty(w_cpu, "stream event"); + + switch (event) { + case SND_SOC_DAPM_STREAM_START: + w_cpu->active = 1; + break; + case SND_SOC_DAPM_STREAM_STOP: + w_cpu->active = 0; + break; + case SND_SOC_DAPM_STREAM_SUSPEND: + case SND_SOC_DAPM_STREAM_RESUME: + case SND_SOC_DAPM_STREAM_PAUSE_PUSH: + case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: + break; + } } - dapm_power_widgets(dapm, event); + if (w_codec) { + + dapm_mark_dirty(w_codec, "stream event"); + + switch (event) { + case SND_SOC_DAPM_STREAM_START: + w_codec->active = 1; + break; + case SND_SOC_DAPM_STREAM_STOP: + w_codec->active = 0; + break; + case SND_SOC_DAPM_STREAM_SUSPEND: + case SND_SOC_DAPM_STREAM_RESUME: + case SND_SOC_DAPM_STREAM_PAUSE_PUSH: + case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: + break; + } + } + + dapm_power_widgets(&rtd->card->dapm, event); } /** @@ -2990,15 +3307,14 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, * * Returns 0 for success else error. */ -int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, - struct snd_soc_dai *dai, int event) +void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, + int event) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = rtd->card; - mutex_lock(&codec->mutex); - soc_dapm_stream_event(&codec->dapm, stream, dai, event); - mutex_unlock(&codec->mutex); - return 0; + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + soc_dapm_stream_event(rtd, stream, event); + mutex_unlock(&card->dapm_mutex); } /** diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ee4353f843ea..7f8b3b7428bb 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -36,6 +36,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, struct snd_soc_jack *jack) { + mutex_init(&jack->mutex); jack->codec = codec; INIT_LIST_HEAD(&jack->pins); INIT_LIST_HEAD(&jack->jack_zones); @@ -75,7 +76,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) codec = jack->codec; dapm = &codec->dapm; - mutex_lock(&codec->mutex); + mutex_lock(&jack->mutex); oldstatus = jack->status; @@ -109,7 +110,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) snd_jack_report(jack->jack, jack->status); out: - mutex_unlock(&codec->mutex); + mutex_unlock(&jack->mutex); } EXPORT_SYMBOL_GPL(snd_soc_jack_report); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 0ad8dcacd2f3..bedd1717a373 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -22,12 +22,38 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/workqueue.h> +#include <linux/export.h> +#include <linux/debugfs.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-dpcm.h> #include <sound/initval.h> +#define DPCM_MAX_BE_USERS 8 + +/* DPCM stream event, send event to FE and all active BEs. */ +static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, + int event) +{ + struct snd_soc_dpcm *dpcm; + + list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + + dev_dbg(be->dev, "pm: BE %s event %d dir %d\n", + be->dai_link->name, event, dir); + + snd_soc_dapm_stream_event(be, dir, event); + } + + snd_soc_dapm_stream_event(fe, dir, event); + + return 0; +} + static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, struct snd_soc_dai *soc_dai) { @@ -156,6 +182,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } + /* Dynamic PCM DAI links compat checks use dynamic capabilities */ + if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) + goto dynamic; + /* Check that the codec and cpu DAIs are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { runtime->hw.rate_min = @@ -248,6 +278,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, runtime->hw.rate_max); +dynamic: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { cpu_dai->playback_active++; codec_dai->playback_active++; @@ -308,7 +339,7 @@ static void close_delayed_work(struct work_struct *work) if (codec_dai->pop_wait == 1) { codec_dai->pop_wait = 0; snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, - codec_dai, SND_SOC_DAPM_STREAM_STOP); + SND_SOC_DAPM_STREAM_STOP); } mutex_unlock(&rtd->pcm_mutex); @@ -373,7 +404,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) /* powered down playback stream now */ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, - codec_dai, SND_SOC_DAPM_STREAM_STOP); } else { /* start delayed pop wq here for playback streams */ @@ -384,7 +414,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) } else { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, - codec_dai, SND_SOC_DAPM_STREAM_STOP); + SND_SOC_DAPM_STREAM_STOP); } mutex_unlock(&rtd->pcm_mutex); @@ -453,8 +483,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) cancel_delayed_work(&rtd->delayed_work); } - snd_soc_dapm_stream_event(rtd, substream->stream, codec_dai, - SND_SOC_DAPM_STREAM_START); + snd_soc_dapm_stream_event(rtd, substream->stream, + SND_SOC_DAPM_STREAM_START); snd_soc_dai_digital_mute(codec_dai, 0); @@ -602,6 +632,34 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } +static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + if (codec_dai->driver->ops->bespoke_trigger) { + ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai); + if (ret < 0) + return ret; + } + + if (platform->driver->bespoke_trigger) { + ret = platform->driver->bespoke_trigger(substream, cmd); + if (ret < 0) + return ret; + } + + if (cpu_dai->driver->ops->bespoke_trigger) { + ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai); + if (ret < 0) + return ret; + } + return 0; +} /* * soc level wrapper for pointer callback * If cpu_dai, codec_dai, platform driver has the delay callback, than @@ -634,6 +692,1308 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) return offset; } +/* connect a FE and BE */ +static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + struct snd_soc_dpcm *dpcm; + + /* only add new dpcms */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + if (dpcm->be == be && dpcm->fe == fe) + return 0; + } + + dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); + if (!dpcm) + return -ENOMEM; + + dpcm->be = be; + dpcm->fe = fe; + be->dpcm[stream].runtime = fe->dpcm[stream].runtime; + dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; + list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); + list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); + + dev_dbg(fe->dev, " connected new DPCM %s path %s %s %s\n", + stream ? "capture" : "playback", fe->dai_link->name, + stream ? "<-" : "->", be->dai_link->name); + +#ifdef CONFIG_DEBUG_FS + dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644, + fe->debugfs_dpcm_root, &dpcm->state); +#endif + return 1; +} + +/* reparent a BE onto another FE */ +static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + struct snd_soc_dpcm *dpcm; + struct snd_pcm_substream *fe_substream, *be_substream; + + /* reparent if BE is connected to other FEs */ + if (!be->dpcm[stream].users) + return; + + be_substream = snd_soc_dpcm_get_substream(be, stream); + + list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { + if (dpcm->fe == fe) + continue; + + dev_dbg(fe->dev, " reparent %s path %s %s %s\n", + stream ? "capture" : "playback", + dpcm->fe->dai_link->name, + stream ? "<-" : "->", dpcm->be->dai_link->name); + + fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream); + be_substream->runtime = fe_substream->runtime; + break; + } +} + +/* disconnect a BE and FE */ +static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_soc_dpcm *dpcm, *d; + + list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) { + dev_dbg(fe->dev, "BE %s disconnect check for %s\n", + stream ? "capture" : "playback", + dpcm->be->dai_link->name); + + if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE) + continue; + + dev_dbg(fe->dev, " freed DSP %s path %s %s %s\n", + stream ? "capture" : "playback", fe->dai_link->name, + stream ? "<-" : "->", dpcm->be->dai_link->name); + + /* BEs still alive need new FE */ + dpcm_be_reparent(fe, dpcm->be, stream); + +#ifdef CONFIG_DEBUG_FS + debugfs_remove(dpcm->debugfs_state); +#endif + list_del(&dpcm->list_be); + list_del(&dpcm->list_fe); + kfree(dpcm); + } +} + +/* get BE for DAI widget and stream */ +static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, + struct snd_soc_dapm_widget *widget, int stream) +{ + struct snd_soc_pcm_runtime *be; + int i; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < card->num_links; i++) { + be = &card->rtd[i]; + + if (be->cpu_dai->playback_widget == widget || + be->codec_dai->playback_widget == widget) + return be; + } + } else { + + for (i = 0; i < card->num_links; i++) { + be = &card->rtd[i]; + + if (be->cpu_dai->capture_widget == widget || + be->codec_dai->capture_widget == widget) + return be; + } + } + + dev_err(card->dev, "can't get %s BE for %s\n", + stream ? "capture" : "playback", widget->name); + return NULL; +} + +static inline struct snd_soc_dapm_widget * + rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream) +{ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + return rtd->cpu_dai->playback_widget; + else + return rtd->cpu_dai->capture_widget; +} + +static inline struct snd_soc_dapm_widget * + rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream) +{ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + return rtd->codec_dai->playback_widget; + else + return rtd->codec_dai->capture_widget; +} + +static int widget_in_list(struct snd_soc_dapm_widget_list *list, + struct snd_soc_dapm_widget *widget) +{ + int i; + + for (i = 0; i < list->num_widgets; i++) { + if (widget == list->widgets[i]) + return 1; + } + + return 0; +} + +static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, + int stream, struct snd_soc_dapm_widget_list **list_) +{ + struct snd_soc_dai *cpu_dai = fe->cpu_dai; + struct snd_soc_dapm_widget_list *list; + int paths; + + list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) + + sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL); + if (list == NULL) + return -ENOMEM; + + /* get number of valid DAI paths and their widgets */ + paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list); + + dev_dbg(fe->dev, "found %d audio %s paths\n", paths, + stream ? "capture" : "playback"); + + *list_ = list; + return paths; +} + +static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) +{ + kfree(*list); +} + +static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, + struct snd_soc_dapm_widget_list **list_) +{ + struct snd_soc_dpcm *dpcm; + struct snd_soc_dapm_widget_list *list = *list_; + struct snd_soc_dapm_widget *widget; + int prune = 0; + + /* Destroy any old FE <--> BE connections */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + + /* is there a valid CPU DAI widget for this BE */ + widget = rtd_get_cpu_widget(dpcm->be, stream); + + /* prune the BE if it's no longer in our active list */ + if (widget && widget_in_list(list, widget)) + continue; + + /* is there a valid CODEC DAI widget for this BE */ + widget = rtd_get_codec_widget(dpcm->be, stream); + + /* prune the BE if it's no longer in our active list */ + if (widget && widget_in_list(list, widget)) + continue; + + dev_dbg(fe->dev, "pruning %s BE %s for %s\n", + stream ? "capture" : "playback", + dpcm->be->dai_link->name, fe->dai_link->name); + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; + prune++; + } + + dev_dbg(fe->dev, "found %d old BE paths for pruning\n", prune); + return prune; +} + +static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, + struct snd_soc_dapm_widget_list **list_) +{ + struct snd_soc_card *card = fe->card; + struct snd_soc_dapm_widget_list *list = *list_; + struct snd_soc_pcm_runtime *be; + int i, new = 0, err; + + /* Create any new FE <--> BE connections */ + for (i = 0; i < list->num_widgets; i++) { + + if (list->widgets[i]->id != snd_soc_dapm_dai) + continue; + + /* is there a valid BE rtd for this widget */ + be = dpcm_get_be(card, list->widgets[i], stream); + if (!be) { + dev_err(fe->dev, "no BE found for %s\n", + list->widgets[i]->name); + continue; + } + + /* make sure BE is a real BE */ + if (!be->dai_link->no_pcm) + continue; + + /* don't connect if FE is not running */ + if (!fe->dpcm[stream].runtime) + continue; + + /* newly connected FE and BE */ + err = dpcm_be_connect(fe, be, stream); + if (err < 0) { + dev_err(fe->dev, "can't connect %s\n", + list->widgets[i]->name); + break; + } else if (err == 0) /* already connected */ + continue; + + /* new */ + be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; + new++; + } + + dev_dbg(fe->dev, "found %d new BE paths\n", new); + return new; +} + +/* + * Find the corresponding BE DAIs that source or sink audio to this + * FE substream. + */ +static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, + int stream, struct snd_soc_dapm_widget_list **list, int new) +{ + if (new) + return dpcm_add_paths(fe, stream, list); + else + return dpcm_prune_paths(fe, stream, list); +} + +static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_soc_dpcm *dpcm; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->be->dpcm[stream].runtime_update = + SND_SOC_DPCM_UPDATE_NO; +} + +static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, + int stream) +{ + struct snd_soc_dpcm *dpcm; + + /* disable any enabled and non active backends */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + if (be->dpcm[stream].users == 0) + dev_err(be->dev, "no users %s at close - state %d\n", + stream ? "capture" : "playback", + be->dpcm[stream].state); + + if (--be->dpcm[stream].users != 0) + continue; + + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) + continue; + + soc_pcm_close(be_substream); + be_substream->runtime = NULL; + be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; + } +} + +static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_soc_dpcm *dpcm; + int err, count = 0; + + /* only startup BE DAIs that are either sinks or sources to this FE DAI */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + /* is this op for this BE ? */ + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + + /* first time the dpcm is open ? */ + if (be->dpcm[stream].users == DPCM_MAX_BE_USERS) + dev_err(be->dev, "too many users %s at open %d\n", + stream ? "capture" : "playback", + be->dpcm[stream].state); + + if (be->dpcm[stream].users++ != 0) + continue; + + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) + continue; + + dev_dbg(be->dev, "dpcm: open BE %s\n", be->dai_link->name); + + be_substream->runtime = be->dpcm[stream].runtime; + err = soc_pcm_open(be_substream); + if (err < 0) { + dev_err(be->dev, "BE open failed %d\n", err); + be->dpcm[stream].users--; + if (be->dpcm[stream].users < 0) + dev_err(be->dev, "no users %s at unwind %d\n", + stream ? "capture" : "playback", + be->dpcm[stream].state); + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; + goto unwind; + } + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; + count++; + } + + return count; + +unwind: + /* disable any enabled and non active backends */ + list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + + if (be->dpcm[stream].users == 0) + dev_err(be->dev, "no users %s at close %d\n", + stream ? "capture" : "playback", + be->dpcm[stream].state); + + if (--be->dpcm[stream].users != 0) + continue; + + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) + continue; + + soc_pcm_close(be_substream); + be_substream->runtime = NULL; + be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; + } + + return err; +} + +static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + runtime->hw.rate_min = cpu_dai_drv->playback.rate_min; + runtime->hw.rate_max = cpu_dai_drv->playback.rate_max; + runtime->hw.channels_min = cpu_dai_drv->playback.channels_min; + runtime->hw.channels_max = cpu_dai_drv->playback.channels_max; + runtime->hw.formats &= cpu_dai_drv->playback.formats; + runtime->hw.rates = cpu_dai_drv->playback.rates; + } else { + runtime->hw.rate_min = cpu_dai_drv->capture.rate_min; + runtime->hw.rate_max = cpu_dai_drv->capture.rate_max; + runtime->hw.channels_min = cpu_dai_drv->capture.channels_min; + runtime->hw.channels_max = cpu_dai_drv->capture.channels_max; + runtime->hw.formats &= cpu_dai_drv->capture.formats; + runtime->hw.rates = cpu_dai_drv->capture.rates; + } +} + +static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) +{ + struct snd_soc_pcm_runtime *fe = fe_substream->private_data; + struct snd_pcm_runtime *runtime = fe_substream->runtime; + int stream = fe_substream->stream, ret = 0; + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_startup(fe, fe_substream->stream); + if (ret < 0) { + dev_err(fe->dev,"dpcm: failed to start some BEs %d\n", ret); + goto be_err; + } + + dev_dbg(fe->dev, "dpcm: open FE %s\n", fe->dai_link->name); + + /* start the DAI frontend */ + ret = soc_pcm_open(fe_substream); + if (ret < 0) { + dev_err(fe->dev,"dpcm: failed to start FE %d\n", ret); + goto unwind; + } + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; + + dpcm_set_fe_runtime(fe_substream); + snd_pcm_limit_hw_rates(runtime); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + return 0; + +unwind: + dpcm_be_dai_startup_unwind(fe, fe_substream->stream); +be_err: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + return ret; +} + +static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_soc_dpcm *dpcm; + + /* only shutdown BEs that are either sinks or sources to this FE DAI */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + /* is this op for this BE ? */ + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + + if (be->dpcm[stream].users == 0) + dev_err(be->dev, "no users %s at close - state %d\n", + stream ? "capture" : "playback", + be->dpcm[stream].state); + + if (--be->dpcm[stream].users != 0) + continue; + + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) + continue; + + dev_dbg(be->dev, "dpcm: close BE %s\n", + dpcm->fe->dai_link->name); + + soc_pcm_close(be_substream); + be_substream->runtime = NULL; + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; + } + return 0; +} + +static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int stream = substream->stream; + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + /* shutdown the BEs */ + dpcm_be_dai_shutdown(fe, substream->stream); + + dev_dbg(fe->dev, "dpcm: close FE %s\n", fe->dai_link->name); + + /* now shutdown the frontend */ + soc_pcm_close(substream); + + /* run the stream event for each BE */ + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + return 0; +} + +static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_soc_dpcm *dpcm; + + /* only hw_params backends that are either sinks or sources + * to this frontend DAI */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + /* is this op for this BE ? */ + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + + /* only free hw when no longer used - check all FEs */ + if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) + continue; + + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + continue; + + dev_dbg(be->dev, "dpcm: hw_free BE %s\n", + dpcm->fe->dai_link->name); + + soc_pcm_hw_free(be_substream); + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; + } + + return 0; +} + +static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int err, stream = substream->stream; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + dev_dbg(fe->dev, "dpcm: hw_free FE %s\n", fe->dai_link->name); + + /* call hw_free on the frontend */ + err = soc_pcm_hw_free(substream); + if (err < 0) + dev_err(fe->dev,"dpcm: hw_free FE %s failed\n", + fe->dai_link->name); + + /* only hw_params backends that are either sinks or sources + * to this frontend DAI */ + err = dpcm_be_dai_hw_free(fe, stream); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + mutex_unlock(&fe->card->mutex); + return 0; +} + +static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_soc_dpcm *dpcm; + int ret; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + /* is this op for this BE ? */ + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + + /* only allow hw_params() if no connected FEs are running */ + if (!snd_soc_dpcm_can_be_params(fe, be, stream)) + continue; + + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) + continue; + + dev_dbg(be->dev, "dpcm: hw_params BE %s\n", + dpcm->fe->dai_link->name); + + /* copy params for each dpcm */ + memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params, + sizeof(struct snd_pcm_hw_params)); + + /* perform any hw_params fixups */ + if (be->dai_link->be_hw_params_fixup) { + ret = be->dai_link->be_hw_params_fixup(be, + &dpcm->hw_params); + if (ret < 0) { + dev_err(be->dev, + "dpcm: hw_params BE fixup failed %d\n", + ret); + goto unwind; + } + } + + ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); + if (ret < 0) { + dev_err(dpcm->be->dev, + "dpcm: hw_params BE failed %d\n", ret); + goto unwind; + } + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; + } + return 0; + +unwind: + /* disable any enabled and non active backends */ + list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + + /* only allow hw_free() if no connected FEs are running */ + if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) + continue; + + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + continue; + + soc_pcm_hw_free(be_substream); + } + + return ret; +} + +static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int ret, stream = substream->stream; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + memcpy(&fe->dpcm[substream->stream].hw_params, params, + sizeof(struct snd_pcm_hw_params)); + ret = dpcm_be_dai_hw_params(fe, substream->stream); + if (ret < 0) { + dev_err(fe->dev,"dpcm: hw_params BE failed %d\n", ret); + goto out; + } + + dev_dbg(fe->dev, "dpcm: hw_params FE %s rate %d chan %x fmt %d\n", + fe->dai_link->name, params_rate(params), + params_channels(params), params_format(params)); + + /* call hw_params on the frontend */ + ret = soc_pcm_hw_params(substream, params); + if (ret < 0) { + dev_err(fe->dev,"dpcm: hw_params FE failed %d\n", ret); + dpcm_be_dai_hw_free(fe, stream); + } else + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; + +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + return ret; +} + +static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, + struct snd_pcm_substream *substream, int cmd) +{ + int ret; + + dev_dbg(dpcm->be->dev, "dpcm: trigger BE %s cmd %d\n", + dpcm->fe->dai_link->name, cmd); + + ret = soc_pcm_trigger(substream, cmd); + if (ret < 0) + dev_err(dpcm->be->dev,"dpcm: trigger BE failed %d\n", ret); + + return ret; +} + +static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, + int cmd) +{ + struct snd_soc_dpcm *dpcm; + int ret = 0; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + /* is this op for this BE ? */ + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + continue; + + ret = dpcm_do_trigger(dpcm, be_substream, cmd); + if (ret) + return ret; + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; + break; + case SNDRV_PCM_TRIGGER_RESUME: + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) + continue; + + ret = dpcm_do_trigger(dpcm, be_substream, cmd); + if (ret) + return ret; + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) + continue; + + ret = dpcm_do_trigger(dpcm, be_substream, cmd); + if (ret) + return ret; + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) + continue; + + if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) + continue; + + ret = dpcm_do_trigger(dpcm, be_substream, cmd); + if (ret) + return ret; + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) + continue; + + if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) + continue; + + ret = dpcm_do_trigger(dpcm, be_substream, cmd); + if (ret) + return ret; + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) + continue; + + if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) + continue; + + ret = dpcm_do_trigger(dpcm, be_substream, cmd); + if (ret) + return ret; + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); + +static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int stream = substream->stream, ret; + enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + switch (trigger) { + case SND_SOC_DPCM_TRIGGER_PRE: + /* call trigger on the frontend before the backend. */ + + dev_dbg(fe->dev, "dpcm: pre trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = soc_pcm_trigger(substream, cmd); + if (ret < 0) { + dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret); + goto out; + } + + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); + break; + case SND_SOC_DPCM_TRIGGER_POST: + /* call trigger on the frontend after the backend. */ + + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); + if (ret < 0) { + dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret); + goto out; + } + + dev_dbg(fe->dev, "dpcm: post trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = soc_pcm_trigger(substream, cmd); + break; + case SND_SOC_DPCM_TRIGGER_BESPOKE: + /* bespoke trigger() - handles both FE and BEs */ + + dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = soc_pcm_bespoke_trigger(substream, cmd); + if (ret < 0) { + dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret); + goto out; + } + break; + default: + dev_err(fe->dev, "dpcm: invalid trigger cmd %d for %s\n", cmd, + fe->dai_link->name); + ret = -EINVAL; + goto out; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; + break; + } + +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + return ret; +} + +static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_soc_dpcm *dpcm; + int ret = 0; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + + /* is this op for this BE ? */ + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + continue; + + dev_dbg(be->dev, "dpcm: prepare BE %s\n", + dpcm->fe->dai_link->name); + + ret = soc_pcm_prepare(be_substream); + if (ret < 0) { + dev_err(be->dev, "dpcm: backend prepare failed %d\n", + ret); + break; + } + + be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; + } + return ret; +} + +static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int stream = substream->stream, ret = 0; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + dev_dbg(fe->dev, "dpcm: prepare FE %s\n", fe->dai_link->name); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + /* there is no point preparing this FE if there are no BEs */ + if (list_empty(&fe->dpcm[stream].be_clients)) { + dev_err(fe->dev, "dpcm: no backend DAIs enabled for %s\n", + fe->dai_link->name); + ret = -EINVAL; + goto out; + } + + ret = dpcm_be_dai_prepare(fe, substream->stream); + if (ret < 0) + goto out; + + /* call prepare on the frontend */ + ret = soc_pcm_prepare(substream); + if (ret < 0) { + dev_err(fe->dev,"dpcm: prepare FE %s failed\n", + fe->dai_link->name); + goto out; + } + + /* run the stream event for each BE */ + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; + +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + + return ret; +} + +static int soc_pcm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + + if (platform->driver->ops->ioctl) + return platform->driver->ops->ioctl(substream, cmd, arg); + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_pcm_substream *substream = + snd_soc_dpcm_get_substream(fe, stream); + enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; + int err; + + dev_dbg(fe->dev, "runtime %s close on FE %s\n", + stream ? "capture" : "playback", fe->dai_link->name); + + if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { + /* call bespoke trigger - FE takes care of all BE triggers */ + dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd stop\n", + fe->dai_link->name); + + err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP); + if (err < 0) + dev_err(fe->dev,"dpcm: trigger FE failed %d\n", err); + } else { + dev_dbg(fe->dev, "dpcm: trigger FE %s cmd stop\n", + fe->dai_link->name); + + err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); + if (err < 0) + dev_err(fe->dev,"dpcm: trigger FE failed %d\n", err); + } + + err = dpcm_be_dai_hw_free(fe, stream); + if (err < 0) + dev_err(fe->dev,"dpcm: hw_free FE failed %d\n", err); + + err = dpcm_be_dai_shutdown(fe, stream); + if (err < 0) + dev_err(fe->dev,"dpcm: shutdown FE failed %d\n", err); + + /* run the stream event for each BE */ + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); + + return 0; +} + +static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_pcm_substream *substream = + snd_soc_dpcm_get_substream(fe, stream); + struct snd_soc_dpcm *dpcm; + enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; + int ret; + + dev_dbg(fe->dev, "runtime %s open on FE %s\n", + stream ? "capture" : "playback", fe->dai_link->name); + + /* Only start the BE if the FE is ready */ + if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE || + fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE) + return -EINVAL; + + /* startup must always be called for new BEs */ + ret = dpcm_be_dai_startup(fe, stream); + if (ret < 0) { + goto disconnect; + return ret; + } + + /* keep going if FE state is > open */ + if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) + return 0; + + ret = dpcm_be_dai_hw_params(fe, stream); + if (ret < 0) { + goto close; + return ret; + } + + /* keep going if FE state is > hw_params */ + if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) + return 0; + + + ret = dpcm_be_dai_prepare(fe, stream); + if (ret < 0) { + goto hw_free; + return ret; + } + + /* run the stream event for each BE */ + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); + + /* keep going if FE state is > prepare */ + if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE || + fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP) + return 0; + + if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { + /* call trigger on the frontend - FE takes care of all BE triggers */ + dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd start\n", + fe->dai_link->name); + + ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(fe->dev,"dpcm: bespoke trigger FE failed %d\n", ret); + goto hw_free; + } + } else { + dev_dbg(fe->dev, "dpcm: trigger FE %s cmd start\n", + fe->dai_link->name); + + ret = dpcm_be_dai_trigger(fe, stream, + SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret); + goto hw_free; + } + } + + return 0; + +hw_free: + dpcm_be_dai_hw_free(fe, stream); +close: + dpcm_be_dai_shutdown(fe, stream); +disconnect: + /* disconnect any non started BEs */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + } + + return ret; +} + +static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream) +{ + int ret; + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; + ret = dpcm_run_update_startup(fe, stream); + if (ret < 0) + dev_err(fe->dev, "failed to startup some BEs\n"); + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + return ret; +} + +static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) +{ + int ret; + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; + ret = dpcm_run_update_shutdown(fe, stream); + if (ret < 0) + dev_err(fe->dev, "failed to shutdown some BEs\n"); + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + return ret; +} + +/* Called by DAPM mixer/mux changes to update audio routing between PCMs and + * any DAI links. + */ +int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget) +{ + struct snd_soc_card *card; + int i, old, new, paths; + + if (widget->codec) + card = widget->codec->card; + else if (widget->platform) + card = widget->platform->card; + else + return -EINVAL; + + mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_dapm_widget_list *list; + struct snd_soc_pcm_runtime *fe = &card->rtd[i]; + + /* make sure link is FE */ + if (!fe->dai_link->dynamic) + continue; + + /* only check active links */ + if (!fe->cpu_dai->active) + continue; + + /* DAPM sync will call this to update DSP paths */ + dev_dbg(fe->dev, "DPCM runtime update for FE %s\n", + fe->dai_link->name); + + /* skip if FE doesn't have playback capability */ + if (!fe->cpu_dai->driver->playback.channels_min) + goto capture; + + paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); + if (paths < 0) { + dev_warn(fe->dev, "%s no valid %s path\n", + fe->dai_link->name, "playback"); + mutex_unlock(&card->mutex); + return paths; + } + + /* update any new playback paths */ + new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1); + if (new) { + dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); + dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); + dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); + } + + /* update any old playback paths */ + old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0); + if (old) { + dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK); + dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); + dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); + } + +capture: + /* skip if FE doesn't have capture capability */ + if (!fe->cpu_dai->driver->capture.channels_min) + continue; + + paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); + if (paths < 0) { + dev_warn(fe->dev, "%s no valid %s path\n", + fe->dai_link->name, "capture"); + mutex_unlock(&card->mutex); + return paths; + } + + /* update any new capture paths */ + new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1); + if (new) { + dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE); + dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); + dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); + } + + /* update any old capture paths */ + old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0); + if (old) { + dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE); + dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); + dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); + } + + dpcm_path_put(&list); + } + + mutex_unlock(&card->mutex); + return 0; +} +int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) +{ + struct snd_soc_dpcm *dpcm; + struct list_head *clients = + &fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients; + + list_for_each_entry(dpcm, clients, list_be) { + + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai = be->codec_dai; + struct snd_soc_dai_driver *drv = dai->driver; + + if (be->dai_link->ignore_suspend) + continue; + + dev_dbg(be->dev, "BE digital mute %s\n", be->dai_link->name); + + if (drv->ops->digital_mute && dai->playback_active) + drv->ops->digital_mute(dai, mute); + } + + return 0; +} + +static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) +{ + struct snd_soc_pcm_runtime *fe = fe_substream->private_data; + struct snd_soc_dpcm *dpcm; + struct snd_soc_dapm_widget_list *list; + int ret; + int stream = fe_substream->stream; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + fe->dpcm[stream].runtime = fe_substream->runtime; + + if (dpcm_path_get(fe, stream, &list) <= 0) { + dev_warn(fe->dev, "asoc: %s no valid %s route\n", + fe->dai_link->name, stream ? "capture" : "playback"); + mutex_unlock(&fe->card->mutex); + return -EINVAL; + } + + /* calculate valid and active FE <-> BE dpcms */ + dpcm_process_paths(fe, stream, &list, 1); + + ret = dpcm_fe_dai_startup(fe_substream); + if (ret < 0) { + /* clean up all links */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + dpcm_be_disconnect(fe, stream); + fe->dpcm[stream].runtime = NULL; + } + + dpcm_clear_pending_state(fe, stream); + dpcm_path_put(&list); + mutex_unlock(&fe->card->mutex); + return ret; +} + +static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) +{ + struct snd_soc_pcm_runtime *fe = fe_substream->private_data; + struct snd_soc_dpcm *dpcm; + int stream = fe_substream->stream, ret; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + ret = dpcm_fe_dai_shutdown(fe_substream); + + /* mark FE's links ready to prune */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + dpcm_be_disconnect(fe, stream); + + fe->dpcm[stream].runtime = NULL; + mutex_unlock(&fe->card->mutex); + return ret; +} + /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { @@ -641,56 +2001,94 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_pcm_ops *soc_pcm_ops = &rtd->ops; struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; - soc_pcm_ops->open = soc_pcm_open; - soc_pcm_ops->close = soc_pcm_close; - soc_pcm_ops->hw_params = soc_pcm_hw_params; - soc_pcm_ops->hw_free = soc_pcm_hw_free; - soc_pcm_ops->prepare = soc_pcm_prepare; - soc_pcm_ops->trigger = soc_pcm_trigger; - soc_pcm_ops->pointer = soc_pcm_pointer; - - /* check client and interface hw capabilities */ - snprintf(new_name, sizeof(new_name), "%s %s-%d", - rtd->dai_link->stream_name, codec_dai->name, num); - - if (codec_dai->driver->playback.channels_min) - playback = 1; - if (codec_dai->driver->capture.channels_min) - capture = 1; - - dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); - ret = snd_pcm_new(rtd->card->snd_card, new_name, - num, playback, capture, &pcm); + if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { + if (cpu_dai->driver->playback.channels_min) + playback = 1; + if (cpu_dai->driver->capture.channels_min) + capture = 1; + } else { + if (codec_dai->driver->playback.channels_min) + playback = 1; + if (codec_dai->driver->capture.channels_min) + capture = 1; + } + + /* create the PCM */ + if (rtd->dai_link->no_pcm) { + snprintf(new_name, sizeof(new_name), "(%s)", + rtd->dai_link->stream_name); + + ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, + playback, capture, &pcm); + } else { + if (rtd->dai_link->dynamic) + snprintf(new_name, sizeof(new_name), "%s (*)", + rtd->dai_link->stream_name); + else + snprintf(new_name, sizeof(new_name), "%s %s-%d", + rtd->dai_link->stream_name, codec_dai->name, num); + + ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, + capture, &pcm); + } if (ret < 0) { printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); return ret; } + dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num, new_name); /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); rtd->pcm = pcm; pcm->private_data = rtd; + + if (rtd->dai_link->no_pcm) { + if (playback) + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; + if (capture) + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; + goto out; + } + + /* ASoC PCM operations */ + if (rtd->dai_link->dynamic) { + rtd->ops.open = dpcm_fe_dai_open; + rtd->ops.hw_params = dpcm_fe_dai_hw_params; + rtd->ops.prepare = dpcm_fe_dai_prepare; + rtd->ops.trigger = dpcm_fe_dai_trigger; + rtd->ops.hw_free = dpcm_fe_dai_hw_free; + rtd->ops.close = dpcm_fe_dai_close; + rtd->ops.pointer = soc_pcm_pointer; + rtd->ops.ioctl = soc_pcm_ioctl; + } else { + rtd->ops.open = soc_pcm_open; + rtd->ops.hw_params = soc_pcm_hw_params; + rtd->ops.prepare = soc_pcm_prepare; + rtd->ops.trigger = soc_pcm_trigger; + rtd->ops.hw_free = soc_pcm_hw_free; + rtd->ops.close = soc_pcm_close; + rtd->ops.pointer = soc_pcm_pointer; + rtd->ops.ioctl = soc_pcm_ioctl; + } + if (platform->driver->ops) { - soc_pcm_ops->mmap = platform->driver->ops->mmap; - soc_pcm_ops->pointer = platform->driver->ops->pointer; - soc_pcm_ops->ioctl = platform->driver->ops->ioctl; - soc_pcm_ops->copy = platform->driver->ops->copy; - soc_pcm_ops->silence = platform->driver->ops->silence; - soc_pcm_ops->ack = platform->driver->ops->ack; - soc_pcm_ops->page = platform->driver->ops->page; + rtd->ops.ack = platform->driver->ops->ack; + rtd->ops.copy = platform->driver->ops->copy; + rtd->ops.silence = platform->driver->ops->silence; + rtd->ops.page = platform->driver->ops->page; + rtd->ops.mmap = platform->driver->ops->mmap; } if (playback) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops); if (capture) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); if (platform->driver->pcm_new) { ret = platform->driver->pcm_new(rtd); @@ -701,7 +2099,257 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) } pcm->private_free = platform->driver->pcm_free; +out: printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); return ret; } + +/* is the current PCM operation for this FE ? */ +int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream) +{ + if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) + return 1; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update); + +/* is the current PCM operation for this BE ? */ +int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) || + ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) && + be->dpcm[stream].runtime_update)) + return 1; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update); + +/* get the substream for this BE */ +struct snd_pcm_substream * + snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream) +{ + return be->pcm->streams[stream].substream; +} +EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream); + +/* get the BE runtime state */ +enum snd_soc_dpcm_state + snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream) +{ + return be->dpcm[stream].state; +} +EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state); + +/* set the BE runtime state */ +void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, + int stream, enum snd_soc_dpcm_state state) +{ + be->dpcm[stream].state = state; +} +EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state); + +/* + * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE + * are not running, paused or suspended for the specified stream direction. + */ +int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + struct snd_soc_dpcm *dpcm; + int state; + + list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { + + if (dpcm->fe == fe) + continue; + + state = dpcm->fe->dpcm[stream].state; + if (state == SND_SOC_DPCM_STATE_START || + state == SND_SOC_DPCM_STATE_PAUSED || + state == SND_SOC_DPCM_STATE_SUSPEND) + return 0; + } + + /* it's safe to free/stop this BE DAI */ + return 1; +} +EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop); + +/* + * We can only change hw params a BE DAI if any of it's FE are not prepared, + * running, paused or suspended for the specified stream direction. + */ +int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + struct snd_soc_dpcm *dpcm; + int state; + + list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { + + if (dpcm->fe == fe) + continue; + + state = dpcm->fe->dpcm[stream].state; + if (state == SND_SOC_DPCM_STATE_START || + state == SND_SOC_DPCM_STATE_PAUSED || + state == SND_SOC_DPCM_STATE_SUSPEND || + state == SND_SOC_DPCM_STATE_PREPARE) + return 0; + } + + /* it's safe to change hw_params */ + return 1; +} +EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); + +int snd_soc_platform_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_platform *platform) +{ + if (platform->driver->ops->trigger) + return platform->driver->ops->trigger(substream, cmd); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_platform_trigger); + +#ifdef CONFIG_DEBUG_FS +static char *dpcm_state_string(enum snd_soc_dpcm_state state) +{ + switch (state) { + case SND_SOC_DPCM_STATE_NEW: + return "new"; + case SND_SOC_DPCM_STATE_OPEN: + return "open"; + case SND_SOC_DPCM_STATE_HW_PARAMS: + return "hw_params"; + case SND_SOC_DPCM_STATE_PREPARE: + return "prepare"; + case SND_SOC_DPCM_STATE_START: + return "start"; + case SND_SOC_DPCM_STATE_STOP: + return "stop"; + case SND_SOC_DPCM_STATE_SUSPEND: + return "suspend"; + case SND_SOC_DPCM_STATE_PAUSED: + return "paused"; + case SND_SOC_DPCM_STATE_HW_FREE: + return "hw_free"; + case SND_SOC_DPCM_STATE_CLOSE: + return "close"; + } + + return "unknown"; +} + +static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, + int stream, char *buf, size_t size) +{ + struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; + struct snd_soc_dpcm *dpcm; + ssize_t offset = 0; + + /* FE state */ + offset += snprintf(buf + offset, size - offset, + "[%s - %s]\n", fe->dai_link->name, + stream ? "Capture" : "Playback"); + + offset += snprintf(buf + offset, size - offset, "State: %s\n", + dpcm_state_string(fe->dpcm[stream].state)); + + if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && + (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) + offset += snprintf(buf + offset, size - offset, + "Hardware Params: " + "Format = %s, Channels = %d, Rate = %d\n", + snd_pcm_format_name(params_format(params)), + params_channels(params), + params_rate(params)); + + /* BEs state */ + offset += snprintf(buf + offset, size - offset, "Backends:\n"); + + if (list_empty(&fe->dpcm[stream].be_clients)) { + offset += snprintf(buf + offset, size - offset, + " No active DSP links\n"); + goto out; + } + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + params = &dpcm->hw_params; + + offset += snprintf(buf + offset, size - offset, + "- %s\n", be->dai_link->name); + + offset += snprintf(buf + offset, size - offset, + " State: %s\n", + dpcm_state_string(be->dpcm[stream].state)); + + if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && + (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) + offset += snprintf(buf + offset, size - offset, + " Hardware Params: " + "Format = %s, Channels = %d, Rate = %d\n", + snd_pcm_format_name(params_format(params)), + params_channels(params), + params_rate(params)); + } + +out: + return offset; +} + +static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct snd_soc_pcm_runtime *fe = file->private_data; + ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0; + char *buf; + + buf = kmalloc(out_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (fe->cpu_dai->driver->playback.channels_min) + offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK, + buf + offset, out_count - offset); + + if (fe->cpu_dai->driver->capture.channels_min) + offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE, + buf + offset, out_count - offset); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); + + kfree(buf); + return ret; +} + +static const struct file_operations dpcm_state_fops = { + .open = simple_open, + .read = dpcm_state_read_file, + .llseek = default_llseek, +}; + +int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) +{ + if (!rtd->dai_link) + return 0; + + rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, + rtd->card->debugfs_card_root); + if (!rtd->debugfs_dpcm_root) { + dev_dbg(rtd->dev, + "ASoC: Failed to create dpcm debugfs directory %s\n", + rtd->dai_link->name); + return -EINVAL; + } + + rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444, + rtd->debugfs_dpcm_root, + rtd, &dpcm_state_fops); + + return 0; +} +#endif diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index ce1b773c351f..5331c61faf01 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -1,26 +1,53 @@ config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA + select REGMAP_MMIO help Say Y or M here if you want support for SoC audio on Tegra. -config SND_SOC_TEGRA_I2S +config SND_SOC_TEGRA20_DAS tristate - depends on SND_SOC_TEGRA + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + help + Say Y or M if you want to add support for the Tegra20 DAS module. + You will also need to select the individual machine drivers to + support below. + +config SND_SOC_TEGRA20_I2S + tristate + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA20_DAS help Say Y or M if you want to add support for codecs attached to the - Tegra I2S interface. You will also need to select the individual + Tegra20 I2S interface. You will also need to select the individual machine drivers to support below. -config SND_SOC_TEGRA_SPDIF +config SND_SOC_TEGRA20_SPDIF tristate - depends on SND_SOC_TEGRA + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC default m help - Say Y or M if you want to add support for the SPDIF interface. + Say Y or M if you want to add support for the Tegra20 SPDIF interface. You will also need to select the individual machine drivers to support below. +config SND_SOC_TEGRA30_AHUB + tristate + depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC + help + Say Y or M if you want to add support for the Tegra20 AHUB module. + You will also need to select the individual machine drivers to + support below. + +config SND_SOC_TEGRA30_I2S + tristate + depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC + select SND_SOC_TEGRA30_AHUB + help + Say Y or M if you want to add support for codecs attached to the + Tegra30 I2S interface. You will also need to select the individual + machine drivers to support below. + config MACH_HAS_SND_SOC_TEGRA_WM8903 bool help @@ -32,7 +59,8 @@ config SND_SOC_TEGRA_WM8903 tristate "SoC Audio support for Tegra boards using a WM8903 codec" depends on SND_SOC_TEGRA && I2C depends on MACH_HAS_SND_SOC_TEGRA_WM8903 - select SND_SOC_TEGRA_I2S + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_WM8903 help Say Y or M here if you want to add support for SoC audio on Tegra @@ -42,17 +70,17 @@ config SND_SOC_TEGRA_WM8903 config SND_SOC_TEGRA_TRIMSLICE tristate "SoC Audio support for TrimSlice board" depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C - select SND_SOC_TEGRA_I2S + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TLV320AIC23 help Say Y or M here if you want to add support for SoC audio on the TrimSlice platform. config SND_SOC_TEGRA_ALC5632 - tristate "SoC Audio support for Tegra boards using an ALC5632 codec" - depends on SND_SOC_TEGRA && I2C - select SND_SOC_TEGRA_I2S - select SND_SOC_ALC5632 - help - Say Y or M here if you want to add support for SoC audio on the - Toshiba AC100 netbook. + tristate "SoC Audio support for Tegra boards using an ALC5632 codec" + depends on SND_SOC_TEGRA && I2C + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_ALC5632 + help + Say Y or M here if you want to add support for SoC audio on the + Toshiba AC100 netbook. diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 8e584b8fcfba..98704b48b62a 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -1,15 +1,19 @@ # Tegra platform Support -snd-soc-tegra-das-objs := tegra_das.o snd-soc-tegra-pcm-objs := tegra_pcm.o -snd-soc-tegra-i2s-objs := tegra_i2s.o -snd-soc-tegra-spdif-objs := tegra_spdif.o snd-soc-tegra-utils-objs += tegra_asoc_utils.o +snd-soc-tegra20-das-objs := tegra20_das.o +snd-soc-tegra20-i2s-objs := tegra20_i2s.o +snd-soc-tegra20-spdif-objs := tegra20_spdif.o +snd-soc-tegra30-ahub-objs := tegra30_ahub.o +snd-soc-tegra30-i2s-objs := tegra30_i2s.o -obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o -obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o -obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o -obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o +obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o +obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o +obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o +obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o +obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o +obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o # Tegra machine Support snd-soc-tegra-wm8903-objs := tegra_wm8903.o diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c new file mode 100644 index 000000000000..bf99296bce95 --- /dev/null +++ b/sound/soc/tegra/tegra20_das.c @@ -0,0 +1,233 @@ +/* + * tegra20_das.c - Tegra20 DAS driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/soc.h> +#include "tegra20_das.h" + +#define DRV_NAME "tegra20-das" + +static struct tegra20_das *das; + +static inline void tegra20_das_write(u32 reg, u32 val) +{ + regmap_write(das->regmap, reg, val); +} + +static inline u32 tegra20_das_read(u32 reg) +{ + u32 val; + regmap_read(das->regmap, reg, &val); + return val; +} + +int tegra20_das_connect_dap_to_dac(int dap, int dac) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA20_DAS_DAP_CTRL_SEL + + (dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE); + reg = dac << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P; + + tegra20_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dac); + +int tegra20_das_connect_dap_to_dap(int dap, int otherdap, int master, + int sdata1rx, int sdata2rx) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA20_DAS_DAP_CTRL_SEL + + (dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE); + reg = otherdap << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P | + !!sdata2rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P | + !!sdata1rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P | + !!master << TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P; + + tegra20_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dap); + +int tegra20_das_connect_dac_to_dap(int dac, int dap) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL + + (dac * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); + reg = dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P | + dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P | + dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P; + + tegra20_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra20_das_connect_dac_to_dap); + +#define LAST_REG(name) \ + (TEGRA20_DAS_##name + \ + (TEGRA20_DAS_##name##_STRIDE * (TEGRA20_DAS_##name##_COUNT - 1))) + +static bool tegra20_das_wr_rd_reg(struct device *dev, unsigned int reg) +{ + if ((reg >= TEGRA20_DAS_DAP_CTRL_SEL) && + (reg <= LAST_REG(DAP_CTRL_SEL))) + return true; + if ((reg >= TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL) && + (reg <= LAST_REG(DAC_INPUT_DATA_CLK_SEL))) + return true; + + return false; +} + +static const struct regmap_config tegra20_das_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = LAST_REG(DAC_INPUT_DATA_CLK_SEL), + .writeable_reg = tegra20_das_wr_rd_reg, + .readable_reg = tegra20_das_wr_rd_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit tegra20_das_probe(struct platform_device *pdev) +{ + struct resource *res, *region; + void __iomem *regs; + int ret = 0; + + if (das) + return -ENODEV; + + das = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_das), GFP_KERNEL); + if (!das) { + dev_err(&pdev->dev, "Can't allocate tegra20_das\n"); + ret = -ENOMEM; + goto err; + } + das->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err; + } + + region = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name); + if (!region) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err; + } + + regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err; + } + + das->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra20_das_regmap_config); + if (IS_ERR(das->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(das->regmap); + goto err; + } + + ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_1, + TEGRA20_DAS_DAP_SEL_DAC1); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAS DAP connection\n"); + goto err; + } + ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_1, + TEGRA20_DAS_DAC_SEL_DAP1); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAS DAC connection\n"); + goto err; + } + + platform_set_drvdata(pdev, das); + + return 0; + +err: + das = NULL; + return ret; +} + +static int __devexit tegra20_das_remove(struct platform_device *pdev) +{ + if (!das) + return -ENODEV; + + das = NULL; + + return 0; +} + +static const struct of_device_id tegra20_das_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-das", }, + {}, +}; + +static struct platform_driver tegra20_das_driver = { + .probe = tegra20_das_probe, + .remove = __devexit_p(tegra20_das_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra20_das_of_match, + }, +}; +module_platform_driver(tegra20_das_driver); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra20 DAS driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra20_das_of_match); diff --git a/sound/soc/tegra/tegra20_das.h b/sound/soc/tegra/tegra20_das.h new file mode 100644 index 000000000000..be217f3d3a75 --- /dev/null +++ b/sound/soc/tegra/tegra20_das.h @@ -0,0 +1,134 @@ +/* + * tegra20_das.h - Definitions for Tegra20 DAS driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010,2012 - NVIDIA, 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TEGRA20_DAS_H__ +#define __TEGRA20_DAS_H__ + +/* Register TEGRA20_DAS_DAP_CTRL_SEL */ +#define TEGRA20_DAS_DAP_CTRL_SEL 0x00 +#define TEGRA20_DAS_DAP_CTRL_SEL_COUNT 5 +#define TEGRA20_DAS_DAP_CTRL_SEL_STRIDE 4 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5 + +/* Values for field TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */ +#define TEGRA20_DAS_DAP_SEL_DAC1 0 +#define TEGRA20_DAS_DAP_SEL_DAC2 1 +#define TEGRA20_DAS_DAP_SEL_DAC3 2 +#define TEGRA20_DAS_DAP_SEL_DAP1 16 +#define TEGRA20_DAS_DAP_SEL_DAP2 17 +#define TEGRA20_DAS_DAP_SEL_DAP3 18 +#define TEGRA20_DAS_DAP_SEL_DAP4 19 +#define TEGRA20_DAS_DAP_SEL_DAP5 20 + +/* Register TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL */ +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL 0x40 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4 + +/* + * Values for: + * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL + * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL + * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL + */ +#define TEGRA20_DAS_DAC_SEL_DAP1 0 +#define TEGRA20_DAS_DAC_SEL_DAP2 1 +#define TEGRA20_DAS_DAC_SEL_DAP3 2 +#define TEGRA20_DAS_DAC_SEL_DAP4 3 +#define TEGRA20_DAS_DAC_SEL_DAP5 4 + +/* + * Names/IDs of the DACs/DAPs. + */ + +#define TEGRA20_DAS_DAP_ID_1 0 +#define TEGRA20_DAS_DAP_ID_2 1 +#define TEGRA20_DAS_DAP_ID_3 2 +#define TEGRA20_DAS_DAP_ID_4 3 +#define TEGRA20_DAS_DAP_ID_5 4 + +#define TEGRA20_DAS_DAC_ID_1 0 +#define TEGRA20_DAS_DAC_ID_2 1 +#define TEGRA20_DAS_DAC_ID_3 2 + +struct tegra20_das { + struct device *dev; + struct regmap *regmap; +}; + +/* + * Terminology: + * DAS: Digital audio switch (HW module controlled by this driver) + * DAP: Digital audio port (port/pins on Tegra device) + * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere) + * + * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific + * DAC, or another DAP. When DAPs are connected, one must be the master and + * one the slave. Each DAC allows selection of a specific DAP for input, to + * cater for the case where N DAPs are connected to 1 DAC for broadcast + * output. + * + * This driver is dumb; no attempt is made to ensure that a valid routing + * configuration is programmed. + */ + +/* + * Connect a DAP to to a DAC + * dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_* + * dac_sel: DAC to connect to: TEGRA20_DAS_DAP_SEL_DAC* + */ +extern int tegra20_das_connect_dap_to_dac(int dap_id, int dac_sel); + +/* + * Connect a DAP to to another DAP + * dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_* + * other_dap_sel: DAP to connect to: TEGRA20_DAS_DAP_SEL_DAP* + * master: Is this DAP the master (1) or slave (0) + * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0) + * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0) + */ +extern int tegra20_das_connect_dap_to_dap(int dap_id, int other_dap_sel, + int master, int sdata1rx, + int sdata2rx); + +/* + * Connect a DAC's input to a DAP + * (DAC outputs are selected by the DAP) + * dac_id: DAC ID to connect: TEGRA20_DAS_DAC_ID_* + * dap_sel: DAP to receive input from: TEGRA20_DAS_DAC_SEL_DAP* + */ +extern int tegra20_das_connect_dac_to_dap(int dac_id, int dap_sel); + +#endif diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c new file mode 100644 index 000000000000..0c7af63d444b --- /dev/null +++ b/sound/soc/tegra/tegra20_i2s.c @@ -0,0 +1,494 @@ +/* + * tegra20_i2s.c - Tegra20 I2S driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010,2012 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra20_i2s.h" + +#define DRV_NAME "tegra20-i2s" + +static inline void tegra20_i2s_write(struct tegra20_i2s *i2s, u32 reg, u32 val) +{ + regmap_write(i2s->regmap, reg, val); +} + +static inline u32 tegra20_i2s_read(struct tegra20_i2s *i2s, u32 reg) +{ + u32 val; + regmap_read(i2s->regmap, reg, &val); + return val; +} + +static int tegra20_i2s_runtime_suspend(struct device *dev) +{ + struct tegra20_i2s *i2s = dev_get_drvdata(dev); + + clk_disable(i2s->clk_i2s); + + return 0; +} + +static int tegra20_i2s_runtime_resume(struct device *dev) +{ + struct tegra20_i2s *i2s = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(i2s->clk_i2s); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_MASTER_ENABLE; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_MASTER_ENABLE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~(TEGRA20_I2S_CTRL_BIT_FORMAT_MASK | + TEGRA20_I2S_CTRL_LRCK_MASK); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_DSP_B: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_R_LOW; + break; + case SND_SOC_DAIFMT_I2S: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = substream->pcm->card->dev; + struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 reg; + int ret, sample_size, srate, i2sclock, bitcnt; + + i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_BIT_SIZE_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_16; + sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_24; + sample_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_32; + sample_size = 32; + break; + default: + return -EINVAL; + } + + srate = params_rate(params); + + /* Final "* 2" required by Tegra hardware */ + i2sclock = srate * params_channels(params) * sample_size * 2; + + ret = clk_set_rate(i2s->clk_i2s, i2sclock); + if (ret) { + dev_err(dev, "Can't set I2S clock rate: %d\n", ret); + return ret; + } + + bitcnt = (i2sclock / (2 * srate)) - 1; + if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) + return -EINVAL; + reg = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; + + if (i2sclock % (2 * srate)) + reg |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE; + + tegra20_i2s_write(i2s, TEGRA20_I2S_TIMING, reg); + + tegra20_i2s_write(i2s, TEGRA20_I2S_FIFO_SCR, + TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | + TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); + + return 0; +} + +static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s) +{ + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_FIFO1_ENABLE; + tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s) +{ + i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_FIFO1_ENABLE; + tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s) +{ + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_FIFO2_ENABLE; + tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s) +{ + i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_FIFO2_ENABLE; + tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl); +} + +static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra20_i2s_start_playback(i2s); + else + tegra20_i2s_start_capture(i2s); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra20_i2s_stop_playback(i2s); + else + tegra20_i2s_stop_capture(i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra20_i2s_probe(struct snd_soc_dai *dai) +{ + struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &i2s->capture_dma_data; + dai->playback_dma_data = &i2s->playback_dma_data; + + return 0; +} + +static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { + .set_fmt = tegra20_i2s_set_fmt, + .hw_params = tegra20_i2s_hw_params, + .trigger = tegra20_i2s_trigger, +}; + +static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { + .probe = tegra20_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra20_i2s_dai_ops, + .symmetric_rates = 1, +}; + +static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_I2S_CTRL: + case TEGRA20_I2S_STATUS: + case TEGRA20_I2S_TIMING: + case TEGRA20_I2S_FIFO_SCR: + case TEGRA20_I2S_PCM_CTRL: + case TEGRA20_I2S_NW_CTRL: + case TEGRA20_I2S_TDM_CTRL: + case TEGRA20_I2S_TDM_TX_RX_CTRL: + case TEGRA20_I2S_FIFO1: + case TEGRA20_I2S_FIFO2: + return true; + default: + return false; + }; +} + +static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_I2S_STATUS: + case TEGRA20_I2S_FIFO_SCR: + case TEGRA20_I2S_FIFO1: + case TEGRA20_I2S_FIFO2: + return true; + default: + return false; + }; +} + +static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_I2S_FIFO1: + case TEGRA20_I2S_FIFO2: + return true; + default: + return false; + }; +} + +static const struct regmap_config tegra20_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA20_I2S_FIFO2, + .writeable_reg = tegra20_i2s_wr_rd_reg, + .readable_reg = tegra20_i2s_wr_rd_reg, + .volatile_reg = tegra20_i2s_volatile_reg, + .precious_reg = tegra20_i2s_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static __devinit int tegra20_i2s_platform_probe(struct platform_device *pdev) +{ + struct tegra20_i2s *i2s; + struct resource *mem, *memregion, *dmareq; + u32 of_dma[2]; + u32 dma_ch; + void __iomem *regs; + int ret; + + i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL); + if (!i2s) { + dev_err(&pdev->dev, "Can't allocate tegra20_i2s\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, i2s); + + i2s->dai = tegra20_i2s_dai_template; + i2s->dai.name = dev_name(&pdev->dev); + + i2s->clk_i2s = clk_get(&pdev->dev, NULL); + if (IS_ERR(i2s->clk_i2s)) { + dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); + ret = PTR_ERR(i2s->clk_i2s); + goto err; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmareq) { + if (of_property_read_u32_array(pdev->dev.of_node, + "nvidia,dma-request-selector", + of_dma, 2) < 0) { + dev_err(&pdev->dev, "No DMA resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + dma_ch = of_dma[1]; + } else { + dma_ch = dmareq->start; + } + + memregion = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_clk_put; + } + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra20_i2s_regmap_config); + if (IS_ERR(i2s->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(i2s->regmap); + goto err_clk_put; + } + + i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2; + i2s->capture_dma_data.wrap = 4; + i2s->capture_dma_data.width = 32; + i2s->capture_dma_data.req_sel = dma_ch; + + i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1; + i2s->playback_dma_data.wrap = 4; + i2s->playback_dma_data.width = 32; + i2s->playback_dma_data.req_sel = dma_ch; + + i2s->reg_ctrl = TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED; + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra20_i2s_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = snd_soc_register_dai(&pdev->dev, &i2s->dai); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_suspend; + } + + ret = tegra_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + goto err_unregister_dai; + } + + return 0; + +err_unregister_dai: + snd_soc_unregister_dai(&pdev->dev); +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra20_i2s_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err_clk_put: + clk_put(i2s->clk_i2s); +err: + return ret; +} + +static int __devexit tegra20_i2s_platform_remove(struct platform_device *pdev) +{ + struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra20_i2s_runtime_suspend(&pdev->dev); + + tegra_pcm_platform_unregister(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + clk_put(i2s->clk_i2s); + + return 0; +} + +static const struct of_device_id tegra20_i2s_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-i2s", }, + {}, +}; + +static const struct dev_pm_ops tegra20_i2s_pm_ops __devinitconst = { + SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend, + tegra20_i2s_runtime_resume, NULL) +}; + +static struct platform_driver tegra20_i2s_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra20_i2s_of_match, + .pm = &tegra20_i2s_pm_ops, + }, + .probe = tegra20_i2s_platform_probe, + .remove = __devexit_p(tegra20_i2s_platform_remove), +}; +module_platform_driver(tegra20_i2s_driver); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra20 I2S ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match); diff --git a/sound/soc/tegra/tegra20_i2s.h b/sound/soc/tegra/tegra20_i2s.h new file mode 100644 index 000000000000..a57efc6a597e --- /dev/null +++ b/sound/soc/tegra/tegra20_i2s.h @@ -0,0 +1,164 @@ +/* + * tegra20_i2s.h - Definitions for Tegra20 I2S driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010,2012 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TEGRA20_I2S_H__ +#define __TEGRA20_I2S_H__ + +#include "tegra_pcm.h" + +/* Register offsets from TEGRA20_I2S1_BASE and TEGRA20_I2S2_BASE */ + +#define TEGRA20_I2S_CTRL 0x00 +#define TEGRA20_I2S_STATUS 0x04 +#define TEGRA20_I2S_TIMING 0x08 +#define TEGRA20_I2S_FIFO_SCR 0x0c +#define TEGRA20_I2S_PCM_CTRL 0x10 +#define TEGRA20_I2S_NW_CTRL 0x14 +#define TEGRA20_I2S_TDM_CTRL 0x20 +#define TEGRA20_I2S_TDM_TX_RX_CTRL 0x24 +#define TEGRA20_I2S_FIFO1 0x40 +#define TEGRA20_I2S_FIFO2 0x80 + +/* Fields in TEGRA20_I2S_CTRL */ + +#define TEGRA20_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30) +#define TEGRA20_I2S_CTRL_FIFO1_ENABLE (1 << 29) +#define TEGRA20_I2S_CTRL_FIFO2_ENABLE (1 << 28) +#define TEGRA20_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27) +#define TEGRA20_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26) +#define TEGRA20_I2S_CTRL_MASTER_ENABLE (1 << 25) + +#define TEGRA20_I2S_LRCK_LEFT_LOW 0 +#define TEGRA20_I2S_LRCK_RIGHT_LOW 1 + +#define TEGRA20_I2S_CTRL_LRCK_SHIFT 24 +#define TEGRA20_I2S_CTRL_LRCK_MASK (1 << TEGRA20_I2S_CTRL_LRCK_SHIFT) +#define TEGRA20_I2S_CTRL_LRCK_L_LOW (TEGRA20_I2S_LRCK_LEFT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT) +#define TEGRA20_I2S_CTRL_LRCK_R_LOW (TEGRA20_I2S_LRCK_RIGHT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT) + +#define TEGRA20_I2S_BIT_FORMAT_I2S 0 +#define TEGRA20_I2S_BIT_FORMAT_RJM 1 +#define TEGRA20_I2S_BIT_FORMAT_LJM 2 +#define TEGRA20_I2S_BIT_FORMAT_DSP 3 + +#define TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT 10 +#define TEGRA20_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_FORMAT_I2S (TEGRA20_I2S_BIT_FORMAT_I2S << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_FORMAT_RJM (TEGRA20_I2S_BIT_FORMAT_RJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_FORMAT_LJM (TEGRA20_I2S_BIT_FORMAT_LJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_FORMAT_DSP (TEGRA20_I2S_BIT_FORMAT_DSP << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) + +#define TEGRA20_I2S_BIT_SIZE_16 0 +#define TEGRA20_I2S_BIT_SIZE_20 1 +#define TEGRA20_I2S_BIT_SIZE_24 2 +#define TEGRA20_I2S_BIT_SIZE_32 3 + +#define TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT 8 +#define TEGRA20_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_SIZE_16 (TEGRA20_I2S_BIT_SIZE_16 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_SIZE_20 (TEGRA20_I2S_BIT_SIZE_20 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_SIZE_24 (TEGRA20_I2S_BIT_SIZE_24 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_SIZE_32 (TEGRA20_I2S_BIT_SIZE_32 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) + +#define TEGRA20_I2S_FIFO_16_LSB 0 +#define TEGRA20_I2S_FIFO_20_LSB 1 +#define TEGRA20_I2S_FIFO_24_LSB 2 +#define TEGRA20_I2S_FIFO_32 3 +#define TEGRA20_I2S_FIFO_PACKED 7 + +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT 4 +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA20_I2S_FIFO_16_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA20_I2S_FIFO_20_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA20_I2S_FIFO_24_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_32 (TEGRA20_I2S_FIFO_32 << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA20_I2S_FIFO_PACKED << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) + +#define TEGRA20_I2S_CTRL_IE_FIFO1_ERR (1 << 3) +#define TEGRA20_I2S_CTRL_IE_FIFO2_ERR (1 << 2) +#define TEGRA20_I2S_CTRL_QE_FIFO1 (1 << 1) +#define TEGRA20_I2S_CTRL_QE_FIFO2 (1 << 0) + +/* Fields in TEGRA20_I2S_STATUS */ + +#define TEGRA20_I2S_STATUS_FIFO1_RDY (1 << 31) +#define TEGRA20_I2S_STATUS_FIFO2_RDY (1 << 30) +#define TEGRA20_I2S_STATUS_FIFO1_BSY (1 << 29) +#define TEGRA20_I2S_STATUS_FIFO2_BSY (1 << 28) +#define TEGRA20_I2S_STATUS_FIFO1_ERR (1 << 3) +#define TEGRA20_I2S_STATUS_FIFO2_ERR (1 << 2) +#define TEGRA20_I2S_STATUS_QS_FIFO1 (1 << 1) +#define TEGRA20_I2S_STATUS_QS_FIFO2 (1 << 0) + +/* Fields in TEGRA20_I2S_TIMING */ + +#define TEGRA20_I2S_TIMING_NON_SYM_ENABLE (1 << 12) +#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0 +#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff +#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT) + +/* Fields in TEGRA20_I2S_FIFO_SCR */ + +#define TEGRA20_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24 +#define TEGRA20_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16 +#define TEGRA20_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f + +#define TEGRA20_I2S_FIFO_SCR_FIFO2_CLR (1 << 12) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_CLR (1 << 8) + +#define TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT 0 +#define TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1 +#define TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2 +#define TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3 + +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4 +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) + +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0 +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) + +struct tegra20_i2s { + struct snd_soc_dai_driver dai; + struct clk *clk_i2s; + struct tegra_pcm_dma_params capture_dma_data; + struct tegra_pcm_dma_params playback_dma_data; + struct regmap *regmap; + u32 reg_ctrl; +}; + +#endif diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c new file mode 100644 index 000000000000..f9b57418bd08 --- /dev/null +++ b/sound/soc/tegra/tegra20_spdif.c @@ -0,0 +1,404 @@ +/* + * tegra20_spdif.c - Tegra20 SPDIF driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2011-2012 - NVIDIA, 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra20_spdif.h" + +#define DRV_NAME "tegra20-spdif" + +static inline void tegra20_spdif_write(struct tegra20_spdif *spdif, u32 reg, + u32 val) +{ + regmap_write(spdif->regmap, reg, val); +} + +static inline u32 tegra20_spdif_read(struct tegra20_spdif *spdif, u32 reg) +{ + u32 val; + regmap_read(spdif->regmap, reg, &val); + return val; +} + +static int tegra20_spdif_runtime_suspend(struct device *dev) +{ + struct tegra20_spdif *spdif = dev_get_drvdata(dev); + + clk_disable(spdif->clk_spdif_out); + + return 0; +} + +static int tegra20_spdif_runtime_resume(struct device *dev) +{ + struct tegra20_spdif *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(spdif->clk_spdif_out); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = substream->pcm->card->dev; + struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + int ret, spdifclock; + + spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_PACK; + spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_PACK; + spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 32000: + spdifclock = 4096000; + break; + case 44100: + spdifclock = 5644800; + break; + case 48000: + spdifclock = 6144000; + break; + case 88200: + spdifclock = 11289600; + break; + case 96000: + spdifclock = 12288000; + break; + case 176400: + spdifclock = 22579200; + break; + case 192000: + spdifclock = 24576000; + break; + default: + return -EINVAL; + } + + ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); + if (ret) { + dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); + return ret; + } + + return 0; +} + +static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif) +{ + spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_TX_EN; + tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl); +} + +static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) +{ + spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_TX_EN; + tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl); +} + +static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + tegra20_spdif_start_playback(spdif); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + tegra20_spdif_stop_playback(spdif); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra20_spdif_probe(struct snd_soc_dai *dai) +{ + struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = NULL; + dai->playback_dma_data = &spdif->playback_dma_data; + + return 0; +} + +static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { + .hw_params = tegra20_spdif_hw_params, + .trigger = tegra20_spdif_trigger, +}; + +static struct snd_soc_dai_driver tegra20_spdif_dai = { + .name = DRV_NAME, + .probe = tegra20_spdif_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra20_spdif_dai_ops, +}; + +static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_SPDIF_CTRL: + case TEGRA20_SPDIF_STATUS: + case TEGRA20_SPDIF_STROBE_CTRL: + case TEGRA20_SPDIF_DATA_FIFO_CSR: + case TEGRA20_SPDIF_DATA_OUT: + case TEGRA20_SPDIF_DATA_IN: + case TEGRA20_SPDIF_CH_STA_RX_A: + case TEGRA20_SPDIF_CH_STA_RX_B: + case TEGRA20_SPDIF_CH_STA_RX_C: + case TEGRA20_SPDIF_CH_STA_RX_D: + case TEGRA20_SPDIF_CH_STA_RX_E: + case TEGRA20_SPDIF_CH_STA_RX_F: + case TEGRA20_SPDIF_CH_STA_TX_A: + case TEGRA20_SPDIF_CH_STA_TX_B: + case TEGRA20_SPDIF_CH_STA_TX_C: + case TEGRA20_SPDIF_CH_STA_TX_D: + case TEGRA20_SPDIF_CH_STA_TX_E: + case TEGRA20_SPDIF_CH_STA_TX_F: + case TEGRA20_SPDIF_USR_STA_RX_A: + case TEGRA20_SPDIF_USR_DAT_TX_A: + return true; + default: + return false; + }; +} + +static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_SPDIF_STATUS: + case TEGRA20_SPDIF_DATA_FIFO_CSR: + case TEGRA20_SPDIF_DATA_OUT: + case TEGRA20_SPDIF_DATA_IN: + case TEGRA20_SPDIF_CH_STA_RX_A: + case TEGRA20_SPDIF_CH_STA_RX_B: + case TEGRA20_SPDIF_CH_STA_RX_C: + case TEGRA20_SPDIF_CH_STA_RX_D: + case TEGRA20_SPDIF_CH_STA_RX_E: + case TEGRA20_SPDIF_CH_STA_RX_F: + case TEGRA20_SPDIF_USR_STA_RX_A: + case TEGRA20_SPDIF_USR_DAT_TX_A: + return true; + default: + return false; + }; +} + +static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_SPDIF_DATA_OUT: + case TEGRA20_SPDIF_DATA_IN: + case TEGRA20_SPDIF_USR_STA_RX_A: + case TEGRA20_SPDIF_USR_DAT_TX_A: + return true; + default: + return false; + }; +} + +static const struct regmap_config tegra20_spdif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA20_SPDIF_USR_DAT_TX_A, + .writeable_reg = tegra20_spdif_wr_rd_reg, + .readable_reg = tegra20_spdif_wr_rd_reg, + .volatile_reg = tegra20_spdif_volatile_reg, + .precious_reg = tegra20_spdif_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev) +{ + struct tegra20_spdif *spdif; + struct resource *mem, *memregion, *dmareq; + void __iomem *regs; + int ret; + + spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif), + GFP_KERNEL); + if (!spdif) { + dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, spdif); + + spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out"); + if (IS_ERR(spdif->clk_spdif_out)) { + pr_err("Can't retrieve spdif clock\n"); + ret = PTR_ERR(spdif->clk_spdif_out); + goto err; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmareq) { + dev_err(&pdev->dev, "No DMA resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + memregion = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_clk_put; + } + + spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra20_spdif_regmap_config); + if (IS_ERR(spdif->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(spdif->regmap); + goto err_clk_put; + } + + spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; + spdif->playback_dma_data.wrap = 4; + spdif->playback_dma_data.width = 32; + spdif->playback_dma_data.req_sel = dmareq->start; + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra20_spdif_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_suspend; + } + + ret = tegra_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + goto err_unregister_dai; + } + + return 0; + +err_unregister_dai: + snd_soc_unregister_dai(&pdev->dev); +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra20_spdif_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err_clk_put: + clk_put(spdif->clk_spdif_out); +err: + return ret; +} + +static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev) +{ + struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra20_spdif_runtime_suspend(&pdev->dev); + + tegra_pcm_platform_unregister(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + clk_put(spdif->clk_spdif_out); + + return 0; +} + +static const struct dev_pm_ops tegra20_spdif_pm_ops __devinitconst = { + SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, + tegra20_spdif_runtime_resume, NULL) +}; + +static struct platform_driver tegra20_spdif_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &tegra20_spdif_pm_ops, + }, + .probe = tegra20_spdif_platform_probe, + .remove = __devexit_p(tegra20_spdif_platform_remove), +}; + +module_platform_driver(tegra20_spdif_driver); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h new file mode 100644 index 000000000000..ed756527efea --- /dev/null +++ b/sound/soc/tegra/tegra20_spdif.h @@ -0,0 +1,471 @@ +/* + * tegra20_spdif.h - Definitions for Tegra20 SPDIF driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2011 - NVIDIA, Inc. + * + * Based on code copyright/by: + * Copyright (c) 2008-2009, NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TEGRA20_SPDIF_H__ +#define __TEGRA20_SPDIF_H__ + +#include "tegra_pcm.h" + +/* Offsets from TEGRA20_SPDIF_BASE */ + +#define TEGRA20_SPDIF_CTRL 0x0 +#define TEGRA20_SPDIF_STATUS 0x4 +#define TEGRA20_SPDIF_STROBE_CTRL 0x8 +#define TEGRA20_SPDIF_DATA_FIFO_CSR 0x0C +#define TEGRA20_SPDIF_DATA_OUT 0x40 +#define TEGRA20_SPDIF_DATA_IN 0x80 +#define TEGRA20_SPDIF_CH_STA_RX_A 0x100 +#define TEGRA20_SPDIF_CH_STA_RX_B 0x104 +#define TEGRA20_SPDIF_CH_STA_RX_C 0x108 +#define TEGRA20_SPDIF_CH_STA_RX_D 0x10C +#define TEGRA20_SPDIF_CH_STA_RX_E 0x110 +#define TEGRA20_SPDIF_CH_STA_RX_F 0x114 +#define TEGRA20_SPDIF_CH_STA_TX_A 0x140 +#define TEGRA20_SPDIF_CH_STA_TX_B 0x144 +#define TEGRA20_SPDIF_CH_STA_TX_C 0x148 +#define TEGRA20_SPDIF_CH_STA_TX_D 0x14C +#define TEGRA20_SPDIF_CH_STA_TX_E 0x150 +#define TEGRA20_SPDIF_CH_STA_TX_F 0x154 +#define TEGRA20_SPDIF_USR_STA_RX_A 0x180 +#define TEGRA20_SPDIF_USR_DAT_TX_A 0x1C0 + +/* Fields in TEGRA20_SPDIF_CTRL */ + +/* Start capturing from 0=right, 1=left channel */ +#define TEGRA20_SPDIF_CTRL_CAP_LC (1 << 30) + +/* SPDIF receiver(RX) enable */ +#define TEGRA20_SPDIF_CTRL_RX_EN (1 << 29) + +/* SPDIF Transmitter(TX) enable */ +#define TEGRA20_SPDIF_CTRL_TX_EN (1 << 28) + +/* Transmit Channel status */ +#define TEGRA20_SPDIF_CTRL_TC_EN (1 << 27) + +/* Transmit user Data */ +#define TEGRA20_SPDIF_CTRL_TU_EN (1 << 26) + +/* Interrupt on transmit error */ +#define TEGRA20_SPDIF_CTRL_IE_TXE (1 << 25) + +/* Interrupt on receive error */ +#define TEGRA20_SPDIF_CTRL_IE_RXE (1 << 24) + +/* Interrupt on invalid preamble */ +#define TEGRA20_SPDIF_CTRL_IE_P (1 << 23) + +/* Interrupt on "B" preamble */ +#define TEGRA20_SPDIF_CTRL_IE_B (1 << 22) + +/* Interrupt when block of channel status received */ +#define TEGRA20_SPDIF_CTRL_IE_C (1 << 21) + +/* Interrupt when a valid information unit (IU) is received */ +#define TEGRA20_SPDIF_CTRL_IE_U (1 << 20) + +/* Interrupt when RX user FIFO attention level is reached */ +#define TEGRA20_SPDIF_CTRL_QE_RU (1 << 19) + +/* Interrupt when TX user FIFO attention level is reached */ +#define TEGRA20_SPDIF_CTRL_QE_TU (1 << 18) + +/* Interrupt when RX data FIFO attention level is reached */ +#define TEGRA20_SPDIF_CTRL_QE_RX (1 << 17) + +/* Interrupt when TX data FIFO attention level is reached */ +#define TEGRA20_SPDIF_CTRL_QE_TX (1 << 16) + +/* Loopback test mode enable */ +#define TEGRA20_SPDIF_CTRL_LBK_EN (1 << 15) + +/* + * Pack data mode: + * 0 = Single data (16 bit needs to be padded to match the + * interface data bit size). + * 1 = Packeted left/right channel data into a single word. + */ +#define TEGRA20_SPDIF_CTRL_PACK (1 << 14) + +/* + * 00 = 16bit data + * 01 = 20bit data + * 10 = 24bit data + * 11 = raw data + */ +#define TEGRA20_SPDIF_BIT_MODE_16BIT 0 +#define TEGRA20_SPDIF_BIT_MODE_20BIT 1 +#define TEGRA20_SPDIF_BIT_MODE_24BIT 2 +#define TEGRA20_SPDIF_BIT_MODE_RAW 3 + +#define TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT 12 +#define TEGRA20_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA20_SPDIF_BIT_MODE_16BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA20_SPDIF_BIT_MODE_20BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA20_SPDIF_BIT_MODE_24BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_RAW (TEGRA20_SPDIF_BIT_MODE_RAW << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) + +/* Fields in TEGRA20_SPDIF_STATUS */ + +/* + * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must + * write a 1 to the corresponding bit location to clear the status. + */ + +/* + * Receiver(RX) shifter is busy receiving data. + * This bit is asserted when the receiver first locked onto the + * preamble of the data stream after RX_EN is asserted. This bit is + * deasserted when either, + * (a) the end of a frame is reached after RX_EN is deeasserted, or + * (b) the SPDIF data stream becomes inactive. + */ +#define TEGRA20_SPDIF_STATUS_RX_BSY (1 << 29) + +/* + * Transmitter(TX) shifter is busy transmitting data. + * This bit is asserted when TX_EN is asserted. + * This bit is deasserted when the end of a frame is reached after + * TX_EN is deasserted. + */ +#define TEGRA20_SPDIF_STATUS_TX_BSY (1 << 28) + +/* + * TX is busy shifting out channel status. + * This bit is asserted when both TX_EN and TC_EN are asserted and + * data from CH_STA_TX_A register is loaded into the internal shifter. + * This bit is deasserted when either, + * (a) the end of a frame is reached after TX_EN is deasserted, or + * (b) CH_STA_TX_F register is loaded into the internal shifter. + */ +#define TEGRA20_SPDIF_STATUS_TC_BSY (1 << 27) + +/* + * TX User data FIFO busy. + * This bit is asserted when TX_EN and TXU_EN are asserted and + * there's data in the TX user FIFO. This bit is deassert when either, + * (a) the end of a frame is reached after TX_EN is deasserted, or + * (b) there's no data left in the TX user FIFO. + */ +#define TEGRA20_SPDIF_STATUS_TU_BSY (1 << 26) + +/* TX FIFO Underrun error status */ +#define TEGRA20_SPDIF_STATUS_TX_ERR (1 << 25) + +/* RX FIFO Overrun error status */ +#define TEGRA20_SPDIF_STATUS_RX_ERR (1 << 24) + +/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */ +#define TEGRA20_SPDIF_STATUS_IS_P (1 << 23) + +/* B-preamble detection status: 0=not detected, 1=B-preamble detected */ +#define TEGRA20_SPDIF_STATUS_IS_B (1 << 22) + +/* + * RX channel block data receive status: + * 0=entire block not recieved yet. + * 1=received entire block of channel status, + */ +#define TEGRA20_SPDIF_STATUS_IS_C (1 << 21) + +/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */ +#define TEGRA20_SPDIF_STATUS_IS_U (1 << 20) + +/* + * RX User FIFO Status: + * 1=attention level reached, 0=attention level not reached. + */ +#define TEGRA20_SPDIF_STATUS_QS_RU (1 << 19) + +/* + * TX User FIFO Status: + * 1=attention level reached, 0=attention level not reached. + */ +#define TEGRA20_SPDIF_STATUS_QS_TU (1 << 18) + +/* + * RX Data FIFO Status: + * 1=attention level reached, 0=attention level not reached. + */ +#define TEGRA20_SPDIF_STATUS_QS_RX (1 << 17) + +/* + * TX Data FIFO Status: + * 1=attention level reached, 0=attention level not reached. + */ +#define TEGRA20_SPDIF_STATUS_QS_TX (1 << 16) + +/* Fields in TEGRA20_SPDIF_STROBE_CTRL */ + +/* + * Indicates the approximate number of detected SPDIFIN clocks within a + * bi-phase period. + */ +#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16 +#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT) + +/* Data strobe mode: 0=Auto-locked 1=Manual locked */ +#define TEGRA20_SPDIF_STROBE_CTRL_STROBE (1 << 15) + +/* + * Manual data strobe time within the bi-phase clock period (in terms of + * the number of over-sampling clocks). + */ +#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8 +#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT) + +/* + * Manual SPDIFIN bi-phase clock period (in terms of the number of + * over-sampling clocks). + */ +#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0 +#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT) + +/* Fields in SPDIF_DATA_FIFO_CSR */ + +/* Clear Receiver User FIFO (RX USR.FIFO) */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31) + +#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3 + +/* RU FIFO attention level */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \ + (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) + +/* Number of RX USR.FIFO levels with valid data. */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT) + +/* Clear Transmitter User FIFO (TX USR.FIFO) */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23) + +/* TU FIFO attention level */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \ + (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) + +/* Number of TX USR.FIFO levels that could be filled. */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT) + +/* Clear Receiver Data FIFO (RX DATA.FIFO) */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15) + +#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3 + +/* RU FIFO attention level */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \ + (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) + +/* Number of RX DATA.FIFO levels with valid data. */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT) + +/* Clear Transmitter Data FIFO (TX DATA.FIFO) */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7) + +/* TU FIFO attention level */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \ + (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) + +/* Number of TX DATA.FIFO levels that could be filled. */ +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT) + +/* Fields in TEGRA20_SPDIF_DATA_OUT */ + +/* + * This register has 5 different formats: + * 16-bit (BIT_MODE=00, PACK=0) + * 20-bit (BIT_MODE=01, PACK=0) + * 24-bit (BIT_MODE=10, PACK=0) + * raw (BIT_MODE=11, PACK=0) + * 16-bit packed (BIT_MODE=00, PACK=1) + */ + +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT) + +#define TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT) + +#define TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT) + +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28) + +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8 +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT) + +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4 +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT) + +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT) + +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16 +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT) + +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT) + +/* Fields in TEGRA20_SPDIF_DATA_IN */ + +/* + * This register has 5 different formats: + * 16-bit (BIT_MODE=00, PACK=0) + * 20-bit (BIT_MODE=01, PACK=0) + * 24-bit (BIT_MODE=10, PACK=0) + * raw (BIT_MODE=11, PACK=0) + * 16-bit packed (BIT_MODE=00, PACK=1) + * + * Bits 31:24 are common to all modes except 16-bit packed + */ + +#define TEGRA20_SPDIF_DATA_IN_DATA_P (1 << 31) +#define TEGRA20_SPDIF_DATA_IN_DATA_C (1 << 30) +#define TEGRA20_SPDIF_DATA_IN_DATA_U (1 << 29) +#define TEGRA20_SPDIF_DATA_IN_DATA_V (1 << 28) + +#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24 +#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT) + +#define TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT) + +#define TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT) + +#define TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT) + +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8 +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT) + +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4 +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT) + +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT) + +#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16 +#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT) + +#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT) + +/* Fields in TEGRA20_SPDIF_CH_STA_RX_A */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_B */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_C */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_D */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_E */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_F */ + +/* + * The 6-word receive channel data page buffer holds a block (192 frames) of + * channel status information. The order of receive is from LSB to MSB + * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A. + */ + +/* Fields in TEGRA20_SPDIF_CH_STA_TX_A */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_B */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_C */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_D */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_E */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_F */ + +/* + * The 6-word transmit channel data page buffer holds a block (192 frames) of + * channel status information. The order of transmission is from LSB to MSB + * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A. + */ + +/* Fields in TEGRA20_SPDIF_USR_STA_RX_A */ + +/* + * This 4-word deep FIFO receives user FIFO field information. The order of + * receive is from LSB to MSB bit. + */ + +/* Fields in TEGRA20_SPDIF_USR_DAT_TX_A */ + +/* + * This 4-word deep FIFO transmits user FIFO field information. The order of + * transmission is from LSB to MSB bit. + */ + +struct tegra20_spdif { + struct clk *clk_spdif_out; + struct tegra_pcm_dma_params capture_dma_data; + struct tegra_pcm_dma_params playback_dma_data; + struct regmap *regmap; + u32 reg_ctrl; +}; + +#endif diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c new file mode 100644 index 000000000000..57cd419f743e --- /dev/null +++ b/sound/soc/tegra/tegra30_ahub.c @@ -0,0 +1,631 @@ +/* + * tegra30_ahub.c - Tegra30 AHUB driver + * + * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <mach/clk.h> +#include <mach/dma.h> +#include <sound/soc.h> +#include "tegra30_ahub.h" + +#define DRV_NAME "tegra30-ahub" + +static struct tegra30_ahub *ahub; + +static inline void tegra30_apbif_write(u32 reg, u32 val) +{ + regmap_write(ahub->regmap_apbif, reg, val); +} + +static inline u32 tegra30_apbif_read(u32 reg) +{ + u32 val; + regmap_read(ahub->regmap_apbif, reg, &val); + return val; +} + +static inline void tegra30_audio_write(u32 reg, u32 val) +{ + regmap_write(ahub->regmap_ahub, reg, val); +} + +static int tegra30_ahub_runtime_suspend(struct device *dev) +{ + regcache_cache_only(ahub->regmap_apbif, true); + regcache_cache_only(ahub->regmap_ahub, true); + + clk_disable(ahub->clk_apbif); + clk_disable(ahub->clk_d_audio); + + return 0; +} + +/* + * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data + * is read from or sent to memory. However, that's not something the rest of + * the driver supports right now, so we'll just treat the two clocks as one + * for now. + * + * These functions should not be a plain ref-count. Instead, each active stream + * contributes some requirement to the minimum clock rate, so starting or + * stopping streams should dynamically adjust the clock as required. However, + * this is not yet implemented. + */ +static int tegra30_ahub_runtime_resume(struct device *dev) +{ + int ret; + + ret = clk_enable(ahub->clk_d_audio); + if (ret) { + dev_err(dev, "clk_enable d_audio failed: %d\n", ret); + return ret; + } + ret = clk_enable(ahub->clk_apbif); + if (ret) { + dev_err(dev, "clk_enable apbif failed: %d\n", ret); + clk_disable(ahub->clk_d_audio); + return ret; + } + + regcache_cache_only(ahub->regmap_apbif, false); + regcache_cache_only(ahub->regmap_ahub, false); + + return 0; +} + +int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, + unsigned long *fiforeg, + unsigned long *reqsel) +{ + int channel; + u32 reg, val; + + channel = find_first_zero_bit(ahub->rx_usage, + TEGRA30_AHUB_CHANNEL_CTRL_COUNT); + if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) + return -EBUSY; + + __set_bit(channel, ahub->rx_usage); + + *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel; + *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO + + (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE); + *reqsel = ahub->dma_sel + channel; + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK | + TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK); + val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) | + TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN | + TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16; + tegra30_apbif_write(reg, val); + + reg = TEGRA30_AHUB_CIF_RX_CTRL + + (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); + val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX; + tegra30_apbif_write(reg, val); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo); + +int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + int reg, val; + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; + tegra30_apbif_write(reg, val); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo); + +int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + int reg, val; + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; + tegra30_apbif_write(reg, val); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo); + +int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + + __clear_bit(channel, ahub->rx_usage); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo); + +int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, + unsigned long *fiforeg, + unsigned long *reqsel) +{ + int channel; + u32 reg, val; + + channel = find_first_zero_bit(ahub->tx_usage, + TEGRA30_AHUB_CHANNEL_CTRL_COUNT); + if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) + return -EBUSY; + + __set_bit(channel, ahub->tx_usage); + + *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel; + *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO + + (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE); + *reqsel = ahub->dma_sel + channel; + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK | + TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK); + val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) | + TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN | + TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16; + tegra30_apbif_write(reg, val); + + reg = TEGRA30_AHUB_CIF_TX_CTRL + + (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); + val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX; + tegra30_apbif_write(reg, val); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo); + +int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif) +{ + int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; + int reg, val; + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; + tegra30_apbif_write(reg, val); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo); + +int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif) +{ + int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; + int reg, val; + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; + tegra30_apbif_write(reg, val); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo); + +int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif) +{ + int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; + + __clear_bit(channel, ahub->tx_usage); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo); + +int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, + enum tegra30_ahub_txcif txcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + int reg; + + reg = TEGRA30_AHUB_AUDIO_RX + + (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); + tegra30_audio_write(reg, 1 << txcif); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source); + +int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + int reg; + + reg = TEGRA30_AHUB_AUDIO_RX + + (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); + tegra30_audio_write(reg, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source); + +static const char * const configlink_clocks[] __devinitconst = { + "i2s0", + "i2s1", + "i2s2", + "i2s3", + "i2s4", + "dam0", + "dam1", + "dam2", + "spdif_in", +}; + +struct of_dev_auxdata ahub_auxdata[] __devinitdata = { + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080300, "tegra30-i2s.0", NULL), + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080400, "tegra30-i2s.1", NULL), + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080500, "tegra30-i2s.2", NULL), + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080600, "tegra30-i2s.3", NULL), + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080700, "tegra30-i2s.4", NULL), + {} +}; + +#define LAST_REG(name) \ + (TEGRA30_AHUB_##name + \ + (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4) + +#define REG_IN_ARRAY(reg, name) \ + ((reg >= TEGRA30_AHUB_##name) && \ + (reg <= LAST_REG(name) && \ + (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE)))) + +static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA30_AHUB_CONFIG_LINK_CTRL: + case TEGRA30_AHUB_MISC_CTRL: + case TEGRA30_AHUB_APBDMA_LIVE_STATUS: + case TEGRA30_AHUB_I2S_LIVE_STATUS: + case TEGRA30_AHUB_SPDIF_LIVE_STATUS: + case TEGRA30_AHUB_I2S_INT_MASK: + case TEGRA30_AHUB_DAM_INT_MASK: + case TEGRA30_AHUB_SPDIF_INT_MASK: + case TEGRA30_AHUB_APBIF_INT_MASK: + case TEGRA30_AHUB_I2S_INT_STATUS: + case TEGRA30_AHUB_DAM_INT_STATUS: + case TEGRA30_AHUB_SPDIF_INT_STATUS: + case TEGRA30_AHUB_APBIF_INT_STATUS: + case TEGRA30_AHUB_I2S_INT_SOURCE: + case TEGRA30_AHUB_DAM_INT_SOURCE: + case TEGRA30_AHUB_SPDIF_INT_SOURCE: + case TEGRA30_AHUB_APBIF_INT_SOURCE: + case TEGRA30_AHUB_I2S_INT_SET: + case TEGRA30_AHUB_DAM_INT_SET: + case TEGRA30_AHUB_SPDIF_INT_SET: + case TEGRA30_AHUB_APBIF_INT_SET: + return true; + default: + break; + }; + + if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || + REG_IN_ARRAY(reg, CHANNEL_CLEAR) || + REG_IN_ARRAY(reg, CHANNEL_STATUS) || + REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || + REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || + REG_IN_ARRAY(reg, CIF_TX_CTRL) || + REG_IN_ARRAY(reg, CIF_RX_CTRL) || + REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) + return true; + + return false; +} + +static bool tegra30_ahub_apbif_volatile_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case TEGRA30_AHUB_CONFIG_LINK_CTRL: + case TEGRA30_AHUB_MISC_CTRL: + case TEGRA30_AHUB_APBDMA_LIVE_STATUS: + case TEGRA30_AHUB_I2S_LIVE_STATUS: + case TEGRA30_AHUB_SPDIF_LIVE_STATUS: + case TEGRA30_AHUB_I2S_INT_STATUS: + case TEGRA30_AHUB_DAM_INT_STATUS: + case TEGRA30_AHUB_SPDIF_INT_STATUS: + case TEGRA30_AHUB_APBIF_INT_STATUS: + case TEGRA30_AHUB_I2S_INT_SET: + case TEGRA30_AHUB_DAM_INT_SET: + case TEGRA30_AHUB_SPDIF_INT_SET: + case TEGRA30_AHUB_APBIF_INT_SET: + return true; + default: + break; + }; + + if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || + REG_IN_ARRAY(reg, CHANNEL_STATUS) || + REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || + REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || + REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) + return true; + + return false; +} + +static bool tegra30_ahub_apbif_precious_reg(struct device *dev, + unsigned int reg) +{ + if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || + REG_IN_ARRAY(reg, CHANNEL_RXFIFO)) + return true; + + return false; +} + +static const struct regmap_config tegra30_ahub_apbif_regmap_config = { + .name = "apbif", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TEGRA30_AHUB_APBIF_INT_SET, + .writeable_reg = tegra30_ahub_apbif_wr_rd_reg, + .readable_reg = tegra30_ahub_apbif_wr_rd_reg, + .volatile_reg = tegra30_ahub_apbif_volatile_reg, + .precious_reg = tegra30_ahub_apbif_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg) +{ + if (REG_IN_ARRAY(reg, AUDIO_RX)) + return true; + + return false; +} + +static const struct regmap_config tegra30_ahub_ahub_regmap_config = { + .name = "ahub", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LAST_REG(AUDIO_RX), + .writeable_reg = tegra30_ahub_ahub_wr_rd_reg, + .readable_reg = tegra30_ahub_ahub_wr_rd_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit tegra30_ahub_probe(struct platform_device *pdev) +{ + struct clk *clk; + int i; + struct resource *res0, *res1, *region; + u32 of_dma[2]; + void __iomem *regs_apbif, *regs_ahub; + int ret = 0; + + if (ahub) + return -ENODEV; + + /* + * The AHUB hosts a register bus: the "configlink". For this to + * operate correctly, all devices on this bus must be out of reset. + * Ensure that here. + */ + for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) { + clk = clk_get_sys(NULL, configlink_clocks[i]); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Can't get clock %s\n", + configlink_clocks[i]); + ret = PTR_ERR(clk); + goto err; + } + tegra_periph_reset_deassert(clk); + clk_put(clk); + } + + ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub), + GFP_KERNEL); + if (!ahub) { + dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, ahub); + + ahub->dev = &pdev->dev; + + ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio"); + if (IS_ERR(ahub->clk_d_audio)) { + dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n"); + ret = PTR_ERR(ahub->clk_d_audio); + goto err; + } + + ahub->clk_apbif = clk_get(&pdev->dev, "apbif"); + if (IS_ERR(ahub->clk_apbif)) { + dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n"); + ret = PTR_ERR(ahub->clk_apbif); + goto err_clk_put_d_audio; + } + + if (of_property_read_u32_array(pdev->dev.of_node, + "nvidia,dma-request-selector", + of_dma, 2) < 0) { + dev_err(&pdev->dev, + "Missing property nvidia,dma-request-selector\n"); + ret = -ENODEV; + goto err_clk_put_d_audio; + } + ahub->dma_sel = of_dma[1]; + + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res0) { + dev_err(&pdev->dev, "No apbif memory resource\n"); + ret = -ENODEV; + goto err_clk_put_apbif; + } + + region = devm_request_mem_region(&pdev->dev, res0->start, + resource_size(res0), DRV_NAME); + if (!region) { + dev_err(&pdev->dev, "request region apbif failed\n"); + ret = -EBUSY; + goto err_clk_put_apbif; + } + ahub->apbif_addr = res0->start; + + regs_apbif = devm_ioremap(&pdev->dev, res0->start, + resource_size(res0)); + if (!regs_apbif) { + dev_err(&pdev->dev, "ioremap apbif failed\n"); + ret = -ENOMEM; + goto err_clk_put_apbif; + } + + ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif, + &tegra30_ahub_apbif_regmap_config); + if (IS_ERR(ahub->regmap_apbif)) { + dev_err(&pdev->dev, "apbif regmap init failed\n"); + ret = PTR_ERR(ahub->regmap_apbif); + goto err_clk_put_apbif; + } + regcache_cache_only(ahub->regmap_apbif, true); + + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res1) { + dev_err(&pdev->dev, "No ahub memory resource\n"); + ret = -ENODEV; + goto err_clk_put_apbif; + } + + region = devm_request_mem_region(&pdev->dev, res1->start, + resource_size(res1), DRV_NAME); + if (!region) { + dev_err(&pdev->dev, "request region ahub failed\n"); + ret = -EBUSY; + goto err_clk_put_apbif; + } + + regs_ahub = devm_ioremap(&pdev->dev, res1->start, + resource_size(res1)); + if (!regs_ahub) { + dev_err(&pdev->dev, "ioremap ahub failed\n"); + ret = -ENOMEM; + goto err_clk_put_apbif; + } + + ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub, + &tegra30_ahub_ahub_regmap_config); + if (IS_ERR(ahub->regmap_ahub)) { + dev_err(&pdev->dev, "ahub regmap init failed\n"); + ret = PTR_ERR(ahub->regmap_ahub); + goto err_clk_put_apbif; + } + regcache_cache_only(ahub->regmap_ahub, true); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra30_ahub_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + of_platform_populate(pdev->dev.of_node, NULL, ahub_auxdata, + &pdev->dev); + + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err_clk_put_apbif: + clk_put(ahub->clk_apbif); +err_clk_put_d_audio: + clk_put(ahub->clk_d_audio); + ahub = 0; +err: + return ret; +} + +static int __devexit tegra30_ahub_remove(struct platform_device *pdev) +{ + if (!ahub) + return -ENODEV; + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra30_ahub_runtime_suspend(&pdev->dev); + + clk_put(ahub->clk_apbif); + clk_put(ahub->clk_d_audio); + + ahub = 0; + + return 0; +} + +static const struct of_device_id tegra30_ahub_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra30-ahub", }, + {}, +}; + +static const struct dev_pm_ops tegra30_ahub_pm_ops __devinitconst = { + SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, + tegra30_ahub_runtime_resume, NULL) +}; + +static struct platform_driver tegra30_ahub_driver = { + .probe = tegra30_ahub_probe, + .remove = __devexit_p(tegra30_ahub_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra30_ahub_of_match, + .pm = &tegra30_ahub_pm_ops, + }, +}; +module_platform_driver(tegra30_ahub_driver); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra30 AHUB driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h new file mode 100644 index 000000000000..e690e2eecc92 --- /dev/null +++ b/sound/soc/tegra/tegra30_ahub.h @@ -0,0 +1,483 @@ +/* + * tegra30_ahub.h - Definitions for Tegra30 AHUB driver + * + * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __TEGRA30_AHUB_H__ +#define __TEGRA30_AHUB_H__ + +/* Fields in *_CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */ + +#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT 28 +#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0xf +#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) + +/* Channel count minus 1 */ +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT 24 +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 7 +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) + +/* Channel count minus 1 */ +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT 16 +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US 7 +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) + +#define TEGRA30_AUDIOCIF_BITS_4 0 +#define TEGRA30_AUDIOCIF_BITS_8 1 +#define TEGRA30_AUDIOCIF_BITS_12 2 +#define TEGRA30_AUDIOCIF_BITS_16 3 +#define TEGRA30_AUDIOCIF_BITS_20 4 +#define TEGRA30_AUDIOCIF_BITS_24 5 +#define TEGRA30_AUDIOCIF_BITS_28 6 +#define TEGRA30_AUDIOCIF_BITS_32 7 + +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT 12 +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK (7 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_4 (TEGRA30_AUDIOCIF_BITS_4 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_8 (TEGRA30_AUDIOCIF_BITS_8 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_12 (TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 (TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_20 (TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_24 (TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_28 (TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_32 (TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) + +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT 8 +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK (7 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_4 (TEGRA30_AUDIOCIF_BITS_4 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_8 (TEGRA30_AUDIOCIF_BITS_8 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_12 (TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 (TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_20 (TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_24 (TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_28 (TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_32 (TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) + +#define TEGRA30_AUDIOCIF_EXPAND_ZERO 0 +#define TEGRA30_AUDIOCIF_EXPAND_ONE 1 +#define TEGRA30_AUDIOCIF_EXPAND_LFSR 2 + +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT 6 +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_MASK (3 << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ZERO (TEGRA30_AUDIOCIF_EXPAND_ZERO << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ONE (TEGRA30_AUDIOCIF_EXPAND_ONE << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_LFSR (TEGRA30_AUDIOCIF_EXPAND_LFSR << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) + +#define TEGRA30_AUDIOCIF_STEREO_CONV_CH0 0 +#define TEGRA30_AUDIOCIF_STEREO_CONV_CH1 1 +#define TEGRA30_AUDIOCIF_STEREO_CONV_AVG 2 + +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT 4 +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_MASK (3 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH0 (TEGRA30_AUDIOCIF_STEREO_CONV_CH0 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH1 (TEGRA30_AUDIOCIF_STEREO_CONV_CH1 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_AVG (TEGRA30_AUDIOCIF_STEREO_CONV_AVG << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) + +#define TEGRA30_AUDIOCIF_CTRL_REPLICATE 3 + +#define TEGRA30_AUDIOCIF_DIRECTION_TX 0 +#define TEGRA30_AUDIOCIF_DIRECTION_RX 1 + +#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT 2 +#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_MASK (1 << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX (TEGRA30_AUDIOCIF_DIRECTION_TX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX (TEGRA30_AUDIOCIF_DIRECTION_RX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) + +#define TEGRA30_AUDIOCIF_TRUNCATE_ROUND 0 +#define TEGRA30_AUDIOCIF_TRUNCATE_CHOP 1 + +#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT 1 +#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_MASK (1 << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_ROUND (TEGRA30_AUDIOCIF_TRUNCATE_ROUND << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_CHOP (TEGRA30_AUDIOCIF_TRUNCATE_CHOP << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) + +#define TEGRA30_AUDIOCIF_MONO_CONV_ZERO 0 +#define TEGRA30_AUDIOCIF_MONO_CONV_COPY 1 + +#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT 0 +#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_MASK (1 << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_ZERO (TEGRA30_AUDIOCIF_MONO_CONV_ZERO << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_COPY (TEGRA30_AUDIOCIF_MONO_CONV_COPY << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT) + +/* Registers within TEGRA30_AUDIO_CLUSTER_BASE */ + +/* TEGRA30_AHUB_CHANNEL_CTRL */ + +#define TEGRA30_AHUB_CHANNEL_CTRL 0x0 +#define TEGRA30_AHUB_CHANNEL_CTRL_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_CTRL_COUNT 4 +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_EN (1 << 31) +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_EN (1 << 30) +#define TEGRA30_AHUB_CHANNEL_CTRL_LOOPBACK (1 << 29) + +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT 16 +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US 0xff +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK (TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) + +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT 8 +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US 0xff +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK (TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) + +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN (1 << 6) + +#define TEGRA30_PACK_8_4 2 +#define TEGRA30_PACK_16 3 + +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT 4 +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US 3 +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK (TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT) +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_8_4 (TEGRA30_PACK_8_4 << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT) +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16 (TEGRA30_PACK_16 << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT) + +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN (1 << 2) + +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT 0 +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US 3 +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK (TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT) +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_8_4 (TEGRA30_PACK_8_4 << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT) +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16 (TEGRA30_PACK_16 << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT) + +/* TEGRA30_AHUB_CHANNEL_CLEAR */ + +#define TEGRA30_AHUB_CHANNEL_CLEAR 0x4 +#define TEGRA30_AHUB_CHANNEL_CLEAR_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_CLEAR_COUNT 4 +#define TEGRA30_AHUB_CHANNEL_CLEAR_TX_SOFT_RESET (1 << 31) +#define TEGRA30_AHUB_CHANNEL_CLEAR_RX_SOFT_RESET (1 << 30) + +/* TEGRA30_AHUB_CHANNEL_STATUS */ + +#define TEGRA30_AHUB_CHANNEL_STATUS 0x8 +#define TEGRA30_AHUB_CHANNEL_STATUS_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_STATUS_COUNT 4 +#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT 24 +#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US 0xff +#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK (TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US << TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT) +#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT 16 +#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US 0xff +#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK (TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US << TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT) +#define TEGRA30_AHUB_CHANNEL_STATUS_TX_TRIG (1 << 1) +#define TEGRA30_AHUB_CHANNEL_STATUS_RX_TRIG (1 << 0) + +/* TEGRA30_AHUB_CHANNEL_TXFIFO */ + +#define TEGRA30_AHUB_CHANNEL_TXFIFO 0xc +#define TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_TXFIFO_COUNT 4 + +/* TEGRA30_AHUB_CHANNEL_RXFIFO */ + +#define TEGRA30_AHUB_CHANNEL_RXFIFO 0x10 +#define TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_RXFIFO_COUNT 4 + +/* TEGRA30_AHUB_CIF_TX_CTRL */ + +#define TEGRA30_AHUB_CIF_TX_CTRL 0x14 +#define TEGRA30_AHUB_CIF_TX_CTRL_STRIDE 0x20 +#define TEGRA30_AHUB_CIF_TX_CTRL_COUNT 4 +/* Uses field from TEGRA30_AUDIOCIF_CTRL_* */ + +/* TEGRA30_AHUB_CIF_RX_CTRL */ + +#define TEGRA30_AHUB_CIF_RX_CTRL 0x18 +#define TEGRA30_AHUB_CIF_RX_CTRL_STRIDE 0x20 +#define TEGRA30_AHUB_CIF_RX_CTRL_COUNT 4 +/* Uses field from TEGRA30_AUDIOCIF_CTRL_* */ + +/* TEGRA30_AHUB_CONFIG_LINK_CTRL */ + +#define TEGRA30_AHUB_CONFIG_LINK_CTRL 0x80 +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT 28 +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US 0xf +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT 16 +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US 0xfff +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT 4 +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US 0xfff +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_CG_EN (1 << 2) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_CLEAR_TIMEOUT_CNTR (1 << 1) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_SOFT_RESET (1 << 0) + +/* TEGRA30_AHUB_MISC_CTRL */ + +#define TEGRA30_AHUB_MISC_CTRL 0x84 +#define TEGRA30_AHUB_MISC_CTRL_AUDIO_ACTIVE (1 << 31) +#define TEGRA30_AHUB_MISC_CTRL_AUDIO_CG_EN (1 << 8) +#define TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT 0 +#define TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_MASK (0x1f << TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT) + +/* TEGRA30_AHUB_APBDMA_LIVE_STATUS */ + +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS 0x88 +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_FULL (1 << 31) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_FULL (1 << 30) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_FULL (1 << 29) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_FULL (1 << 28) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_FULL (1 << 27) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_FULL (1 << 26) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_FULL (1 << 25) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_FULL (1 << 24) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_EMPTY (1 << 23) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_EMPTY (1 << 22) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_EMPTY (1 << 21) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_EMPTY (1 << 20) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_EMPTY (1 << 19) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_EMPTY (1 << 18) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_EMPTY (1 << 17) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_EMPTY (1 << 16) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_FULL (1 << 15) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_FULL (1 << 14) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_FULL (1 << 13) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_FULL (1 << 12) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_FULL (1 << 11) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_FULL (1 << 10) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_FULL (1 << 9) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_FULL (1 << 8) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_EMPTY (1 << 7) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_EMPTY (1 << 6) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_EMPTY (1 << 5) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_EMPTY (1 << 4) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_EMPTY (1 << 3) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_EMPTY (1 << 2) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_EMPTY (1 << 1) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_EMPTY (1 << 0) + +/* TEGRA30_AHUB_I2S_LIVE_STATUS */ + +#define TEGRA30_AHUB_I2S_LIVE_STATUS 0x8c +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_FULL (1 << 29) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_FULL (1 << 28) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_FULL (1 << 27) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_FULL (1 << 26) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_FULL (1 << 25) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_FULL (1 << 24) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_FULL (1 << 23) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_FULL (1 << 22) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_FULL (1 << 21) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_FULL (1 << 20) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_ENABLED (1 << 19) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_ENABLED (1 << 18) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_ENABLED (1 << 17) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_ENABLED (1 << 16) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_ENABLED (1 << 15) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_ENABLED (1 << 14) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_ENABLED (1 << 13) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_ENABLED (1 << 12) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_ENABLED (1 << 11) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_ENABLED (1 << 10) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_EMPTY (1 << 9) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_EMPTY (1 << 8) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_EMPTY (1 << 7) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_EMPTY (1 << 6) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_EMPTY (1 << 5) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_EMPTY (1 << 4) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_EMPTY (1 << 3) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_EMPTY (1 << 2) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_EMPTY (1 << 1) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_EMPTY (1 << 0) + +/* TEGRA30_AHUB_DAM0_LIVE_STATUS */ + +#define TEGRA30_AHUB_DAM_LIVE_STATUS 0x90 +#define TEGRA30_AHUB_DAM_LIVE_STATUS_STRIDE 0x8 +#define TEGRA30_AHUB_DAM_LIVE_STATUS_COUNT 3 +#define TEGRA30_AHUB_DAM_LIVE_STATUS_TX_ENABLED (1 << 26) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1_ENABLED (1 << 25) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0_ENABLED (1 << 24) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_TXFIFO_FULL (1 << 15) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1FIFO_FULL (1 << 9) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0FIFO_FULL (1 << 8) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_TXFIFO_EMPTY (1 << 7) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1FIFO_EMPTY (1 << 1) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0FIFO_EMPTY (1 << 0) + +/* TEGRA30_AHUB_SPDIF_LIVE_STATUS */ + +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS 0xa8 +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TX_ENABLED (1 << 11) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RX_ENABLED (1 << 10) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TX_ENABLED (1 << 9) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RX_ENABLED (1 << 8) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_FULL (1 << 7) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_FULL (1 << 6) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_FULL (1 << 5) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_FULL (1 << 4) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_EMPTY (1 << 3) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_EMPTY (1 << 2) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_EMPTY (1 << 1) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_EMPTY (1 << 0) + +/* TEGRA30_AHUB_I2S_INT_MASK */ + +#define TEGRA30_AHUB_I2S_INT_MASK 0xb0 + +/* TEGRA30_AHUB_DAM_INT_MASK */ + +#define TEGRA30_AHUB_DAM_INT_MASK 0xb4 + +/* TEGRA30_AHUB_SPDIF_INT_MASK */ + +#define TEGRA30_AHUB_SPDIF_INT_MASK 0xbc + +/* TEGRA30_AHUB_APBIF_INT_MASK */ + +#define TEGRA30_AHUB_APBIF_INT_MASK 0xc0 + +/* TEGRA30_AHUB_I2S_INT_STATUS */ + +#define TEGRA30_AHUB_I2S_INT_STATUS 0xc8 + +/* TEGRA30_AHUB_DAM_INT_STATUS */ + +#define TEGRA30_AHUB_DAM_INT_STATUS 0xcc + +/* TEGRA30_AHUB_SPDIF_INT_STATUS */ + +#define TEGRA30_AHUB_SPDIF_INT_STATUS 0xd4 + +/* TEGRA30_AHUB_APBIF_INT_STATUS */ + +#define TEGRA30_AHUB_APBIF_INT_STATUS 0xd8 + +/* TEGRA30_AHUB_I2S_INT_SOURCE */ + +#define TEGRA30_AHUB_I2S_INT_SOURCE 0xe0 + +/* TEGRA30_AHUB_DAM_INT_SOURCE */ + +#define TEGRA30_AHUB_DAM_INT_SOURCE 0xe4 + +/* TEGRA30_AHUB_SPDIF_INT_SOURCE */ + +#define TEGRA30_AHUB_SPDIF_INT_SOURCE 0xec + +/* TEGRA30_AHUB_APBIF_INT_SOURCE */ + +#define TEGRA30_AHUB_APBIF_INT_SOURCE 0xf0 + +/* TEGRA30_AHUB_I2S_INT_SET */ + +#define TEGRA30_AHUB_I2S_INT_SET 0xf8 + +/* TEGRA30_AHUB_DAM_INT_SET */ + +#define TEGRA30_AHUB_DAM_INT_SET 0xfc + +/* TEGRA30_AHUB_SPDIF_INT_SET */ + +#define TEGRA30_AHUB_SPDIF_INT_SET 0x100 + +/* TEGRA30_AHUB_APBIF_INT_SET */ + +#define TEGRA30_AHUB_APBIF_INT_SET 0x104 + +/* Registers within TEGRA30_AHUB_BASE */ + +#define TEGRA30_AHUB_AUDIO_RX 0x0 +#define TEGRA30_AHUB_AUDIO_RX_STRIDE 0x4 +#define TEGRA30_AHUB_AUDIO_RX_COUNT 17 +/* This register repeats once for each entry in enum tegra30_ahub_rxcif */ +/* The fields in this register are 1 bit per entry in tegra30_ahub_txcif */ + +/* + * Terminology: + * AHUB: Audio Hub; a cross-bar switch between the audio devices: DMA FIFOs, + * I2S controllers, SPDIF controllers, and DAMs. + * XBAR: The core cross-bar component of the AHUB. + * CIF: Client Interface; the HW module connecting an audio device to the + * XBAR. + * DAM: Digital Audio Mixer: A HW module that mixes multiple audio streams, + * possibly including sample-rate conversion. + * + * Each TX CIF transmits data into the XBAR. Each RX CIF can receive audio + * transmitted by a particular TX CIF. + * + * This driver is currently very simplistic; many HW features are not + * exposed; DAMs are not supported, only 16-bit stereo audio is supported, + * etc. + */ + +enum tegra30_ahub_txcif { + TEGRA30_AHUB_TXCIF_APBIF_TX0, + TEGRA30_AHUB_TXCIF_APBIF_TX1, + TEGRA30_AHUB_TXCIF_APBIF_TX2, + TEGRA30_AHUB_TXCIF_APBIF_TX3, + TEGRA30_AHUB_TXCIF_I2S0_TX0, + TEGRA30_AHUB_TXCIF_I2S1_TX0, + TEGRA30_AHUB_TXCIF_I2S2_TX0, + TEGRA30_AHUB_TXCIF_I2S3_TX0, + TEGRA30_AHUB_TXCIF_I2S4_TX0, + TEGRA30_AHUB_TXCIF_DAM0_TX0, + TEGRA30_AHUB_TXCIF_DAM1_TX0, + TEGRA30_AHUB_TXCIF_DAM2_TX0, + TEGRA30_AHUB_TXCIF_SPDIF_TX0, + TEGRA30_AHUB_TXCIF_SPDIF_TX1, +}; + +enum tegra30_ahub_rxcif { + TEGRA30_AHUB_RXCIF_APBIF_RX0, + TEGRA30_AHUB_RXCIF_APBIF_RX1, + TEGRA30_AHUB_RXcIF_APBIF_RX2, + TEGRA30_AHUB_RXCIF_APBIF_RX3, + TEGRA30_AHUB_RXCIF_I2S0_RX0, + TEGRA30_AHUB_RXCIF_I2S1_RX0, + TEGRA30_AHUB_RXCIF_I2S2_RX0, + TEGRA30_AHUB_RXCIF_I2S3_RX0, + TEGRA30_AHUB_RXCIF_I2S4_RX0, + TEGRA30_AHUB_RXCIF_DAM0_RX0, + TEGRA30_AHUB_RXCIF_DAM0_RX1, + TEGRA30_AHUB_RXCIF_DAM1_RX0, + TEGRA30_AHUB_RXCIF_DAM2_RX1, + TEGRA30_AHUB_RXCIF_DAM3_RX0, + TEGRA30_AHUB_RXCIF_DAM3_RX1, + TEGRA30_AHUB_RXCIF_SPDIF_RX0, + TEGRA30_AHUB_RXCIF_SPDIF_RX1, +}; + +extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, + unsigned long *fiforeg, + unsigned long *reqsel); +extern int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif); +extern int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif); +extern int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif); + +extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, + unsigned long *fiforeg, + unsigned long *reqsel); +extern int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif); +extern int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif); +extern int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif); + +extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, + enum tegra30_ahub_txcif txcif); +extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif); + +struct tegra30_ahub { + struct device *dev; + struct clk *clk_d_audio; + struct clk *clk_apbif; + int dma_sel; + resource_size_t apbif_addr; + struct regmap *regmap_apbif; + struct regmap *regmap_ahub; + DECLARE_BITMAP(rx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT); + DECLARE_BITMAP(tx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT); +}; + +#endif diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c new file mode 100644 index 000000000000..8596032985dc --- /dev/null +++ b/sound/soc/tegra/tegra30_i2s.c @@ -0,0 +1,536 @@ +/* + * tegra30_i2s.c - Tegra30 I2S driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra30_ahub.h" +#include "tegra30_i2s.h" + +#define DRV_NAME "tegra30-i2s" + +static inline void tegra30_i2s_write(struct tegra30_i2s *i2s, u32 reg, u32 val) +{ + regmap_write(i2s->regmap, reg, val); +} + +static inline u32 tegra30_i2s_read(struct tegra30_i2s *i2s, u32 reg) +{ + u32 val; + regmap_read(i2s->regmap, reg, &val); + return val; +} + +static int tegra30_i2s_runtime_suspend(struct device *dev) +{ + struct tegra30_i2s *i2s = dev_get_drvdata(dev); + + regcache_cache_only(i2s->regmap, true); + + clk_disable(i2s->clk_i2s); + + return 0; +} + +static int tegra30_i2s_runtime_resume(struct device *dev) +{ + struct tegra30_i2s *i2s = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(i2s->clk_i2s); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + regcache_cache_only(i2s->regmap, false); + + return 0; +} + +int tegra30_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif, + &i2s->playback_dma_data.addr, + &i2s->playback_dma_data.req_sel); + i2s->playback_dma_data.wrap = 4; + i2s->playback_dma_data.width = 32; + tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif, + i2s->playback_fifo_cif); + } else { + ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif, + &i2s->capture_dma_data.addr, + &i2s->capture_dma_data.req_sel); + i2s->capture_dma_data.wrap = 4; + i2s->capture_dma_data.width = 32; + tegra30_ahub_set_rx_cif_source(i2s->capture_fifo_cif, + i2s->capture_i2s_cif); + } + + return ret; +} + +void tegra30_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif); + tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif); + } else { + tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif); + tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif); + } +} + +static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_MASTER_ENABLE; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_MASTER_ENABLE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~(TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK | + TEGRA30_I2S_CTRL_LRCK_MASK); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_DSP_B: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_R_LOW; + break; + case SND_SOC_DAIFMT_I2S: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = substream->pcm->card->dev; + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 val; + int ret, sample_size, srate, i2sclock, bitcnt; + + if (params_channels(params) != 2) + return -EINVAL; + + i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_BIT_SIZE_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_16; + sample_size = 16; + break; + default: + return -EINVAL; + } + + srate = params_rate(params); + + /* Final "* 2" required by Tegra hardware */ + i2sclock = srate * params_channels(params) * sample_size * 2; + + bitcnt = (i2sclock / (2 * srate)) - 1; + if (bitcnt < 0 || bitcnt > TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) + return -EINVAL; + + ret = clk_set_rate(i2s->clk_i2s, i2sclock); + if (ret) { + dev_err(dev, "Can't set I2S clock rate: %d\n", ret); + return ret; + } + + val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; + + if (i2sclock % (2 * srate)) + val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE; + + tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val); + + val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_RX_CTRL, val); + } else { + val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_TX_CTRL, val); + } + + val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) | + (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT); + tegra30_i2s_write(i2s, TEGRA30_I2S_OFFSET, val); + + return 0; +} + +static void tegra30_i2s_start_playback(struct tegra30_i2s *i2s) +{ + tegra30_ahub_enable_tx_fifo(i2s->playback_fifo_cif); + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_TX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s) +{ + tegra30_ahub_disable_tx_fifo(i2s->playback_fifo_cif); + i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s) +{ + tegra30_ahub_enable_rx_fifo(i2s->capture_fifo_cif); + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_RX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s) +{ + tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif); + i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl); +} + +static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra30_i2s_start_playback(i2s); + else + tegra30_i2s_start_capture(i2s); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra30_i2s_stop_playback(i2s); + else + tegra30_i2s_stop_capture(i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra30_i2s_probe(struct snd_soc_dai *dai) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &i2s->capture_dma_data; + dai->playback_dma_data = &i2s->playback_dma_data; + + return 0; +} + +static struct snd_soc_dai_ops tegra30_i2s_dai_ops = { + .startup = tegra30_i2s_startup, + .shutdown = tegra30_i2s_shutdown, + .set_fmt = tegra30_i2s_set_fmt, + .hw_params = tegra30_i2s_hw_params, + .trigger = tegra30_i2s_trigger, +}; + +static const struct snd_soc_dai_driver tegra30_i2s_dai_template = { + .probe = tegra30_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra30_i2s_dai_ops, + .symmetric_rates = 1, +}; + +static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA30_I2S_CTRL: + case TEGRA30_I2S_TIMING: + case TEGRA30_I2S_OFFSET: + case TEGRA30_I2S_CH_CTRL: + case TEGRA30_I2S_SLOT_CTRL: + case TEGRA30_I2S_CIF_RX_CTRL: + case TEGRA30_I2S_CIF_TX_CTRL: + case TEGRA30_I2S_FLOWCTL: + case TEGRA30_I2S_TX_STEP: + case TEGRA30_I2S_FLOW_STATUS: + case TEGRA30_I2S_FLOW_TOTAL: + case TEGRA30_I2S_FLOW_OVER: + case TEGRA30_I2S_FLOW_UNDER: + case TEGRA30_I2S_LCOEF_1_4_0: + case TEGRA30_I2S_LCOEF_1_4_1: + case TEGRA30_I2S_LCOEF_1_4_2: + case TEGRA30_I2S_LCOEF_1_4_3: + case TEGRA30_I2S_LCOEF_1_4_4: + case TEGRA30_I2S_LCOEF_1_4_5: + case TEGRA30_I2S_LCOEF_2_4_0: + case TEGRA30_I2S_LCOEF_2_4_1: + case TEGRA30_I2S_LCOEF_2_4_2: + return true; + default: + return false; + }; +} + +static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA30_I2S_FLOW_STATUS: + case TEGRA30_I2S_FLOW_TOTAL: + case TEGRA30_I2S_FLOW_OVER: + case TEGRA30_I2S_FLOW_UNDER: + return true; + default: + return false; + }; +} + +static const struct regmap_config tegra30_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA30_I2S_LCOEF_2_4_2, + .writeable_reg = tegra30_i2s_wr_rd_reg, + .readable_reg = tegra30_i2s_wr_rd_reg, + .volatile_reg = tegra30_i2s_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev) +{ + struct tegra30_i2s *i2s; + u32 cif_ids[2]; + struct resource *mem, *memregion; + void __iomem *regs; + int ret; + + i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_i2s), GFP_KERNEL); + if (!i2s) { + dev_err(&pdev->dev, "Can't allocate tegra30_i2s\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, i2s); + + i2s->dai = tegra30_i2s_dai_template; + i2s->dai.name = dev_name(&pdev->dev); + + ret = of_property_read_u32_array(pdev->dev.of_node, + "nvidia,ahub-cif-ids", cif_ids, + ARRAY_SIZE(cif_ids)); + if (ret < 0) + goto err; + + i2s->playback_i2s_cif = cif_ids[0]; + i2s->capture_i2s_cif = cif_ids[1]; + + i2s->clk_i2s = clk_get(&pdev->dev, NULL); + if (IS_ERR(i2s->clk_i2s)) { + dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); + ret = PTR_ERR(i2s->clk_i2s); + goto err; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + memregion = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_clk_put; + } + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra30_i2s_regmap_config); + if (IS_ERR(i2s->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(i2s->regmap); + goto err_clk_put; + } + regcache_cache_only(i2s->regmap, true); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra30_i2s_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = snd_soc_register_dai(&pdev->dev, &i2s->dai); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_suspend; + } + + ret = tegra_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + goto err_unregister_dai; + } + + return 0; + +err_unregister_dai: + snd_soc_unregister_dai(&pdev->dev); +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra30_i2s_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err_clk_put: + clk_put(i2s->clk_i2s); +err: + return ret; +} + +static int __devexit tegra30_i2s_platform_remove(struct platform_device *pdev) +{ + struct tegra30_i2s *i2s = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra30_i2s_runtime_suspend(&pdev->dev); + + tegra_pcm_platform_unregister(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + clk_put(i2s->clk_i2s); + + return 0; +} + +static const struct of_device_id tegra30_i2s_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra30-i2s", }, + {}, +}; + +static const struct dev_pm_ops tegra30_i2s_pm_ops __devinitconst = { + SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend, + tegra30_i2s_runtime_resume, NULL) +}; + +static struct platform_driver tegra30_i2s_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra30_i2s_of_match, + .pm = &tegra30_i2s_pm_ops, + }, + .probe = tegra30_i2s_platform_probe, + .remove = __devexit_p(tegra30_i2s_platform_remove), +}; +module_platform_driver(tegra30_i2s_driver); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra30 I2S ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra30_i2s_of_match); diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h new file mode 100644 index 000000000000..91adf29c7a87 --- /dev/null +++ b/sound/soc/tegra/tegra30_i2s.h @@ -0,0 +1,242 @@ +/* + * tegra30_i2s.h - Definitions for Tegra30 I2S driver + * + * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __TEGRA30_I2S_H__ +#define __TEGRA30_I2S_H__ + +#include "tegra_pcm.h" + +/* Register offsets from TEGRA30_I2S*_BASE */ + +#define TEGRA30_I2S_CTRL 0x0 +#define TEGRA30_I2S_TIMING 0x4 +#define TEGRA30_I2S_OFFSET 0x08 +#define TEGRA30_I2S_CH_CTRL 0x0c +#define TEGRA30_I2S_SLOT_CTRL 0x10 +#define TEGRA30_I2S_CIF_RX_CTRL 0x14 +#define TEGRA30_I2S_CIF_TX_CTRL 0x18 +#define TEGRA30_I2S_FLOWCTL 0x1c +#define TEGRA30_I2S_TX_STEP 0x20 +#define TEGRA30_I2S_FLOW_STATUS 0x24 +#define TEGRA30_I2S_FLOW_TOTAL 0x28 +#define TEGRA30_I2S_FLOW_OVER 0x2c +#define TEGRA30_I2S_FLOW_UNDER 0x30 +#define TEGRA30_I2S_LCOEF_1_4_0 0x34 +#define TEGRA30_I2S_LCOEF_1_4_1 0x38 +#define TEGRA30_I2S_LCOEF_1_4_2 0x3c +#define TEGRA30_I2S_LCOEF_1_4_3 0x40 +#define TEGRA30_I2S_LCOEF_1_4_4 0x44 +#define TEGRA30_I2S_LCOEF_1_4_5 0x48 +#define TEGRA30_I2S_LCOEF_2_4_0 0x4c +#define TEGRA30_I2S_LCOEF_2_4_1 0x50 +#define TEGRA30_I2S_LCOEF_2_4_2 0x54 + +/* Fields in TEGRA30_I2S_CTRL */ + +#define TEGRA30_I2S_CTRL_XFER_EN_TX (1 << 31) +#define TEGRA30_I2S_CTRL_XFER_EN_RX (1 << 30) +#define TEGRA30_I2S_CTRL_CG_EN (1 << 29) +#define TEGRA30_I2S_CTRL_SOFT_RESET (1 << 28) +#define TEGRA30_I2S_CTRL_TX_FLOWCTL_EN (1 << 27) + +#define TEGRA30_I2S_CTRL_OBS_SEL_SHIFT 24 +#define TEGRA30_I2S_CTRL_OBS_SEL_MASK (7 << TEGRA30_I2S_CTRL_OBS_SEL_SHIFT) + +#define TEGRA30_I2S_FRAME_FORMAT_LRCK 0 +#define TEGRA30_I2S_FRAME_FORMAT_FSYNC 1 + +#define TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT 12 +#define TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK (7 << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT) +#define TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK (TEGRA30_I2S_FRAME_FORMAT_LRCK << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT) +#define TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC (TEGRA30_I2S_FRAME_FORMAT_FSYNC << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT) + +#define TEGRA30_I2S_CTRL_MASTER_ENABLE (1 << 10) + +#define TEGRA30_I2S_LRCK_LEFT_LOW 0 +#define TEGRA30_I2S_LRCK_RIGHT_LOW 1 + +#define TEGRA30_I2S_CTRL_LRCK_SHIFT 9 +#define TEGRA30_I2S_CTRL_LRCK_MASK (1 << TEGRA30_I2S_CTRL_LRCK_SHIFT) +#define TEGRA30_I2S_CTRL_LRCK_L_LOW (TEGRA30_I2S_LRCK_LEFT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT) +#define TEGRA30_I2S_CTRL_LRCK_R_LOW (TEGRA30_I2S_LRCK_RIGHT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT) + +#define TEGRA30_I2S_CTRL_LPBK_ENABLE (1 << 8) + +#define TEGRA30_I2S_BIT_CODE_LINEAR 0 +#define TEGRA30_I2S_BIT_CODE_ULAW 1 +#define TEGRA30_I2S_BIT_CODE_ALAW 2 + +#define TEGRA30_I2S_CTRL_BIT_CODE_SHIFT 4 +#define TEGRA30_I2S_CTRL_BIT_CODE_MASK (3 << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_CODE_LINEAR (TEGRA30_I2S_BIT_CODE_LINEAR << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_CODE_ULAW (TEGRA30_I2S_BIT_CODE_ULAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_CODE_ALAW (TEGRA30_I2S_BIT_CODE_ALAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT) + +#define TEGRA30_I2S_BITS_8 1 +#define TEGRA30_I2S_BITS_12 2 +#define TEGRA30_I2S_BITS_16 3 +#define TEGRA30_I2S_BITS_20 4 +#define TEGRA30_I2S_BITS_24 5 +#define TEGRA30_I2S_BITS_28 6 +#define TEGRA30_I2S_BITS_32 7 + +/* Sample container size; see {RX,TX}_MASK field in CH_CTRL below */ +#define TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT 0 +#define TEGRA30_I2S_CTRL_BIT_SIZE_MASK (7 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_8 (TEGRA30_I2S_BITS_8 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_12 (TEGRA30_I2S_BITS_12 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_16 (TEGRA30_I2S_BITS_16 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_20 (TEGRA30_I2S_BITS_20 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_24 (TEGRA30_I2S_BITS_24 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_28 (TEGRA30_I2S_BITS_28 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_32 (TEGRA30_I2S_BITS_32 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) + +/* Fields in TEGRA30_I2S_TIMING */ + +#define TEGRA30_I2S_TIMING_NON_SYM_ENABLE (1 << 12) +#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0 +#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff +#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT) + +/* Fields in TEGRA30_I2S_OFFSET */ + +#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT 16 +#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US 0x7ff +#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK (TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) +#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT 0 +#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US 0x7ff +#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK (TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT) + +/* Fields in TEGRA30_I2S_CH_CTRL */ + +/* (FSYNC width - 1) in bit clocks */ +#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT 24 +#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US 0xff +#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK (TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US << TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT) + +#define TEGRA30_I2S_HIGHZ_NO 0 +#define TEGRA30_I2S_HIGHZ_YES 1 +#define TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK 2 + +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT 12 +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_MASK (3 << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_NO (TEGRA30_I2S_HIGHZ_NO << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_YES (TEGRA30_I2S_HIGHZ_YES << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_ON_HALF_BIT_CLK (TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT) + +#define TEGRA30_I2S_MSB_FIRST 0 +#define TEGRA30_I2S_LSB_FIRST 1 + +#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT 10 +#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MASK (1 << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MSB_FIRST (TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_LSB_FIRST (TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT 9 +#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MASK (1 << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MSB_FIRST (TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_LSB_FIRST (TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT) + +#define TEGRA30_I2S_POS_EDGE 0 +#define TEGRA30_I2S_NEG_EDGE 1 + +#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT 8 +#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_MASK (1 << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_POS_EDGE (TEGRA30_I2S_POS_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_NEG_EDGE (TEGRA30_I2S_NEG_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT) + +/* Sample size is # bits from BIT_SIZE minus this field */ +#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT 4 +#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US 7 +#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK (TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT) + +#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT 0 +#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US 7 +#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK (TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT) + +/* Fields in TEGRA30_I2S_SLOT_CTRL */ + +/* Number of slots in frame, minus 1 */ +#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 16 +#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US 7 +#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT) + +/* TDM mode slot enable bitmask */ +#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT 8 +#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK (0xff << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT) + +#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT 0 +#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK (0xff << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT) + +/* Fields in TEGRA30_I2S_CIF_RX_CTRL */ +/* Uses field from TEGRA30_AUDIOCIF_CTRL_* in tegra30_ahub.h */ + +/* Fields in TEGRA30_I2S_CIF_TX_CTRL */ +/* Uses field from TEGRA30_AUDIOCIF_CTRL_* in tegra30_ahub.h */ + +/* Fields in TEGRA30_I2S_FLOWCTL */ + +#define TEGRA30_I2S_FILTER_LINEAR 0 +#define TEGRA30_I2S_FILTER_QUAD 1 + +#define TEGRA30_I2S_FLOWCTL_FILTER_SHIFT 31 +#define TEGRA30_I2S_FLOWCTL_FILTER_MASK (1 << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT) +#define TEGRA30_I2S_FLOWCTL_FILTER_LINEAR (TEGRA30_I2S_FILTER_LINEAR << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT) +#define TEGRA30_I2S_FLOWCTL_FILTER_QUAD (TEGRA30_I2S_FILTER_QUAD << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT) + +/* Fields in TEGRA30_I2S_TX_STEP */ + +#define TEGRA30_I2S_TX_STEP_SHIFT 0 +#define TEGRA30_I2S_TX_STEP_MASK_US 0xffff +#define TEGRA30_I2S_TX_STEP_MASK (TEGRA30_I2S_TX_STEP_MASK_US << TEGRA30_I2S_TX_STEP_SHIFT) + +/* Fields in TEGRA30_I2S_FLOW_STATUS */ + +#define TEGRA30_I2S_FLOW_STATUS_UNDERFLOW (1 << 31) +#define TEGRA30_I2S_FLOW_STATUS_OVERFLOW (1 << 30) +#define TEGRA30_I2S_FLOW_STATUS_MONITOR_INT_EN (1 << 4) +#define TEGRA30_I2S_FLOW_STATUS_COUNTER_CLR (1 << 3) +#define TEGRA30_I2S_FLOW_STATUS_MONITOR_CLR (1 << 2) +#define TEGRA30_I2S_FLOW_STATUS_COUNTER_EN (1 << 1) +#define TEGRA30_I2S_FLOW_STATUS_MONITOR_EN (1 << 0) + +/* + * There are no fields in TEGRA30_I2S_FLOW_TOTAL, TEGRA30_I2S_FLOW_OVER, + * TEGRA30_I2S_FLOW_UNDER; they are counters taking the whole register. + */ + +/* Fields in TEGRA30_I2S_LCOEF_* */ + +#define TEGRA30_I2S_LCOEF_COEF_SHIFT 0 +#define TEGRA30_I2S_LCOEF_COEF_MASK_US 0xffff +#define TEGRA30_I2S_LCOEF_COEF_MASK (TEGRA30_I2S_LCOEF_COEF_MASK_US << TEGRA30_I2S_LCOEF_COEF_SHIFT) + +struct tegra30_i2s { + struct snd_soc_dai_driver dai; + int cif_id; + struct clk *clk_i2s; + enum tegra30_ahub_txcif capture_i2s_cif; + enum tegra30_ahub_rxcif capture_fifo_cif; + struct tegra_pcm_dma_params capture_dma_data; + enum tegra30_ahub_rxcif playback_i2s_cif; + enum tegra30_ahub_txcif playback_fifo_cif; + struct tegra_pcm_dma_params playback_dma_data; + struct regmap *regmap; + u32 reg_ctrl; +}; + +#endif diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index e45ccd851f6a..32de7006daf0 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -1,16 +1,17 @@ /* -* tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver -* -* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net> -* -* Authors: Leon Romanovsky <leon@leon.nu> -* Andrey Danin <danindrey@mail.ru> -* Marc Dietrich <marvin24@gmx.de> -* -* 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. -*/ + * tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver + * + * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net> + * Copyright (C) 2012 - NVIDIA, Inc. + * + * Authors: Leon Romanovsky <leon@leon.nu> + * Andrey Danin <danindrey@mail.ru> + * Marc Dietrich <marvin24@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ #include <asm/mach-types.h> @@ -28,9 +29,6 @@ #include "../codecs/alc5632.h" -#include "tegra_das.h" -#include "tegra_i2s.h" -#include "tegra_pcm.h" #include "tegra_asoc_utils.h" #define DRV_NAME "tegra-alc5632" @@ -39,7 +37,6 @@ struct tegra_alc5632 { struct tegra_asoc_utils_data util_data; - struct platform_device *pcm_dev; int gpio_requested; int gpio_hp_det; }; @@ -140,7 +137,6 @@ static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link tegra_alc5632_dai = { .name = "ALC5632", .stream_name = "ALC5632 PCM", - .platform_name = "tegra-pcm-audio", .codec_dai_name = "alc5632-hifi", .init = tegra_alc5632_asoc_init, .ops = &tegra_alc5632_asoc_ops, @@ -179,8 +175,6 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev) platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, alc5632); - alc5632->pcm_dev = ERR_PTR(-EINVAL); - if (!(pdev->dev.of_node)) { dev_err(&pdev->dev, "Must be instantiated using device tree\n"); ret = -EINVAL; @@ -214,18 +208,11 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev) goto err; } - alc5632->pcm_dev = platform_device_register_simple( - "tegra-pcm-audio", -1, NULL, 0); - if (IS_ERR(alc5632->pcm_dev)) { - dev_err(&pdev->dev, - "Can't instantiate tegra-pcm-audio\n"); - ret = PTR_ERR(alc5632->pcm_dev); - goto err; - } + tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_dai_of_node; ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); if (ret) - goto err_unregister; + goto err; ret = snd_soc_register_card(card); if (ret) { @@ -238,9 +225,6 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev) err_fini_utils: tegra_asoc_utils_fini(&alc5632->util_data); -err_unregister: - if (!IS_ERR(alc5632->pcm_dev)) - platform_device_unregister(alc5632->pcm_dev); err: return ret; } @@ -259,8 +243,6 @@ static int __devexit tegra_alc5632_remove(struct platform_device *pdev) snd_soc_unregister_card(card); tegra_asoc_utils_fini(&machine->util_data); - if (!IS_ERR(machine->pcm_dev)) - platform_device_unregister(machine->pcm_dev); return 0; } diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index f8428e410e05..9515ce58ea02 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -2,7 +2,7 @@ * tegra_asoc_utils.c - Harmony machine ASoC driver * * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010,2012 - NVIDIA, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,6 +25,7 @@ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include "tegra_asoc_utils.h" @@ -40,7 +41,10 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, case 22050: case 44100: case 88200: - new_baseclock = 56448000; + if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) + new_baseclock = 56448000; + else + new_baseclock = 564480000; break; case 8000: case 16000: @@ -48,7 +52,10 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, case 48000: case 64000: case 96000: - new_baseclock = 73728000; + if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) + new_baseclock = 73728000; + else + new_baseclock = 552960000; break; default: return -EINVAL; @@ -78,7 +85,7 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, return err; } - /* Don't set cdev1 rate; its locked to pll_a_out0 */ + /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */ err = clk_enable(data->clk_pll_a); if (err) { @@ -112,6 +119,17 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, data->dev = dev; + if (of_machine_is_compatible("nvidia,tegra20")) + data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20; + else if (of_machine_is_compatible("nvidia,tegra30")) + data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30; + else if (!dev->of_node) + /* non-DT is always Tegra20 */ + data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20; + else + /* DT boot, but unknown SoC */ + return -EINVAL; + data->clk_pll_a = clk_get_sys(NULL, "pll_a"); if (IS_ERR(data->clk_pll_a)) { dev_err(data->dev, "Can't retrieve clk pll_a\n"); @@ -126,15 +144,24 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, goto err_put_pll_a; } - data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); + if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) + data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); + else + data->clk_cdev1 = clk_get_sys("extern1", NULL); if (IS_ERR(data->clk_cdev1)) { dev_err(data->dev, "Can't retrieve clk cdev1\n"); ret = PTR_ERR(data->clk_cdev1); goto err_put_pll_a_out0; } + ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100); + if (ret) + goto err_put_cdev1; + return 0; +err_put_cdev1: + clk_put(data->clk_cdev1); err_put_pll_a_out0: clk_put(data->clk_pll_a_out0); err_put_pll_a: diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 4818195da25c..44db1dbb8f21 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -2,7 +2,7 @@ * tegra_asoc_utils.h - Definitions for Tegra DAS driver * * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010,2012 - NVIDIA, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,8 +26,14 @@ struct clk; struct device; +enum tegra_asoc_utils_soc { + TEGRA_ASOC_UTILS_SOC_TEGRA20, + TEGRA_ASOC_UTILS_SOC_TEGRA30, +}; + struct tegra_asoc_utils_data { struct device *dev; + enum tegra_asoc_utils_soc soc; struct clk *clk_pll_a; struct clk *clk_pll_a_out0; struct clk *clk_cdev1; @@ -42,4 +48,3 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data); #endif - diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c deleted file mode 100644 index 3b3c1ba4d235..000000000000 --- a/sound/soc/tegra/tegra_das.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * tegra_das.c - Tegra DAS driver - * - * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, 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. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/module.h> -#include <linux/debugfs.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <mach/iomap.h> -#include <sound/soc.h> -#include "tegra_das.h" - -#define DRV_NAME "tegra-das" - -static struct tegra_das *das; - -static inline void tegra_das_write(u32 reg, u32 val) -{ - __raw_writel(val, das->regs + reg); -} - -static inline u32 tegra_das_read(u32 reg) -{ - return __raw_readl(das->regs + reg); -} - -int tegra_das_connect_dap_to_dac(int dap, int dac) -{ - u32 addr; - u32 reg; - - if (!das) - return -ENODEV; - - addr = TEGRA_DAS_DAP_CTRL_SEL + - (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); - reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P; - - tegra_das_write(addr, reg); - - return 0; -} -EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac); - -int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master, - int sdata1rx, int sdata2rx) -{ - u32 addr; - u32 reg; - - if (!das) - return -ENODEV; - - addr = TEGRA_DAS_DAP_CTRL_SEL + - (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); - reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P | - !!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P | - !!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P | - !!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P; - - tegra_das_write(addr, reg); - - return 0; -} -EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap); - -int tegra_das_connect_dac_to_dap(int dac, int dap) -{ - u32 addr; - u32 reg; - - if (!das) - return -ENODEV; - - addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL + - (dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); - reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P | - dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P | - dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P; - - tegra_das_write(addr, reg); - - return 0; -} -EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap); - -#ifdef CONFIG_DEBUG_FS -static int tegra_das_show(struct seq_file *s, void *unused) -{ - int i; - u32 addr; - u32 reg; - - for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) { - addr = TEGRA_DAS_DAP_CTRL_SEL + - (i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); - reg = tegra_das_read(addr); - seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg); - } - - for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) { - addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL + - (i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); - reg = tegra_das_read(addr); - seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n", - i, reg); - } - - return 0; -} - -static int tegra_das_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, tegra_das_show, inode->i_private); -} - -static const struct file_operations tegra_das_debug_fops = { - .open = tegra_das_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void tegra_das_debug_add(struct tegra_das *das) -{ - das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, - snd_soc_debugfs_root, das, - &tegra_das_debug_fops); -} - -static void tegra_das_debug_remove(struct tegra_das *das) -{ - if (das->debug) - debugfs_remove(das->debug); -} -#else -static inline void tegra_das_debug_add(struct tegra_das *das) -{ -} - -static inline void tegra_das_debug_remove(struct tegra_das *das) -{ -} -#endif - -static int __devinit tegra_das_probe(struct platform_device *pdev) -{ - struct resource *res, *region; - int ret = 0; - - if (das) - return -ENODEV; - - das = devm_kzalloc(&pdev->dev, sizeof(struct tegra_das), GFP_KERNEL); - if (!das) { - dev_err(&pdev->dev, "Can't allocate tegra_das\n"); - ret = -ENOMEM; - goto err; - } - das->dev = &pdev->dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err; - } - - region = devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name); - if (!region) { - dev_err(&pdev->dev, "Memory region already claimed\n"); - ret = -EBUSY; - goto err; - } - - das->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!das->regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err; - } - - ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1, - TEGRA_DAS_DAP_SEL_DAC1); - if (ret) { - dev_err(&pdev->dev, "Can't set up DAS DAP connection\n"); - goto err; - } - ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1, - TEGRA_DAS_DAC_SEL_DAP1); - if (ret) { - dev_err(&pdev->dev, "Can't set up DAS DAC connection\n"); - goto err; - } - - tegra_das_debug_add(das); - - platform_set_drvdata(pdev, das); - - return 0; - -err: - das = NULL; - return ret; -} - -static int __devexit tegra_das_remove(struct platform_device *pdev) -{ - if (!das) - return -ENODEV; - - tegra_das_debug_remove(das); - - das = NULL; - - return 0; -} - -static const struct of_device_id tegra_das_of_match[] __devinitconst = { - { .compatible = "nvidia,tegra20-das", }, - {}, -}; - -static struct platform_driver tegra_das_driver = { - .probe = tegra_das_probe, - .remove = __devexit_p(tegra_das_remove), - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .of_match_table = tegra_das_of_match, - }, -}; -module_platform_driver(tegra_das_driver); - -MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); -MODULE_DESCRIPTION("Tegra DAS driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); -MODULE_DEVICE_TABLE(of, tegra_das_of_match); diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h deleted file mode 100644 index 2c96c7b3c459..000000000000 --- a/sound/soc/tegra/tegra_das.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * tegra_das.h - Definitions for Tegra DAS driver - * - * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, 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. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __TEGRA_DAS_H__ -#define __TEGRA_DAS_H__ - -/* Register TEGRA_DAS_DAP_CTRL_SEL */ -#define TEGRA_DAS_DAP_CTRL_SEL 0x00 -#define TEGRA_DAS_DAP_CTRL_SEL_COUNT 5 -#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE 4 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5 - -/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */ -#define TEGRA_DAS_DAP_SEL_DAC1 0 -#define TEGRA_DAS_DAP_SEL_DAC2 1 -#define TEGRA_DAS_DAP_SEL_DAC3 2 -#define TEGRA_DAS_DAP_SEL_DAP1 16 -#define TEGRA_DAS_DAP_SEL_DAP2 17 -#define TEGRA_DAS_DAP_SEL_DAP3 18 -#define TEGRA_DAS_DAP_SEL_DAP4 19 -#define TEGRA_DAS_DAP_SEL_DAP5 20 - -/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */ -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL 0x40 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4 - -/* - * Values for: - * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL - * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL - * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL - */ -#define TEGRA_DAS_DAC_SEL_DAP1 0 -#define TEGRA_DAS_DAC_SEL_DAP2 1 -#define TEGRA_DAS_DAC_SEL_DAP3 2 -#define TEGRA_DAS_DAC_SEL_DAP4 3 -#define TEGRA_DAS_DAC_SEL_DAP5 4 - -/* - * Names/IDs of the DACs/DAPs. - */ - -#define TEGRA_DAS_DAP_ID_1 0 -#define TEGRA_DAS_DAP_ID_2 1 -#define TEGRA_DAS_DAP_ID_3 2 -#define TEGRA_DAS_DAP_ID_4 3 -#define TEGRA_DAS_DAP_ID_5 4 - -#define TEGRA_DAS_DAC_ID_1 0 -#define TEGRA_DAS_DAC_ID_2 1 -#define TEGRA_DAS_DAC_ID_3 2 - -struct tegra_das { - struct device *dev; - void __iomem *regs; - struct dentry *debug; -}; - -/* - * Terminology: - * DAS: Digital audio switch (HW module controlled by this driver) - * DAP: Digital audio port (port/pins on Tegra device) - * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere) - * - * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific - * DAC, or another DAP. When DAPs are connected, one must be the master and - * one the slave. Each DAC allows selection of a specific DAP for input, to - * cater for the case where N DAPs are connected to 1 DAC for broadcast - * output. - * - * This driver is dumb; no attempt is made to ensure that a valid routing - * configuration is programmed. - */ - -/* - * Connect a DAP to to a DAC - * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_* - * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC* - */ -extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel); - -/* - * Connect a DAP to to another DAP - * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_* - * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP* - * master: Is this DAP the master (1) or slave (0) - * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0) - * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0) - */ -extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel, - int master, int sdata1rx, - int sdata2rx); - -/* - * Connect a DAC's input to a DAP - * (DAC outputs are selected by the DAP) - * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_* - * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP* - */ -extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel); - -#endif diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c deleted file mode 100644 index e53349912b2e..000000000000 --- a/sound/soc/tegra/tegra_i2s.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - * tegra_i2s.c - Tegra I2S driver - * - * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, Inc. - * - * Based on code copyright/by: - * - * Copyright (c) 2009-2010, NVIDIA Corporation. - * Scott Peterson <speterson@nvidia.com> - * - * Copyright (C) 2010 Google, Inc. - * Iliyan Malchev <malchev@google.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/clk.h> -#include <linux/module.h> -#include <linux/debugfs.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <linux/of.h> -#include <mach/iomap.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> - -#include "tegra_i2s.h" - -#define DRV_NAME "tegra-i2s" - -static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val) -{ - __raw_writel(val, i2s->regs + reg); -} - -static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg) -{ - return __raw_readl(i2s->regs + reg); -} - -#ifdef CONFIG_DEBUG_FS -static int tegra_i2s_show(struct seq_file *s, void *unused) -{ -#define REG(r) { r, #r } - static const struct { - int offset; - const char *name; - } regs[] = { - REG(TEGRA_I2S_CTRL), - REG(TEGRA_I2S_STATUS), - REG(TEGRA_I2S_TIMING), - REG(TEGRA_I2S_FIFO_SCR), - REG(TEGRA_I2S_PCM_CTRL), - REG(TEGRA_I2S_NW_CTRL), - REG(TEGRA_I2S_TDM_CTRL), - REG(TEGRA_I2S_TDM_TX_RX_CTRL), - }; -#undef REG - - struct tegra_i2s *i2s = s->private; - int i; - - clk_enable(i2s->clk_i2s); - - for (i = 0; i < ARRAY_SIZE(regs); i++) { - u32 val = tegra_i2s_read(i2s, regs[i].offset); - seq_printf(s, "%s = %08x\n", regs[i].name, val); - } - - clk_disable(i2s->clk_i2s); - - return 0; -} - -static int tegra_i2s_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, tegra_i2s_show, inode->i_private); -} - -static const struct file_operations tegra_i2s_debug_fops = { - .open = tegra_i2s_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void tegra_i2s_debug_add(struct tegra_i2s *i2s) -{ - i2s->debug = debugfs_create_file(i2s->dai.name, S_IRUGO, - snd_soc_debugfs_root, i2s, - &tegra_i2s_debug_fops); -} - -static void tegra_i2s_debug_remove(struct tegra_i2s *i2s) -{ - if (i2s->debug) - debugfs_remove(i2s->debug); -} -#else -static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s) -{ -} - -static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s) -{ -} -#endif - -static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, - unsigned int fmt) -{ - struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - break; - default: - return -EINVAL; - } - - i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE; - break; - case SND_SOC_DAIFMT_CBM_CFM: - break; - default: - return -EINVAL; - } - - i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | - TEGRA_I2S_CTRL_LRCK_MASK); - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_DSP_A: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; - break; - case SND_SOC_DAIFMT_DSP_B: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW; - break; - case SND_SOC_DAIFMT_I2S: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; - break; - case SND_SOC_DAIFMT_RIGHT_J: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; - break; - case SND_SOC_DAIFMT_LEFT_J: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct device *dev = substream->pcm->card->dev; - struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); - u32 reg; - int ret, sample_size, srate, i2sclock, bitcnt; - - i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16; - sample_size = 16; - break; - case SNDRV_PCM_FORMAT_S24_LE: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24; - sample_size = 24; - break; - case SNDRV_PCM_FORMAT_S32_LE: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32; - sample_size = 32; - break; - default: - return -EINVAL; - } - - srate = params_rate(params); - - /* Final "* 2" required by Tegra hardware */ - i2sclock = srate * params_channels(params) * sample_size * 2; - - ret = clk_set_rate(i2s->clk_i2s, i2sclock); - if (ret) { - dev_err(dev, "Can't set I2S clock rate: %d\n", ret); - return ret; - } - - bitcnt = (i2sclock / (2 * srate)) - 1; - if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) - return -EINVAL; - reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; - - if (i2sclock % (2 * srate)) - reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE; - - if (!i2s->clk_refs) - clk_enable(i2s->clk_i2s); - - tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg); - - tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR, - TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | - TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); - - if (!i2s->clk_refs) - clk_disable(i2s->clk_i2s); - - return 0; -} - -static void tegra_i2s_start_playback(struct tegra_i2s *i2s) -{ - i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE; - tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); -} - -static void tegra_i2s_stop_playback(struct tegra_i2s *i2s) -{ - i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE; - tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); -} - -static void tegra_i2s_start_capture(struct tegra_i2s *i2s) -{ - i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE; - tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); -} - -static void tegra_i2s_stop_capture(struct tegra_i2s *i2s) -{ - i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE; - tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); -} - -static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - if (!i2s->clk_refs) - clk_enable(i2s->clk_i2s); - i2s->clk_refs++; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - tegra_i2s_start_playback(i2s); - else - tegra_i2s_start_capture(i2s); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - tegra_i2s_stop_playback(i2s); - else - tegra_i2s_stop_capture(i2s); - i2s->clk_refs--; - if (!i2s->clk_refs) - clk_disable(i2s->clk_i2s); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int tegra_i2s_probe(struct snd_soc_dai *dai) -{ - struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai); - - dai->capture_dma_data = &i2s->capture_dma_data; - dai->playback_dma_data = &i2s->playback_dma_data; - - return 0; -} - -static const struct snd_soc_dai_ops tegra_i2s_dai_ops = { - .set_fmt = tegra_i2s_set_fmt, - .hw_params = tegra_i2s_hw_params, - .trigger = tegra_i2s_trigger, -}; - -static const struct snd_soc_dai_driver tegra_i2s_dai_template = { - .probe = tegra_i2s_probe, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &tegra_i2s_dai_ops, - .symmetric_rates = 1, -}; - -static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) -{ - struct tegra_i2s * i2s; - struct resource *mem, *memregion, *dmareq; - u32 of_dma[2]; - u32 dma_ch; - int ret; - - i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra_i2s), GFP_KERNEL); - if (!i2s) { - dev_err(&pdev->dev, "Can't allocate tegra_i2s\n"); - ret = -ENOMEM; - goto err; - } - dev_set_drvdata(&pdev->dev, i2s); - - i2s->dai = tegra_i2s_dai_template; - i2s->dai.name = dev_name(&pdev->dev); - - i2s->clk_i2s = clk_get(&pdev->dev, NULL); - if (IS_ERR(i2s->clk_i2s)) { - dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); - ret = PTR_ERR(i2s->clk_i2s); - goto err; - } - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - - dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmareq) { - if (of_property_read_u32_array(pdev->dev.of_node, - "nvidia,dma-request-selector", - of_dma, 2) < 0) { - dev_err(&pdev->dev, "No DMA resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - dma_ch = of_dma[1]; - } else { - dma_ch = dmareq->start; - } - - memregion = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), DRV_NAME); - if (!memregion) { - dev_err(&pdev->dev, "Memory region already claimed\n"); - ret = -EBUSY; - goto err_clk_put; - } - - i2s->regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - if (!i2s->regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err_clk_put; - } - - i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2; - i2s->capture_dma_data.wrap = 4; - i2s->capture_dma_data.width = 32; - i2s->capture_dma_data.req_sel = dma_ch; - - i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1; - i2s->playback_dma_data.wrap = 4; - i2s->playback_dma_data.width = 32; - i2s->playback_dma_data.req_sel = dma_ch; - - i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED; - - ret = snd_soc_register_dai(&pdev->dev, &i2s->dai); - if (ret) { - dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); - ret = -ENOMEM; - goto err_clk_put; - } - - tegra_i2s_debug_add(i2s); - - return 0; - -err_clk_put: - clk_put(i2s->clk_i2s); -err: - return ret; -} - -static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev) -{ - struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev); - - snd_soc_unregister_dai(&pdev->dev); - - tegra_i2s_debug_remove(i2s); - - clk_put(i2s->clk_i2s); - - return 0; -} - -static const struct of_device_id tegra_i2s_of_match[] __devinitconst = { - { .compatible = "nvidia,tegra20-i2s", }, - {}, -}; - -static struct platform_driver tegra_i2s_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .of_match_table = tegra_i2s_of_match, - }, - .probe = tegra_i2s_platform_probe, - .remove = __devexit_p(tegra_i2s_platform_remove), -}; -module_platform_driver(tegra_i2s_driver); - -MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); -MODULE_DESCRIPTION("Tegra I2S ASoC driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); -MODULE_DEVICE_TABLE(of, tegra_i2s_of_match); diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h deleted file mode 100644 index 15ce1e2e8bde..000000000000 --- a/sound/soc/tegra/tegra_i2s.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * tegra_i2s.h - Definitions for Tegra I2S driver - * - * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, Inc. - * - * Based on code copyright/by: - * - * Copyright (c) 2009-2010, NVIDIA Corporation. - * Scott Peterson <speterson@nvidia.com> - * - * Copyright (C) 2010 Google, Inc. - * Iliyan Malchev <malchev@google.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __TEGRA_I2S_H__ -#define __TEGRA_I2S_H__ - -#include "tegra_pcm.h" - -/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */ - -#define TEGRA_I2S_CTRL 0x00 -#define TEGRA_I2S_STATUS 0x04 -#define TEGRA_I2S_TIMING 0x08 -#define TEGRA_I2S_FIFO_SCR 0x0c -#define TEGRA_I2S_PCM_CTRL 0x10 -#define TEGRA_I2S_NW_CTRL 0x14 -#define TEGRA_I2S_TDM_CTRL 0x20 -#define TEGRA_I2S_TDM_TX_RX_CTRL 0x24 -#define TEGRA_I2S_FIFO1 0x40 -#define TEGRA_I2S_FIFO2 0x80 - -/* Fields in TEGRA_I2S_CTRL */ - -#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30) -#define TEGRA_I2S_CTRL_FIFO1_ENABLE (1 << 29) -#define TEGRA_I2S_CTRL_FIFO2_ENABLE (1 << 28) -#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27) -#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26) -#define TEGRA_I2S_CTRL_MASTER_ENABLE (1 << 25) - -#define TEGRA_I2S_LRCK_LEFT_LOW 0 -#define TEGRA_I2S_LRCK_RIGHT_LOW 1 - -#define TEGRA_I2S_CTRL_LRCK_SHIFT 24 -#define TEGRA_I2S_CTRL_LRCK_MASK (1 << TEGRA_I2S_CTRL_LRCK_SHIFT) -#define TEGRA_I2S_CTRL_LRCK_L_LOW (TEGRA_I2S_LRCK_LEFT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT) -#define TEGRA_I2S_CTRL_LRCK_R_LOW (TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT) - -#define TEGRA_I2S_BIT_FORMAT_I2S 0 -#define TEGRA_I2S_BIT_FORMAT_RJM 1 -#define TEGRA_I2S_BIT_FORMAT_LJM 2 -#define TEGRA_I2S_BIT_FORMAT_DSP 3 - -#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT 10 -#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S (TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM (TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM (TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP (TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) - -#define TEGRA_I2S_BIT_SIZE_16 0 -#define TEGRA_I2S_BIT_SIZE_20 1 -#define TEGRA_I2S_BIT_SIZE_24 2 -#define TEGRA_I2S_BIT_SIZE_32 3 - -#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT 8 -#define TEGRA_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) -#define TEGRA_I2S_CTRL_BIT_SIZE_16 (TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) -#define TEGRA_I2S_CTRL_BIT_SIZE_20 (TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) -#define TEGRA_I2S_CTRL_BIT_SIZE_24 (TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) -#define TEGRA_I2S_CTRL_BIT_SIZE_32 (TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) - -#define TEGRA_I2S_FIFO_16_LSB 0 -#define TEGRA_I2S_FIFO_20_LSB 1 -#define TEGRA_I2S_FIFO_24_LSB 2 -#define TEGRA_I2S_FIFO_32 3 -#define TEGRA_I2S_FIFO_PACKED 7 - -#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT 4 -#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_32 (TEGRA_I2S_FIFO_32 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) - -#define TEGRA_I2S_CTRL_IE_FIFO1_ERR (1 << 3) -#define TEGRA_I2S_CTRL_IE_FIFO2_ERR (1 << 2) -#define TEGRA_I2S_CTRL_QE_FIFO1 (1 << 1) -#define TEGRA_I2S_CTRL_QE_FIFO2 (1 << 0) - -/* Fields in TEGRA_I2S_STATUS */ - -#define TEGRA_I2S_STATUS_FIFO1_RDY (1 << 31) -#define TEGRA_I2S_STATUS_FIFO2_RDY (1 << 30) -#define TEGRA_I2S_STATUS_FIFO1_BSY (1 << 29) -#define TEGRA_I2S_STATUS_FIFO2_BSY (1 << 28) -#define TEGRA_I2S_STATUS_FIFO1_ERR (1 << 3) -#define TEGRA_I2S_STATUS_FIFO2_ERR (1 << 2) -#define TEGRA_I2S_STATUS_QS_FIFO1 (1 << 1) -#define TEGRA_I2S_STATUS_QS_FIFO2 (1 << 0) - -/* Fields in TEGRA_I2S_TIMING */ - -#define TEGRA_I2S_TIMING_NON_SYM_ENABLE (1 << 12) -#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0 -#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff -#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT) - -/* Fields in TEGRA_I2S_FIFO_SCR */ - -#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24 -#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16 -#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f - -#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR (1 << 12) -#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR (1 << 8) - -#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT 0 -#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1 -#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2 -#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3 - -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4 -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) - -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0 -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) - -struct tegra_i2s { - struct snd_soc_dai_driver dai; - struct clk *clk_i2s; - int clk_refs; - struct tegra_pcm_dma_params capture_dma_data; - struct tegra_pcm_dma_params playback_dma_data; - void __iomem *regs; - struct dentry *debug; - u32 reg_ctrl; -}; - -#endif diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 8b4457137c7c..127348dc09b1 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -2,7 +2,7 @@ * tegra_pcm.c - Tegra PCM driver * * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010,2012 - NVIDIA, Inc. * * Based on code copyright/by: * @@ -29,8 +29,8 @@ * */ -#include <linux/module.h> #include <linux/dma-mapping.h> +#include <linux/module.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -39,8 +39,6 @@ #include "tegra_pcm.h" -#define DRV_NAME "tegra-pcm-audio" - static const struct snd_pcm_hardware tegra_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -372,28 +370,18 @@ static struct snd_soc_platform_driver tegra_pcm_platform = { .pcm_free = tegra_pcm_free, }; -static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev) +int __devinit tegra_pcm_platform_register(struct device *dev) { - return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform); + return snd_soc_register_platform(dev, &tegra_pcm_platform); } +EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); -static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev) +void __devexit tegra_pcm_platform_unregister(struct device *dev) { - snd_soc_unregister_platform(&pdev->dev); - return 0; + snd_soc_unregister_platform(dev); } - -static struct platform_driver tegra_pcm_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, - .probe = tegra_pcm_platform_probe, - .remove = __devexit_p(tegra_pcm_platform_remove), -}; -module_platform_driver(tegra_pcm_driver); +EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Tegra PCM ASoC driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index dbb90339fe0d..985d418a35e7 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -2,7 +2,7 @@ * tegra_pcm.h - Definitions for Tegra PCM driver * * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010,2012 - NVIDIA, Inc. * * Based on code copyright/by: * @@ -52,4 +52,7 @@ struct tegra_runtime_data { struct tegra_dma_channel *dma_chan; }; +int tegra_pcm_platform_register(struct device *dev); +void tegra_pcm_platform_unregister(struct device *dev); + #endif diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c deleted file mode 100644 index 9ff2c601445f..000000000000 --- a/sound/soc/tegra/tegra_spdif.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * tegra_spdif.c - Tegra SPDIF driver - * - * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2011 - NVIDIA, 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. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/clk.h> -#include <linux/module.h> -#include <linux/debugfs.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <mach/iomap.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> - -#include "tegra_spdif.h" - -#define DRV_NAME "tegra-spdif" - -static inline void tegra_spdif_write(struct tegra_spdif *spdif, u32 reg, - u32 val) -{ - __raw_writel(val, spdif->regs + reg); -} - -static inline u32 tegra_spdif_read(struct tegra_spdif *spdif, u32 reg) -{ - return __raw_readl(spdif->regs + reg); -} - -#ifdef CONFIG_DEBUG_FS -static int tegra_spdif_show(struct seq_file *s, void *unused) -{ -#define REG(r) { r, #r } - static const struct { - int offset; - const char *name; - } regs[] = { - REG(TEGRA_SPDIF_CTRL), - REG(TEGRA_SPDIF_STATUS), - REG(TEGRA_SPDIF_STROBE_CTRL), - REG(TEGRA_SPDIF_DATA_FIFO_CSR), - REG(TEGRA_SPDIF_CH_STA_RX_A), - REG(TEGRA_SPDIF_CH_STA_RX_B), - REG(TEGRA_SPDIF_CH_STA_RX_C), - REG(TEGRA_SPDIF_CH_STA_RX_D), - REG(TEGRA_SPDIF_CH_STA_RX_E), - REG(TEGRA_SPDIF_CH_STA_RX_F), - REG(TEGRA_SPDIF_CH_STA_TX_A), - REG(TEGRA_SPDIF_CH_STA_TX_B), - REG(TEGRA_SPDIF_CH_STA_TX_C), - REG(TEGRA_SPDIF_CH_STA_TX_D), - REG(TEGRA_SPDIF_CH_STA_TX_E), - REG(TEGRA_SPDIF_CH_STA_TX_F), - }; -#undef REG - - struct tegra_spdif *spdif = s->private; - int i; - - clk_enable(spdif->clk_spdif_out); - - for (i = 0; i < ARRAY_SIZE(regs); i++) { - u32 val = tegra_spdif_read(spdif, regs[i].offset); - seq_printf(s, "%s = %08x\n", regs[i].name, val); - } - - clk_disable(spdif->clk_spdif_out); - - return 0; -} - -static int tegra_spdif_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, tegra_spdif_show, inode->i_private); -} - -static const struct file_operations tegra_spdif_debug_fops = { - .open = tegra_spdif_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void tegra_spdif_debug_add(struct tegra_spdif *spdif) -{ - spdif->debug = debugfs_create_file(DRV_NAME, S_IRUGO, - snd_soc_debugfs_root, spdif, - &tegra_spdif_debug_fops); -} - -static void tegra_spdif_debug_remove(struct tegra_spdif *spdif) -{ - if (spdif->debug) - debugfs_remove(spdif->debug); -} -#else -static inline void tegra_spdif_debug_add(struct tegra_spdif *spdif) -{ -} - -static inline void tegra_spdif_debug_remove(struct tegra_spdif *spdif) -{ -} -#endif - -static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct device *dev = substream->pcm->card->dev; - struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); - int ret, spdifclock; - - spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_PACK; - spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_BIT_MODE_MASK; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_PACK; - spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_BIT_MODE_16BIT; - break; - default: - return -EINVAL; - } - - switch (params_rate(params)) { - case 32000: - spdifclock = 4096000; - break; - case 44100: - spdifclock = 5644800; - break; - case 48000: - spdifclock = 6144000; - break; - case 88200: - spdifclock = 11289600; - break; - case 96000: - spdifclock = 12288000; - break; - case 176400: - spdifclock = 22579200; - break; - case 192000: - spdifclock = 24576000; - break; - default: - return -EINVAL; - } - - ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); - if (ret) { - dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); - return ret; - } - - return 0; -} - -static void tegra_spdif_start_playback(struct tegra_spdif *spdif) -{ - spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_TX_EN; - tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl); -} - -static void tegra_spdif_stop_playback(struct tegra_spdif *spdif) -{ - spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_TX_EN; - tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl); -} - -static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - if (!spdif->clk_refs) - clk_enable(spdif->clk_spdif_out); - spdif->clk_refs++; - tegra_spdif_start_playback(spdif); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - tegra_spdif_stop_playback(spdif); - spdif->clk_refs--; - if (!spdif->clk_refs) - clk_disable(spdif->clk_spdif_out); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int tegra_spdif_probe(struct snd_soc_dai *dai) -{ - struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); - - dai->capture_dma_data = NULL; - dai->playback_dma_data = &spdif->playback_dma_data; - - return 0; -} - -static const struct snd_soc_dai_ops tegra_spdif_dai_ops = { - .hw_params = tegra_spdif_hw_params, - .trigger = tegra_spdif_trigger, -}; - -static struct snd_soc_dai_driver tegra_spdif_dai = { - .name = DRV_NAME, - .probe = tegra_spdif_probe, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &tegra_spdif_dai_ops, -}; - -static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev) -{ - struct tegra_spdif *spdif; - struct resource *mem, *memregion, *dmareq; - int ret; - - spdif = kzalloc(sizeof(struct tegra_spdif), GFP_KERNEL); - if (!spdif) { - dev_err(&pdev->dev, "Can't allocate tegra_spdif\n"); - ret = -ENOMEM; - goto exit; - } - dev_set_drvdata(&pdev->dev, spdif); - - spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out"); - if (IS_ERR(spdif->clk_spdif_out)) { - pr_err("Can't retrieve spdif clock\n"); - ret = PTR_ERR(spdif->clk_spdif_out); - goto err_free; - } - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - - dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmareq) { - dev_err(&pdev->dev, "No DMA resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - - memregion = request_mem_region(mem->start, resource_size(mem), - DRV_NAME); - if (!memregion) { - dev_err(&pdev->dev, "Memory region already claimed\n"); - ret = -EBUSY; - goto err_clk_put; - } - - spdif->regs = ioremap(mem->start, resource_size(mem)); - if (!spdif->regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err_release; - } - - spdif->playback_dma_data.addr = mem->start + TEGRA_SPDIF_DATA_OUT; - spdif->playback_dma_data.wrap = 4; - spdif->playback_dma_data.width = 32; - spdif->playback_dma_data.req_sel = dmareq->start; - - ret = snd_soc_register_dai(&pdev->dev, &tegra_spdif_dai); - if (ret) { - dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); - ret = -ENOMEM; - goto err_unmap; - } - - tegra_spdif_debug_add(spdif); - - return 0; - -err_unmap: - iounmap(spdif->regs); -err_release: - release_mem_region(mem->start, resource_size(mem)); -err_clk_put: - clk_put(spdif->clk_spdif_out); -err_free: - kfree(spdif); -exit: - return ret; -} - -static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev) -{ - struct tegra_spdif *spdif = dev_get_drvdata(&pdev->dev); - struct resource *res; - - snd_soc_unregister_dai(&pdev->dev); - - tegra_spdif_debug_remove(spdif); - - iounmap(spdif->regs); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - clk_put(spdif->clk_spdif_out); - - kfree(spdif); - - return 0; -} - -static struct platform_driver tegra_spdif_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, - .probe = tegra_spdif_platform_probe, - .remove = __devexit_p(tegra_spdif_platform_remove), -}; - -module_platform_driver(tegra_spdif_driver); - -MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); -MODULE_DESCRIPTION("Tegra SPDIF ASoC driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_spdif.h b/sound/soc/tegra/tegra_spdif.h deleted file mode 100644 index 2e03db430279..000000000000 --- a/sound/soc/tegra/tegra_spdif.h +++ /dev/null @@ -1,473 +0,0 @@ -/* - * tegra_spdif.h - Definitions for Tegra SPDIF driver - * - * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2011 - NVIDIA, Inc. - * - * Based on code copyright/by: - * Copyright (c) 2008-2009, NVIDIA Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __TEGRA_SPDIF_H__ -#define __TEGRA_SPDIF_H__ - -#include "tegra_pcm.h" - -/* Offsets from TEGRA_SPDIF_BASE */ - -#define TEGRA_SPDIF_CTRL 0x0 -#define TEGRA_SPDIF_STATUS 0x4 -#define TEGRA_SPDIF_STROBE_CTRL 0x8 -#define TEGRA_SPDIF_DATA_FIFO_CSR 0x0C -#define TEGRA_SPDIF_DATA_OUT 0x40 -#define TEGRA_SPDIF_DATA_IN 0x80 -#define TEGRA_SPDIF_CH_STA_RX_A 0x100 -#define TEGRA_SPDIF_CH_STA_RX_B 0x104 -#define TEGRA_SPDIF_CH_STA_RX_C 0x108 -#define TEGRA_SPDIF_CH_STA_RX_D 0x10C -#define TEGRA_SPDIF_CH_STA_RX_E 0x110 -#define TEGRA_SPDIF_CH_STA_RX_F 0x114 -#define TEGRA_SPDIF_CH_STA_TX_A 0x140 -#define TEGRA_SPDIF_CH_STA_TX_B 0x144 -#define TEGRA_SPDIF_CH_STA_TX_C 0x148 -#define TEGRA_SPDIF_CH_STA_TX_D 0x14C -#define TEGRA_SPDIF_CH_STA_TX_E 0x150 -#define TEGRA_SPDIF_CH_STA_TX_F 0x154 -#define TEGRA_SPDIF_USR_STA_RX_A 0x180 -#define TEGRA_SPDIF_USR_DAT_TX_A 0x1C0 - -/* Fields in TEGRA_SPDIF_CTRL */ - -/* Start capturing from 0=right, 1=left channel */ -#define TEGRA_SPDIF_CTRL_CAP_LC (1 << 30) - -/* SPDIF receiver(RX) enable */ -#define TEGRA_SPDIF_CTRL_RX_EN (1 << 29) - -/* SPDIF Transmitter(TX) enable */ -#define TEGRA_SPDIF_CTRL_TX_EN (1 << 28) - -/* Transmit Channel status */ -#define TEGRA_SPDIF_CTRL_TC_EN (1 << 27) - -/* Transmit user Data */ -#define TEGRA_SPDIF_CTRL_TU_EN (1 << 26) - -/* Interrupt on transmit error */ -#define TEGRA_SPDIF_CTRL_IE_TXE (1 << 25) - -/* Interrupt on receive error */ -#define TEGRA_SPDIF_CTRL_IE_RXE (1 << 24) - -/* Interrupt on invalid preamble */ -#define TEGRA_SPDIF_CTRL_IE_P (1 << 23) - -/* Interrupt on "B" preamble */ -#define TEGRA_SPDIF_CTRL_IE_B (1 << 22) - -/* Interrupt when block of channel status received */ -#define TEGRA_SPDIF_CTRL_IE_C (1 << 21) - -/* Interrupt when a valid information unit (IU) is received */ -#define TEGRA_SPDIF_CTRL_IE_U (1 << 20) - -/* Interrupt when RX user FIFO attention level is reached */ -#define TEGRA_SPDIF_CTRL_QE_RU (1 << 19) - -/* Interrupt when TX user FIFO attention level is reached */ -#define TEGRA_SPDIF_CTRL_QE_TU (1 << 18) - -/* Interrupt when RX data FIFO attention level is reached */ -#define TEGRA_SPDIF_CTRL_QE_RX (1 << 17) - -/* Interrupt when TX data FIFO attention level is reached */ -#define TEGRA_SPDIF_CTRL_QE_TX (1 << 16) - -/* Loopback test mode enable */ -#define TEGRA_SPDIF_CTRL_LBK_EN (1 << 15) - -/* - * Pack data mode: - * 0 = Single data (16 bit needs to be padded to match the - * interface data bit size). - * 1 = Packeted left/right channel data into a single word. - */ -#define TEGRA_SPDIF_CTRL_PACK (1 << 14) - -/* - * 00 = 16bit data - * 01 = 20bit data - * 10 = 24bit data - * 11 = raw data - */ -#define TEGRA_SPDIF_BIT_MODE_16BIT 0 -#define TEGRA_SPDIF_BIT_MODE_20BIT 1 -#define TEGRA_SPDIF_BIT_MODE_24BIT 2 -#define TEGRA_SPDIF_BIT_MODE_RAW 3 - -#define TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT 12 -#define TEGRA_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) -#define TEGRA_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA_SPDIF_BIT_MODE_16BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) -#define TEGRA_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA_SPDIF_BIT_MODE_20BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) -#define TEGRA_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA_SPDIF_BIT_MODE_24BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) -#define TEGRA_SPDIF_CTRL_BIT_MODE_RAW (TEGRA_SPDIF_BIT_MODE_RAW << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) - -/* Fields in TEGRA_SPDIF_STATUS */ - -/* - * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must - * write a 1 to the corresponding bit location to clear the status. - */ - -/* - * Receiver(RX) shifter is busy receiving data. - * This bit is asserted when the receiver first locked onto the - * preamble of the data stream after RX_EN is asserted. This bit is - * deasserted when either, - * (a) the end of a frame is reached after RX_EN is deeasserted, or - * (b) the SPDIF data stream becomes inactive. - */ -#define TEGRA_SPDIF_STATUS_RX_BSY (1 << 29) - -/* - * Transmitter(TX) shifter is busy transmitting data. - * This bit is asserted when TX_EN is asserted. - * This bit is deasserted when the end of a frame is reached after - * TX_EN is deasserted. - */ -#define TEGRA_SPDIF_STATUS_TX_BSY (1 << 28) - -/* - * TX is busy shifting out channel status. - * This bit is asserted when both TX_EN and TC_EN are asserted and - * data from CH_STA_TX_A register is loaded into the internal shifter. - * This bit is deasserted when either, - * (a) the end of a frame is reached after TX_EN is deasserted, or - * (b) CH_STA_TX_F register is loaded into the internal shifter. - */ -#define TEGRA_SPDIF_STATUS_TC_BSY (1 << 27) - -/* - * TX User data FIFO busy. - * This bit is asserted when TX_EN and TXU_EN are asserted and - * there's data in the TX user FIFO. This bit is deassert when either, - * (a) the end of a frame is reached after TX_EN is deasserted, or - * (b) there's no data left in the TX user FIFO. - */ -#define TEGRA_SPDIF_STATUS_TU_BSY (1 << 26) - -/* TX FIFO Underrun error status */ -#define TEGRA_SPDIF_STATUS_TX_ERR (1 << 25) - -/* RX FIFO Overrun error status */ -#define TEGRA_SPDIF_STATUS_RX_ERR (1 << 24) - -/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */ -#define TEGRA_SPDIF_STATUS_IS_P (1 << 23) - -/* B-preamble detection status: 0=not detected, 1=B-preamble detected */ -#define TEGRA_SPDIF_STATUS_IS_B (1 << 22) - -/* - * RX channel block data receive status: - * 0=entire block not recieved yet. - * 1=received entire block of channel status, - */ -#define TEGRA_SPDIF_STATUS_IS_C (1 << 21) - -/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */ -#define TEGRA_SPDIF_STATUS_IS_U (1 << 20) - -/* - * RX User FIFO Status: - * 1=attention level reached, 0=attention level not reached. - */ -#define TEGRA_SPDIF_STATUS_QS_RU (1 << 19) - -/* - * TX User FIFO Status: - * 1=attention level reached, 0=attention level not reached. - */ -#define TEGRA_SPDIF_STATUS_QS_TU (1 << 18) - -/* - * RX Data FIFO Status: - * 1=attention level reached, 0=attention level not reached. - */ -#define TEGRA_SPDIF_STATUS_QS_RX (1 << 17) - -/* - * TX Data FIFO Status: - * 1=attention level reached, 0=attention level not reached. - */ -#define TEGRA_SPDIF_STATUS_QS_TX (1 << 16) - -/* Fields in TEGRA_SPDIF_STROBE_CTRL */ - -/* - * Indicates the approximate number of detected SPDIFIN clocks within a - * bi-phase period. - */ -#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16 -#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT) - -/* Data strobe mode: 0=Auto-locked 1=Manual locked */ -#define TEGRA_SPDIF_STROBE_CTRL_STROBE (1 << 15) - -/* - * Manual data strobe time within the bi-phase clock period (in terms of - * the number of over-sampling clocks). - */ -#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8 -#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT) - -/* - * Manual SPDIFIN bi-phase clock period (in terms of the number of - * over-sampling clocks). - */ -#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0 -#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT) - -/* Fields in SPDIF_DATA_FIFO_CSR */ - -/* Clear Receiver User FIFO (RX USR.FIFO) */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31) - -#define TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0 -#define TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1 -#define TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2 -#define TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3 - -/* RU FIFO attention level */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29 -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \ - (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) - -/* Number of RX USR.FIFO levels with valid data. */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24 -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT) - -/* Clear Transmitter User FIFO (TX USR.FIFO) */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23) - -/* TU FIFO attention level */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21 -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \ - (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) - -/* Number of TX USR.FIFO levels that could be filled. */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16 -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT) - -/* Clear Receiver Data FIFO (RX DATA.FIFO) */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15) - -#define TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0 -#define TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1 -#define TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2 -#define TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3 - -/* RU FIFO attention level */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13 -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \ - (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) - -/* Number of RX DATA.FIFO levels with valid data. */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8 -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT) - -/* Clear Transmitter Data FIFO (TX DATA.FIFO) */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7) - -/* TU FIFO attention level */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5 -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \ - (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) - -/* Number of TX DATA.FIFO levels that could be filled. */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0 -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT) - -/* Fields in TEGRA_SPDIF_DATA_OUT */ - -/* - * This register has 5 different formats: - * 16-bit (BIT_MODE=00, PACK=0) - * 20-bit (BIT_MODE=01, PACK=0) - * 24-bit (BIT_MODE=10, PACK=0) - * raw (BIT_MODE=11, PACK=0) - * 16-bit packed (BIT_MODE=00, PACK=1) - */ - -#define TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT) - -#define TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT) - -#define TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT) - -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28) - -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8 -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT) - -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4 -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT) - -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT) - -#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16 -#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT) - -#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT) - -/* Fields in TEGRA_SPDIF_DATA_IN */ - -/* - * This register has 5 different formats: - * 16-bit (BIT_MODE=00, PACK=0) - * 20-bit (BIT_MODE=01, PACK=0) - * 24-bit (BIT_MODE=10, PACK=0) - * raw (BIT_MODE=11, PACK=0) - * 16-bit packed (BIT_MODE=00, PACK=1) - * - * Bits 31:24 are common to all modes except 16-bit packed - */ - -#define TEGRA_SPDIF_DATA_IN_DATA_P (1 << 31) -#define TEGRA_SPDIF_DATA_IN_DATA_C (1 << 30) -#define TEGRA_SPDIF_DATA_IN_DATA_U (1 << 29) -#define TEGRA_SPDIF_DATA_IN_DATA_V (1 << 28) - -#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24 -#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT) - -#define TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT) - -#define TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT) - -#define TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT) - -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8 -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT) - -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4 -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT) - -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT) - -#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16 -#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT) - -#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT) - -/* Fields in TEGRA_SPDIF_CH_STA_RX_A */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_B */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_C */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_D */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_E */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_F */ - -/* - * The 6-word receive channel data page buffer holds a block (192 frames) of - * channel status information. The order of receive is from LSB to MSB - * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A. - */ - -/* Fields in TEGRA_SPDIF_CH_STA_TX_A */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_B */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_C */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_D */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_E */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_F */ - -/* - * The 6-word transmit channel data page buffer holds a block (192 frames) of - * channel status information. The order of transmission is from LSB to MSB - * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A. - */ - -/* Fields in TEGRA_SPDIF_USR_STA_RX_A */ - -/* - * This 4-word deep FIFO receives user FIFO field information. The order of - * receive is from LSB to MSB bit. - */ - -/* Fields in TEGRA_SPDIF_USR_DAT_TX_A */ - -/* - * This 4-word deep FIFO transmits user FIFO field information. The order of - * transmission is from LSB to MSB bit. - */ - -struct tegra_spdif { - struct clk *clk_spdif_out; - int clk_refs; - struct tegra_pcm_dma_params capture_dma_data; - struct tegra_pcm_dma_params playback_dma_data; - void __iomem *regs; - struct dentry *debug; - u32 reg_ctrl; -}; - -#endif diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 566655e23b7d..0b0df49d9d33 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -2,7 +2,7 @@ * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec. * * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010-2011 - NVIDIA, Inc. + * Copyright (C) 2010-2012 - NVIDIA, Inc. * * Based on code copyright/by: * @@ -46,9 +46,6 @@ #include "../codecs/wm8903.h" -#include "tegra_das.h" -#include "tegra_i2s.h" -#include "tegra_pcm.h" #include "tegra_asoc_utils.h" #define DRV_NAME "tegra-snd-wm8903" @@ -61,7 +58,6 @@ struct tegra_wm8903 { struct tegra_wm8903_platform_data pdata; - struct platform_device *pcm_dev; struct tegra_asoc_utils_data util_data; int gpio_requested; }; @@ -354,8 +350,8 @@ static struct snd_soc_dai_link tegra_wm8903_dai = { .name = "WM8903", .stream_name = "WM8903 PCM", .codec_name = "wm8903.0-001a", - .platform_name = "tegra-pcm-audio", - .cpu_dai_name = "tegra-i2s.0", + .platform_name = "tegra20-i2s.0", + .cpu_dai_name = "tegra20-i2s.0", .codec_dai_name = "wm8903-hifi", .init = tegra_wm8903_init, .ops = &tegra_wm8903_ops, @@ -392,7 +388,6 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) ret = -ENOMEM; goto err; } - machine->pcm_dev = ERR_PTR(-EINVAL); card->dev = &pdev->dev; platform_set_drvdata(pdev, card); @@ -428,14 +423,9 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) goto err; } - machine->pcm_dev = platform_device_register_simple( - "tegra-pcm-audio", -1, NULL, 0); - if (IS_ERR(machine->pcm_dev)) { - dev_err(&pdev->dev, - "Can't instantiate tegra-pcm-audio\n"); - ret = PTR_ERR(machine->pcm_dev); - goto err; - } + tegra_wm8903_dai.platform_name = NULL; + tegra_wm8903_dai.platform_of_node = + tegra_wm8903_dai.cpu_dai_of_node; } else { if (machine_is_harmony()) { card->dapm_routes = harmony_audio_map; @@ -454,7 +444,7 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); if (ret) - goto err_unregister; + goto err; ret = snd_soc_register_card(card); if (ret) { @@ -467,9 +457,6 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) err_fini_utils: tegra_asoc_utils_fini(&machine->util_data); -err_unregister: - if (!IS_ERR(machine->pcm_dev)) - platform_device_unregister(machine->pcm_dev); err: return ret; } @@ -497,8 +484,6 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev) snd_soc_unregister_card(card); tegra_asoc_utils_fini(&machine->util_data); - if (!IS_ERR(machine->pcm_dev)) - platform_device_unregister(machine->pcm_dev); return 0; } diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 2bdfc550cff8..4a8d5b672c9f 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -27,6 +27,7 @@ #include <asm/mach-types.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -38,9 +39,6 @@ #include "../codecs/tlv320aic23.h" -#include "tegra_das.h" -#include "tegra_i2s.h" -#include "tegra_pcm.h" #include "tegra_asoc_utils.h" #define DRV_NAME "tegra-snd-trimslice" @@ -119,8 +117,8 @@ static struct snd_soc_dai_link trimslice_tlv320aic23_dai = { .name = "TLV320AIC23", .stream_name = "AIC23", .codec_name = "tlv320aic23-codec.2-001a", - .platform_name = "tegra-pcm-audio", - .cpu_dai_name = "tegra-i2s.0", + .platform_name = "tegra20-i2s.0", + .cpu_dai_name = "tegra20-i2s.0", .codec_dai_name = "tlv320aic23-hifi", .ops = &trimslice_asoc_ops, }; @@ -152,6 +150,32 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev) goto err; } + if (pdev->dev.of_node) { + trimslice_tlv320aic23_dai.codec_name = NULL; + trimslice_tlv320aic23_dai.codec_of_node = of_parse_phandle( + pdev->dev.of_node, "nvidia,audio-codec", 0); + if (!trimslice_tlv320aic23_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + trimslice_tlv320aic23_dai.cpu_dai_name = NULL; + trimslice_tlv320aic23_dai.cpu_dai_of_node = of_parse_phandle( + pdev->dev.of_node, "nvidia,i2s-controller", 0); + if (!trimslice_tlv320aic23_dai.cpu_dai_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + trimslice_tlv320aic23_dai.platform_name = NULL; + trimslice_tlv320aic23_dai.platform_of_node = + trimslice_tlv320aic23_dai.cpu_dai_of_node; + } + ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev); if (ret) goto err; @@ -187,10 +211,17 @@ static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id trimslice_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra-audio-trimslice", }, + {}, +}; +MODULE_DEVICE_TABLE(of, trimslice_of_match); + static struct platform_driver tegra_snd_trimslice_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = trimslice_of_match, }, .probe = tegra_snd_trimslice_probe, .remove = __devexit_p(tegra_snd_trimslice_remove), diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig new file mode 100644 index 000000000000..44cf43404cd9 --- /dev/null +++ b/sound/soc/ux500/Kconfig @@ -0,0 +1,14 @@ +# +# Ux500 SoC audio configuration +# +menuconfig SND_SOC_UX500 + tristate "SoC Audio support for Ux500 platform" + depends on SND_SOC + depends on MFD_DB8500_PRCMU + help + Say Y if you want to enable ASoC-support for + any of the Ux500 platforms (e.g. U8500). + +config SND_SOC_UX500_PLAT_MSP_I2S + tristate + depends on SND_SOC_UX500 diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile new file mode 100644 index 000000000000..19974c5a2ea1 --- /dev/null +++ b/sound/soc/ux500/Makefile @@ -0,0 +1,4 @@ +# Ux500 Platform Support + +snd-soc-ux500-plat-msp-i2s-objs := ux500_msp_dai.o ux500_msp_i2s.o +obj-$(CONFIG_SND_SOC_UX500_PLAT_MSP_I2S) += snd-soc-ux500-plat-msp-i2s.o diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c new file mode 100644 index 000000000000..93c6c40e724c --- /dev/null +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -0,0 +1,843 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja <ola.o.lilja@stericsson.com>, + * Roger Nilsson <roger.xr.nilsson@stericsson.com> + * for ST-Ericsson. + * + * License terms: + * + * 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/slab.h> +#include <linux/bitops.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/regulator/consumer.h> +#include <linux/mfd/dbx500-prcmu.h> + +#include <mach/hardware.h> +#include <mach/board-mop500-msp.h> + +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "ux500_msp_i2s.h" +#include "ux500_msp_dai.h" + +static int setup_pcm_multichan(struct snd_soc_dai *dai, + struct ux500_msp_config *msp_config) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + struct msp_multichannel_config *multi = + &msp_config->multichannel_config; + + if (drvdata->slots > 1) { + msp_config->multichannel_configured = 1; + + multi->tx_multichannel_enable = true; + multi->rx_multichannel_enable = true; + multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED; + + multi->tx_channel_0_enable = drvdata->tx_mask; + multi->tx_channel_1_enable = 0; + multi->tx_channel_2_enable = 0; + multi->tx_channel_3_enable = 0; + + multi->rx_channel_0_enable = drvdata->rx_mask; + multi->rx_channel_1_enable = 0; + multi->rx_channel_2_enable = 0; + multi->rx_channel_3_enable = 0; + + dev_dbg(dai->dev, + "%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n", + __func__, drvdata->slots, multi->tx_channel_0_enable, + multi->rx_channel_0_enable); + } + + return 0; +} + +static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate, + struct msp_protdesc *prot_desc) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + + switch (drvdata->slots) { + case 1: + switch (rate) { + case 8000: + prot_desc->frame_period = + FRAME_PER_SINGLE_SLOT_8_KHZ; + break; + + case 16000: + prot_desc->frame_period = + FRAME_PER_SINGLE_SLOT_16_KHZ; + break; + + case 44100: + prot_desc->frame_period = + FRAME_PER_SINGLE_SLOT_44_1_KHZ; + break; + + case 48000: + prot_desc->frame_period = + FRAME_PER_SINGLE_SLOT_48_KHZ; + break; + + default: + dev_err(dai->dev, + "%s: Error: Unsupported sample-rate (freq = %d)!\n", + __func__, rate); + return -EINVAL; + } + break; + + case 2: + prot_desc->frame_period = FRAME_PER_2_SLOTS; + break; + + case 8: + prot_desc->frame_period = FRAME_PER_8_SLOTS; + break; + + case 16: + prot_desc->frame_period = FRAME_PER_16_SLOTS; + break; + default: + dev_err(dai->dev, + "%s: Error: Unsupported slot-count (slots = %d)!\n", + __func__, drvdata->slots); + return -EINVAL; + } + + prot_desc->clocks_per_frame = + prot_desc->frame_period+1; + + dev_dbg(dai->dev, "%s: Clocks per frame: %u\n", + __func__, + prot_desc->clocks_per_frame); + + return 0; +} + +static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, + struct msp_protdesc *prot_desc) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + + u32 frame_length = MSP_FRAME_LEN_1; + prot_desc->frame_width = 0; + + switch (drvdata->slots) { + case 1: + frame_length = MSP_FRAME_LEN_1; + break; + + case 2: + frame_length = MSP_FRAME_LEN_2; + break; + + case 8: + frame_length = MSP_FRAME_LEN_8; + break; + + case 16: + frame_length = MSP_FRAME_LEN_16; + break; + default: + dev_err(dai->dev, + "%s: Error: Unsupported slot-count (slots = %d)!\n", + __func__, drvdata->slots); + return -EINVAL; + } + + prot_desc->tx_frame_len_1 = frame_length; + prot_desc->rx_frame_len_1 = frame_length; + prot_desc->tx_frame_len_2 = frame_length; + prot_desc->rx_frame_len_2 = frame_length; + + prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; + prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; + prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; + prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; + + return setup_frameper(dai, rate, prot_desc); +} + +static int setup_clocking(struct snd_soc_dai *dai, + unsigned int fmt, + struct ux500_msp_config *msp_config) +{ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + + case SND_SOC_DAIFMT_NB_IF: + msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT; + msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT; + + break; + + default: + dev_err(dai->dev, + "%s: Error: Unsopported inversion (fmt = 0x%x)!\n", + __func__, fmt); + + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + dev_dbg(dai->dev, "%s: Codec is master.\n", __func__); + + msp_config->iodelay = 0x20; + msp_config->rx_fsync_sel = 0; + msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT; + msp_config->tx_clk_sel = 0; + msp_config->rx_clk_sel = 0; + msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT; + + break; + + case SND_SOC_DAIFMT_CBS_CFS: + dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__); + + msp_config->tx_clk_sel = TX_CLK_SEL_SRG; + msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG; + msp_config->rx_clk_sel = RX_CLK_SEL_SRG; + msp_config->rx_fsync_sel = RX_SYNC_SRG; + msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT; + + break; + + default: + dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n", + __func__, fmt); + + return -EINVAL; + } + + return 0; +} + +static int setup_pcm_protdesc(struct snd_soc_dai *dai, + unsigned int fmt, + struct msp_protdesc *prot_desc) +{ + prot_desc->rx_phase_mode = MSP_SINGLE_PHASE; + prot_desc->tx_phase_mode = MSP_SINGLE_PHASE; + prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; + prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; + prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; + prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; + prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI); + prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT; + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) { + dev_dbg(dai->dev, "%s: DSP_A.\n", __func__); + prot_desc->rx_clk_pol = MSP_RISING_EDGE; + prot_desc->tx_clk_pol = MSP_FALLING_EDGE; + + prot_desc->rx_data_delay = MSP_DELAY_1; + prot_desc->tx_data_delay = MSP_DELAY_1; + } else { + dev_dbg(dai->dev, "%s: DSP_B.\n", __func__); + prot_desc->rx_clk_pol = MSP_FALLING_EDGE; + prot_desc->tx_clk_pol = MSP_RISING_EDGE; + + prot_desc->rx_data_delay = MSP_DELAY_0; + prot_desc->tx_data_delay = MSP_DELAY_0; + } + + prot_desc->rx_half_word_swap = MSP_SWAP_NONE; + prot_desc->tx_half_word_swap = MSP_SWAP_NONE; + prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; + prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; + prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; + + return 0; +} + +static int setup_i2s_protdesc(struct msp_protdesc *prot_desc) +{ + prot_desc->rx_phase_mode = MSP_DUAL_PHASE; + prot_desc->tx_phase_mode = MSP_DUAL_PHASE; + prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; + prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; + prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; + prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; + prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO); + prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT; + + prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1; + prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1; + prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1; + prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1; + prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; + prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; + prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; + prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; + + prot_desc->rx_clk_pol = MSP_RISING_EDGE; + prot_desc->tx_clk_pol = MSP_FALLING_EDGE; + + prot_desc->rx_data_delay = MSP_DELAY_0; + prot_desc->tx_data_delay = MSP_DELAY_0; + + prot_desc->tx_half_word_swap = MSP_SWAP_NONE; + prot_desc->rx_half_word_swap = MSP_SWAP_NONE; + prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; + prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; + prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; + + return 0; +} + +static int setup_msp_config(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + struct ux500_msp_config *msp_config) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + struct msp_protdesc *prot_desc = &msp_config->protdesc; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int fmt = drvdata->fmt; + int ret; + + memset(msp_config, 0, sizeof(*msp_config)); + + msp_config->f_inputclk = drvdata->master_clk; + + msp_config->tx_fifo_config = TX_FIFO_ENABLE; + msp_config->rx_fifo_config = RX_FIFO_ENABLE; + msp_config->def_elem_len = 1; + msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + MSP_DIR_TX : MSP_DIR_RX; + msp_config->data_size = MSP_DATA_BITS_32; + msp_config->frame_freq = runtime->rate; + + dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n", + __func__, msp_config->f_inputclk, msp_config->frame_freq); + /* To avoid division by zero */ + prot_desc->clocks_per_frame = 1; + + dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n", __func__, + runtime->rate, runtime->channels); + switch (fmt & + (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: + dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__); + + msp_config->default_protdesc = 1; + msp_config->protocol = MSP_I2S_PROTOCOL; + break; + + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: + dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__); + + msp_config->data_size = MSP_DATA_BITS_16; + msp_config->protocol = MSP_I2S_PROTOCOL; + + ret = setup_i2s_protdesc(prot_desc); + if (ret < 0) + return ret; + + break; + + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM: + dev_dbg(dai->dev, "%s: PCM format.\n", __func__); + + msp_config->data_size = MSP_DATA_BITS_16; + msp_config->protocol = MSP_PCM_PROTOCOL; + + ret = setup_pcm_protdesc(dai, fmt, prot_desc); + if (ret < 0) + return ret; + + ret = setup_pcm_multichan(dai, msp_config); + if (ret < 0) + return ret; + + ret = setup_pcm_framing(dai, runtime->rate, prot_desc); + if (ret < 0) + return ret; + + break; + + default: + dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n", + __func__, fmt); + return -EINVAL; + } + + return setup_clocking(dai, fmt, msp_config); +} + +static int ux500_msp_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, + snd_pcm_stream_str(substream)); + + /* Enable regulator */ + ret = regulator_enable(drvdata->reg_vape); + if (ret != 0) { + dev_err(drvdata->msp->dev, + "%s: Failed to enable regulator!\n", __func__); + return ret; + } + + /* Enable clock */ + dev_dbg(dai->dev, "%s: Enabling MSP-clock.\n", __func__); + clk_enable(drvdata->clk); + + return 0; +} + +static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + + dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, + snd_pcm_stream_str(substream)); + + if (drvdata->vape_opp_constraint == 1) { + prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, + "ux500_msp_i2s", 50); + drvdata->vape_opp_constraint = 0; + } + + if (ux500_msp_i2s_close(drvdata->msp, + is_playback ? MSP_DIR_TX : MSP_DIR_RX)) { + dev_err(dai->dev, + "%s: Error: MSP %d (%s): Unable to close i2s.\n", + __func__, dai->id, snd_pcm_stream_str(substream)); + } + + /* Disable clock */ + clk_disable(drvdata->clk); + + /* Disable regulator */ + ret = regulator_disable(drvdata->reg_vape); + if (ret < 0) + dev_err(dai->dev, + "%s: ERROR: Failed to disable regulator (%d)!\n", + __func__, ret); +} + +static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ux500_msp_config msp_config; + + dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__, + dai->id, snd_pcm_stream_str(substream), runtime->rate); + + setup_msp_config(substream, dai, &msp_config); + + ret = ux500_msp_i2s_open(drvdata->msp, &msp_config); + if (ret < 0) { + dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n", + __func__, ret); + return ret; + } + + /* Set OPP-level */ + if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) && + (drvdata->msp->f_bitclk > 19200000)) { + /* If the bit-clock is higher than 19.2MHz, Vape should be + * run in 100% OPP. Only when bit-clock is used (MSP master) */ + prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, + "ux500-msp-i2s", 100); + drvdata->vape_opp_constraint = 1; + } else { + prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, + "ux500-msp-i2s", 50); + drvdata->vape_opp_constraint = 0; + } + + return ret; +} + +static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + unsigned int mask, slots_active; + struct snd_pcm_runtime *runtime = substream->runtime; + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", + __func__, dai->id, snd_pcm_stream_str(substream)); + + switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 1, 2); + break; + + case SND_SOC_DAIFMT_DSP_B: + case SND_SOC_DAIFMT_DSP_A: + mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + drvdata->tx_mask : + drvdata->rx_mask; + + slots_active = hweight32(mask); + dev_dbg(dai->dev, "TDM-slots active: %d", slots_active); + + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + slots_active, slots_active); + break; + + default: + dev_err(dai->dev, + "%s: Error: Unsupported protocol (fmt = 0x%x)!\n", + __func__, drvdata->fmt); + return -EINVAL; + } + + return 0; +} + +static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "%s: MSP %d: Enter.\n", __func__, dai->id); + + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | + SND_SOC_DAIFMT_MASTER_MASK)) { + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: + break; + + default: + dev_err(dai->dev, + "%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n", + __func__, drvdata->fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + break; + + default: + dev_err(dai->dev, + "%s: Error: Unsupported inversion (fmt = 0x%x)!\n", + __func__, drvdata->fmt); + return -EINVAL; + } + + drvdata->fmt = fmt; + return 0; +} + +static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + unsigned int cap; + + switch (slots) { + case 1: + cap = 0x01; + break; + case 2: + cap = 0x03; + break; + case 8: + cap = 0xFF; + break; + case 16: + cap = 0xFFFF; + break; + default: + dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n", + __func__, slots); + return -EINVAL; + } + drvdata->slots = slots; + + if (!(slot_width == 16)) { + dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n", + __func__, slot_width); + return -EINVAL; + } + drvdata->slot_width = slot_width; + + drvdata->tx_mask = tx_mask & cap; + drvdata->rx_mask = rx_mask & cap; + + return 0; +} + +static int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n", + __func__, dai->id, clk_id, freq); + + switch (clk_id) { + case UX500_MSP_MASTER_CLOCK: + drvdata->master_clk = freq; + break; + + default: + dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n", + __func__, dai->id, clk_id); + return -EINVAL; + } + + return 0; +} + +static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + int ret = 0; + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n", + __func__, dai->id, snd_pcm_stream_str(substream), + (int)drvdata->msp->id, cmd); + + ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream); + + return ret; +} + +static int ux500_msp_dai_probe(struct snd_soc_dai *dai) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + + drvdata->playback_dma_data.dma_cfg = drvdata->msp->dma_cfg_tx; + drvdata->capture_dma_data.dma_cfg = drvdata->msp->dma_cfg_rx; + + dai->playback_dma_data = &drvdata->playback_dma_data; + dai->capture_dma_data = &drvdata->capture_dma_data; + + drvdata->playback_dma_data.data_size = drvdata->slot_width; + drvdata->capture_dma_data.data_size = drvdata->slot_width; + + return 0; +} + +static struct snd_soc_dai_ops ux500_msp_dai_ops[] = { + { + .set_sysclk = ux500_msp_dai_set_dai_sysclk, + .set_fmt = ux500_msp_dai_set_dai_fmt, + .set_tdm_slot = ux500_msp_dai_set_tdm_slot, + .startup = ux500_msp_dai_startup, + .shutdown = ux500_msp_dai_shutdown, + .prepare = ux500_msp_dai_prepare, + .trigger = ux500_msp_dai_trigger, + .hw_params = ux500_msp_dai_hw_params, + } +}; + +static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { + { + .name = "ux500-msp-i2s.0", + .probe = ux500_msp_dai_probe, + .id = 0, + .suspend = NULL, + .resume = NULL, + .playback = { + .channels_min = UX500_MSP_MIN_CHANNELS, + .channels_max = UX500_MSP_MAX_CHANNELS, + .rates = UX500_I2S_RATES, + .formats = UX500_I2S_FORMATS, + }, + .capture = { + .channels_min = UX500_MSP_MIN_CHANNELS, + .channels_max = UX500_MSP_MAX_CHANNELS, + .rates = UX500_I2S_RATES, + .formats = UX500_I2S_FORMATS, + }, + .ops = ux500_msp_dai_ops, + }, + { + .name = "ux500-msp-i2s.1", + .probe = ux500_msp_dai_probe, + .id = 1, + .suspend = NULL, + .resume = NULL, + .playback = { + .channels_min = UX500_MSP_MIN_CHANNELS, + .channels_max = UX500_MSP_MAX_CHANNELS, + .rates = UX500_I2S_RATES, + .formats = UX500_I2S_FORMATS, + }, + .capture = { + .channels_min = UX500_MSP_MIN_CHANNELS, + .channels_max = UX500_MSP_MAX_CHANNELS, + .rates = UX500_I2S_RATES, + .formats = UX500_I2S_FORMATS, + }, + .ops = ux500_msp_dai_ops, + }, + { + .name = "ux500-msp-i2s.2", + .id = 2, + .probe = ux500_msp_dai_probe, + .suspend = NULL, + .resume = NULL, + .playback = { + .channels_min = UX500_MSP_MIN_CHANNELS, + .channels_max = UX500_MSP_MAX_CHANNELS, + .rates = UX500_I2S_RATES, + .formats = UX500_I2S_FORMATS, + }, + .capture = { + .channels_min = UX500_MSP_MIN_CHANNELS, + .channels_max = UX500_MSP_MAX_CHANNELS, + .rates = UX500_I2S_RATES, + .formats = UX500_I2S_FORMATS, + }, + .ops = ux500_msp_dai_ops, + }, + { + .name = "ux500-msp-i2s.3", + .probe = ux500_msp_dai_probe, + .id = 3, + .suspend = NULL, + .resume = NULL, + .playback = { + .channels_min = UX500_MSP_MIN_CHANNELS, + .channels_max = UX500_MSP_MAX_CHANNELS, + .rates = UX500_I2S_RATES, + .formats = UX500_I2S_FORMATS, + }, + .capture = { + .channels_min = UX500_MSP_MIN_CHANNELS, + .channels_max = UX500_MSP_MAX_CHANNELS, + .rates = UX500_I2S_RATES, + .formats = UX500_I2S_FORMATS, + }, + .ops = ux500_msp_dai_ops, + }, +}; + +static int __devinit ux500_msp_drv_probe(struct platform_device *pdev) +{ + struct ux500_msp_i2s_drvdata *drvdata; + int ret = 0; + + dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__, + pdev->name); + + drvdata = devm_kzalloc(&pdev->dev, + sizeof(struct ux500_msp_i2s_drvdata), + GFP_KERNEL); + drvdata->fmt = 0; + drvdata->slots = 1; + drvdata->tx_mask = 0x01; + drvdata->rx_mask = 0x01; + drvdata->slot_width = 16; + drvdata->master_clk = MSP_INPUT_FREQ_APB; + + drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape"); + if (IS_ERR(drvdata->reg_vape)) { + ret = (int)PTR_ERR(drvdata->reg_vape); + dev_err(&pdev->dev, + "%s: ERROR: Failed to get Vape supply (%d)!\n", + __func__, ret); + return ret; + } + prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50); + + drvdata->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(drvdata->clk)) { + ret = (int)PTR_ERR(drvdata->clk); + dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n", + __func__, ret); + goto err_clk; + } + + ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp, + pdev->dev.platform_data); + if (!drvdata->msp) { + dev_err(&pdev->dev, + "%s: ERROR: Failed to init MSP-struct (%d)!", + __func__, ret); + goto err_init_msp; + } + dev_set_drvdata(&pdev->dev, drvdata); + + ret = snd_soc_register_dai(&pdev->dev, + &ux500_msp_dai_drv[drvdata->msp->id]); + if (ret < 0) { + dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n", + __func__, drvdata->msp->id); + goto err_init_msp; + } + + return 0; + +err_init_msp: + clk_put(drvdata->clk); + +err_clk: + devm_regulator_put(drvdata->reg_vape); + + return ret; +} + +static int __devexit ux500_msp_drv_remove(struct platform_device *pdev) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv)); + + devm_regulator_put(drvdata->reg_vape); + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s"); + + clk_put(drvdata->clk); + + ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp); + + return 0; +} + +static struct platform_driver msp_i2s_driver = { + .driver = { + .name = "ux500-msp-i2s", + .owner = THIS_MODULE, + }, + .probe = ux500_msp_drv_probe, + .remove = ux500_msp_drv_remove, +}; +module_platform_driver(msp_i2s_driver); + +MODULE_LICENSE("GPLv2"); diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h new file mode 100644 index 000000000000..98202a34a5dd --- /dev/null +++ b/sound/soc/ux500/ux500_msp_dai.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja <ola.o.lilja@stericsson.com>, + * Roger Nilsson <roger.xr.nilsson@stericsson.com> + * for ST-Ericsson. + * + * License terms: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef UX500_msp_dai_H +#define UX500_msp_dai_H + +#include <linux/types.h> +#include <linux/spinlock.h> + +#include "ux500_msp_i2s.h" + +#define UX500_NBR_OF_DAI 4 + +#define UX500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +#define FRAME_PER_SINGLE_SLOT_8_KHZ 31 +#define FRAME_PER_SINGLE_SLOT_16_KHZ 124 +#define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63 +#define FRAME_PER_SINGLE_SLOT_48_KHZ 49 +#define FRAME_PER_2_SLOTS 31 +#define FRAME_PER_8_SLOTS 138 +#define FRAME_PER_16_SLOTS 277 + +#ifndef CONFIG_SND_SOC_UX500_AB5500 +#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000 +#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ +#else +#define UX500_MSP_INTERNAL_CLOCK_FREQ 13000000 +#define UX500_MSP1_INTERNAL_CLOCK_FREQ (UX500_MSP_INTERNAL_CLOCK_FREQ * 2) +#endif + +#define UX500_MSP_MIN_CHANNELS 1 +#define UX500_MSP_MAX_CHANNELS 8 + +#define PLAYBACK_CONFIGURED 1 +#define CAPTURE_CONFIGURED 2 + +enum ux500_msp_clock_id { + UX500_MSP_MASTER_CLOCK, +}; + +struct ux500_msp_i2s_drvdata { + struct ux500_msp *msp; + struct regulator *reg_vape; + struct ux500_msp_dma_params playback_dma_data; + struct ux500_msp_dma_params capture_dma_data; + unsigned int fmt; + unsigned int tx_mask; + unsigned int rx_mask; + int slots; + int slot_width; + u8 configured; + int data_delay; + + /* Clocks */ + unsigned int master_clk; + struct clk *clk; + + /* Regulators */ + int vape_opp_constraint; +}; + +int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay); + +#endif diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c new file mode 100644 index 000000000000..496dec10c96e --- /dev/null +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -0,0 +1,742 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja <ola.o.lilja@stericsson.com>, + * Roger Nilsson <roger.xr.nilsson@stericsson.com>, + * Sandeep Kaushik <sandeep.kaushik@st.com> + * for ST-Ericsson. + * + * License terms: + * + * 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/platform_device.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <mach/hardware.h> +#include <mach/board-mop500-msp.h> + +#include <sound/soc.h> + +#include "ux500_msp_i2s.h" + + /* Protocol desciptors */ +static const struct msp_protdesc prot_descs[] = { + { /* I2S */ + MSP_SINGLE_PHASE, + MSP_SINGLE_PHASE, + MSP_PHASE2_START_MODE_IMEDIATE, + MSP_PHASE2_START_MODE_IMEDIATE, + MSP_BTF_MS_BIT_FIRST, + MSP_BTF_MS_BIT_FIRST, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_ELEM_LEN_32, + MSP_ELEM_LEN_32, + MSP_ELEM_LEN_32, + MSP_ELEM_LEN_32, + MSP_DELAY_1, + MSP_DELAY_1, + MSP_RISING_EDGE, + MSP_FALLING_EDGE, + MSP_FSYNC_POL_ACT_LO, + MSP_FSYNC_POL_ACT_LO, + MSP_SWAP_NONE, + MSP_SWAP_NONE, + MSP_COMPRESS_MODE_LINEAR, + MSP_EXPAND_MODE_LINEAR, + MSP_FSYNC_IGNORE, + 31, + 15, + 32, + }, { /* PCM */ + MSP_DUAL_PHASE, + MSP_DUAL_PHASE, + MSP_PHASE2_START_MODE_FSYNC, + MSP_PHASE2_START_MODE_FSYNC, + MSP_BTF_MS_BIT_FIRST, + MSP_BTF_MS_BIT_FIRST, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_ELEM_LEN_16, + MSP_ELEM_LEN_16, + MSP_ELEM_LEN_16, + MSP_ELEM_LEN_16, + MSP_DELAY_0, + MSP_DELAY_0, + MSP_RISING_EDGE, + MSP_FALLING_EDGE, + MSP_FSYNC_POL_ACT_HI, + MSP_FSYNC_POL_ACT_HI, + MSP_SWAP_NONE, + MSP_SWAP_NONE, + MSP_COMPRESS_MODE_LINEAR, + MSP_EXPAND_MODE_LINEAR, + MSP_FSYNC_IGNORE, + 255, + 0, + 256, + }, { /* Companded PCM */ + MSP_SINGLE_PHASE, + MSP_SINGLE_PHASE, + MSP_PHASE2_START_MODE_FSYNC, + MSP_PHASE2_START_MODE_FSYNC, + MSP_BTF_MS_BIT_FIRST, + MSP_BTF_MS_BIT_FIRST, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_FRAME_LEN_1, + MSP_ELEM_LEN_8, + MSP_ELEM_LEN_8, + MSP_ELEM_LEN_8, + MSP_ELEM_LEN_8, + MSP_DELAY_0, + MSP_DELAY_0, + MSP_RISING_EDGE, + MSP_RISING_EDGE, + MSP_FSYNC_POL_ACT_HI, + MSP_FSYNC_POL_ACT_HI, + MSP_SWAP_NONE, + MSP_SWAP_NONE, + MSP_COMPRESS_MODE_LINEAR, + MSP_EXPAND_MODE_LINEAR, + MSP_FSYNC_IGNORE, + 255, + 0, + 256, + }, +}; + +static void set_prot_desc_tx(struct ux500_msp *msp, + struct msp_protdesc *protdesc, + enum msp_data_size data_size) +{ + u32 temp_reg = 0; + + temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode); + temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode); + temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1); + temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2); + if (msp->def_elem_len) { + temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1); + temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2); + } else { + temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size); + temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size); + } + temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay); + temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order); + temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol); + temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap); + temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode); + temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore); + + writel(temp_reg, msp->registers + MSP_TCF); +} + +static void set_prot_desc_rx(struct ux500_msp *msp, + struct msp_protdesc *protdesc, + enum msp_data_size data_size) +{ + u32 temp_reg = 0; + + temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode); + temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode); + temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1); + temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2); + if (msp->def_elem_len) { + temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1); + temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2); + } else { + temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size); + temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size); + } + + temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay); + temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order); + temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol); + temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap); + temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode); + temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore); + + writel(temp_reg, msp->registers + MSP_RCF); +} + +static int configure_protocol(struct ux500_msp *msp, + struct ux500_msp_config *config) +{ + struct msp_protdesc *protdesc; + enum msp_data_size data_size; + u32 temp_reg = 0; + + data_size = config->data_size; + msp->def_elem_len = config->def_elem_len; + if (config->default_protdesc == 1) { + if (config->protocol >= MSP_INVALID_PROTOCOL) { + dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n", + __func__); + return -EINVAL; + } + protdesc = + (struct msp_protdesc *)&prot_descs[config->protocol]; + } else { + protdesc = (struct msp_protdesc *)&config->protdesc; + } + + if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) { + dev_err(msp->dev, + "%s: ERROR: Invalid data-size requested (data_size = %d)!\n", + __func__, data_size); + return -EINVAL; + } + + if (config->direction & MSP_DIR_TX) + set_prot_desc_tx(msp, protdesc, data_size); + if (config->direction & MSP_DIR_RX) + set_prot_desc_rx(msp, protdesc, data_size); + + /* The code below should not be separated. */ + temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING; + temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol); + writel(temp_reg, msp->registers + MSP_GCR); + temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING; + temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol); + writel(temp_reg, msp->registers + MSP_GCR); + + return 0; +} + +static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config) +{ + u32 reg_val_GCR; + u32 frame_per = 0; + u32 sck_div = 0; + u32 frame_width = 0; + u32 temp_reg = 0; + struct msp_protdesc *protdesc = NULL; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR); + + if (config->default_protdesc) + protdesc = + (struct msp_protdesc *)&prot_descs[config->protocol]; + else + protdesc = (struct msp_protdesc *)&config->protdesc; + + switch (config->protocol) { + case MSP_PCM_PROTOCOL: + case MSP_PCM_COMPAND_PROTOCOL: + frame_width = protdesc->frame_width; + sck_div = config->f_inputclk / (config->frame_freq * + (protdesc->clocks_per_frame)); + frame_per = protdesc->frame_period; + break; + case MSP_I2S_PROTOCOL: + frame_width = protdesc->frame_width; + sck_div = config->f_inputclk / (config->frame_freq * + (protdesc->clocks_per_frame)); + frame_per = protdesc->frame_period; + break; + default: + dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n", + __func__, + config->protocol); + return -EINVAL; + } + + temp_reg = (sck_div - 1) & SCK_DIV_MASK; + temp_reg |= FRAME_WIDTH_BITS(frame_width); + temp_reg |= FRAME_PERIOD_BITS(frame_per); + writel(temp_reg, msp->registers + MSP_SRG); + + msp->f_bitclk = (config->f_inputclk)/(sck_div + 1); + + /* Enable bit-clock */ + udelay(100); + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR); + udelay(100); + + return 0; +} + +static int configure_multichannel(struct ux500_msp *msp, + struct ux500_msp_config *config) +{ + struct msp_protdesc *protdesc; + struct msp_multichannel_config *mcfg; + u32 reg_val_MCR; + + if (config->default_protdesc == 1) { + if (config->protocol >= MSP_INVALID_PROTOCOL) { + dev_err(msp->dev, + "%s: ERROR: Invalid protocol (%d)!\n", + __func__, config->protocol); + return -EINVAL; + } + protdesc = (struct msp_protdesc *) + &prot_descs[config->protocol]; + } else { + protdesc = (struct msp_protdesc *)&config->protdesc; + } + + mcfg = &config->multichannel_config; + if (mcfg->tx_multichannel_enable) { + if (protdesc->tx_phase_mode == MSP_SINGLE_PHASE) { + reg_val_MCR = readl(msp->registers + MSP_MCR); + writel(reg_val_MCR | (mcfg->tx_multichannel_enable ? + 1 << TMCEN_BIT : 0), + msp->registers + MSP_MCR); + writel(mcfg->tx_channel_0_enable, + msp->registers + MSP_TCE0); + writel(mcfg->tx_channel_1_enable, + msp->registers + MSP_TCE1); + writel(mcfg->tx_channel_2_enable, + msp->registers + MSP_TCE2); + writel(mcfg->tx_channel_3_enable, + msp->registers + MSP_TCE3); + } else { + dev_err(msp->dev, + "%s: ERROR: Only single-phase supported (TX-mode: %d)!\n", + __func__, protdesc->tx_phase_mode); + return -EINVAL; + } + } + if (mcfg->rx_multichannel_enable) { + if (protdesc->rx_phase_mode == MSP_SINGLE_PHASE) { + reg_val_MCR = readl(msp->registers + MSP_MCR); + writel(reg_val_MCR | (mcfg->rx_multichannel_enable ? + 1 << RMCEN_BIT : 0), + msp->registers + MSP_MCR); + writel(mcfg->rx_channel_0_enable, + msp->registers + MSP_RCE0); + writel(mcfg->rx_channel_1_enable, + msp->registers + MSP_RCE1); + writel(mcfg->rx_channel_2_enable, + msp->registers + MSP_RCE2); + writel(mcfg->rx_channel_3_enable, + msp->registers + MSP_RCE3); + } else { + dev_err(msp->dev, + "%s: ERROR: Only single-phase supported (RX-mode: %d)!\n", + __func__, protdesc->rx_phase_mode); + return -EINVAL; + } + if (mcfg->rx_comparison_enable_mode) { + reg_val_MCR = readl(msp->registers + MSP_MCR); + writel(reg_val_MCR | + (mcfg->rx_comparison_enable_mode << RCMPM_BIT), + msp->registers + MSP_MCR); + + writel(mcfg->comparison_mask, + msp->registers + MSP_RCM); + writel(mcfg->comparison_value, + msp->registers + MSP_RCV); + + } + } + + return 0; +} + +static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) +{ + int status = 0; + u32 reg_val_DMACR, reg_val_GCR; + + /* Check msp state whether in RUN or CONFIGURED Mode */ + if ((msp->msp_state == MSP_STATE_IDLE) && (msp->plat_init)) { + status = msp->plat_init(); + if (status) { + dev_err(msp->dev, "%s: ERROR: Failed to init MSP (%d)!\n", + __func__, status); + return status; + } + } + + /* Configure msp with protocol dependent settings */ + configure_protocol(msp, config); + setup_bitclk(msp, config); + if (config->multichannel_configured == 1) { + status = configure_multichannel(msp, config); + if (status) + dev_warn(msp->dev, + "%s: WARN: configure_multichannel failed (%d)!\n", + __func__, status); + } + + /* Make sure the correct DMA-directions are configured */ + if ((config->direction & MSP_DIR_RX) && (!msp->dma_cfg_rx)) { + dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!", + __func__); + return -EINVAL; + } + if ((config->direction == MSP_DIR_TX) && (!msp->dma_cfg_tx)) { + dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!", + __func__); + return -EINVAL; + } + + reg_val_DMACR = readl(msp->registers + MSP_DMACR); + if (config->direction & MSP_DIR_RX) + reg_val_DMACR |= RX_DMA_ENABLE; + if (config->direction & MSP_DIR_TX) + reg_val_DMACR |= TX_DMA_ENABLE; + writel(reg_val_DMACR, msp->registers + MSP_DMACR); + + writel(config->iodelay, msp->registers + MSP_IODLY); + + /* Enable frame generation logic */ + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR); + + return status; +} + +static void flush_fifo_rx(struct ux500_msp *msp) +{ + u32 reg_val_DR, reg_val_GCR, reg_val_FLR; + u32 limit = 32; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR); + + reg_val_FLR = readl(msp->registers + MSP_FLR); + while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) { + reg_val_DR = readl(msp->registers + MSP_DR); + reg_val_FLR = readl(msp->registers + MSP_FLR); + } + + writel(reg_val_GCR, msp->registers + MSP_GCR); +} + +static void flush_fifo_tx(struct ux500_msp *msp) +{ + u32 reg_val_TSTDR, reg_val_GCR, reg_val_FLR; + u32 limit = 32; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR); + writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR); + + reg_val_FLR = readl(msp->registers + MSP_FLR); + while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) { + reg_val_TSTDR = readl(msp->registers + MSP_TSTDR); + reg_val_FLR = readl(msp->registers + MSP_FLR); + } + writel(0x0, msp->registers + MSP_ITCR); + writel(reg_val_GCR, msp->registers + MSP_GCR); +} + +int ux500_msp_i2s_open(struct ux500_msp *msp, + struct ux500_msp_config *config) +{ + u32 old_reg, new_reg, mask; + int res; + unsigned int tx_sel, rx_sel, tx_busy, rx_busy; + + if (in_interrupt()) { + dev_err(msp->dev, + "%s: ERROR: Open called in interrupt context!\n", + __func__); + return -1; + } + + tx_sel = (config->direction & MSP_DIR_TX) > 0; + rx_sel = (config->direction & MSP_DIR_RX) > 0; + if (!tx_sel && !rx_sel) { + dev_err(msp->dev, "%s: Error: No direction selected!\n", + __func__); + return -EINVAL; + } + + tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0; + rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0; + if (tx_busy && tx_sel) { + dev_err(msp->dev, "%s: Error: TX is in use!\n", __func__); + return -EBUSY; + } + if (rx_busy && rx_sel) { + dev_err(msp->dev, "%s: Error: RX is in use!\n", __func__); + return -EBUSY; + } + + msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0); + + /* First do the global config register */ + mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK | + TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK | + RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK | + LOOPBACK_MASK | TX_EXTRA_DELAY_MASK; + + new_reg = (config->tx_clk_sel | config->rx_clk_sel | + config->rx_fsync_pol | config->tx_fsync_pol | + config->rx_fsync_sel | config->tx_fsync_sel | + config->rx_fifo_config | config->tx_fifo_config | + config->srg_clk_sel | config->loopback_enable | + config->tx_data_enable); + + old_reg = readl(msp->registers + MSP_GCR); + old_reg &= ~mask; + new_reg |= old_reg; + writel(new_reg, msp->registers + MSP_GCR); + + res = enable_msp(msp, config); + if (res < 0) { + dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n", + __func__, res); + return -EBUSY; + } + if (config->loopback_enable & 0x80) + msp->loopback_enable = 1; + + /* Flush FIFOs */ + flush_fifo_tx(msp); + flush_fifo_rx(msp); + + msp->msp_state = MSP_STATE_CONFIGURED; + return 0; +} + +static void disable_msp_rx(struct ux500_msp *msp) +{ + u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR); + reg_val_DMACR = readl(msp->registers + MSP_DMACR); + writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR); + reg_val_IMSC = readl(msp->registers + MSP_IMSC); + writel(reg_val_IMSC & + ~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT), + msp->registers + MSP_IMSC); + + msp->dir_busy &= ~MSP_DIR_RX; +} + +static void disable_msp_tx(struct ux500_msp *msp) +{ + u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR); + reg_val_DMACR = readl(msp->registers + MSP_DMACR); + writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR); + reg_val_IMSC = readl(msp->registers + MSP_IMSC); + writel(reg_val_IMSC & + ~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT), + msp->registers + MSP_IMSC); + + msp->dir_busy &= ~MSP_DIR_TX; +} + +static int disable_msp(struct ux500_msp *msp, unsigned int dir) +{ + u32 reg_val_GCR; + int status = 0; + unsigned int disable_tx, disable_rx; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + disable_tx = dir & MSP_DIR_TX; + disable_rx = dir & MSP_DIR_TX; + if (disable_tx && disable_rx) { + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | LOOPBACK_MASK, + msp->registers + MSP_GCR); + + /* Flush TX-FIFO */ + flush_fifo_tx(msp); + + /* Disable TX-channel */ + writel((readl(msp->registers + MSP_GCR) & + (~TX_ENABLE)), msp->registers + MSP_GCR); + + /* Flush RX-FIFO */ + flush_fifo_rx(msp); + + /* Disable Loopback and Receive channel */ + writel((readl(msp->registers + MSP_GCR) & + (~(RX_ENABLE | LOOPBACK_MASK))), + msp->registers + MSP_GCR); + + disable_msp_tx(msp); + disable_msp_rx(msp); + } else if (disable_tx) + disable_msp_tx(msp); + else if (disable_rx) + disable_msp_rx(msp); + + return status; +} + +int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction) +{ + u32 reg_val_GCR, enable_bit; + + if (msp->msp_state == MSP_STATE_IDLE) { + dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n", + __func__); + return -EINVAL; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + enable_bit = TX_ENABLE; + else + enable_bit = RX_ENABLE; + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + disable_msp_tx(msp); + else + disable_msp_rx(msp); + break; + default: + return -EINVAL; + break; + } + + return 0; +} + +int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) +{ + int status = 0; + + dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir); + + status = disable_msp(msp, dir); + if (msp->dir_busy == 0) { + /* disable sample rate and frame generators */ + msp->msp_state = MSP_STATE_IDLE; + writel((readl(msp->registers + MSP_GCR) & + (~(FRAME_GEN_ENABLE | SRG_ENABLE))), + msp->registers + MSP_GCR); + if (msp->plat_exit) + status = msp->plat_exit(); + if (status) + dev_warn(msp->dev, + "%s: WARN: ux500_msp_i2s_exit failed (%d)!\n", + __func__, status); + writel(0, msp->registers + MSP_GCR); + writel(0, msp->registers + MSP_TCF); + writel(0, msp->registers + MSP_RCF); + writel(0, msp->registers + MSP_DMACR); + writel(0, msp->registers + MSP_SRG); + writel(0, msp->registers + MSP_MCR); + writel(0, msp->registers + MSP_RCM); + writel(0, msp->registers + MSP_RCV); + writel(0, msp->registers + MSP_TCE0); + writel(0, msp->registers + MSP_TCE1); + writel(0, msp->registers + MSP_TCE2); + writel(0, msp->registers + MSP_TCE3); + writel(0, msp->registers + MSP_RCE0); + writel(0, msp->registers + MSP_RCE1); + writel(0, msp->registers + MSP_RCE2); + writel(0, msp->registers + MSP_RCE3); + } + + return status; + +} + +int ux500_msp_i2s_init_msp(struct platform_device *pdev, + struct ux500_msp **msp_p, + struct msp_i2s_platform_data *platform_data) +{ + int ret = 0; + struct resource *res = NULL; + struct i2s_controller *i2s_cont; + struct ux500_msp *msp; + + dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__, + pdev->name, platform_data->id); + + *msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL); + msp = *msp_p; + + msp->id = platform_data->id; + msp->dev = &pdev->dev; + msp->plat_init = platform_data->msp_i2s_init; + msp->plat_exit = platform_data->msp_i2s_exit; + msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx; + msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n", + __func__); + ret = -ENOMEM; + goto err_res; + } + + msp->registers = ioremap(res->start, (res->end - res->start + 1)); + if (msp->registers == NULL) { + dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__); + ret = -ENOMEM; + goto err_res; + } + + msp->msp_state = MSP_STATE_IDLE; + msp->loopback_enable = 0; + + /* I2S-controller is allocated and added in I2S controller class. */ + i2s_cont = devm_kzalloc(&pdev->dev, sizeof(*i2s_cont), GFP_KERNEL); + if (!i2s_cont) { + dev_err(&pdev->dev, + "%s: ERROR: Failed to allocate I2S-controller!\n", + __func__); + goto err_i2s_cont; + } + i2s_cont->dev.parent = &pdev->dev; + i2s_cont->data = (void *)msp; + i2s_cont->id = (s16)msp->id; + snprintf(i2s_cont->name, sizeof(i2s_cont->name), "ux500-msp-i2s.%04x", + msp->id); + dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name); + msp->i2s_cont = i2s_cont; + + return 0; + +err_i2s_cont: + iounmap(msp->registers); + +err_res: + devm_kfree(&pdev->dev, msp); + + return ret; +} + +void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev, + struct ux500_msp *msp) +{ + dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id); + + device_unregister(&msp->i2s_cont->dev); + devm_kfree(&pdev->dev, msp->i2s_cont); + + iounmap(msp->registers); + + devm_kfree(&pdev->dev, msp); +} + +MODULE_LICENSE("GPLv2"); diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h new file mode 100644 index 000000000000..7f71b4a0d4bc --- /dev/null +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -0,0 +1,553 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja <ola.o.lilja@stericsson.com>, + * for ST-Ericsson. + * + * License terms: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + + +#ifndef UX500_MSP_I2S_H +#define UX500_MSP_I2S_H + +#include <linux/platform_device.h> + +#include <mach/board-mop500-msp.h> + +#define MSP_INPUT_FREQ_APB 48000000 + +/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono), + * 32 bits accesses (stereo). + ***/ +enum msp_stereo_mode { + MSP_MONO, + MSP_STEREO +}; + +/* Direction (Transmit/Receive mode) */ +enum msp_direction { + MSP_TX = 1, + MSP_RX = 2 +}; + +/* Transmit and receive configuration register */ +#define MSP_BIG_ENDIAN 0x00000000 +#define MSP_LITTLE_ENDIAN 0x00001000 +#define MSP_UNEXPECTED_FS_ABORT 0x00000000 +#define MSP_UNEXPECTED_FS_IGNORE 0x00008000 +#define MSP_NON_MODE_BIT_MASK 0x00009000 + +/* Global configuration register */ +#define RX_ENABLE 0x00000001 +#define RX_FIFO_ENABLE 0x00000002 +#define RX_SYNC_SRG 0x00000010 +#define RX_CLK_POL_RISING 0x00000020 +#define RX_CLK_SEL_SRG 0x00000040 +#define TX_ENABLE 0x00000100 +#define TX_FIFO_ENABLE 0x00000200 +#define TX_SYNC_SRG_PROG 0x00001800 +#define TX_SYNC_SRG_AUTO 0x00001000 +#define TX_CLK_POL_RISING 0x00002000 +#define TX_CLK_SEL_SRG 0x00004000 +#define TX_EXTRA_DELAY_ENABLE 0x00008000 +#define SRG_ENABLE 0x00010000 +#define FRAME_GEN_ENABLE 0x00100000 +#define SRG_CLK_SEL_APB 0x00000000 +#define RX_FIFO_SYNC_HI 0x00000000 +#define TX_FIFO_SYNC_HI 0x00000000 +#define SPI_CLK_MODE_NORMAL 0x00000000 + +#define MSP_FRAME_SIZE_AUTO -1 + +#define MSP_DR 0x00 +#define MSP_GCR 0x04 +#define MSP_TCF 0x08 +#define MSP_RCF 0x0c +#define MSP_SRG 0x10 +#define MSP_FLR 0x14 +#define MSP_DMACR 0x18 + +#define MSP_IMSC 0x20 +#define MSP_RIS 0x24 +#define MSP_MIS 0x28 +#define MSP_ICR 0x2c +#define MSP_MCR 0x30 +#define MSP_RCV 0x34 +#define MSP_RCM 0x38 + +#define MSP_TCE0 0x40 +#define MSP_TCE1 0x44 +#define MSP_TCE2 0x48 +#define MSP_TCE3 0x4c + +#define MSP_RCE0 0x60 +#define MSP_RCE1 0x64 +#define MSP_RCE2 0x68 +#define MSP_RCE3 0x6c +#define MSP_IODLY 0x70 + +#define MSP_ITCR 0x80 +#define MSP_ITIP 0x84 +#define MSP_ITOP 0x88 +#define MSP_TSTDR 0x8c + +#define MSP_PID0 0xfe0 +#define MSP_PID1 0xfe4 +#define MSP_PID2 0xfe8 +#define MSP_PID3 0xfec + +#define MSP_CID0 0xff0 +#define MSP_CID1 0xff4 +#define MSP_CID2 0xff8 +#define MSP_CID3 0xffc + +/* Protocol dependant parameters list */ +#define RX_ENABLE_MASK BIT(0) +#define RX_FIFO_ENABLE_MASK BIT(1) +#define RX_FSYNC_MASK BIT(2) +#define DIRECT_COMPANDING_MASK BIT(3) +#define RX_SYNC_SEL_MASK BIT(4) +#define RX_CLK_POL_MASK BIT(5) +#define RX_CLK_SEL_MASK BIT(6) +#define LOOPBACK_MASK BIT(7) +#define TX_ENABLE_MASK BIT(8) +#define TX_FIFO_ENABLE_MASK BIT(9) +#define TX_FSYNC_MASK BIT(10) +#define TX_MSP_TDR_TSR BIT(11) +#define TX_SYNC_SEL_MASK (BIT(12) | BIT(11)) +#define TX_CLK_POL_MASK BIT(13) +#define TX_CLK_SEL_MASK BIT(14) +#define TX_EXTRA_DELAY_MASK BIT(15) +#define SRG_ENABLE_MASK BIT(16) +#define SRG_CLK_POL_MASK BIT(17) +#define SRG_CLK_SEL_MASK (BIT(19) | BIT(18)) +#define FRAME_GEN_EN_MASK BIT(20) +#define SPI_CLK_MODE_MASK (BIT(22) | BIT(21)) +#define SPI_BURST_MODE_MASK BIT(23) + +#define RXEN_SHIFT 0 +#define RFFEN_SHIFT 1 +#define RFSPOL_SHIFT 2 +#define DCM_SHIFT 3 +#define RFSSEL_SHIFT 4 +#define RCKPOL_SHIFT 5 +#define RCKSEL_SHIFT 6 +#define LBM_SHIFT 7 +#define TXEN_SHIFT 8 +#define TFFEN_SHIFT 9 +#define TFSPOL_SHIFT 10 +#define TFSSEL_SHIFT 11 +#define TCKPOL_SHIFT 13 +#define TCKSEL_SHIFT 14 +#define TXDDL_SHIFT 15 +#define SGEN_SHIFT 16 +#define SCKPOL_SHIFT 17 +#define SCKSEL_SHIFT 18 +#define FGEN_SHIFT 20 +#define SPICKM_SHIFT 21 +#define TBSWAP_SHIFT 28 + +#define RCKPOL_MASK BIT(0) +#define TCKPOL_MASK BIT(0) +#define SPICKM_MASK (BIT(1) | BIT(0)) +#define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT) +#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT) + +#define P1ELEN_SHIFT 0 +#define P1FLEN_SHIFT 3 +#define DTYP_SHIFT 10 +#define ENDN_SHIFT 12 +#define DDLY_SHIFT 13 +#define FSIG_SHIFT 15 +#define P2ELEN_SHIFT 16 +#define P2FLEN_SHIFT 19 +#define P2SM_SHIFT 26 +#define P2EN_SHIFT 27 +#define FSYNC_SHIFT 15 + +#define P1ELEN_MASK 0x00000007 +#define P2ELEN_MASK 0x00070000 +#define P1FLEN_MASK 0x00000378 +#define P2FLEN_MASK 0x03780000 +#define DDLY_MASK 0x00003000 +#define DTYP_MASK 0x00000600 +#define P2SM_MASK 0x04000000 +#define P2EN_MASK 0x08000000 +#define ENDN_MASK 0x00001000 +#define TFSPOL_MASK 0x00000400 +#define TBSWAP_MASK 0x30000000 +#define COMPANDING_MODE_MASK 0x00000c00 +#define FSYNC_MASK 0x00008000 + +#define MSP_P1_ELEM_LEN_BITS(n) (n & P1ELEN_MASK) +#define MSP_P2_ELEM_LEN_BITS(n) (((n) << P2ELEN_SHIFT) & P2ELEN_MASK) +#define MSP_P1_FRAME_LEN_BITS(n) (((n) << P1FLEN_SHIFT) & P1FLEN_MASK) +#define MSP_P2_FRAME_LEN_BITS(n) (((n) << P2FLEN_SHIFT) & P2FLEN_MASK) +#define MSP_DATA_DELAY_BITS(n) (((n) << DDLY_SHIFT) & DDLY_MASK) +#define MSP_DATA_TYPE_BITS(n) (((n) << DTYP_SHIFT) & DTYP_MASK) +#define MSP_P2_START_MODE_BIT(n) ((n << P2SM_SHIFT) & P2SM_MASK) +#define MSP_P2_ENABLE_BIT(n) ((n << P2EN_SHIFT) & P2EN_MASK) +#define MSP_SET_ENDIANNES_BIT(n) ((n << ENDN_SHIFT) & ENDN_MASK) +#define MSP_FSYNC_POL(n) ((n << TFSPOL_SHIFT) & TFSPOL_MASK) +#define MSP_DATA_WORD_SWAP(n) ((n << TBSWAP_SHIFT) & TBSWAP_MASK) +#define MSP_SET_COMPANDING_MODE(n) ((n << DTYP_SHIFT) & \ + COMPANDING_MODE_MASK) +#define MSP_SET_FSYNC_IGNORE(n) ((n << FSYNC_SHIFT) & FSYNC_MASK) + +/* Flag register */ +#define RX_BUSY BIT(0) +#define RX_FIFO_EMPTY BIT(1) +#define RX_FIFO_FULL BIT(2) +#define TX_BUSY BIT(3) +#define TX_FIFO_EMPTY BIT(4) +#define TX_FIFO_FULL BIT(5) + +#define RBUSY_SHIFT 0 +#define RFE_SHIFT 1 +#define RFU_SHIFT 2 +#define TBUSY_SHIFT 3 +#define TFE_SHIFT 4 +#define TFU_SHIFT 5 + +/* Multichannel control register */ +#define RMCEN_SHIFT 0 +#define RMCSF_SHIFT 1 +#define RCMPM_SHIFT 3 +#define TMCEN_SHIFT 5 +#define TNCSF_SHIFT 6 + +/* Sample rate generator register */ +#define SCKDIV_SHIFT 0 +#define FRWID_SHIFT 10 +#define FRPER_SHIFT 16 + +#define SCK_DIV_MASK 0x0000003FF +#define FRAME_WIDTH_BITS(n) (((n) << FRWID_SHIFT) & 0x0000FC00) +#define FRAME_PERIOD_BITS(n) (((n) << FRPER_SHIFT) & 0x1FFF0000) + +/* DMA controller register */ +#define RX_DMA_ENABLE BIT(0) +#define TX_DMA_ENABLE BIT(1) + +#define RDMAE_SHIFT 0 +#define TDMAE_SHIFT 1 + +/* Interrupt Register */ +#define RX_SERVICE_INT BIT(0) +#define RX_OVERRUN_ERROR_INT BIT(1) +#define RX_FSYNC_ERR_INT BIT(2) +#define RX_FSYNC_INT BIT(3) +#define TX_SERVICE_INT BIT(4) +#define TX_UNDERRUN_ERR_INT BIT(5) +#define TX_FSYNC_ERR_INT BIT(6) +#define TX_FSYNC_INT BIT(7) +#define ALL_INT 0x000000ff + +/* MSP test control register */ +#define MSP_ITCR_ITEN BIT(0) +#define MSP_ITCR_TESTFIFO BIT(1) + +#define RMCEN_BIT 0 +#define RMCSF_BIT 1 +#define RCMPM_BIT 3 +#define TMCEN_BIT 5 +#define TNCSF_BIT 6 + +/* Single or dual phase mode */ +enum msp_phase_mode { + MSP_SINGLE_PHASE, + MSP_DUAL_PHASE +}; + +/* Frame length */ +enum msp_frame_length { + MSP_FRAME_LEN_1 = 0, + MSP_FRAME_LEN_2 = 1, + MSP_FRAME_LEN_4 = 3, + MSP_FRAME_LEN_8 = 7, + MSP_FRAME_LEN_12 = 11, + MSP_FRAME_LEN_16 = 15, + MSP_FRAME_LEN_20 = 19, + MSP_FRAME_LEN_32 = 31, + MSP_FRAME_LEN_48 = 47, + MSP_FRAME_LEN_64 = 63 +}; + +/* Element length */ +enum msp_elem_length { + MSP_ELEM_LEN_8 = 0, + MSP_ELEM_LEN_10 = 1, + MSP_ELEM_LEN_12 = 2, + MSP_ELEM_LEN_14 = 3, + MSP_ELEM_LEN_16 = 4, + MSP_ELEM_LEN_20 = 5, + MSP_ELEM_LEN_24 = 6, + MSP_ELEM_LEN_32 = 7 +}; + +enum msp_data_xfer_width { + MSP_DATA_TRANSFER_WIDTH_BYTE, + MSP_DATA_TRANSFER_WIDTH_HALFWORD, + MSP_DATA_TRANSFER_WIDTH_WORD +}; + +enum msp_frame_sync { + MSP_FSYNC_UNIGNORE = 0, + MSP_FSYNC_IGNORE = 1, +}; + +enum msp_phase2_start_mode { + MSP_PHASE2_START_MODE_IMEDIATE, + MSP_PHASE2_START_MODE_FSYNC +}; + +enum msp_btf { + MSP_BTF_MS_BIT_FIRST = 0, + MSP_BTF_LS_BIT_FIRST = 1 +}; + +enum msp_fsync_pol { + MSP_FSYNC_POL_ACT_HI = 0, + MSP_FSYNC_POL_ACT_LO = 1 +}; + +/* Data delay (in bit clock cycles) */ +enum msp_delay { + MSP_DELAY_0 = 0, + MSP_DELAY_1 = 1, + MSP_DELAY_2 = 2, + MSP_DELAY_3 = 3 +}; + +/* Configurations of clocks (transmit, receive or sample rate generator) */ +enum msp_edge { + MSP_FALLING_EDGE = 0, + MSP_RISING_EDGE = 1, +}; + +enum msp_hws { + MSP_SWAP_NONE = 0, + MSP_SWAP_BYTE_PER_WORD = 1, + MSP_SWAP_BYTE_PER_HALF_WORD = 2, + MSP_SWAP_HALF_WORD_PER_WORD = 3 +}; + +enum msp_compress_mode { + MSP_COMPRESS_MODE_LINEAR = 0, + MSP_COMPRESS_MODE_MU_LAW = 2, + MSP_COMPRESS_MODE_A_LAW = 3 +}; + +enum msp_spi_burst_mode { + MSP_SPI_BURST_MODE_DISABLE = 0, + MSP_SPI_BURST_MODE_ENABLE = 1 +}; + +enum msp_expand_mode { + MSP_EXPAND_MODE_LINEAR = 0, + MSP_EXPAND_MODE_LINEAR_SIGNED = 1, + MSP_EXPAND_MODE_MU_LAW = 2, + MSP_EXPAND_MODE_A_LAW = 3 +}; + +#define MSP_FRAME_PERIOD_IN_MONO_MODE 256 +#define MSP_FRAME_PERIOD_IN_STEREO_MODE 32 +#define MSP_FRAME_WIDTH_IN_STEREO_MODE 16 + +enum msp_protocol { + MSP_I2S_PROTOCOL, + MSP_PCM_PROTOCOL, + MSP_PCM_COMPAND_PROTOCOL, + MSP_INVALID_PROTOCOL +}; + +/* + * No of registers to backup during + * suspend resume + */ +#define MAX_MSP_BACKUP_REGS 36 + +enum enum_i2s_controller { + MSP_0_I2S_CONTROLLER = 0, + MSP_1_I2S_CONTROLLER, + MSP_2_I2S_CONTROLLER, + MSP_3_I2S_CONTROLLER, +}; + +enum i2s_direction_t { + MSP_DIR_TX = 0x01, + MSP_DIR_RX = 0x02, +}; + +enum msp_data_size { + MSP_DATA_BITS_DEFAULT = -1, + MSP_DATA_BITS_8 = 0x00, + MSP_DATA_BITS_10, + MSP_DATA_BITS_12, + MSP_DATA_BITS_14, + MSP_DATA_BITS_16, + MSP_DATA_BITS_20, + MSP_DATA_BITS_24, + MSP_DATA_BITS_32, +}; + +enum msp_state { + MSP_STATE_IDLE = 0, + MSP_STATE_CONFIGURED = 1, + MSP_STATE_RUNNING = 2, +}; + +enum msp_rx_comparison_enable_mode { + MSP_COMPARISON_DISABLED = 0, + MSP_COMPARISON_NONEQUAL_ENABLED = 2, + MSP_COMPARISON_EQUAL_ENABLED = 3 +}; + +struct msp_multichannel_config { + bool rx_multichannel_enable; + bool tx_multichannel_enable; + enum msp_rx_comparison_enable_mode rx_comparison_enable_mode; + u8 padding; + u32 comparison_value; + u32 comparison_mask; + u32 rx_channel_0_enable; + u32 rx_channel_1_enable; + u32 rx_channel_2_enable; + u32 rx_channel_3_enable; + u32 tx_channel_0_enable; + u32 tx_channel_1_enable; + u32 tx_channel_2_enable; + u32 tx_channel_3_enable; +}; + +struct msp_protdesc { + u32 rx_phase_mode; + u32 tx_phase_mode; + u32 rx_phase2_start_mode; + u32 tx_phase2_start_mode; + u32 rx_byte_order; + u32 tx_byte_order; + u32 rx_frame_len_1; + u32 rx_frame_len_2; + u32 tx_frame_len_1; + u32 tx_frame_len_2; + u32 rx_elem_len_1; + u32 rx_elem_len_2; + u32 tx_elem_len_1; + u32 tx_elem_len_2; + u32 rx_data_delay; + u32 tx_data_delay; + u32 rx_clk_pol; + u32 tx_clk_pol; + u32 rx_fsync_pol; + u32 tx_fsync_pol; + u32 rx_half_word_swap; + u32 tx_half_word_swap; + u32 compression_mode; + u32 expansion_mode; + u32 frame_sync_ignore; + u32 frame_period; + u32 frame_width; + u32 clocks_per_frame; +}; + +struct i2s_message { + enum i2s_direction_t i2s_direction; + void *txdata; + void *rxdata; + size_t txbytes; + size_t rxbytes; + int dma_flag; + int tx_offset; + int rx_offset; + bool cyclic_dma; + dma_addr_t buf_addr; + size_t buf_len; + size_t period_len; +}; + +struct i2s_controller { + struct module *owner; + unsigned int id; + unsigned int class; + const struct i2s_algorithm *algo; /* the algorithm to access the bus */ + void *data; + struct mutex bus_lock; + struct device dev; /* the controller device */ + char name[48]; +}; + +struct ux500_msp_config { + unsigned int f_inputclk; + unsigned int rx_clk_sel; + unsigned int tx_clk_sel; + unsigned int srg_clk_sel; + unsigned int rx_fsync_pol; + unsigned int tx_fsync_pol; + unsigned int rx_fsync_sel; + unsigned int tx_fsync_sel; + unsigned int rx_fifo_config; + unsigned int tx_fifo_config; + unsigned int spi_clk_mode; + unsigned int spi_burst_mode; + unsigned int loopback_enable; + unsigned int tx_data_enable; + unsigned int default_protdesc; + struct msp_protdesc protdesc; + int multichannel_configured; + struct msp_multichannel_config multichannel_config; + unsigned int direction; + unsigned int protocol; + unsigned int frame_freq; + unsigned int frame_size; + enum msp_data_size data_size; + unsigned int def_elem_len; + unsigned int iodelay; + void (*handler) (void *data); + void *tx_callback_data; + void *rx_callback_data; +}; + +struct ux500_msp { + enum enum_i2s_controller id; + void __iomem *registers; + struct device *dev; + struct i2s_controller *i2s_cont; + struct stedma40_chan_cfg *dma_cfg_rx; + struct stedma40_chan_cfg *dma_cfg_tx; + struct dma_chan *tx_pipeid; + struct dma_chan *rx_pipeid; + enum msp_state msp_state; + int (*transfer) (struct ux500_msp *msp, struct i2s_message *message); + int (*plat_init) (void); + int (*plat_exit) (void); + struct timer_list notify_timer; + int def_elem_len; + unsigned int dir_busy; + int loopback_enable; + u32 backup_regs[MAX_MSP_BACKUP_REGS]; + unsigned int f_bitclk; +}; + +struct ux500_msp_dma_params { + unsigned int data_size; + struct stedma40_chan_cfg *dma_cfg; +}; + +int ux500_msp_i2s_init_msp(struct platform_device *pdev, + struct ux500_msp **msp_p, + struct msp_i2s_platform_data *platform_data); +void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev, + struct ux500_msp *msp); +int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config); +int ux500_msp_i2s_close(struct ux500_msp *msp, + unsigned int dir); +int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, + int direction); + +#endif diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 416684be0ad3..26b823b61aa1 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -19,3 +19,5 @@ TAGS cscope* config.mak config.mak.autogen +*-bison.* +*-flex.* diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 820371f10d1b..9bf3fc759344 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -234,24 +234,23 @@ endif export PERL_PATH -FLEX = $(CROSS_COMPILE)flex -BISON= $(CROSS_COMPILE)bison +FLEX = flex +BISON= bison -event-parser: - $(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o $(OUTPUT)util/parse-events-bison.c +$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c -$(OUTPUT)util/parse-events-flex.c: event-parser -$(OUTPUT)util/parse-events-bison.c: event-parser +$(OUTPUT)util/parse-events-bison.c: util/parse-events.y + $(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o $(OUTPUT)util/parse-events-bison.c -pmu-parser: - $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c +$(OUTPUT)util/pmu-flex.c: util/pmu.l $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c -$(OUTPUT)util/pmu-flex.c: pmu-parser -$(OUTPUT)util/pmu-bison.c: pmu-parser +$(OUTPUT)util/pmu-bison.c: util/pmu.y + $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c -$(OUTPUT)util/parse-events.o: event-parser pmu-parser +$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c +$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c LIB_FILE=$(OUTPUT)libperf.a @@ -527,7 +526,7 @@ else endif ifdef NO_GTK2 - BASIC_CFLAGS += -DNO_GTK2 + BASIC_CFLAGS += -DNO_GTK2_SUPPORT else FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0) ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y) @@ -852,8 +851,6 @@ help: @echo ' html - make html documentation' @echo ' info - make GNU info documentation (access with info <foo>)' @echo ' pdf - make pdf documentation' - @echo ' event-parser - make event parser code' - @echo ' pmu-parser - make pmu format parser code' @echo ' TAGS - use etags to make tag information for source browsing' @echo ' tags - use ctags to make tag information for source browsing' @echo ' cscope - use cscope to make interactive browsing database' diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2e317438980b..cdae9b2db1cc 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -374,16 +374,23 @@ static int __cmd_report(struct perf_report *rep) (kernel_map->dso->hit && (kernel_kmap->ref_reloc_sym == NULL || kernel_kmap->ref_reloc_sym->addr == 0))) { - const struct dso *kdso = kernel_map->dso; + const char *desc = + "As no suitable kallsyms nor vmlinux was found, kernel samples\n" + "can't be resolved."; + + if (kernel_map) { + const struct dso *kdso = kernel_map->dso; + if (!RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION])) { + desc = "If some relocation was applied (e.g. " + "kexec) symbols may be misresolved."; + } + } ui__warning( "Kernel address maps (/proc/{kallsyms,modules}) were restricted.\n\n" "Check /proc/sys/kernel/kptr_restrict before running 'perf record'.\n\n%s\n\n" "Samples in kernel modules can't be resolved as well.\n\n", - RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION]) ? -"As no suitable kallsyms nor vmlinux was found, kernel samples\n" -"can't be resolved." : -"If some relocation was applied (e.g. kexec) symbols may be misresolved."); + desc); } if (dump_trace) { diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index fb8b5f83b4a0..1cad3af4bf4c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -17,6 +17,7 @@ #include "util/debug.h" #include <sys/prctl.h> +#include <sys/resource.h> #include <semaphore.h> #include <pthread.h> diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 1c5b9801ac61..223ffdcc0fd8 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -851,6 +851,28 @@ static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist) return test__checkevent_symbolic_name(evlist); } +static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); + + return test__checkevent_symbolic_name(evlist); +} + +static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); + + return test__checkevent_symbolic_name(evlist); +} + static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist) { struct perf_evsel *evsel = list_entry(evlist->entries.next, @@ -1091,6 +1113,14 @@ static struct test__event_st { .name = "r1,syscalls:sys_enter_open:k,1:1:hp", .check = test__checkevent_list, }, + { + .name = "instructions:G", + .check = test__checkevent_exclude_host_modifier, + }, + { + .name = "instructions:H", + .check = test__checkevent_exclude_guest_modifier, + }, }; #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e3c63aef8efc..8ef59f8262bb 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -42,6 +42,7 @@ #include "util/debug.h" #include <assert.h> +#include <elf.h> #include <fcntl.h> #include <stdio.h> @@ -59,6 +60,7 @@ #include <sys/prctl.h> #include <sys/wait.h> #include <sys/uio.h> +#include <sys/utsname.h> #include <sys/mman.h> #include <linux/unistd.h> @@ -162,12 +164,40 @@ static void __zero_source_counters(struct hist_entry *he) symbol__annotate_zero_histograms(sym); } +static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip) +{ + struct utsname uts; + int err = uname(&uts); + + ui__warning("Out of bounds address found:\n\n" + "Addr: %" PRIx64 "\n" + "DSO: %s %c\n" + "Map: %" PRIx64 "-%" PRIx64 "\n" + "Symbol: %" PRIx64 "-%" PRIx64 " %c %s\n" + "Arch: %s\n" + "Kernel: %s\n" + "Tools: %s\n\n" + "Not all samples will be on the annotation output.\n\n" + "Please report to linux-kernel@vger.kernel.org\n", + ip, map->dso->long_name, dso__symtab_origin(map->dso), + map->start, map->end, sym->start, sym->end, + sym->binding == STB_GLOBAL ? 'g' : + sym->binding == STB_LOCAL ? 'l' : 'w', sym->name, + err ? "[unknown]" : uts.machine, + err ? "[unknown]" : uts.release, perf_version_string); + if (use_browser <= 0) + sleep(5); + + map->erange_warned = true; +} + static void perf_top__record_precise_ip(struct perf_top *top, struct hist_entry *he, int counter, u64 ip) { struct annotation *notes; struct symbol *sym; + int err; if (he == NULL || he->ms.sym == NULL || ((top->sym_filter_entry == NULL || @@ -189,9 +219,12 @@ static void perf_top__record_precise_ip(struct perf_top *top, } ip = he->ms.map->map_ip(he->ms.map, ip); - symbol__inc_addr_samples(sym, he->ms.map, counter, ip); + err = symbol__inc_addr_samples(sym, he->ms.map, counter, ip); pthread_mutex_unlock(¬es->lock); + + if (err == -ERANGE && !he->ms.map->erange_warned) + ui__warn_map_erange(he->ms.map, sym, ip); } static void perf_top__show_details(struct perf_top *top) @@ -615,6 +648,7 @@ process_hotkey: /* Tag samples to be skipped. */ static const char *skip_symbols[] = { + "intel_idle", "default_idle", "native_safe_halt", "cpu_idle", diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh index 677e59d62a8d..95b6f8b6177a 100644 --- a/tools/perf/perf-archive.sh +++ b/tools/perf/perf-archive.sh @@ -29,13 +29,14 @@ if [ ! -s $BUILDIDS ] ; then fi MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX) +PERF_BUILDID_LINKDIR=$(readlink -f $PERF_BUILDID_DIR)/ cut -d ' ' -f 1 $BUILDIDS | \ while read build_id ; do linkname=$PERF_BUILDID_DIR.build-id/${build_id:0:2}/${build_id:2} filename=$(readlink -f $linkname) echo ${linkname#$PERF_BUILDID_DIR} >> $MANIFEST - echo ${filename#$PERF_BUILDID_DIR} >> $MANIFEST + echo ${filename#$PERF_BUILDID_LINKDIR} >> $MANIFEST done tar cfj $PERF_DATA.tar.bz2 -C $PERF_BUILDID_DIR -T $MANIFEST diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 199f69ec656f..08c6d138a655 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -64,8 +64,8 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); - if (addr > sym->end) - return 0; + if (addr < sym->start || addr > sym->end) + return -ERANGE; offset = addr - sym->start; h = annotation__histogram(notes, evidx); @@ -561,16 +561,12 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) { struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); - struct objdump_line *pos; - int len = sym->end - sym->start; + int len = sym->end - sym->start, offset; h->sum = 0; - - list_for_each_entry(pos, ¬es->src->source, node) { - if (pos->offset != -1 && pos->offset < len) { - h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8; - h->sum += h->addr[pos->offset]; - } + for (offset = 0; offset < len; ++offset) { + h->addr[offset] = h->addr[offset] * 7 / 8; + h->sum += h->addr[offset]; } } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 2ec4b60aff6c..9f6d630d5316 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -256,6 +256,18 @@ static struct hist_entry *add_hist_entry(struct hists *hists, if (!cmp) { he->period += period; ++he->nr_events; + + /* If the map of an existing hist_entry has + * become out-of-date due to an exec() or + * similar, update it. Otherwise we will + * mis-adjust symbol addresses when computing + * the history counter to increment. + */ + if (he->ms.map != entry->ms.map) { + he->ms.map = entry->ms.map; + if (he->ms.map) + he->ms.map->referenced = true; + } goto out; } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index dea6d1c1a954..35ae56864e4f 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -38,6 +38,7 @@ void map__init(struct map *self, enum map_type type, RB_CLEAR_NODE(&self->rb_node); self->groups = NULL; self->referenced = false; + self->erange_warned = false; } struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index b100c20b7f94..81371bad4ef0 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -33,6 +33,7 @@ struct map { u64 end; u8 /* enum map_type */ type; bool referenced; + bool erange_warned; u32 priv; u64 pgoff; diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 05d766e3ecb5..1fcf1bbc5458 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -54,7 +54,7 @@ num_dec [0-9]+ num_hex 0x[a-fA-F0-9]+ num_raw_hex [a-fA-F0-9]+ name [a-zA-Z_*?][a-zA-Z0-9_*?]* -modifier_event [ukhp]{1,5} +modifier_event [ukhpGH]{1,8} modifier_bp [rwx] %% diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 9412e3b05f68..1efd3bee6336 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -826,8 +826,16 @@ static struct machine * { const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) - return perf_session__find_machine(session, event->ip.pid); + if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { + u32 pid; + + if (event->header.type == PERF_RECORD_MMAP) + pid = event->mmap.pid; + else + pid = event->ip.pid; + + return perf_session__find_machine(session, pid); + } return perf_session__find_host_machine(session); } @@ -868,11 +876,11 @@ static int perf_session_deliver_event(struct perf_session *session, dump_sample(session, event, sample); if (evsel == NULL) { ++session->hists.stats.nr_unknown_id; - return -1; + return 0; } if (machine == NULL) { ++session->hists.stats.nr_unprocessable_samples; - return -1; + return 0; } return tool->sample(tool, event, sample, evsel, machine); case PERF_RECORD_MMAP: diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index c0a028c3ebaf..ab9867b2b433 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -977,8 +977,9 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, * And always look at the original dso, not at debuginfo packages, that * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). */ -static int dso__synthesize_plt_symbols(struct dso *dso, struct map *map, - symbol_filter_t filter) +static int +dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map, + symbol_filter_t filter) { uint32_t nr_rel_entries, idx; GElf_Sym sym; @@ -993,10 +994,7 @@ static int dso__synthesize_plt_symbols(struct dso *dso, struct map *map, char sympltname[1024]; Elf *elf; int nr = 0, symidx, fd, err = 0; - char name[PATH_MAX]; - snprintf(name, sizeof(name), "%s%s", - symbol_conf.symfs, dso->long_name); fd = open(name, O_RDONLY); if (fd < 0) goto out; @@ -1703,8 +1701,9 @@ restart: continue; if (ret > 0) { - int nr_plt = dso__synthesize_plt_symbols(dso, map, - filter); + int nr_plt; + + nr_plt = dso__synthesize_plt_symbols(dso, name, map, filter); if (nr_plt > 0) ret += nr_plt; break; diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index d7a1c4afe28b..2f83e5dc9967 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -125,6 +125,9 @@ static int callchain__count_rows(struct rb_root *chain) static bool map_symbol__toggle_fold(struct map_symbol *self) { + if (!self) + return false; + if (!self->has_children) return false; diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 95d6a6f7c33a..4915408f6a98 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -183,6 +183,9 @@ my %force_config; # do not force reboots on config problems my $no_reboot = 1; +# reboot on success +my $reboot_success = 0; + my %option_map = ( "MACHINE" => \$machine, "SSH_USER" => \$ssh_user, @@ -2192,7 +2195,7 @@ sub run_bisect { } # Are we looking for where it worked, not failed? - if ($reverse_bisect) { + if ($reverse_bisect && $ret >= 0) { $ret = !$ret; } @@ -3469,6 +3472,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { # Do not reboot on failing test options $no_reboot = 1; + $reboot_success = 0; $iteration = $i; @@ -3554,9 +3558,11 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { die "failed to checkout $checkout"; } + $no_reboot = 0; + # A test may opt to not reboot the box if ($reboot_on_success) { - $no_reboot = 0; + $reboot_success = 1; } if ($test_type eq "bisect") { @@ -3600,7 +3606,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { if ($opt{"POWEROFF_ON_SUCCESS"}) { halt; -} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) { +} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot && $reboot_success) { reboot_to_good; } elsif (defined($switch_to_good)) { # still need to get to the good kernel diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c index a457d2138f49..e9fff9830bf0 100644 --- a/virt/kvm/iommu.c +++ b/virt/kvm/iommu.c @@ -240,9 +240,13 @@ int kvm_iommu_map_guest(struct kvm *kvm) return -ENODEV; } + mutex_lock(&kvm->slots_lock); + kvm->arch.iommu_domain = iommu_domain_alloc(&pci_bus_type); - if (!kvm->arch.iommu_domain) - return -ENOMEM; + if (!kvm->arch.iommu_domain) { + r = -ENOMEM; + goto out_unlock; + } if (!allow_unsafe_assigned_interrupts && !iommu_domain_has_cap(kvm->arch.iommu_domain, @@ -253,17 +257,16 @@ int kvm_iommu_map_guest(struct kvm *kvm) " module option.\n", __func__); iommu_domain_free(kvm->arch.iommu_domain); kvm->arch.iommu_domain = NULL; - return -EPERM; + r = -EPERM; + goto out_unlock; } r = kvm_iommu_map_memslots(kvm); if (r) - goto out_unmap; - - return 0; + kvm_iommu_unmap_memslots(kvm); -out_unmap: - kvm_iommu_unmap_memslots(kvm); +out_unlock: + mutex_unlock(&kvm->slots_lock); return r; } @@ -310,6 +313,11 @@ static void kvm_iommu_put_pages(struct kvm *kvm, } } +void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot) +{ + kvm_iommu_put_pages(kvm, slot->base_gfn, slot->npages); +} + static int kvm_iommu_unmap_memslots(struct kvm *kvm) { int idx; @@ -320,7 +328,7 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm) slots = kvm_memslots(kvm); kvm_for_each_memslot(memslot, slots) - kvm_iommu_put_pages(kvm, memslot->base_gfn, memslot->npages); + kvm_iommu_unmap_pages(kvm, memslot); srcu_read_unlock(&kvm->srcu, idx); @@ -335,7 +343,11 @@ int kvm_iommu_unmap_guest(struct kvm *kvm) if (!domain) return 0; + mutex_lock(&kvm->slots_lock); kvm_iommu_unmap_memslots(kvm); + kvm->arch.iommu_domain = NULL; + mutex_unlock(&kvm->slots_lock); + iommu_domain_free(domain); return 0; } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 42b73930a6de..9739b533ca2e 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -808,12 +808,13 @@ int __kvm_set_memory_region(struct kvm *kvm, if (r) goto out_free; - /* map the pages in iommu page table */ + /* map/unmap the pages in iommu page table */ if (npages) { r = kvm_iommu_map_pages(kvm, &new); if (r) goto out_free; - } + } else + kvm_iommu_unmap_pages(kvm, &old); r = -ENOMEM; slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots), |