Merge "msm: kgsl: Enhance GFT to avoid hang->recover->hang cycle"
diff --git a/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt b/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
index 795af3b..2fbe4ca 100644
--- a/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
+++ b/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
@@ -12,6 +12,7 @@
The required properties for PM-8x60 are:
- compatible: "qcom,pm-8x60"
+- qcom,lpm-levels: phandle for associated lpm_levels device.
The optional properties are:
@@ -39,4 +40,5 @@
reg = <0xfe800664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
+ qcom,lpm-levels = <&lpm_levels>;
};
diff --git a/Documentation/devicetree/bindings/sound/voice-svc.txt b/Documentation/devicetree/bindings/sound/voice-svc.txt
new file mode 100644
index 0000000..deca7f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/voice-svc.txt
@@ -0,0 +1,11 @@
+* Voice Service binding
+
+Required properties:
+- compatible : "qcom,msm-voice-svc"
+
+Example:
+
+ qcom,msm-voice-svc {
+ compatible = "qcom,msm-voice-svc";
+ };
+
diff --git a/Documentation/devicetree/bindings/usb/ice40-hcd.txt b/Documentation/devicetree/bindings/usb/ice40-hcd.txt
new file mode 100644
index 0000000..43d24dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/ice40-hcd.txt
@@ -0,0 +1,45 @@
+ICE40 FPGA based SPI-USB bridge
+
+Documentation/devicetree/bindings/spi/spi-bus.txt provides the details
+of the required and optional properties of a SPI slave device node.
+
+The purpose of this document is to provide the additional properties
+that are required to use the ICE40 FPGA based SPI slave device as a
+USB host controller.
+
+Required properties:
+- compatible : should be "lattice,ice40-spi-usb"
+- <supply-name>-supply: handle to the regulator device tree node
+ Required "supply-name" is "core-vcc" and "spi-vcc"
+- reset-gpio: gpio used to assert the bridge chip reset
+- slave-select-gpio: gpio used to select the slave during configuration
+ loading
+- config-done-gpio: gpio used to indicate the configuration status
+- vcc-en-gpio: gpio used to enable the chip power supply
+
+Optional properties:
+- interrupts: IRQ lines used by this controller
+- clk-en-gpio: gpio used to enable the 19.2 MHZ clock to the bridge
+ chip. If it is not present, assume that the clock is available on
+ the bridge chip board.
+- <supply-name>-supply: handle to the regulator device tree node
+ Optional "supply-name" is "gpio" used to power up the gpio bank
+ used by this device
+
+ spi@f9923000 {
+ lattice,spi-usb@3 {
+ compatible = "lattice,ice40-spi-usb";
+ reg = <3>;
+ spi-max-frequency = <50000000>;
+ spi-cpol = <1>;
+ spi-cpha = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <121 0x8>;
+ core-vcc-supply = <&pm8226_l2>;
+ spi-vcc-supply = <&pm8226_l5>;
+ lattice,reset-gpio = <&msmgpio 114 0>;
+ lattice,slave-select-gpio = <&msmgpio 118 0>;
+ lattice,config-done-gpio = <&msmgpio 115 0>;
+ lattice,vcc-en-gpio = <&msmgpio 117 0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index b94b587..440dac1 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -29,6 +29,7 @@
idt Integrated Device Technologies, Inc.
intercontrol Inter Control Group
invn InvenSense Inc.
+lattice Lattice Semiconductor.
linux Linux-specific binding
kionix Kionix Inc.
marvell Marvell Technology Group Ltd.
diff --git a/Documentation/usb/ice40-hcd.txt b/Documentation/usb/ice40-hcd.txt
new file mode 100644
index 0000000..54f845e
--- /dev/null
+++ b/Documentation/usb/ice40-hcd.txt
@@ -0,0 +1,247 @@
+Introduction
+============
+
+USB UICC connectivity is required for MSM8x12. This SoC has only 1 USB
+controller which is used for peripheral mode and charging. Hence an external
+USB host controller over SPI is used to connect a USB UICC card. ICE40 FPGA
+based SPI to IC-USB (Inter-Chip USB) bridge chip is used.
+
+The ICE40 Host controller driver (ice40-hcd) is registered as a SPI protocol
+driver and interacts with the SPI subsystem on one side and interacts with the
+USB core on the other side.
+
+Hardware description
+====================
+
+The ICE40 devices are SRAM-based FPGAs. The SRAM memory cells are volatile,
+meaning that once power is removed from the device, its configuration is lost
+and must be reloaded on the next power-up. An on-chip non-volatile configuration
+memory or an external SPI flash are not used to store the configuration data due
+to increased power consumption. Instead, the software loads the configuration
+data through SPI interface after powering up the bridge chip. Once the
+configuration data is programmed successfully, the bridge chip will be ready for
+the USB host controller operations.
+
+The ICE40 device has an interrupt signal apart from the standard SPI signals
+CSn, SCLK, MOSI and MISO. It has support for 25 to 50 MHz frequencies. The
+maximum operating frequency during configuration loading is 25 MHz.
+
+The bridge chip requires two power supplies, SPI_VCC (1.8v - 3.3v) and VCC_CORE
+(1.2v). The SPI_VCC manages the SPI slave portion and VCC_CORE manages the USB
+serial engine (SIE) portion. It requires a 19.2 MHz reference clock and a
+32 MHz clock is required for remote wakeup detection during suspend.
+
+The configuration loading sequence:
+
+- Assert the RSTn pin. This keeps bridge chip in reset state after downloading
+the configuration data.
+- The bridge chip samples the SPI interface chip select pin during power-up and
+enters SPI slave mode if it is low. Drive the chip select pin low before
+powering up the bridge chip.
+- Power-up the bridge chip by enabling SPI_VCC and VCC_CORE
+- De-assert the chip select pin after 50 usec.
+- Transfer the configuration data over SPI. Note that the bridge chip requires
+49 dummy clock cycles after sending the data.
+- The bridge chip indicates the status of the configuration loading via config
+done pin. It may take 50 usec to assert this pin.
+
+The 19.2 MHz clock should be supplied before de-asserting the RSTn pin. A PLL
+is used to generate a 48MHz clock signal that then creates a 12MHz clock signal
+by a divider. When the PLLOK bit is set in USB Transfer Result register, it
+indicates that the PLL output is locked to the input reference clock. When it
+is 0, it indicates that the PLL is out of lock. It is recommended to assert the
+RSTn pin to re-synchronize the PLL to the reference clock when the PLL loses
+lock. The chip will be ready for the USB host controller operations after it is
+brought out of reset and PLL is synchronized to the reference clock.
+
+The software is responsible for initiating all the USB host transfers by writing
+the associated registers. The SIE in the bridge chip performs the USB host
+operations via the IC-USB bus based on the registers set by the software. The
+USB transfer results as well as the bus status like the peripheral connection,
+disconnection, resume, etc. are notified to software through the interrupt and
+the internal registers.
+
+The bridge chip provides the DP & DM pull-down resistor control to the software.
+The pull-down resistors are enabled automatically after the power up to force
+the SE0 condition on the bus. The software is required to disable these
+resistors before driving the reset on the bus. Control, Bulk and Interrupt
+transfers are supported. The data toggling states are not maintained in the
+hardware and should be serviced by the software. The bridge chip returns
+one of the following values for a USB transaction (SETUP/IN/OUT) via Transfer
+result register.
+
+xSUCCESS: Successful transfer.
+xBUSY: The SIE is busy with a USB transfer.
+xPKTERR: Packet Error (stuff, EOP).
+xPIDERR: PID check bits are incorrect.
+xNAK: Device returned NAK. This is not an error condition for IN/OUT. But it
+is an error condition for SETUP.
+xSTALL: Device returned STALL.
+xWRONGPID: Wrong PID is received. For example a IN transaction is attempted on
+OUT endpoint.
+xCRCERR: CRC error.
+xTOGERR: Toggle bit error. The SIE returns ACK when the toggle mismatch happens
+for IN transaction and returns this error code. Software should discard the
+data as it was received already in the previous transaction.
+xBADLEN: Too big packet size received.
+xTIMEOUT: Device failed to respond in time.
+
+Software description
+====================
+
+This driver is compiled as a module and is loaded by the userspace after
+getting the UICC card insertion event from the modem processor. The module is
+unloaded upon the UICC card removal.
+
+This driver registers as a SPI protocol driver. The SPI controller driver
+manages the chip select pin. This pin needs to be driven low before powering
+up the bridge chip. Hence this pin settings are overridden temporarily during
+the bridge chip power-up sequence. The original settings are restored before
+sending the configuration data to the bridge chip which acts as a SPI slave.
+Both pinctl and gpiomux framework allow this type of use case.
+
+The configuration data file is stored on the eMMC card. Firmware class API
+request_firmware() is used to read the configuration data file. The
+configuration data is then sent to the bridge chip via SPI interface. The
+bridge chip asserts the config done pin once the configuration is completed.
+
+The driver registers as a Full Speed (USB 1.1) HCD. The following methods
+are implemented that are part of hc_drive struct:
+
+reset: It is called one time by the core during HCD registration. The
+default address 0 is programmed and the line state is sampled to check if any
+device is connected. If any device is connected, the port flags are updated
+accordingly. As the module is loaded after the UICC card is inserted, the
+device would be present at this time.
+
+start: This method is called one time by the core during HCD registration.
+The bridge chip is programmed to transmit the SOFs.
+
+stop: The method is called one time by the core during HCD deregistration.
+The bridge chip is programmed to stop transmitting the SOFs.
+
+hub_control: This method is called by the core to manage the Root HUB. The
+hardware does not maintain port state. The software maintain the port
+state and provide the information to the core when required. The following
+HUB class requests are supported.
+
+- GetHubDescriptor: The HUB descriptor is sent to the core. Only 1 port
+is present. Over current protection and port power control are not supported.
+- SetPortFeature: The device reset and suspend are supported. The The DP & DM
+pull-down resistors are disabled before driving the reset as per the IC-USB
+spec. The reset signaling is stopped when the core queries the port status.
+- GetPortStatus: The device connection status is sent to the core. If a reset
+is in progress, it is stopped before returning the port status.
+- ClearPortFeature: The device resume (clear suspend) is supported.
+
+urb_enqueue: This method is called by the core to initiate a USB Control/Bulk
+transfer. If the endpoint private context is not present, it will be created to
+hold the endpoint number, host endpoint structure, transaction error count, halt
+state and unlink state. The URB is attached to the endpoint URB list. If the
+endpoint is not active, it is attached to the asynchronous schedule list and the
+work is scheduled to traverse this list. The traversal algorithm is explained
+later in this document.
+
+urb_dequeue: This method is called by the core when an URB is unlinked. If the
+endpoint is not active, the URB is unlinked immediately. Otherwise the endpoint
+is marked for unlink and URB is unlinked from the asynchronous schedule work.
+
+bus_suspend: This method is called by the core during root hub suspend. The SOFs
+are already stopped during the port suspend which happens before root hub
+suspend. Assert the RSTn pin to put the bridge chip in reset state and stop XO
+(19.2 MHz) clock.
+
+bus_resume: This method is called by the core during root hub resume. Turn on
+the XO clock and de-assert the RSTn signal to bring the chip out of reset.
+
+endpoint_disable: This method is called by the core during the device
+disconnect. All the URB are unlinked by this time, so free the endpoint private
+structure.
+
+Asynchronous scheduling:
+
+All the active endpoints are queued to the asynchronous schedule list. A worker
+thread iterates over this circular list and process the URBs. Processing an URB
+involves initiating multiple SETUP/IN/OUT transactions and checking the result.
+After receiving the DATA/ACK, the toggle bit is inverted.
+
+A URB is finished when any of the following events occur:
+
+- The entire data is received for an OUT endpoint or a short packet is received
+for an IN endpoint.
+- The endpoint is stalled by the device. -EPIPE is returned.
+- Transaction error is occurred consecutively 3 times. -EPROTO is returned.
+- A NAK received for a SETUP transaction.
+- The URB is unlinked.
+
+The next transaction is issued on the next endpoint (if available) irrespective
+of the result of the current transaction. But the IN/OUT transaction of data
+or status phase is attempted immediately after the SETUP transaction for a
+control endpoint. If a NAK is received for this transaction, the control
+transfer is resumed next time when the control endpoint is encountered in the
+asynchronous schedule list. This is to give the control transfers priority
+over the bulk transfers.
+
+The endpoint is marked as halted when a URB is finished due to transaction
+errors or stall condition. The halted endpoint is removed from the asynchronous
+schedule list. It will be added again next time when a URB is enqueued on this
+endpoint.
+
+This driver provides debugfs interface and exports a file called "command" under
+<debugfs root>/ice40 directory. The following strings can be echoed to this
+file.
+
+"poll": If the device is connected after the module is loaded, it will not be
+detected automatically. The bus is sampled when this string is echoed. If a
+device is connected, port flags are updated and core is notified about the
+device connect event.
+
+"rwtest": Function Address register is written and read back to validate the
+contents. This should NOT be used while the usb device is connected. This is
+strictly for debugging purpose.
+
+"dump": Dumps all the register values to the kernel log buffer.
+
+Design Goals:
+=============
+
+- Handle errors gracefully. Implement retry mechanism for transaction errors,
+memory failures. Mark HCD as dead for serious errors like SPI transaction
+errors to avoid further interactions with the attached USB device.
+- Keep the asynchronous schedule algorithm simple and efficient. Take advantage
+of the static configuration of the USB device. UICC cards has only CCID and Mass
+storage interfaces. These interface protocol allows only 1 active transfer on
+either in or out endpoint.
+- Add trace points to capture USB transactions.
+
+Driver parameters
+=================
+
+The driver is compiled as a module and it accepts the configuration data file
+name as a module param called "firmware". The default configuration file name
+is "ice40.bin".
+
+Config options
+==============
+
+Set CONFIG_USB_SL811_HCD to m to compile this driver as a module. The driver
+should not be compiled statically, because the configuration data is not
+available during kernel boot.
+
+To do
+=====
+
+- The bridge chip has 2 IN FIFO and 2 OUT FIFO. Implement double buffering.
+- The bridge chip has an interrupt to indicate the transaction (IN/OUT)
+completion. The current implementation uses polling for simplicity and to avoid
+interrupt latencies. Evaluate interrupt approach.
+- The bridge chip can be completely power collapsed during suspend to avoid
+leakage currents. As the bridge chip does not have any non-volatile memory,
+the configuration data needs to be loaded during resume. This method has higher
+power savings with higher resume latencies. Evaluate this approach.
+- Implement Interrupt transfers if required.
+- The request_firmware() API copies the configuration data file to the kernel
+virtual memory. This memory can't be used for DMA. The current implementation
+copies this data into contiguous physical memory which is allocated via
+kmalloc. If this memory allocation fails, try to allocate multiple pages
+and submit the SPI message with multiple transfers.
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index 5e890d3..78e1a63 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -78,7 +78,7 @@
vdd-apc-supply = <&pm8226_s2>;
vdd-mx-supply = <&pm8226_l3_ao>;
- qcom,vdd-mx-vmax = <1350000>;
+ qcom,vdd-mx-vmax = <1337500>;
qcom,vdd-mx-vmin-method = <1>;
qcom,cpr-ref-clk = <19200>;
@@ -109,7 +109,7 @@
qcom,cpr-fuse-uplift-sel = <22 53 1 0 0>;
qcom,cpr-uplift-voltage = <50000>;
qcom,cpr-uplift-quotient = <0 0 120>;
- qcom,cpr-uplift-max-volt = <1350000>;
+ qcom,cpr-uplift-max-volt = <1330000>;
qcom,cpr-uplift-speed-bin = <1>;
qcom,speed-bin-fuse-sel = <22 0 3 0>;
};
diff --git a/arch/arm/boot/dts/msm8226-v1-pm.dtsi b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
index d59fab3..10aff70 100644
--- a/arch/arm/boot/dts/msm8226-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 The Linux Foundation. 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
@@ -105,7 +105,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -300,6 +300,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8226-v2-pm.dtsi b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
index bc8fe5d..7af2c7f 100644
--- a/arch/arm/boot/dts/msm8226-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 The Linux Foundation. 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
@@ -107,7 +107,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -312,6 +312,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index cf8e3b3..4117d9d 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -499,6 +499,10 @@
compatible = "qti,msm-pcm-loopback";
};
+ qcom,msm-voice-svc {
+ compatible = "qcom,msm-voice-svc";
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
diff --git a/arch/arm/boot/dts/msm8610-v1-pm.dtsi b/arch/arm/boot/dts/msm8610-v1-pm.dtsi
index dc1dc8b..adc66d7 100644
--- a/arch/arm/boot/dts/msm8610-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 The Linux Foundation. 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
@@ -105,7 +105,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -296,6 +296,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8610-v2-pm.dtsi b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
index 2859744..b69b061 100644
--- a/arch/arm/boot/dts/msm8610-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 The Linux Foundation. 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
@@ -107,7 +107,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -308,6 +308,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 90e8fd6..43cd7c6 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -606,6 +606,7 @@
qcom,gpio-miso = <&msmgpio 87 0>;
qcom,gpio-clk = <&msmgpio 89 0>;
qcom,gpio-cs0 = <&msmgpio 88 0>;
+ qcom,gpio-cs2 = <&msmgpio 85 0>;
qcom,infinite-mode = <0>;
qcom,use-bam;
@@ -613,6 +614,21 @@
qcom,bam-consumer-pipe-index = <18>;
qcom,bam-producer-pipe-index = <19>;
qcom,master-id = <86>;
+
+ lattice,spi-usb@2 {
+ compatible = "lattice,ice40-spi-usb";
+ reg = <2>;
+ spi-max-frequency = <50000000>;
+ spi-cpol = <1>;
+ spi-cpha = <1>;
+ core-vcc-supply = <&pm8110_l2>;
+ spi-vcc-supply = <&pm8110_l6>;
+ gpio-supply = <&pm8110_l22>;
+ lattice,reset-gpio = <&msmgpio 95 0>;
+ lattice,slave-select-gpio = <&msmgpio 85 0>;
+ lattice,config-done-gpio = <&msmgpio 94 0>;
+ lattice,vcc-en-gpio = <&msmgpio 96 0>;
+ };
};
qcom,pronto@fb21b000 {
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
index 886177d..516d068 100644
--- a/arch/arm/boot/dts/msm8974-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. 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
@@ -130,7 +130,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_retention";
#address-cells = <1>;
@@ -311,6 +311,7 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008 {
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index 84a8c2d..cde5e5a9 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. 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
@@ -126,7 +126,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_retention";
#address-cells = <1>;
@@ -324,6 +324,7 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
qcom,pm-snoc-client {
compatible = "qcom,pm-snoc-client";
diff --git a/arch/arm/boot/dts/msm8974pro-pm.dtsi b/arch/arm/boot/dts/msm8974pro-pm.dtsi
index f735b65..0307e2a 100644
--- a/arch/arm/boot/dts/msm8974pro-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974pro-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. 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
@@ -126,7 +126,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,allow-synced-levels;
qcom,default-l2-state = "l2_cache_retention";
@@ -334,6 +334,7 @@
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
qcom,pm-snoc-client {
compatible = "qcom,pm-snoc-client";
diff --git a/arch/arm/boot/dts/msm8974pro.dtsi b/arch/arm/boot/dts/msm8974pro.dtsi
index a72ebb2..ae0547f 100644
--- a/arch/arm/boot/dts/msm8974pro.dtsi
+++ b/arch/arm/boot/dts/msm8974pro.dtsi
@@ -1578,13 +1578,13 @@
/* Off */
<26 512 0 0>, <89 604 0 0>,
/* Sub-SVS / SVS */
- <26 512 0 1600000>, <89 604 0 3200000>,
+ <26 512 1200000 2456000>, <89 604 0 3200000>,
/* SVS */
- <26 512 0 2456000>, <89 604 0 3200000>,
+ <26 512 1200000 2456000>, <89 604 0 3200000>,
/* low Nominal / SVS */
<26 512 0 3680000>, <89 604 0 3200000>,
/* SVS / low Nominal */
- <26 512 0 2456000>, <89 604 0 5280000>,
+ <26 512 1200000 2456000>, <89 604 0 5280000>,
/* low Nominal / low Nominal */
<26 512 0 3680000>, <89 604 0 5280000>,
/* Nominal / low Nominal */
@@ -1657,6 +1657,7 @@
};
&mdss_mdp {
+ qcom,max-bandwidth-low-kbps = <2750000>;
qcom,vbif-settings = <0x0004 0x00000001>;
qcom,mdss-wb-off = <0x00011100 0x00011500
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 1e6cdf2..ec62cd4 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. 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,7 +28,7 @@
3e 0f];
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,no-l2-saw;
#address-cells = <1>;
@@ -167,6 +167,7 @@
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_ext";
qcom,use-sync-timer;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,rpm-log@fc19dc00 {
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 1ba8527..ede654d 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -212,6 +212,13 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_QSEECOM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -335,6 +342,12 @@
CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MICROSOFT=y
CONFIG_HID_ELECOM=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_ICE40_HCD=m
+CONFIG_USB_CCID_BRIDGE=y
+CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 458faac..4f60013 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -213,6 +213,13 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_QSEECOM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -357,6 +364,12 @@
CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MICROSOFT=y
CONFIG_HID_ELECOM=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_ICE40_HCD=m
+CONFIG_USB_CCID_BRIDGE=y
+CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
diff --git a/arch/arm/include/asm/system_misc.h b/arch/arm/include/asm/system_misc.h
index 71f4827..0827df7 100644
--- a/arch/arm/include/asm/system_misc.h
+++ b/arch/arm/include/asm/system_misc.h
@@ -24,6 +24,7 @@
extern void disable_hlt(void);
extern void enable_hlt(void);
extern int get_hlt(void);
+extern char* (*arch_read_hardware_id)(void);
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 7298f9a..c110f0f 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -110,6 +110,9 @@
unsigned int cold_boot;
EXPORT_SYMBOL(cold_boot);
+char* (*arch_read_hardware_id)(void);
+EXPORT_SYMBOL(arch_read_hardware_id);
+
#ifdef MULTI_CPU
struct processor processor __read_mostly;
#endif
@@ -1108,7 +1111,10 @@
seq_puts(m, "\n");
- seq_printf(m, "Hardware\t: %s\n", machine_name);
+ if (!arch_read_hardware_id)
+ seq_printf(m, "Hardware\t: %s\n", machine_name);
+ else
+ seq_printf(m, "Hardware\t: %s\n", arch_read_hardware_id());
seq_printf(m, "Revision\t: %04x\n", system_rev);
seq_printf(m, "Serial\t\t: %08x%08x\n",
system_serial_high, system_serial_low);
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index fe7de31..08566bb 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -57,6 +57,42 @@
};
#endif
+static struct gpiomux_setting smsc_hub_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting smsc_hub_susp_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct msm_gpiomux_config smsc_hub_configs[] = {
+ {
+ .gpio = 114, /* reset_n */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+ {
+ .gpio = 8, /* clk_en */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+ {
+ .gpio = 9, /* int_n */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+};
+
#define KS8851_IRQ_GPIO 115
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
@@ -940,6 +976,9 @@
}
msm_gpiomux_install(msm_hsic_configs, ARRAY_SIZE(msm_hsic_configs));
#endif
+ if (machine_is_msm8926() && of_board_is_mtp())
+ msm_gpiomux_install(smsc_hub_configs,
+ ARRAY_SIZE(smsc_hub_configs));
}
static void wcnss_switch_to_gpio(void)
diff --git a/arch/arm/mach-msm/board-8610-gpiomux.c b/arch/arm/mach-msm/board-8610-gpiomux.c
index 4234f2a..c91deb2 100644
--- a/arch/arm/mach-msm/board-8610-gpiomux.c
+++ b/arch/arm/mach-msm/board-8610-gpiomux.c
@@ -730,6 +730,61 @@
},
};
+static struct gpiomux_setting ice40_spi_cs_act_config = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting ice40_spi_cs_susp_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting ice40_act_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting ice40_susp_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct msm_gpiomux_config ice40_spi_usb_configs[] __initdata = {
+ {
+ .gpio = 85,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ice40_spi_cs_act_config,
+ [GPIOMUX_SUSPENDED] = &ice40_spi_cs_susp_config,
+ },
+ },
+ {
+ .gpio = 94,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ice40_act_config,
+ [GPIOMUX_SUSPENDED] = &ice40_susp_config,
+ },
+ },
+ {
+ .gpio = 95,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ice40_act_config,
+ [GPIOMUX_SUSPENDED] = &ice40_susp_config,
+ },
+ },
+ {
+ .gpio = 96,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ice40_act_config,
+ [GPIOMUX_SUSPENDED] = &ice40_susp_config,
+ },
+ },
+};
+
void __init msm8610_init_gpiomux(void)
{
int rc;
@@ -770,6 +825,10 @@
if (of_board_is_cdp())
msm_gpiomux_install(msm_cdc_dmic_configs,
ARRAY_SIZE(msm_cdc_dmic_configs));
+
+ if (of_board_is_cdp())
+ msm_gpiomux_install(ice40_spi_usb_configs,
+ ARRAY_SIZE(ice40_spi_usb_configs));
}
static void wcnss_switch_to_gpio(void)
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index b42878d..1771090 100755
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -2197,6 +2197,7 @@
.en_mask = BIT(5),
.base = &virt_bases[GCC_BASE],
.c = {
+ .parent = &ce1_clk_src.c,
.dbg_name = "gcc_ce1_clk",
.ops = &clk_ops_vote,
CLK_INIT(gcc_ce1_clk.c),
@@ -2233,6 +2234,7 @@
.en_mask = BIT(2),
.base = &virt_bases[GCC_BASE],
.c = {
+ .parent = &ce2_clk_src.c,
.dbg_name = "gcc_ce2_clk",
.ops = &clk_ops_vote,
CLK_INIT(gcc_ce2_clk.c),
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index aeb32f8..24b5181 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -138,6 +138,11 @@
MSM_CPU_SAMARIUM,
};
+struct msm_soc_info {
+ enum msm_cpu generic_soc_type;
+ char *soc_id_string;
+};
+
enum pmic_model {
PMIC_MODEL_PM8058 = 13,
PMIC_MODEL_PM8028 = 14,
diff --git a/arch/arm/mach-msm/include/mach/subsystem_notif.h b/arch/arm/mach-msm/include/mach/subsystem_notif.h
index 5865eff..59e212f 100644
--- a/arch/arm/mach-msm/include/mach/subsystem_notif.h
+++ b/arch/arm/mach-msm/include/mach/subsystem_notif.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011, 2013-2014, The Linux Foundation. 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
@@ -25,6 +25,7 @@
SUBSYS_BEFORE_POWERUP,
SUBSYS_AFTER_POWERUP,
SUBSYS_RAMDUMP_NOTIFICATION,
+ SUBSYS_POWERUP_FAILURE,
SUBSYS_NOTIF_TYPE_COUNT
};
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 9857162..bd28131 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -82,6 +82,7 @@
static struct lpm_system_state sys_state;
static bool suspend_in_progress;
+static int64_t suspend_time;
struct lpm_lookup_table {
uint32_t modes;
@@ -545,11 +546,11 @@
if (!dev->cpu && msm_rpm_waiting_for_ack())
break;
- if ((next_wakeup_us >> 10) > pwr->latency_us) {
+ if ((next_wakeup_us >> 10) > pwr->time_overhead_us) {
power = pwr->ss_power;
} else {
power = pwr->ss_power;
- power -= (pwr->latency_us * pwr->ss_power)
+ power -= (pwr->time_overhead_us * pwr->ss_power)
/ next_wakeup_us;
power += pwr->energy_overhead / next_wakeup_us;
}
@@ -779,6 +780,11 @@
static int lpm_suspend_prepare(void)
{
+ struct timespec ts;
+
+ getnstimeofday(&ts);
+ suspend_time = timespec_to_ns(&ts);
+
suspend_in_progress = true;
msm_mpm_suspend_prepare();
return 0;
@@ -786,6 +792,12 @@
static void lpm_suspend_wake(void)
{
+ struct timespec ts;
+
+ getnstimeofday(&ts);
+ suspend_time = timespec_to_ns(&ts) - suspend_time;
+ msm_pm_add_stat(MSM_PM_STAT_SUSPEND, suspend_time);
+
msm_mpm_suspend_wake();
suspend_in_progress = false;
}
diff --git a/arch/arm/mach-msm/msm-pm.c b/arch/arm/mach-msm/msm-pm.c
index f9a9343..cb65a70 100644
--- a/arch/arm/mach-msm/msm-pm.c
+++ b/arch/arm/mach-msm/msm-pm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, The Linux Foundation. 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
@@ -25,10 +25,12 @@
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/cpu_pm.h>
+#include <linux/remote_spinlock.h>
#include <asm/uaccess.h>
#include <asm/suspend.h>
#include <asm/cacheflush.h>
#include <asm/outercache.h>
+#include <mach/remote_spinlock.h>
#include <mach/scm.h>
#include <mach/msm_bus.h>
#include <mach/jtag.h>
@@ -117,6 +119,12 @@
DEFINE_PER_CPU(struct clk *, cpu_clks);
static struct clk *l2_clk;
+static int cpu_count;
+static DEFINE_SPINLOCK(cpu_cnt_lock);
+#define SCM_HANDOFF_LOCK_ID "S:7"
+static bool need_scm_handoff_lock;
+static remote_spinlock_t scm_handoff_lock;
+
static void (*msm_pm_disable_l2_fn)(void);
static void (*msm_pm_enable_l2_fn)(void);
static void (*msm_pm_flush_l2_fn)(void);
@@ -478,8 +486,30 @@
static int msm_pm_collapse(unsigned long unused)
{
uint32_t cpu = smp_processor_id();
+ enum msm_pm_l2_scm_flag flag = MSM_SCM_L2_ON;
- if (msm_pm_get_l2_flush_flag() == MSM_SCM_L2_OFF) {
+ spin_lock(&cpu_cnt_lock);
+ cpu_count++;
+ if (cpu_count == num_online_cpus())
+ flag = msm_pm_get_l2_flush_flag();
+
+ pr_debug("cpu:%d cores_in_pc:%d L2 flag: %d\n",
+ cpu, cpu_count, flag);
+
+ /*
+ * The scm_handoff_lock will be release by the secure monitor.
+ * It is used to serialize power-collapses from this point on,
+ * so that both Linux and the secure context have a consistent
+ * view regarding the number of running cpus (cpu_count).
+ *
+ * It must be acquired before releasing cpu_cnt_lock.
+ */
+ if (need_scm_handoff_lock)
+ remote_spin_lock_rlock_id(&scm_handoff_lock,
+ REMOTE_SPINLOCK_TID_START + cpu);
+ spin_unlock(&cpu_cnt_lock);
+
+ if (flag == MSM_SCM_L2_OFF) {
flush_cache_all();
if (msm_pm_flush_l2_fn)
msm_pm_flush_l2_fn();
@@ -491,8 +521,7 @@
msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
- scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC,
- msm_pm_get_l2_flush_flag());
+ scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
@@ -534,6 +563,12 @@
collapsed = save_cpu_regs ?
!cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
+ if (save_cpu_regs) {
+ spin_lock(&cpu_cnt_lock);
+ cpu_count--;
+ BUG_ON(cpu_count > num_online_cpus());
+ spin_unlock(&cpu_cnt_lock);
+ }
msm_jtag_restore_state();
if (collapsed) {
@@ -764,17 +799,19 @@
pr_info("CPU%u: %s mode:%d\n",
smp_processor_id(), __func__, mode);
- time = sched_clock();
+ if (from_idle)
+ time = sched_clock();
+
if (execute[mode])
exit_stat = execute[mode](from_idle);
- time = sched_clock() - time;
- if (from_idle)
+
+ if (from_idle) {
+ time = sched_clock() - time;
msm_pm_ftrace_lpm_exit(smp_processor_id(), mode, collapsed);
- else
- exit_stat = MSM_PM_STAT_SUSPEND;
- if (exit_stat >= 0)
- msm_pm_add_stat(exit_stat, time);
- do_div(time, 1000);
+ if (exit_stat >= 0)
+ msm_pm_add_stat(exit_stat, time);
+ }
+
return collapsed;
}
@@ -1166,6 +1203,7 @@
struct resource *res = NULL;
int i;
struct msm_pm_init_data_type pdata_local;
+ struct device_node *lpm_node;
int ret = 0;
memset(&pdata_local, 0, sizeof(struct msm_pm_init_data_type));
@@ -1192,6 +1230,23 @@
msm_pc_debug_counters_phys = 0;
}
+ lpm_node = of_parse_phandle(pdev->dev.of_node, "qcom,lpm-levels", 0);
+ if (!lpm_node) {
+ pr_warn("Could not get qcom,lpm-levels handle\n");
+ return -EINVAL;
+ }
+ need_scm_handoff_lock = of_property_read_bool(lpm_node,
+ "qcom,allow-synced-levels");
+ if (need_scm_handoff_lock) {
+ ret = remote_spin_lock_init(&scm_handoff_lock,
+ SCM_HANDOFF_LOCK_ID);
+ if (ret) {
+ pr_err("%s: Failed initializing scm_handoff_lock (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
if (pdev->dev.of_node) {
enum msm_pm_pc_mode_type pc_mode;
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index 6bd3efb..3d7638d 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -12,7 +12,7 @@
obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += lpa_if_hdmi.o
endif
obj-$(CONFIG_MSM_QDSP6_APR) += apr.o apr_v1.o apr_tal.o q6core.o dsp_debug.o
-obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o dsp_debug.o
+obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o dsp_debug.o voice_svc.o
ifdef CONFIG_ARCH_MSM9615
obj-y += audio_acdb.o
obj-y += rtac.o
diff --git a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
index 399e073..df7760a 100644
--- a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
+++ b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
@@ -344,7 +344,7 @@
pr_err("%s: ion import dma buffer failed\n",
__func__);
rc = -EINVAL;
- goto err_destroy_client;
+ goto err;
}
if (ionflag != NULL) {
@@ -380,10 +380,6 @@
err_ion_handle:
ion_free(client, *handle);
-err_destroy_client:
- msm_audio_ion_client_destroy(client);
- client = NULL;
- *handle = NULL;
err:
return rc;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/voice_svc.c b/arch/arm/mach-msm/qdsp6v2/voice_svc.c
new file mode 100644
index 0000000..92b3003
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/voice_svc.c
@@ -0,0 +1,593 @@
+/* Copyright (c) 2014, The Linux Foundation. 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
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <sound/voice_svc.h>
+#include <mach/qdsp6v2/apr_tal.h>
+#include <mach/qdsp6v2/apr.h>
+
+#define DRIVER_NAME "voice_svc"
+#define MINOR_NUMBER 1
+#define APR_MAX_RESPONSE 10
+
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+
+struct voice_svc_device {
+ struct cdev *cdev;
+ struct device *dev;
+ int major;
+};
+
+struct voice_svc_prvt {
+ void* apr_q6_mvm;
+ void* apr_q6_cvs;
+ uint16_t response_count;
+ struct list_head response_queue;
+ wait_queue_head_t response_wait;
+ spinlock_t response_lock;
+};
+
+struct apr_data {
+ struct apr_hdr hdr;
+ __u8 payload[0];
+} __packed;
+
+struct apr_response_list {
+ struct list_head list;
+ struct voice_svc_cmd_response resp;
+};
+
+static struct voice_svc_device *voice_svc_dev;
+static struct class *voice_svc_class;
+dev_t device_num;
+
+static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv)
+{
+ struct voice_svc_prvt *prtd;
+ struct apr_response_list *response_list;
+ unsigned long spin_flags;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ prtd = (struct voice_svc_prvt*)priv;
+
+ pr_debug("%s: data->opcode %x\n", __func__,
+ data->opcode);
+
+ if (data->opcode == RESET_EVENTS) {
+ if (data->reset_proc == APR_DEST_QDSP6) {
+ pr_debug("%s: Received reset event\n", __func__);
+
+ if (prtd->apr_q6_mvm != NULL) {
+ apr_reset(prtd->apr_q6_mvm);
+ prtd->apr_q6_mvm = NULL;
+ }
+
+ if (prtd->apr_q6_cvs != NULL) {
+ apr_reset(prtd->apr_q6_cvs);
+ prtd->apr_q6_cvs = NULL;
+ }
+ } else if (data->reset_proc ==APR_DEST_MODEM) {
+ pr_debug("%s: Received Modem reset event\n", __func__);
+ }
+ }
+
+ spin_lock_irqsave(&prtd->response_lock, spin_flags);
+
+ if (prtd->response_count < APR_MAX_RESPONSE) {
+ response_list = (struct apr_response_list *)kmalloc(
+ sizeof(struct apr_response_list) + data->payload_size,
+ GFP_ATOMIC);
+ if (response_list == NULL) {
+ pr_err("%s: kmalloc failed\n", __func__);
+
+ return -ENOMEM;
+ }
+
+ response_list->resp.src_port = data->src_port;
+ response_list->resp.dest_port = ((data->dest_port) >> 8);
+ response_list->resp.token = data->token;
+ response_list->resp.opcode = data->opcode;
+ response_list->resp.payload_size = data->payload_size;
+ if (data->payload != NULL && data->payload_size > 0) {
+ memcpy(response_list->resp.payload, data->payload,
+ data->payload_size);
+ }
+
+ list_add_tail(&response_list->list, &prtd->response_queue);
+ prtd->response_count++;
+
+ wake_up(&prtd->response_wait);
+ } else {
+ pr_err("%s: Response dropped since the queue is full\n", __func__);
+ }
+
+ spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+
+ return 0;
+}
+
+static void voice_svc_update_hdr(struct voice_svc_cmd_request* apr_req_data,
+ struct apr_data *aprdata,
+ struct voice_svc_prvt *prtd)
+{
+
+ aprdata->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ aprdata->hdr.src_port = ((apr_req_data->src_port) << 8 | 0x0001);
+ aprdata->hdr.dest_port = apr_req_data->dest_port;
+ aprdata->hdr.token = apr_req_data->token;
+ aprdata->hdr.opcode = apr_req_data->opcode;
+ aprdata->hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ apr_req_data->payload_size);
+ memcpy(aprdata->payload, apr_req_data->payload,
+ apr_req_data->payload_size);
+}
+
+static int voice_svc_send_req(struct voice_svc_cmd_request *apr_request,
+ struct voice_svc_prvt *prtd)
+{
+ int ret = 0;
+ void *apr_handle = NULL;
+ struct apr_data *aprdata = NULL;
+ uint32_t user_payload_size = 0;
+
+ if (apr_request == NULL) {
+ pr_err("%s: apr_request is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ user_payload_size = apr_request->payload_size;
+
+ aprdata = kmalloc(sizeof(struct apr_data) + user_payload_size,
+ GFP_KERNEL);
+
+ if (aprdata == NULL) {
+ pr_err("%s: aprdata kmalloc failed.", __func__);
+
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ voice_svc_update_hdr(apr_request, aprdata, prtd);
+
+ if (!strncmp(apr_request->svc_name, VOICE_SVC_CVS_STR,
+ MAX(sizeof(apr_request->svc_name), sizeof(VOICE_SVC_CVS_STR)))) {
+ apr_handle = prtd->apr_q6_cvs;
+ } else if (!strncmp(apr_request->svc_name, VOICE_SVC_MVM_STR,
+ MAX(sizeof(apr_request->svc_name), sizeof(VOICE_SVC_MVM_STR)))) {
+ apr_handle = prtd->apr_q6_mvm;
+ } else {
+ pr_err("%s: Invalid service %s\n", __func__,
+ apr_request->svc_name);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = apr_send_pkt(apr_handle, (uint32_t *)aprdata);
+
+ if (ret < 0) {
+ pr_err("%s: Fail in sending SNDRV_VOICE_SVC_REQUEST\n",
+ __func__);
+ ret = -EINVAL;
+ } else {
+ pr_debug("%s: apr packet sent successfully %d\n",
+ __func__, ret);
+ ret = 0;
+ }
+
+done:
+ if (aprdata != NULL)
+ kfree(aprdata);
+
+ return ret;
+}
+static int voice_svc_reg(char *svc, uint32_t src_port,
+ struct voice_svc_prvt *prtd, void **handle)
+{
+ int ret = 0;
+
+ if (handle == NULL) {
+ pr_err("%s: handle is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*handle != NULL) {
+ pr_err("%s: svc handle not NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *handle = apr_register("ADSP",
+ svc, qdsp_apr_callback,
+ ((src_port) << 8 | 0x0001),
+ prtd);
+
+ if (*handle == NULL) {
+ pr_err("%s: Unable to register %s\n",
+ __func__, svc);
+
+ ret = -EFAULT;
+ goto done;
+ }
+ pr_debug("%s: register %s successful\n",
+ __func__, svc);
+done:
+ return ret;
+}
+
+static int voice_svc_dereg(char *svc, void **handle)
+{
+ int ret = 0;
+ if (handle == NULL) {
+ pr_err("%s: handle is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_deregister(*handle);
+ *handle = NULL;
+ pr_debug("%s: deregister %s successful\n",
+ __func__, svc);
+
+done:
+ return 0;
+}
+
+static int process_reg_cmd(struct voice_svc_register apr_reg_svc,
+ struct voice_svc_prvt *prtd)
+{
+ int ret = 0;
+ char *svc = NULL;
+ void **handle = NULL;
+
+ if (!strncmp(apr_reg_svc.svc_name, VOICE_SVC_MVM_STR,
+ MAX(sizeof(apr_reg_svc.svc_name), sizeof(VOICE_SVC_MVM_STR)))) {
+ svc = VOICE_SVC_MVM_STR;
+ handle = &prtd->apr_q6_mvm;
+ } else if (!strncmp(apr_reg_svc.svc_name, VOICE_SVC_CVS_STR,
+ MAX(sizeof(apr_reg_svc.svc_name), sizeof(VOICE_SVC_CVS_STR)))) {
+ svc = VOICE_SVC_CVS_STR;
+ handle = &prtd->apr_q6_cvs;
+ } else {
+ pr_err("%s: Invalid Service: %s\n", __func__,
+ apr_reg_svc.svc_name);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*handle == NULL &&
+ apr_reg_svc.reg_flag) {
+ ret = voice_svc_reg(svc, apr_reg_svc.src_port, prtd,
+ handle);
+ } else if (handle != NULL &&
+ !apr_reg_svc.reg_flag) {
+ ret = voice_svc_dereg(svc, handle);
+ }
+
+done:
+ return ret;
+}
+
+static long voice_svc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long u_arg)
+{
+ int ret = 0;
+ struct voice_svc_prvt *prtd;
+ struct voice_svc_register apr_reg_svc;
+ struct voice_svc_cmd_request *apr_request = NULL;
+ struct voice_svc_cmd_response *apr_response = NULL;
+ struct apr_response_list *resp;
+ void __user *arg = (void __user *)u_arg;
+ uint32_t user_payload_size = 0;
+ unsigned long spin_flags;
+
+ pr_debug("%s: cmd: %u\n", __func__, cmd);
+
+ prtd = (struct voice_svc_prvt*)file->private_data;
+
+ switch (cmd) {
+ case SNDRV_VOICE_SVC_REGISTER_SVC:
+ pr_debug("%s: size of struct: %d\n", __func__,
+ sizeof(apr_reg_svc));
+ if (copy_from_user(&apr_reg_svc, arg, sizeof(apr_reg_svc))) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = process_reg_cmd(apr_reg_svc, prtd);
+
+ break;
+ case SNDRV_VOICE_SVC_CMD_REQUEST:
+ if (!access_ok(VERIFY_READ, arg,
+ sizeof(struct voice_svc_cmd_request))) {
+ pr_err("%s: Unable to read user data", __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ user_payload_size =
+ ((struct voice_svc_cmd_request*)arg)->payload_size;
+
+ apr_request = kmalloc(sizeof(struct voice_svc_cmd_request) +
+ user_payload_size, GFP_KERNEL);
+
+ if (apr_request == NULL) {
+ pr_err("%s: apr_request kmalloc failed.", __func__);
+
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(apr_request, arg,
+ sizeof(struct voice_svc_cmd_request) +
+ user_payload_size)) {
+ pr_err("%s: copy from user failed, size %d\n", __func__,
+ sizeof(struct voice_svc_cmd_request) +
+ user_payload_size);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = voice_svc_send_req(apr_request, prtd);
+
+ break;
+
+ case SNDRV_VOICE_SVC_CMD_RESPONSE:
+ do {
+ if (!access_ok(VERIFY_READ, arg,
+ sizeof(struct voice_svc_cmd_response))) {
+ pr_err("%s: Unable to read user data",
+ __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ user_payload_size =
+ ((struct voice_svc_cmd_response*)arg)->payload_size;
+ pr_debug("%s: RESPONSE: user payload size %d",
+ __func__, user_payload_size);
+
+ spin_lock_irqsave(&prtd->response_lock, spin_flags);
+ if (!list_empty(&prtd->response_queue)) {
+ resp = list_first_entry(&prtd->response_queue,
+ struct apr_response_list, list);
+
+ if (user_payload_size <
+ resp->resp.payload_size) {
+ pr_err("%s: Invalid payload size %d,%d",
+ __func__, user_payload_size,
+ resp->resp.payload_size);
+ ret = -ENOMEM;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ if (!access_ok(VERIFY_WRITE, arg,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size)) {
+ ret = -EFAULT;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ if (copy_to_user(arg, &resp->resp,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size)) {
+ pr_err("%s: copy to user failed, size \
+ %d\n", __func__,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size);
+
+ ret = -EFAULT;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ prtd->response_count--;
+
+ list_del(&resp->list);
+ kfree(resp);
+ spin_unlock_irqrestore(&prtd->response_lock,
+ spin_flags);
+ goto done;
+ } else {
+ spin_unlock_irqrestore(&prtd->response_lock,
+ spin_flags);
+ wait_event_interruptible(prtd->response_wait,
+ !list_empty(&prtd->response_queue));
+ pr_debug("%s: Interupt recieved for response",
+ __func__);
+ }
+ } while(!apr_response);
+ break;
+ default:
+ pr_debug("%s: cmd: %u\n", __func__, cmd);
+ ret = -EINVAL;
+ }
+
+done:
+ if (apr_request != NULL)
+ kfree(apr_request);
+
+ return ret;
+}
+
+static int voice_svc_open(struct inode *inode, struct file *file)
+{
+ struct voice_svc_prvt *prtd = NULL;
+
+ prtd = kmalloc(sizeof(struct voice_svc_prvt), GFP_KERNEL);
+
+ if (prtd == NULL) {
+ pr_err("%s: kmalloc failed", __func__);
+
+ return -ENOMEM;
+ }
+
+ memset(prtd, 0, sizeof(struct voice_svc_prvt));
+ prtd->apr_q6_cvs = NULL;
+ prtd->apr_q6_mvm = NULL;
+ prtd->response_count = 0;
+
+ INIT_LIST_HEAD(&prtd->response_queue);
+ init_waitqueue_head(&prtd->response_wait);
+ spin_lock_init(&prtd->response_lock);
+
+ file->private_data = (void*)prtd;
+
+ return 0;
+}
+
+static int voice_svc_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static const struct file_operations voice_svc_fops = {
+ .owner = THIS_MODULE,
+ .open = voice_svc_open,
+ .unlocked_ioctl = voice_svc_ioctl,
+ .release = voice_svc_release,
+};
+
+
+static int voice_svc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ voice_svc_dev = devm_kzalloc(&pdev->dev, sizeof(struct voice_svc_device),
+ GFP_KERNEL);
+ if (!voice_svc_dev) {
+ pr_err("%s: kzalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = alloc_chrdev_region(&device_num, 0, MINOR_NUMBER, DRIVER_NAME);
+ if (ret) {
+ pr_err("%s: Failed to alloc chrdev\n", __func__);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ voice_svc_dev->major = MAJOR(device_num);
+ voice_svc_class = class_create(THIS_MODULE, DRIVER_NAME);
+ if (IS_ERR(voice_svc_class)) {
+ ret = PTR_ERR(voice_svc_class);
+ pr_err("%s: Failed to create class; err = %d\n", __func__,
+ ret);
+ goto class_err;
+ }
+
+ voice_svc_dev->dev = device_create(voice_svc_class, NULL, device_num,
+ NULL, DRIVER_NAME);
+ if (IS_ERR(voice_svc_dev->dev)) {
+ ret = PTR_ERR(voice_svc_dev->dev);
+ pr_err("%s: Failed to create device; err = %d\n", __func__,
+ ret);
+ goto dev_err;
+ }
+
+ voice_svc_dev->cdev = cdev_alloc();
+ cdev_init(voice_svc_dev->cdev, &voice_svc_fops);
+ ret = cdev_add(voice_svc_dev->cdev, device_num, MINOR_NUMBER);
+ if (ret) {
+ pr_err("%s: Failed to register chrdev; err = %d\n", __func__,
+ ret);
+ goto add_err;
+ }
+ pr_debug("%s: Device created\n", __func__);
+ goto done;
+
+add_err:
+ cdev_del(voice_svc_dev->cdev);
+ device_destroy(voice_svc_class, device_num);
+dev_err:
+ class_destroy(voice_svc_class);
+class_err:
+ unregister_chrdev_region(0, MINOR_NUMBER);
+done:
+ return ret;
+}
+
+static int voice_svc_remove(struct platform_device *pdev)
+{
+ cdev_del(voice_svc_dev->cdev);
+ kfree(voice_svc_dev->cdev);
+ device_destroy(voice_svc_class, device_num);
+ class_destroy(voice_svc_class);
+ unregister_chrdev_region(0, MINOR_NUMBER);
+ kfree(voice_svc_dev);
+
+ return 0;
+}
+
+static struct of_device_id voice_svc_of_match[] = {
+ {.compatible = "qcom,msm-voice-svc"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, voice_svc_of_match);
+
+static struct platform_driver voice_svc_driver = {
+ .probe = voice_svc_probe,
+ .remove = voice_svc_remove,
+ .driver = {
+ .name = "msm-voice-svc",
+ .owner = THIS_MODULE,
+ .of_match_table = voice_svc_of_match,
+ },
+};
+
+static int __init voice_svc_init(void)
+{
+ return platform_driver_register(&voice_svc_driver);
+}
+
+static void __exit voice_svc_exit(void)
+{
+ platform_driver_unregister(&voice_svc_driver);
+}
+
+module_init(voice_svc_init);
+module_exit(voice_svc_exit);
+
+MODULE_DESCRIPTION("Soc QDSP6v2 Audio APR driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index 428d5b0..9cb26e1 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -551,7 +551,7 @@
static void smd_tty_close(struct tty_struct *tty, struct file *f)
{
- struct smd_tty_info *info = tty->driver_data;
+ struct smd_tty_info *info = smd_tty + tty->index;
tty_port_close(&info->port, tty, f);
}
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index ab03712..8e7adba 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -21,10 +21,12 @@
#include <linux/sys_soc.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <linux/string.h>
#include <linux/sysdev.h>
#include <linux/types.h>
#include <asm/mach-types.h>
+#include <asm/system_misc.h>
#include <mach/socinfo.h>
#include <mach/msm_smem.h>
@@ -185,246 +187,247 @@
struct socinfo_v8 v8;
} *socinfo;
-static enum msm_cpu cpu_of_id[] = {
+static struct msm_soc_info cpu_of_id[] = {
/* 7x01 IDs */
- [1] = MSM_CPU_7X01,
- [16] = MSM_CPU_7X01,
- [17] = MSM_CPU_7X01,
- [18] = MSM_CPU_7X01,
- [19] = MSM_CPU_7X01,
- [23] = MSM_CPU_7X01,
- [25] = MSM_CPU_7X01,
- [26] = MSM_CPU_7X01,
- [32] = MSM_CPU_7X01,
- [33] = MSM_CPU_7X01,
- [34] = MSM_CPU_7X01,
- [35] = MSM_CPU_7X01,
+ [0] = {MSM_CPU_UNKNOWN, "Unknown CPU"},
+ [1] = {MSM_CPU_7X01, "MSM7X01"},
+ [16] = {MSM_CPU_7X01, "MSM7X01"},
+ [17] = {MSM_CPU_7X01, "MSM7X01"},
+ [18] = {MSM_CPU_7X01, "MSM7X01"},
+ [19] = {MSM_CPU_7X01, "MSM7X01"},
+ [23] = {MSM_CPU_7X01, "MSM7X01"},
+ [25] = {MSM_CPU_7X01, "MSM7X01"},
+ [26] = {MSM_CPU_7X01, "MSM7X01"},
+ [32] = {MSM_CPU_7X01, "MSM7X01"},
+ [33] = {MSM_CPU_7X01, "MSM7X01"},
+ [34] = {MSM_CPU_7X01, "MSM7X01"},
+ [35] = {MSM_CPU_7X01, "MSM7X01"},
/* 7x25 IDs */
- [20] = MSM_CPU_7X25,
- [21] = MSM_CPU_7X25, /* 7225 */
- [24] = MSM_CPU_7X25, /* 7525 */
- [27] = MSM_CPU_7X25, /* 7625 */
- [39] = MSM_CPU_7X25,
- [40] = MSM_CPU_7X25,
- [41] = MSM_CPU_7X25,
- [42] = MSM_CPU_7X25,
- [62] = MSM_CPU_7X25, /* 7625-1 */
- [63] = MSM_CPU_7X25, /* 7225-1 */
- [66] = MSM_CPU_7X25, /* 7225-2 */
+ [20] = {MSM_CPU_7X25, "MSM7X25"},
+ [21] = {MSM_CPU_7X25, "MSM7X25"},
+ [24] = {MSM_CPU_7X25, "MSM7X25"},
+ [27] = {MSM_CPU_7X25, "MSM7X25"},
+ [39] = {MSM_CPU_7X25, "MSM7X25"},
+ [40] = {MSM_CPU_7X25, "MSM7X25"},
+ [41] = {MSM_CPU_7X25, "MSM7X25"},
+ [42] = {MSM_CPU_7X25, "MSM7X25"},
+ [62] = {MSM_CPU_7X25, "MSM7X25"},
+ [63] = {MSM_CPU_7X25, "MSM7X25"},
+ [66] = {MSM_CPU_7X25, "MSM7X25"},
/* 7x27 IDs */
- [43] = MSM_CPU_7X27,
- [44] = MSM_CPU_7X27,
- [61] = MSM_CPU_7X27,
- [67] = MSM_CPU_7X27, /* 7227-1 */
- [68] = MSM_CPU_7X27, /* 7627-1 */
- [69] = MSM_CPU_7X27, /* 7627-2 */
+ [43] = {MSM_CPU_7X27, "MSM7X27"},
+ [44] = {MSM_CPU_7X27, "MSM7X27"},
+ [61] = {MSM_CPU_7X27, "MSM7X27"},
+ [67] = {MSM_CPU_7X27, "MSM7X27"},
+ [68] = {MSM_CPU_7X27, "MSM7X27"},
+ [69] = {MSM_CPU_7X27, "MSM7X27"},
/* 8x50 IDs */
- [30] = MSM_CPU_8X50,
- [36] = MSM_CPU_8X50,
- [37] = MSM_CPU_8X50,
- [38] = MSM_CPU_8X50,
+ [30] = {MSM_CPU_8X50, "MSM8X50"},
+ [36] = {MSM_CPU_8X50, "MSM8X50"},
+ [37] = {MSM_CPU_8X50, "MSM8X50"},
+ [38] = {MSM_CPU_8X50, "MSM8X50"},
/* 7x30 IDs */
- [59] = MSM_CPU_7X30,
- [60] = MSM_CPU_7X30,
+ [59] = {MSM_CPU_7X30, "MSM7X30"},
+ [60] = {MSM_CPU_7X30, "MSM7X30"},
/* 8x55 IDs */
- [74] = MSM_CPU_8X55,
- [75] = MSM_CPU_8X55,
- [85] = MSM_CPU_8X55,
+ [74] = {MSM_CPU_8X55, "MSM8X55"},
+ [75] = {MSM_CPU_8X55, "MSM8X55"},
+ [85] = {MSM_CPU_8X55, "MSM8X55"},
/* 8x60 IDs */
- [70] = MSM_CPU_8X60,
- [71] = MSM_CPU_8X60,
- [86] = MSM_CPU_8X60,
+ [70] = {MSM_CPU_8X60, "MSM8X60"},
+ [71] = {MSM_CPU_8X60, "MSM8X60"},
+ [86] = {MSM_CPU_8X60, "MSM8X60"},
/* 8960 IDs */
- [87] = MSM_CPU_8960,
+ [87] = {MSM_CPU_8960, "MSM8960"},
/* 7x25A IDs */
- [88] = MSM_CPU_7X25A,
- [89] = MSM_CPU_7X25A,
- [96] = MSM_CPU_7X25A,
+ [88] = {MSM_CPU_7X25A, "MSM7X25A"},
+ [89] = {MSM_CPU_7X25A, "MSM7X25A"},
+ [96] = {MSM_CPU_7X25A, "MSM7X25A"},
/* 7x27A IDs */
- [90] = MSM_CPU_7X27A,
- [91] = MSM_CPU_7X27A,
- [92] = MSM_CPU_7X27A,
- [97] = MSM_CPU_7X27A,
+ [90] = {MSM_CPU_7X27A, "MSM7X27A"},
+ [91] = {MSM_CPU_7X27A, "MSM7X27A"},
+ [92] = {MSM_CPU_7X27A, "MSM7X27A"},
+ [97] = {MSM_CPU_7X27A, "MSM7X27A"},
/* FSM9xxx ID */
- [94] = FSM_CPU_9XXX,
- [95] = FSM_CPU_9XXX,
+ [94] = {FSM_CPU_9XXX, "FSM9XXX"},
+ [95] = {FSM_CPU_9XXX, "FSM9XXX"},
/* 7x25AA ID */
- [98] = MSM_CPU_7X25AA,
- [99] = MSM_CPU_7X25AA,
- [100] = MSM_CPU_7X25AA,
+ [98] = {MSM_CPU_7X25AA, "MSM7X25AA"},
+ [99] = {MSM_CPU_7X25AA, "MSM7X25AA"},
+ [100] = {MSM_CPU_7X25AA, "MSM7X25AA"},
/* 7x27AA ID */
- [101] = MSM_CPU_7X27AA,
- [102] = MSM_CPU_7X27AA,
- [103] = MSM_CPU_7X27AA,
- [136] = MSM_CPU_7X27AA,
+ [101] = {MSM_CPU_7X27AA, "MSM7X27AA"},
+ [102] = {MSM_CPU_7X27AA, "MSM7X27AA"},
+ [103] = {MSM_CPU_7X27AA, "MSM7X27AA"},
+ [136] = {MSM_CPU_7X27AA, "MSM7X27AA"},
/* 9x15 ID */
- [104] = MSM_CPU_9615,
- [105] = MSM_CPU_9615,
- [106] = MSM_CPU_9615,
- [107] = MSM_CPU_9615,
- [171] = MSM_CPU_9615,
+ [104] = {MSM_CPU_9615, "MSM9615"},
+ [105] = {MSM_CPU_9615, "MSM9615"},
+ [106] = {MSM_CPU_9615, "MSM9615"},
+ [107] = {MSM_CPU_9615, "MSM9615"},
+ [171] = {MSM_CPU_9615, "MSM9615"},
/* 8064 IDs */
- [109] = MSM_CPU_8064,
+ [109] = {MSM_CPU_8064, "APQ8064"},
/* 8930 IDs */
- [116] = MSM_CPU_8930,
- [117] = MSM_CPU_8930,
- [118] = MSM_CPU_8930,
- [119] = MSM_CPU_8930,
- [179] = MSM_CPU_8930,
+ [116] = {MSM_CPU_8930, "MSM8930"},
+ [117] = {MSM_CPU_8930, "MSM8930"},
+ [118] = {MSM_CPU_8930, "MSM8930"},
+ [119] = {MSM_CPU_8930, "MSM8930"},
+ [179] = {MSM_CPU_8930, "MSM8930"},
/* 8627 IDs */
- [120] = MSM_CPU_8627,
- [121] = MSM_CPU_8627,
+ [120] = {MSM_CPU_8627, "MSM8627"},
+ [121] = {MSM_CPU_8627, "MSM8627"},
/* 8660A ID */
- [122] = MSM_CPU_8960,
+ [122] = {MSM_CPU_8960, "MSM8960"},
/* 8260A ID */
- [123] = MSM_CPU_8960,
+ [123] = {MSM_CPU_8960, "MSM8960"},
/* 8060A ID */
- [124] = MSM_CPU_8960,
+ [124] = {MSM_CPU_8960, "MSM8960"},
/* 8974 IDs */
- [126] = MSM_CPU_8974,
- [184] = MSM_CPU_8974,
- [185] = MSM_CPU_8974,
- [186] = MSM_CPU_8974,
+ [126] = {MSM_CPU_8974, "MSM8974"},
+ [184] = {MSM_CPU_8974, "MSM8974"},
+ [185] = {MSM_CPU_8974, "MSM8974"},
+ [186] = {MSM_CPU_8974, "MSM8974"},
/* 8974AA IDs */
- [208] = MSM_CPU_8974PRO_AA,
- [211] = MSM_CPU_8974PRO_AA,
- [214] = MSM_CPU_8974PRO_AA,
- [217] = MSM_CPU_8974PRO_AA,
+ [208] = {MSM_CPU_8974PRO_AA, "MSM8974PRO-AA"},
+ [211] = {MSM_CPU_8974PRO_AA, "MSM8974PRO-AA"},
+ [214] = {MSM_CPU_8974PRO_AA, "MSM8974PRO-AA"},
+ [217] = {MSM_CPU_8974PRO_AA, "MSM8974PRO-AA"},
/* 8974AB IDs */
- [209] = MSM_CPU_8974PRO_AB,
- [212] = MSM_CPU_8974PRO_AB,
- [215] = MSM_CPU_8974PRO_AB,
- [218] = MSM_CPU_8974PRO_AB,
+ [209] = {MSM_CPU_8974PRO_AB, "MSM8974PRO-AB"},
+ [212] = {MSM_CPU_8974PRO_AB, "MSM8974PRO-AB"},
+ [215] = {MSM_CPU_8974PRO_AB, "MSM8974PRO-AB"},
+ [218] = {MSM_CPU_8974PRO_AB, "MSM8974PRO-AB"},
/* 8974AC IDs */
- [194] = MSM_CPU_8974PRO_AC,
- [210] = MSM_CPU_8974PRO_AC,
- [213] = MSM_CPU_8974PRO_AC,
- [216] = MSM_CPU_8974PRO_AC,
+ [194] = {MSM_CPU_8974PRO_AC, "MSM8974PRO-AC"},
+ [210] = {MSM_CPU_8974PRO_AC, "MSM8974PRO-AC"},
+ [213] = {MSM_CPU_8974PRO_AC, "MSM8974PRO-AC"},
+ [216] = {MSM_CPU_8974PRO_AC, "MSM8974PRO-AC"},
/* 8625 IDs */
- [127] = MSM_CPU_8625,
- [128] = MSM_CPU_8625,
- [129] = MSM_CPU_8625,
- [137] = MSM_CPU_8625,
- [167] = MSM_CPU_8625,
+ [127] = {MSM_CPU_8625, "MSM8625"},
+ [128] = {MSM_CPU_8625, "MSM8625"},
+ [129] = {MSM_CPU_8625, "MSM8625"},
+ [137] = {MSM_CPU_8625, "MSM8625"},
+ [167] = {MSM_CPU_8625, "MSM8625"},
/* 8064 MPQ ID */
- [130] = MSM_CPU_8064,
+ [130] = {MSM_CPU_8064, "APQ8064"},
/* 7x25AB IDs */
- [131] = MSM_CPU_7X25AB,
- [132] = MSM_CPU_7X25AB,
- [133] = MSM_CPU_7X25AB,
- [135] = MSM_CPU_7X25AB,
+ [131] = {MSM_CPU_7X25AB, "MSM7X25AB"},
+ [132] = {MSM_CPU_7X25AB, "MSM7X25AB"},
+ [133] = {MSM_CPU_7X25AB, "MSM7X25AB"},
+ [135] = {MSM_CPU_7X25AB, "MSM7X25AB"},
/* 9625 IDs */
- [134] = MSM_CPU_9625,
- [148] = MSM_CPU_9625,
- [149] = MSM_CPU_9625,
- [150] = MSM_CPU_9625,
- [151] = MSM_CPU_9625,
- [152] = MSM_CPU_9625,
- [173] = MSM_CPU_9625,
- [174] = MSM_CPU_9625,
- [175] = MSM_CPU_9625,
+ [134] = {MSM_CPU_9625, "MSM9625"},
+ [148] = {MSM_CPU_9625, "MSM9625"},
+ [149] = {MSM_CPU_9625, "MSM9625"},
+ [150] = {MSM_CPU_9625, "MSM9625"},
+ [151] = {MSM_CPU_9625, "MSM9625"},
+ [152] = {MSM_CPU_9625, "MSM9625"},
+ [173] = {MSM_CPU_9625, "MSM9625"},
+ [174] = {MSM_CPU_9625, "MSM9625"},
+ [175] = {MSM_CPU_9625, "MSM9625"},
/* 8960AB IDs */
- [138] = MSM_CPU_8960AB,
- [139] = MSM_CPU_8960AB,
- [140] = MSM_CPU_8960AB,
- [141] = MSM_CPU_8960AB,
+ [138] = {MSM_CPU_8960AB, "MSM8960AB"},
+ [139] = {MSM_CPU_8960AB, "MSM8960AB"},
+ [140] = {MSM_CPU_8960AB, "MSM8960AB"},
+ [141] = {MSM_CPU_8960AB, "MSM8960AB"},
/* 8930AA IDs */
- [142] = MSM_CPU_8930AA,
- [143] = MSM_CPU_8930AA,
- [144] = MSM_CPU_8930AA,
- [160] = MSM_CPU_8930AA,
- [180] = MSM_CPU_8930AA,
+ [142] = {MSM_CPU_8930AA, "MSM8930AA"},
+ [143] = {MSM_CPU_8930AA, "MSM8930AA"},
+ [144] = {MSM_CPU_8930AA, "MSM8930AA"},
+ [160] = {MSM_CPU_8930AA, "MSM8930AA"},
+ [180] = {MSM_CPU_8930AA, "MSM8930AA"},
/* 8226 IDs */
- [145] = MSM_CPU_8226,
- [158] = MSM_CPU_8226,
- [159] = MSM_CPU_8226,
- [198] = MSM_CPU_8226,
- [199] = MSM_CPU_8226,
- [200] = MSM_CPU_8226,
- [205] = MSM_CPU_8226,
- [219] = MSM_CPU_8226,
- [220] = MSM_CPU_8226,
- [221] = MSM_CPU_8226,
- [222] = MSM_CPU_8226,
- [223] = MSM_CPU_8226,
- [224] = MSM_CPU_8226,
+ [145] = {MSM_CPU_8226, "MSM8626"},
+ [158] = {MSM_CPU_8226, "MSM8226"},
+ [159] = {MSM_CPU_8226, "MSM8526"},
+ [198] = {MSM_CPU_8226, "MSM8126"},
+ [199] = {MSM_CPU_8226, "APQ8026"},
+ [200] = {MSM_CPU_8226, "MSM8926"},
+ [205] = {MSM_CPU_8226, "MSM8326"},
+ [219] = {MSM_CPU_8226, "APQ8028"},
+ [220] = {MSM_CPU_8226, "MSM8128"},
+ [221] = {MSM_CPU_8226, "MSM8228"},
+ [222] = {MSM_CPU_8226, "MSM8528"},
+ [223] = {MSM_CPU_8226, "MSM8628"},
+ [224] = {MSM_CPU_8226, "MSM8928"},
/* 8092 IDs */
- [146] = MSM_CPU_8092,
+ [146] = {MSM_CPU_8092, "MSM8092"},
/* 8610 IDs */
- [147] = MSM_CPU_8610,
- [161] = MSM_CPU_8610,
- [162] = MSM_CPU_8610,
- [163] = MSM_CPU_8610,
- [164] = MSM_CPU_8610,
- [165] = MSM_CPU_8610,
- [166] = MSM_CPU_8610,
- [225] = MSM_CPU_8610,
- [226] = MSM_CPU_8610,
+ [147] = {MSM_CPU_8610, "MSM8610"},
+ [161] = {MSM_CPU_8610, "MSM8110"},
+ [162] = {MSM_CPU_8610, "MSM8210"},
+ [163] = {MSM_CPU_8610, "MSM8810"},
+ [164] = {MSM_CPU_8610, "MSM8212"},
+ [165] = {MSM_CPU_8610, "MSM8612"},
+ [166] = {MSM_CPU_8610, "MSM8112"},
+ [225] = {MSM_CPU_8610, "MSM8510"},
+ [226] = {MSM_CPU_8610, "MSM8512"},
/* 8064AB IDs */
- [153] = MSM_CPU_8064AB,
+ [153] = {MSM_CPU_8064AB, "APQ8064AB"},
/* 8930AB IDs */
- [154] = MSM_CPU_8930AB,
- [155] = MSM_CPU_8930AB,
- [156] = MSM_CPU_8930AB,
- [157] = MSM_CPU_8930AB,
- [181] = MSM_CPU_8930AB,
+ [154] = {MSM_CPU_8930AB, "MSM8930AB"},
+ [155] = {MSM_CPU_8930AB, "MSM8930AB"},
+ [156] = {MSM_CPU_8930AB, "MSM8930AB"},
+ [157] = {MSM_CPU_8930AB, "MSM8930AB"},
+ [181] = {MSM_CPU_8930AB, "MSM8930AB"},
/* 8625Q IDs */
- [168] = MSM_CPU_8625Q,
- [169] = MSM_CPU_8625Q,
- [170] = MSM_CPU_8625Q,
+ [168] = {MSM_CPU_8625Q, "MSM8225Q"},
+ [169] = {MSM_CPU_8625Q, "MSM8625Q"},
+ [170] = {MSM_CPU_8625Q, "MSM8125Q"},
/* 8064AA IDs */
- [172] = MSM_CPU_8064AA,
+ [172] = {MSM_CPU_8064AA, "APQ8064AA"},
/* 8084 IDs */
- [178] = MSM_CPU_8084,
+ [178] = {MSM_CPU_8084, "APQ8084"},
/* krypton IDs */
- [187] = MSM_CPU_KRYPTON,
+ [187] = {MSM_CPU_KRYPTON, "MSMKRYPTON"},
/* FSM9900 ID */
- [188] = FSM_CPU_9900,
+ [188] = {FSM_CPU_9900, "FSM9900"},
/* Samarium IDs */
- [195] = MSM_CPU_SAMARIUM,
+ [195] = {MSM_CPU_SAMARIUM, "MSMSAMARIUM"},
/* Uninitialized IDs are not known to run Linux.
MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
@@ -455,6 +458,25 @@
return (socinfo) ? socinfo->v1.build_id : NULL;
}
+static char *msm_read_hardware_id(void)
+{
+ static char msm_soc_str[128] = "Qualcomm ";
+ static bool string_generated = false;
+
+ if (string_generated)
+ return msm_soc_str;
+ if (!socinfo)
+ goto err_path;
+ if (!cpu_of_id[socinfo->v1.id].soc_id_string)
+ goto err_path;
+
+ string_generated = true;
+ return strncat(msm_soc_str, cpu_of_id[socinfo->v1.id].soc_id_string,
+ sizeof(msm_soc_str) - strlen(msm_soc_str));
+err_path:
+ return "UNKNOWN SOC TYPE";
+}
+
uint32_t socinfo_get_raw_id(void)
{
return socinfo ?
@@ -1437,14 +1459,16 @@
}
WARN(!socinfo_get_id(), "Unknown SOC ID!\n");
- WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id),
- "New IDs added! ID => CPU mapping might need an update.\n");
- if (socinfo->v1.id < ARRAY_SIZE(cpu_of_id))
- cur_cpu = cpu_of_id[socinfo->v1.id];
+ if (socinfo_get_id() >= ARRAY_SIZE(cpu_of_id))
+ BUG_ON("New IDs added! ID => CPU mapping might need an update.\n");
+
+ else
+ cur_cpu = cpu_of_id[socinfo->v1.id].generic_soc_type;
boot_stats_init();
socinfo_print();
+ arch_read_hardware_id = msm_read_hardware_id;
return 0;
}
diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index 01e0985..bc4eb76 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, The Linux Foundation. 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
@@ -466,13 +466,20 @@
pr_info("[%p]: Powering up %s\n", current, name);
init_completion(&dev->err_ready);
- if (dev->desc->powerup(dev->desc) < 0)
+
+ if (dev->desc->powerup(dev->desc) < 0) {
+ notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
+ NULL);
panic("[%p]: Powerup error: %s!", current, name);
+ }
ret = wait_for_err_ready(dev);
- if (ret)
+ if (ret) {
+ notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
+ NULL);
panic("[%p]: Timed out waiting for error ready: %s!",
current, name);
+ }
subsys_set_state(dev, SUBSYS_ONLINE);
}
@@ -500,8 +507,11 @@
init_completion(&subsys->err_ready);
ret = subsys->desc->start(subsys->desc);
- if (ret)
+ if (ret){
+ notify_each_subsys_device(&subsys, 1, SUBSYS_POWERUP_FAILURE,
+ NULL);
return ret;
+ }
if (subsys->desc->is_not_loadable) {
subsys_set_state(subsys, SUBSYS_ONLINE);
@@ -509,12 +519,14 @@
}
ret = wait_for_err_ready(subsys);
- if (ret)
+ if (ret) {
/* pil-boot succeeded but we need to shutdown
* the device because error ready timed out.
*/
+ notify_each_subsys_device(&subsys, 1, SUBSYS_POWERUP_FAILURE,
+ NULL);
subsys->desc->stop(subsys->desc);
- else
+ } else
subsys_set_state(subsys, SUBSYS_ONLINE);
return ret;
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 606953d..99647a7 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -107,6 +107,10 @@
#define DIAG_STM_WCNSS 0x04
#define DIAG_STM_APPS 0x08
+#define DIAG_DIAG_STM 0x214
+
+#define BAD_PARAM_RESPONSE_MESSAGE 20
+
/*
* The status bit masks when received in a signal handler are to be
* used in conjunction with the peripheral list bit mask to determine the
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 8cc7515..0bbb012 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -56,6 +56,8 @@
#define STM_RSP_SMD_COMPLY_INDEX 9
#define STM_RSP_NUM_BYTES 10
+#define STM_COMMAND_VALID 1
+
#define SMD_DRAIN_BUF_SIZE 4096
int diag_debug_buf_idx;
@@ -1136,20 +1138,44 @@
}
}
-int diag_process_stm_cmd(unsigned char *buf)
+int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf)
{
- uint8_t version = *(buf+STM_CMD_VERSION_OFFSET);
- uint8_t mask = *(buf+STM_CMD_MASK_OFFSET);
- uint8_t cmd = *(buf+STM_CMD_DATA_OFFSET);
+ uint8_t version, mask, cmd;
uint8_t rsp_supported = 0;
uint8_t rsp_smd_comply = 0;
- int valid_command = 1;
int i;
- /* Check if command is valid */
- if ((version != 1) || (mask == 0) || (0 != (mask >> 4)) ||
- (cmd != ENABLE_STM && cmd != DISABLE_STM)) {
- valid_command = 0;
+ if (!buf || !dest_buf) {
+ pr_err("diag: Invalid pointers buf: %p, dest_buf %p in %s\n",
+ buf, dest_buf, __func__);
+ return -EIO;
+ }
+
+ version = *(buf + STM_CMD_VERSION_OFFSET);
+ mask = *(buf + STM_CMD_MASK_OFFSET);
+ cmd = *(buf + STM_CMD_DATA_OFFSET);
+
+ /*
+ * Check if command is valid. If the command is asking for
+ * status, then the processor mask field is to be ignored.
+ */
+ if ((version != 1) || (cmd > STATUS_STM) ||
+ ((cmd != STATUS_STM) && ((mask == 0) || (0 != (mask >> 4))))) {
+ /* Command is invalid. Send bad param message response */
+ dest_buf[0] = BAD_PARAM_RESPONSE_MESSAGE;
+ for (i = 0; i < STM_CMD_NUM_BYTES; i++)
+ dest_buf[i+1] = *(buf + i);
+ return STM_CMD_NUM_BYTES+1;
+ } else if (cmd == STATUS_STM) {
+ /*
+ * Only the status is being queried, so fill in whether diag
+ * over stm is supported or not
+ */
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+ if (driver->peripheral_supports_stm[i])
+ rsp_supported |= 1 << i;
+
+ rsp_supported |= DIAG_STM_APPS;
} else {
if (mask & DIAG_STM_MODEM)
diag_process_stm_mask(cmd, DIAG_STM_MODEM, MODEM_DATA,
@@ -1169,15 +1195,13 @@
}
for (i = 0; i < STM_CMD_NUM_BYTES; i++)
- driver->apps_rsp_buf[i] = *(buf+i);
+ dest_buf[i] = *(buf + i);
- driver->apps_rsp_buf[STM_RSP_VALID_INDEX] = valid_command;
- driver->apps_rsp_buf[STM_RSP_SUPPORTED_INDEX] = rsp_supported;
- driver->apps_rsp_buf[STM_RSP_SMD_COMPLY_INDEX] = rsp_smd_comply;
+ dest_buf[STM_RSP_VALID_INDEX] = STM_COMMAND_VALID;
+ dest_buf[STM_RSP_SUPPORTED_INDEX] = rsp_supported;
+ dest_buf[STM_RSP_SMD_COMPLY_INDEX] = rsp_smd_comply;
- encode_rsp_and_send(STM_RSP_NUM_BYTES-1);
-
- return 0;
+ return STM_RSP_NUM_BYTES;
}
int diag_apps_responds()
@@ -1273,8 +1297,13 @@
encode_rsp_and_send(7);
return 0;
} else if ((*buf == 0x4b) && (*(buf+1) == 0x12) &&
- (*(uint16_t *)(buf+2) == 0x020E)) {
- return diag_process_stm_cmd(buf);
+ (*(uint16_t *)(buf+2) == DIAG_DIAG_STM)) {
+ len = diag_process_stm_cmd(buf, driver->apps_rsp_buf);
+ if (len > 0) {
+ encode_rsp_and_send(len - 1);
+ return 0;
+ }
+ return len;
}
/* Check for Apps Only & get event mask request */
else if (diag_apps_responds() && *buf == 0x81) {
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index d79195c..7f5ea03 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -54,8 +54,9 @@
#define ENABLE_SEPARATE_CMDRSP 1
#define DISABLE_SEPARATE_CMDRSP 0
-#define ENABLE_STM 1
#define DISABLE_STM 0
+#define ENABLE_STM 1
+#define STATUS_STM 2
#define UPDATE_PERIPHERAL_STM_STATE 1
#define CLEAR_PERIPHERAL_STM_STATE 2
diff --git a/drivers/gpu/ion/ion_cma_secure_heap.c b/drivers/gpu/ion/ion_cma_secure_heap.c
index d375c00..0aef596 100644
--- a/drivers/gpu/ion/ion_cma_secure_heap.c
+++ b/drivers/gpu/ion/ion_cma_secure_heap.c
@@ -446,6 +446,7 @@
if (ret) {
ret = ion_secure_cma_add_to_pool(sheap, len);
if (ret) {
+ mutex_unlock(&sheap->alloc_lock);
dev_err(sheap->dev, "Fail to allocate buffer\n");
goto err;
}
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 7a92fe6..0ec8f52 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2423,9 +2423,12 @@
if (tmp != adreno_dev->fast_hang_detect) {
if (adreno_dev->fast_hang_detect) {
- if (adreno_dev->gpudev->fault_detect_start)
+ if (adreno_dev->gpudev->fault_detect_start &&
+ !kgsl_active_count_get(&adreno_dev->dev)) {
adreno_dev->gpudev->fault_detect_start(
adreno_dev);
+ kgsl_active_count_put(&adreno_dev->dev);
+ }
} else {
if (adreno_dev->gpudev->fault_detect_stop)
adreno_dev->gpudev->fault_detect_stop(
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 2af8d27..69b953f 100755
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -514,6 +514,9 @@
else
/* something went wrong with the event handling mechanism */
BUG_ON(1);
+
+ /* Free param we are done using it */
+ kfree(param);
}
/*
@@ -638,16 +641,18 @@
phys_addr_t pt_base)
{
struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
- phys_addr_t domain_ptbase = iommu_pt ?
- iommu_get_pt_base_addr(iommu_pt->domain) : 0;
+ phys_addr_t domain_ptbase;
- /* Only compare the valid address bits of the pt_base */
- domain_ptbase &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+ if (iommu_pt == NULL)
+ return 0;
+
+ domain_ptbase = iommu_get_pt_base_addr(iommu_pt->domain)
+ & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
pt_base &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
- return domain_ptbase && pt_base &&
- (domain_ptbase == pt_base);
+ return (domain_ptbase == pt_base);
+
}
/*
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index 067a887..44da261 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -1014,7 +1014,7 @@
int32_t qpnp_iadc_get_rsense(struct qpnp_iadc_chip *iadc, int32_t *rsense)
{
- uint8_t rslt_rsense;
+ uint8_t rslt_rsense = 0;
int32_t rc = 0, sign_bit = 0;
if (qpnp_iadc_is_valid(iadc) < 0)
@@ -1022,36 +1022,37 @@
if (iadc->external_rsense) {
*rsense = iadc->rsense;
- return rc;
- }
-
- if (iadc->default_internal_rsense) {
+ } else if (iadc->default_internal_rsense) {
*rsense = iadc->rsense_workaround_value;
- return rc;
- }
+ } else {
- rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE, &rslt_rsense);
- if (rc < 0) {
- pr_err("qpnp adc rsense read failed with %d\n", rc);
- return rc;
- }
+ rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE,
+ &rslt_rsense);
+ if (rc < 0) {
+ pr_err("qpnp adc rsense read failed with %d\n", rc);
+ return rc;
+ }
- pr_debug("rsense:0%x\n", rslt_rsense);
+ pr_debug("rsense:0%x\n", rslt_rsense);
- if (rslt_rsense & QPNP_RSENSE_MSB_SIGN_CHECK)
- sign_bit = 1;
+ if (rslt_rsense & QPNP_RSENSE_MSB_SIGN_CHECK)
+ sign_bit = 1;
- rslt_rsense &= ~QPNP_RSENSE_MSB_SIGN_CHECK;
+ rslt_rsense &= ~QPNP_RSENSE_MSB_SIGN_CHECK;
- if (sign_bit)
- *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR -
+ if (sign_bit)
+ *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR -
(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
- else
- *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
+ else
+ *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
-
+ }
pr_debug("rsense value is %d\n", *rsense);
+ if (*rsense == 0)
+ pr_err("incorrect rsens value:%d rslt_rsense:%d\n",
+ *rsense, rslt_rsense);
+
return rc;
}
EXPORT_SYMBOL(qpnp_iadc_get_rsense);
@@ -1215,6 +1216,11 @@
if (qpnp_iadc_is_valid(iadc) < 0)
return -EPROBE_DEFER;
+ if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
+ pr_err("raw offset errors! run iadc calibration again\n");
+ return -EINVAL;
+ }
+
mutex_lock(&iadc->adc->adc_lock);
if (iadc->iadc_poll_eoc) {
@@ -1251,6 +1257,11 @@
result_current = i_result->result_uv;
result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
/* Intentional fall through. Process the result w/o comp */
+ if (!rsense_u_ohms) {
+ pr_err("rsense error=%d\n", rsense_u_ohms);
+ goto fail_release_vadc;
+ }
+
do_div(result_current, rsense_u_ohms);
if (sign) {
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index d87520f..eba5ca8 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -691,7 +691,7 @@
{
int rc;
u8 val;
- int duty_us;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
@@ -710,13 +710,23 @@
}
}
if (led->mpp_cfg->pwm_mode == PWM_MODE) {
- pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
- duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / LED_FULL;
/*config pwm for brightness scaling*/
- rc = pwm_config_us(led->mpp_cfg->pwm_cfg->pwm_dev,
+ period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ LED_FULL;
+ rc = pwm_config_us(
+ led->mpp_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->mpp_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ LED_FULL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->mpp_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev, "Failed to " \
"configure pwm for new values\n");
@@ -1219,8 +1229,8 @@
static int qpnp_kpdbl_set(struct qpnp_led_data *led)
{
- int duty_us;
int rc;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (!led->kpdbl_cfg->pwm_cfg->blinking)
@@ -1237,11 +1247,22 @@
}
if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
- duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / KPDBL_MAX_LEVEL;
- rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ KPDBL_MAX_LEVEL;
+ rc = pwm_config_us(
+ led->kpdbl_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ KPDBL_MAX_LEVEL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev, "pwm config failed\n");
return rc;
@@ -1262,7 +1283,7 @@
if (led->kpdbl_cfg->always_on) {
rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
- led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ led->kpdbl_cfg->pwm_cfg->pwm_period_us);
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
"pwm config failed\n");
@@ -1300,19 +1321,30 @@
static int qpnp_rgb_set(struct qpnp_led_data *led)
{
- int duty_us;
int rc;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (!led->rgb_cfg->pwm_cfg->blinking)
led->rgb_cfg->pwm_cfg->mode =
led->rgb_cfg->pwm_cfg->default_mode;
if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
- duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / LED_FULL;
- rc = pwm_config_us(led->rgb_cfg->pwm_cfg->pwm_dev,
+ period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ LED_FULL;
+ rc = pwm_config_us(
+ led->rgb_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->rgb_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ LED_FULL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->rgb_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
"pwm config failed\n");
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 96c2bac..18432dd 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -2652,8 +2652,16 @@
buff_req_buffer->buffer_size : 0;
}
for (i = 0; i < fmt->num_planes; ++i) {
- inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
- f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ inst->bufq[OUTPUT_PORT].vb2_bufq.
+ plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ } else if (f->type ==
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ inst->bufq[CAPTURE_PORT].vb2_bufq.
+ plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
}
} else {
dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index d6000c9..008407d 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -527,6 +527,25 @@
return rc;
}
+static void venus_hfi_set_registers(struct venus_hfi_device *device)
+{
+ struct reg_set *reg_set;
+ int i;
+
+ if (!device->res) {
+ dprintk(VIDC_ERR,
+ "device resources null, cannot set registers\n");
+ return;
+ }
+
+ reg_set = &device->res->reg_set;
+ for (i = 0; i < reg_set->count; i++) {
+ venus_hfi_write_register(device,
+ reg_set->reg_tbl[i].reg,
+ reg_set->reg_tbl[i].value, 0);
+ }
+}
+
static int venus_hfi_core_start_cpu(struct venus_hfi_device *device)
{
u32 ctrl_status = 0, count = 0, rc = 0;
@@ -962,6 +981,7 @@
device->power_enabled = 0;
--device->pwr_cnt;
+ dprintk(VIDC_INFO, "entering power collapse\n");
already_disabled:
return rc;
}
@@ -1001,18 +1021,45 @@
goto err_enable_clk;
}
+
+ /*
+ * Re-program all of the registers that get reset as a result of
+ * regulator_disable() and _enable()
+ */
+ venus_hfi_set_registers(device);
+
+ venus_hfi_write_register(device, VIDC_UC_REGION_ADDR,
+ (u32)device->iface_q_table.align_device_addr, 0);
+ venus_hfi_write_register(device,
+ VIDC_UC_REGION_SIZE, SHARED_QSIZE, 0);
+ venus_hfi_write_register(device, VIDC_CPU_CS_SCIACMDARG2,
+ (u32)device->iface_q_table.align_device_addr,
+ device->iface_q_table.align_virtual_addr);
+
+ if (!IS_ERR_OR_NULL(device->sfr.align_device_addr))
+ venus_hfi_write_register(device, VIDC_SFR_ADDR,
+ (u32)device->sfr.align_device_addr, 0);
+ if (!IS_ERR_OR_NULL(device->qdss.align_device_addr))
+ venus_hfi_write_register(device, VIDC_MMAP_ADDR,
+ (u32)device->qdss.align_device_addr, 0);
+
+ /* Reboot the firmware */
rc = venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME);
if (rc) {
dprintk(VIDC_ERR, "Failed to resume video core %d\n", rc);
goto err_set_video_state;
}
+
+ /* Wait for boot completion */
rc = venus_hfi_reset_core(device);
if (rc) {
dprintk(VIDC_ERR, "Failed to reset venus core");
goto err_reset_core;
}
+
device->power_enabled = 1;
++device->pwr_cnt;
+ dprintk(VIDC_INFO, "resuming from power collapse\n");
return rc;
err_reset_core:
venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND);
@@ -1497,25 +1544,6 @@
return -ENOMEM;
}
-static void venus_hfi_set_registers(struct venus_hfi_device *device)
-{
- struct reg_set *reg_set;
- int i;
-
- if (!device->res) {
- dprintk(VIDC_ERR,
- "device resources null, cannot set registers\n");
- return;
- }
-
- reg_set = &device->res->reg_set;
- for (i = 0; i < reg_set->count; i++) {
- venus_hfi_write_register(device,
- reg_set->reg_tbl[i].reg,
- reg_set->reg_tbl[i].value, 0);
- }
-}
-
static int venus_hfi_sys_set_debug(struct venus_hfi_device *device, int debug)
{
u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
@@ -2025,6 +2053,10 @@
new_session = (struct hal_session *)
kzalloc(sizeof(struct hal_session), GFP_KERNEL);
+ if (!new_session) {
+ dprintk(VIDC_ERR, "new session fail: Out of memory\n");
+ return NULL;
+ }
new_session->session_id = (u32) session_id;
if (session_type == 1)
new_session->is_decoder = 0;
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index d2b381b..a3e88b4 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -40,6 +40,7 @@
#include <mach/msm_smd.h>
#include <mach/msm_iomap.h>
#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
#include "wcnss_prealloc.h"
@@ -145,6 +146,20 @@
#define MSM_PRONTO_PLL_BASE 0xfb21b1c0
#define PRONTO_PLL_STATUS_OFFSET 0x1c
+#define MSM_PRONTO_MCU_BASE 0xfb080c00
+#define MCU_CBR_CCAHB_ERR_OFFSET 0x380
+#define MCU_CBR_CAHB_ERR_OFFSET 0x384
+#define MCU_CBR_CCAHB_TIMEOUT_OFFSET 0x388
+#define MCU_CBR_CAHB_TIMEOUT_OFFSET 0x38c
+#define MCU_DBR_CDAHB_ERR_OFFSET 0x390
+#define MCU_DBR_DAHB_ERR_OFFSET 0x394
+#define MCU_DBR_CDAHB_TIMEOUT_OFFSET 0x398
+#define MCU_DBR_DAHB_TIMEOUT_OFFSET 0x39c
+#define MCU_FDBR_CDAHB_ERR_OFFSET 0x3a0
+#define MCU_FDBR_FDAHB_ERR_OFFSET 0x3a4
+#define MCU_FDBR_CDAHB_TIMEOUT_OFFSET 0x3a8
+#define MCU_FDBR_FDAHB_TIMEOUT_OFFSET 0x3ac
+
#define MSM_PRONTO_TXP_STATUS 0xfb08040c
#define MSM_PRONTO_TXP_PHY_ABORT 0xfb080488
#define MSM_PRONTO_BRDG_ERR_SRC 0xfb080fb0
@@ -362,6 +377,7 @@
void __iomem *pronto_ccpu_base;
void __iomem *pronto_saw2_base;
void __iomem *pronto_pll_base;
+ void __iomem *pronto_mcu_base;
void __iomem *wlan_tx_status;
void __iomem *wlan_tx_phy_aborts;
void __iomem *wlan_brdg_err_source;
@@ -391,6 +407,7 @@
struct mutex vbat_monitor_mutex;
u16 unsafe_ch_count;
u16 unsafe_ch_list[WCNSS_MAX_CH_NUM];
+ void *wcnss_notif_hdle;
} *penv = NULL;
static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
@@ -563,35 +580,31 @@
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_SPARE %08x\n", __func__, reg);
+ pr_err("PRONTO_PMU_SPARE %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_CPU_CBCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_CPU_CBCR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_AHB_CBCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_AHB_CBCR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_CFG %08x\n", __func__, reg);
+ pr_err("PRONTO_PMU_CFG %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_CSR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_CSR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_SOFT_RESET %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_SOFT_RESET %08x\n", reg);
reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_SAW2_SPM_STS %08x\n", __func__, reg);
+ pr_err("PRONTO_SAW2_SPM_STS %08x\n", reg);
reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
reg = readl_relaxed(reg_addr);
@@ -603,13 +616,11 @@
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_GDSCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_GDSCR %08x\n", reg);
reg >>= 31;
if (!reg) {
- pr_info_ratelimited("%s: Cannot log, Pronto common SS is power collapsed\n",
- __func__);
+ pr_err("Cannot log, Pronto common SS is power collapsed\n");
return;
}
reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE
@@ -623,31 +634,31 @@
reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_CFG_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_CFG_OFFSET %08x\n", reg);
reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_INT_SRC_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_INT_SRC_OFFSET %08x\n", reg);
reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_ERR_INFO_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_ERR_INFO_OFFSET %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_INVALID_ADDR %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR0 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR1 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR2 %08x\n", reg);
tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
@@ -657,24 +668,21 @@
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Read data FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Read data FIFO testbus %08x\n", reg);
/* command FIFO */
reg = 0;
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Command FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Command FIFO testbus %08x\n", reg);
/* write data FIFO */
reg = 0;
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Rrite data FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Rrite data FIFO testbus %08x\n", reg);
/* AXIM SEL CFG0 */
reg = 0;
@@ -682,8 +690,7 @@
WCNSS_TSTBUS_CTRL_AXIM_CFG0;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: AXIM SEL CFG0 testbus %08x\n",
- __func__, reg);
+ pr_err("AXIM SEL CFG0 testbus %08x\n", reg);
/* AXIM SEL CFG1 */
reg = 0;
@@ -691,8 +698,7 @@
WCNSS_TSTBUS_CTRL_AXIM_CFG1;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: AXIM SEL CFG1 testbus %08x\n",
- __func__, reg);
+ pr_err("AXIM SEL CFG1 testbus %08x\n", reg);
/* CTRL SEL CFG0 */
reg = 0;
@@ -700,8 +706,7 @@
WCNSS_TSTBUS_CTRL_CTRL_CFG0;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: CTRL SEL CFG0 testbus %08x\n",
- __func__, reg);
+ pr_err("CTRL SEL CFG0 testbus %08x\n", reg);
/* CTRL SEL CFG1 */
reg = 0;
@@ -709,7 +714,7 @@
WCNSS_TSTBUS_CTRL_CTRL_CFG1;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: CTRL SEL CFG1 testbus %08x\n", __func__, reg);
+ pr_err("CTRL SEL CFG1 testbus %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET;
@@ -720,26 +725,75 @@
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET;
reg3 = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PMU_WLAN_AHB_CBCR %08x\n", __func__, reg3);
+ pr_err("PMU_WLAN_AHB_CBCR %08x\n", reg3);
if ((reg & PRONTO_PMU_WLAN_BCR_BLK_ARES) ||
(reg2 & PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE) ||
(!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) ||
(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) ||
(!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) {
- pr_info_ratelimited("%s: Cannot log, wlan domain is power collapsed\n",
- __func__);
+ pr_err("Cannot log, wlan domain is power collapsed\n");
return;
}
+ msleep(50);
+
reg = readl_relaxed(penv->wlan_tx_phy_aborts);
- pr_info_ratelimited("%s: WLAN_TX_PHY_ABORTS %08x\n", __func__, reg);
+ pr_err("WLAN_TX_PHY_ABORTS %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_CBR_CCAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_CBR_CAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_CBR_CCAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_CBR_CAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_DBR_CDAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_DBR_DAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_DBR_CDAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_DBR_DAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_FDBR_CDAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_FDBR_FDAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_FDBR_CDAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_FDBR_FDAHB_TIMEOUT %08x\n", reg);
reg = readl_relaxed(penv->wlan_brdg_err_source);
- pr_info_ratelimited("%s: WLAN_BRDG_ERR_SOURCE %08x\n", __func__, reg);
+ pr_err("WLAN_BRDG_ERR_SOURCE %08x\n", reg);
reg = readl_relaxed(penv->wlan_tx_status);
- pr_info_ratelimited("%s: WLAN_TX_STATUS %08x\n", __func__, reg);
+ pr_err("WLAN_TXP_STATUS %08x\n", reg);
reg = readl_relaxed(penv->alarms_txctl);
pr_err("ALARMS_TXCTL %08x\n", reg);
@@ -2251,6 +2305,13 @@
pr_err("%s: ioremap alarms TACTL failed\n", __func__);
goto fail_ioremap11;
}
+ penv->pronto_mcu_base = ioremap(MSM_PRONTO_MCU_BASE, SZ_1K);
+ if (!penv->pronto_mcu_base) {
+ ret = -ENOMEM;
+ pr_err("%s: ioremap wcnss physical(mcu) failed\n",
+ __func__);
+ goto fail_ioremap12;
+ }
}
penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
if (IS_ERR(penv->adc_tm_dev)) {
@@ -2281,6 +2342,9 @@
fail_pil:
if (penv->riva_ccu_base)
iounmap(penv->riva_ccu_base);
+ if (penv->pronto_mcu_base)
+ iounmap(penv->pronto_mcu_base);
+fail_ioremap12:
if (penv->alarms_tactl)
iounmap(penv->alarms_tactl);
fail_ioremap11:
@@ -2447,6 +2511,22 @@
}
+static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ pr_debug("%s: wcnss notification event: %lu\n", __func__, code);
+
+ if (SUBSYS_POWERUP_FAILURE == code)
+ wcnss_pronto_log_debug_regs();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block wnb = {
+ .notifier_call = wcnss_notif_cb,
+};
+
+
static const struct file_operations wcnss_node_fops = {
.owner = THIS_MODULE,
.open = wcnss_node_open,
@@ -2486,6 +2566,13 @@
return -ENOENT;
}
+ /* register wcnss event notification */
+ penv->wcnss_notif_hdle = subsys_notif_register_notifier("wcnss", &wnb);
+ if (IS_ERR(penv->wcnss_notif_hdle)) {
+ pr_err("wcnss: register event notification failed!\n");
+ return PTR_ERR(penv->wcnss_notif_hdle);
+ }
+
mutex_init(&penv->dev_lock);
mutex_init(&penv->ctrl_lock);
mutex_init(&penv->vbat_monitor_mutex);
@@ -2510,6 +2597,8 @@
static int __devexit
wcnss_wlan_remove(struct platform_device *pdev)
{
+ if (penv->wcnss_notif_hdle)
+ subsys_notif_unregister_notifier(penv->wcnss_notif_hdle, &wnb);
wcnss_remove_sysfs(&pdev->dev);
penv = NULL;
return 0;
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index a0d9a24..82c61c9 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -2205,7 +2205,7 @@
pr_err("adc vbat failed err = %d\n", rc);
return soc;
}
- if (soc == 0 && vbat_uv > chip->v_cutoff_uv) {
+ if (soc <= 0 && vbat_uv > chip->v_cutoff_uv) {
pr_debug("clamping soc to 1, vbat (%d) > cutoff (%d)\n",
vbat_uv, chip->v_cutoff_uv);
return 1;
@@ -2418,8 +2418,13 @@
}
mutex_unlock(&chip->soc_invalidation_mutex);
- pr_debug("SOC before adjustment = %d\n", soc);
- new_calculated_soc = adjust_soc(chip, ¶ms, soc, batt_temp);
+ if (chip->first_time_calc_soc && !chip->shutdown_soc_invalid) {
+ pr_debug("Skip adjustment when shutdown SOC has been forced\n");
+ new_calculated_soc = soc;
+ } else {
+ pr_debug("SOC before adjustment = %d\n", soc);
+ new_calculated_soc = adjust_soc(chip, ¶ms, soc, batt_temp);
+ }
/* always clamp soc due to BMS hw/sw immaturities */
new_calculated_soc = clamp_soc_based_on_voltage(chip,
diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c
index 51e176f..06749b9 100644
--- a/drivers/rtc/alarm.c
+++ b/drivers/rtc/alarm.c
@@ -520,8 +520,11 @@
spin_lock_irqsave(&alarm_slock, flags);
- if (!power_on_alarm)
+ if (!power_on_alarm) {
+ spin_unlock_irqrestore(&alarm_slock, flags);
goto disable_alarm;
+ }
+ spin_unlock_irqrestore(&alarm_slock, flags);
rtc_read_time(alarm_rtc_dev, &rtc_time);
getnstimeofday(&wall_time);
@@ -549,11 +552,9 @@
pr_alarm(FLOW, "Power-on alarm set to %lu\n",
alarm_time);
- spin_unlock_irqrestore(&alarm_slock, flags);
return;
disable_alarm:
- spin_unlock_irqrestore(&alarm_slock, flags);
rtc_alarm_irq_enable(alarm_rtc_dev, 0);
}
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 0f8f1dd..496b31d 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -27,7 +27,6 @@
#include <linux/timer.h>
#include <mach/sps.h>
#include "slim-msm.h"
-#include <mach/qdsp6v2/apr.h>
#define NGD_SLIM_NAME "ngd_msm_ctrl"
#define SLIM_LA_MGR 0xFF
@@ -857,7 +856,7 @@
prev_state);
/* ADSP SSR, send device_up notifications */
if (prev_state == MSM_CTRL_DOWN)
- schedule_work(&dev->slave_notify);
+ complete(&dev->qmi.slave_notify);
} else if (ret == -EIO) {
pr_info("capability message NACKed, retrying");
if (retries < INIT_MX_RETRIES) {
@@ -1108,31 +1107,54 @@
return 0;
}
-static void ngd_laddr_lookup(struct work_struct *work)
+static int ngd_notify_slaves(void *data)
{
- struct msm_slim_ctrl *dev =
- container_of(work, struct msm_slim_ctrl, slave_notify);
+ struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
struct slim_controller *ctrl = &dev->ctrl;
struct slim_device *sbdev;
struct list_head *pos, *next;
- int i;
- slim_framer_booted(ctrl);
- mutex_lock(&ctrl->m_ctrl);
- list_for_each_safe(pos, next, &ctrl->devs) {
- int ret = 0;
- sbdev = list_entry(pos, struct slim_device, dev_list);
- mutex_unlock(&ctrl->m_ctrl);
- for (i = 0; i < LADDR_RETRY; i++) {
- ret = slim_get_logical_addr(sbdev, sbdev->e_addr,
- 6, &sbdev->laddr);
- if (!ret)
- break;
- else /* time for ADSP to assign LA */
- msleep(20);
+ int ret, i = 0;
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ ret = wait_for_completion_timeout(&dev->qmi.slave_notify,
+ HZ);
+ if (!ret) {
+ dev_err(dev->dev, "slave thread wait err:%d", ret);
+ continue;
}
+ /* Probe devices for first notification */
+ if (!i) {
+ dev->err = 0;
+ if (dev->dev->of_node)
+ of_register_slim_devices(&dev->ctrl);
+
+ /*
+ * Add devices registered with board-info now that
+ * controller is up
+ */
+ slim_ctrl_add_boarddevs(&dev->ctrl);
+ } else {
+ slim_framer_booted(ctrl);
+ }
+ i++;
mutex_lock(&ctrl->m_ctrl);
+ list_for_each_safe(pos, next, &ctrl->devs) {
+ sbdev = list_entry(pos, struct slim_device, dev_list);
+ mutex_unlock(&ctrl->m_ctrl);
+ for (i = 0; i < LADDR_RETRY; i++) {
+ ret = slim_get_logical_addr(sbdev,
+ sbdev->e_addr,
+ 6, &sbdev->laddr);
+ if (!ret)
+ break;
+ else /* time for ADSP to assign LA */
+ msleep(20);
+ }
+ mutex_lock(&ctrl->m_ctrl);
+ }
+ mutex_unlock(&ctrl->m_ctrl);
}
- mutex_unlock(&ctrl->m_ctrl);
+ return 0;
}
static void ngd_adsp_down(struct work_struct *work)
@@ -1173,18 +1195,9 @@
struct resource *bam_mem;
struct resource *slim_mem;
struct resource *irq, *bam_irq;
- enum apr_subsys_state q6_state;
bool rxreg_access = false;
bool slim_mdm = false;
- q6_state = apr_get_q6_state();
- if (q6_state == APR_SUBSYS_DOWN) {
- dev_dbg(&pdev->dev, "defering %s, adsp_state %d\n", __func__,
- q6_state);
- return -EPROBE_DEFER;
- } else
- dev_dbg(&pdev->dev, "adsp is ready\n");
-
slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"slimbus_physical");
if (!slim_mem) {
@@ -1290,6 +1303,7 @@
dev->use_tx_msgqs = MSM_MSGQ_RESET;
init_completion(&dev->rx_msgq_notify);
+ init_completion(&dev->qmi.slave_notify);
/* Register with framework */
ret = slim_add_numbered_controller(&dev->ctrl);
@@ -1311,6 +1325,7 @@
}
init_completion(&dev->qmi.qmi_comp);
+ dev->err = -EPROBE_DEFER;
pm_runtime_use_autosuspend(dev->dev);
pm_runtime_set_autosuspend_delay(dev->dev, MSM_SLIM_AUTOSUSPEND);
pm_runtime_set_suspended(dev->dev);
@@ -1326,7 +1341,6 @@
dev->mdm.ssr);
}
- INIT_WORK(&dev->slave_notify, ngd_laddr_lookup);
INIT_WORK(&dev->qmi.ssr_down, ngd_adsp_down);
INIT_WORK(&dev->qmi.ssr_up, ngd_adsp_up);
dev->qmi.nb.notifier_call = ngd_qmi_available;
@@ -1342,23 +1356,27 @@
/* Fire up the Rx message queue thread */
dev->rx_msgq_thread = kthread_run(ngd_slim_rx_msgq_thread, dev,
- NGD_SLIM_NAME "_ngd_msgq_thread");
+ "ngd_rx_thread%d", dev->ctrl.nr);
if (IS_ERR(dev->rx_msgq_thread)) {
ret = PTR_ERR(dev->rx_msgq_thread);
- dev_err(dev->dev, "Failed to start Rx message queue thread\n");
- goto err_thread_create_failed;
+ dev_err(dev->dev, "Failed to start Rx thread:%d\n", ret);
+ goto err_rx_thread_create_failed;
}
- if (pdev->dev.of_node)
- of_register_slim_devices(&dev->ctrl);
-
- /* Add devices registered with board-info now that controller is up */
- slim_ctrl_add_boarddevs(&dev->ctrl);
-
+ /* Start thread to probe, and notify slaves */
+ dev->qmi.slave_thread = kthread_run(ngd_notify_slaves, dev,
+ "ngd_notify_sl%d", dev->ctrl.nr);
+ if (IS_ERR(dev->qmi.slave_thread)) {
+ ret = PTR_ERR(dev->qmi.slave_thread);
+ dev_err(dev->dev, "Failed to start notifier thread:%d\n", ret);
+ goto err_notify_thread_create_failed;
+ }
dev_dbg(dev->dev, "NGD SB controller is up!\n");
return 0;
-err_thread_create_failed:
+err_notify_thread_create_failed:
+ kthread_stop(dev->rx_msgq_thread);
+err_rx_thread_create_failed:
qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_SVC_V1,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 2327b38..63178cc 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -203,6 +203,8 @@
struct msm_slim_qmi {
struct qmi_handle *handle;
struct task_struct *task;
+ struct task_struct *slave_thread;
+ struct completion slave_notify;
struct kthread_work kwork;
struct kthread_worker kworker;
struct completion qmi_comp;
@@ -261,7 +263,6 @@
struct completion ctrl_up;
int nsats;
u32 ver;
- struct work_struct slave_notify;
struct msm_slim_qmi qmi;
struct msm_slim_pdata pdata;
struct msm_slim_mdm mdm;
diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c
index 3e14333..9fc7299 100644
--- a/drivers/spmi/qpnp-int.c
+++ b/drivers/spmi/qpnp-int.c
@@ -184,6 +184,22 @@
return 0;
}
+static void qpnpint_irq_ack(struct irq_data *d)
+{
+ struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+ int rc;
+
+ pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+ rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR,
+ &irq_d->mask_shift, 1);
+ if (rc) {
+ pr_err_ratelimited("spmi write failure on irq %d, rc=%d\n",
+ d->irq, rc);
+ return;
+ }
+}
+
static void qpnpint_irq_mask(struct irq_data *d)
{
struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
@@ -223,44 +239,10 @@
static void qpnpint_irq_mask_ack(struct irq_data *d)
{
- struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
- struct q_chip_data *chip_d = irq_d->chip_d;
- struct q_perip_data *per_d = irq_d->per_d;
- int rc;
- uint8_t prev_int_en = per_d->int_en;
-
pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
- if (!chip_d->cb) {
- pr_warn_ratelimited("No arbiter on bus=%u slave=%u offset=%u\n",
- chip_d->bus_nr, irq_d->spmi_slave,
- irq_d->spmi_offset);
- return;
- }
-
- per_d->int_en &= ~irq_d->mask_shift;
-
- if (prev_int_en && !(per_d->int_en)) {
- /*
- * no interrupt on this peripheral is enabled
- * ask the arbiter to ignore this peripheral
- */
- qpnpint_arbiter_op(d, irq_d, chip_d->cb->mask);
- }
-
- rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
- &irq_d->mask_shift, 1);
- if (rc) {
- pr_err("spmi failure on irq %d\n", d->irq);
- return;
- }
-
- rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR,
- &irq_d->mask_shift, 1);
- if (rc) {
- pr_err("spmi failure on irq %d\n", d->irq);
- return;
- }
+ qpnpint_irq_mask(d);
+ qpnpint_irq_ack(d);
}
static void qpnpint_irq_unmask(struct irq_data *d)
@@ -269,6 +251,7 @@
struct q_chip_data *chip_d = irq_d->chip_d;
struct q_perip_data *per_d = irq_d->per_d;
int rc;
+ uint8_t buf[2];
uint8_t prev_int_en = per_d->int_en;
pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
@@ -289,12 +272,29 @@
*/
qpnpint_arbiter_op(d, irq_d, chip_d->cb->unmask);
}
- rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_SET,
- &irq_d->mask_shift, 1);
+
+ /* Check the current state of the interrupt enable bit. */
+ rc = qpnpint_spmi_read(irq_d, QPNPINT_REG_EN_SET, buf, 1);
if (rc) {
- pr_err("spmi failure on irq %d\n", d->irq);
+ pr_err("SPMI read failure for IRQ %d, rc=%d\n", d->irq, rc);
return;
}
+
+ if (!(buf[0] & irq_d->mask_shift)) {
+ /*
+ * Since the interrupt is currently disabled, write to both the
+ * LATCHED_CLR and EN_SET registers so that a spurious interrupt
+ * cannot be triggered when the interrupt is enabled.
+ */
+ buf[0] = irq_d->mask_shift;
+ buf[1] = irq_d->mask_shift;
+ rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR, buf, 2);
+ if (rc) {
+ pr_err("SPMI write failure for IRQ %d, rc=%d\n", d->irq,
+ rc);
+ return;
+ }
+ }
}
static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -336,6 +336,11 @@
return rc;
}
+ if (flow_type & IRQ_TYPE_EDGE_BOTH)
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ else
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+
return 0;
}
@@ -363,6 +368,7 @@
static struct irq_chip qpnpint_chip = {
.name = "qpnp-int",
+ .irq_ack = qpnpint_irq_ack,
.irq_mask = qpnpint_irq_mask,
.irq_mask_ack = qpnpint_irq_mask_ack,
.irq_unmask = qpnpint_irq_unmask,
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 7ca247a..41ebc1c 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -365,7 +365,9 @@
if (!sc->nr_to_scan)
return lru_count;
- mutex_lock(&ashmem_mutex);
+ if (!mutex_trylock(&ashmem_mutex))
+ return -1;
+
list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) {
struct inode *inode = range->asma->file->f_dentry->d_inode;
loff_t start = range->pgstart * PAGE_SIZE;
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index b1ec3fc..2a66c4c 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -28,6 +28,7 @@
obj-$(CONFIG_USB_IMX21_HCD) += host/
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/
obj-$(CONFIG_USB_PEHCI_HCD) += host/
+obj-$(CONFIG_USB_ICE40_HCD) += host/
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
@@ -37,6 +38,7 @@
obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_WDM) += class/
obj-$(CONFIG_USB_TMC) += class/
+obj-$(CONFIG_USB_CCID_BRIDGE) += class/
obj-$(CONFIG_USB_STORAGE) += storage/
obj-$(CONFIG_USB) += storage/
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 2fa8c63..ebcec96 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, The Linux Foundation. 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
@@ -47,7 +47,7 @@
/* control info */
struct list_head cpkt_resp_q;
- atomic_t notify_count;
+ unsigned long notify_count;
unsigned long cpkts_len;
};
@@ -605,7 +605,7 @@
list_del(&cpkt->list);
rmnet_free_ctrl_pkt(cpkt);
}
- atomic_set(&dev->notify_count, 0);
+ dev->notify_count = 0;
spin_unlock_irqrestore(&dev->lock, flags);
}
@@ -619,6 +619,7 @@
__func__, xport_to_str(dxport),
dev, dev->port_num);
+ usb_ep_fifo_flush(dev->notify);
frmnet_purge_responses(dev);
port_num = rmnet_ports[dev->port_num].data_xport_num;
@@ -754,7 +755,7 @@
return;
}
- if (atomic_inc_return(&dev->notify_count) != 1) {
+ if (++dev->notify_count != 1) {
spin_unlock_irqrestore(&dev->lock, flags);
return;
}
@@ -772,7 +773,14 @@
if (ret) {
spin_lock_irqsave(&dev->lock, flags);
if (!list_empty(&dev->cpkt_resp_q)) {
- atomic_dec(&dev->notify_count);
+ if (dev->notify_count > 0)
+ dev->notify_count--;
+ else {
+ pr_debug("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return;
+ }
cpkt = list_first_entry(&dev->cpkt_resp_q,
struct rmnet_ctrl_pkt, list);
list_del(&cpkt->list);
@@ -911,7 +919,9 @@
case -ECONNRESET:
case -ESHUTDOWN:
/* connection gone */
- atomic_set(&dev->notify_count, 0);
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->notify_count = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
break;
default:
pr_err("rmnet notify ep error %d\n", status);
@@ -920,14 +930,34 @@
if (!atomic_read(&dev->ctrl_online))
break;
- if (atomic_dec_and_test(&dev->notify_count))
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->notify_count > 0) {
+ dev->notify_count--;
+ if (dev->notify_count == 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ break;
+ }
+ } else {
+ pr_debug("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock, flags);
break;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
if (status) {
spin_lock_irqsave(&dev->lock, flags);
if (!list_empty(&dev->cpkt_resp_q)) {
- atomic_dec(&dev->notify_count);
+ if (dev->notify_count > 0)
+ dev->notify_count--;
+ else {
+ pr_err("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock,
+ flags);
+ break;
+ }
cpkt = list_first_entry(&dev->cpkt_resp_q,
struct rmnet_ctrl_pkt, list);
list_del(&cpkt->list);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 4357867..2a24bec 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -693,3 +693,16 @@
config USB_OCTEON2_COMMON
bool
default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI
+
+config USB_ICE40_HCD
+ tristate "ICE40 FPGA based SPI to Inter-Chip USB host controller"
+ depends on USB && SPI
+ help
+ A driver for ICE40 FPGA based SPI to Inter-Chip USB host
+ controller. This driver registers as a SPI protocol driver
+ and interacts with the SPI subsystem on one side and interacts
+ with the USB core on the other side. Control and Bulk transfers
+ are supported.
+
+ To compile this driver a module, choose M here: the module
+ will be called "ice40-hcd".
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 7d35f5b..7c5b452 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -42,3 +42,4 @@
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o
obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o
obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o
+obj-$(CONFIG_USB_ICE40_HCD) += ice40-hcd.o
diff --git a/drivers/usb/host/ice40-hcd.c b/drivers/usb/host/ice40-hcd.c
new file mode 100644
index 0000000..4d62a3e
--- /dev/null
+++ b/drivers/usb/host/ice40-hcd.c
@@ -0,0 +1,2092 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2001-2004 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+/*
+ * Root HUB management and Asynchronous scheduling traversal
+ * Based on ehci-hub.c and ehci-q.c
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/ktime.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
+#include <linux/firmware.h>
+#include <linux/spi/spi.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/ch11.h>
+
+#include <asm/unaligned.h>
+#include <mach/gpiomux.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/ice40.h>
+
+#define FADDR_REG 0x00 /* R/W: Device address */
+#define HCMD_REG 0x01 /* R/W: Host transfer command */
+#define XFRST_REG 0x02 /* R: Transfer status */
+#define IRQ_REG 0x03 /* R/C: IRQ status */
+#define IEN_REG 0x04 /* R/W: IRQ enable */
+#define CTRL0_REG 0x05 /* R/W: Host control command */
+#define CTRL1_REG 0x06 /* R/W: Host control command */
+#define WBUF0_REG 0x10 /* W: Tx fifo 0 */
+#define WBUF1_REG 0x11 /* W: Tx fifo 1 */
+#define SUBUF_REG 0x12 /* W: SETUP fifo */
+#define WBLEN_REG 0x13 /* W: Tx fifo size */
+#define RBUF0_REG 0x18 /* R: Rx fifo 0 */
+#define RBUF1_REG 0x19 /* R: Rx fifo 1 */
+#define RBLEN_REG 0x1B /* R: Rx fifo size */
+
+#define WRITE_CMD(addr) ((addr << 3) | 1)
+#define READ_CMD(addr) ((addr << 3) | 0)
+
+/* Host controller command register definitions */
+#define HCMD_EP(ep) (ep & 0xF)
+#define HCMD_BSEL(sel) (sel << 4)
+#define HCMD_TOGV(toggle) (toggle << 5)
+#define HCMD_PT(token) (token << 6)
+
+/* Transfer status register definitions */
+#define XFR_MASK(xfr) (xfr & 0xF)
+#define XFR_SUCCESS 0x0
+#define XFR_BUSY 0x1
+#define XFR_PKTERR 0x2
+#define XFR_PIDERR 0x3
+#define XFR_NAK 0x4
+#define XFR_STALL 0x5
+#define XFR_WRONGPID 0x6
+#define XFR_CRCERR 0x7
+#define XFR_TOGERR 0x8
+#define XFR_BADLEN 0x9
+#define XFR_TIMEOUT 0xA
+
+#define LINE_STATE(xfr) ((xfr & 0x30) >> 4) /* D+, D- */
+#define DPST BIT(5)
+#define DMST BIT(4)
+#define PLLOK BIT(6)
+#define R64B BIT(7)
+
+/* Interrupt enable/status register definitions */
+#define RESET_IRQ BIT(0)
+#define RESUME_IRQ BIT(1)
+#define SUSP_IRQ BIT(3)
+#define DISCONNECT_IRQ BIT(4)
+#define CONNECT_IRQ BIT(5)
+#define FRAME_IRQ BIT(6)
+#define XFR_IRQ BIT(7)
+
+/* Control 0 register definitions */
+#define RESET_CTRL BIT(0)
+#define FRAME_RESET_CTRL BIT(1)
+#define DET_BUS_CTRL BIT(2)
+#define RESUME_CTRL BIT(3)
+#define SOFEN_CTRL BIT(4)
+#define DM_PD_CTRL BIT(6)
+#define DP_PD_CTRL BIT(7)
+#define HRST_CTRL BIT(5)
+
+/* Control 1 register definitions */
+#define INT_EN_CTRL BIT(0)
+
+enum ice40_xfr_type {
+ FIRMWARE_XFR,
+ REG_WRITE_XFR,
+ REG_READ_XFR,
+ SETUP_XFR,
+ DATA_IN_XFR,
+ DATA_OUT_XFR,
+};
+
+enum ice40_ep_phase {
+ SETUP_PHASE = 1,
+ DATA_PHASE,
+ STATUS_PHASE,
+};
+
+struct ice40_ep {
+ u8 xcat_err;
+ bool unlinking;
+ bool halted;
+ struct usb_host_endpoint *ep;
+ struct list_head ep_list;
+};
+
+struct ice40_hcd {
+ spinlock_t lock;
+
+ struct mutex wlock;
+ struct mutex rlock;
+
+ u8 devnum;
+ u32 port_flags;
+ u8 ctrl0;
+ u8 wblen0;
+
+ enum ice40_ep_phase ep0_state;
+ struct usb_hcd *hcd;
+
+ struct list_head async_list;
+ struct workqueue_struct *wq;
+ struct work_struct async_work;
+
+ int reset_gpio;
+ int slave_select_gpio;
+ int config_done_gpio;
+ int vcc_en_gpio;
+ int clk_en_gpio;
+
+ struct regulator *core_vcc;
+ struct regulator *spi_vcc;
+ struct regulator *gpio_vcc;
+ bool powered;
+
+ struct dentry *dbg_root;
+ bool pcd_pending;
+
+ /* SPI stuff later */
+ struct spi_device *spi;
+
+ struct spi_message *fmsg;
+ struct spi_transfer *fmsg_xfr; /* size 1 */
+
+ struct spi_message *wmsg;
+ struct spi_transfer *wmsg_xfr; /* size 1 */
+ u8 *w_tx_buf;
+ u8 *w_rx_buf;
+
+ struct spi_message *rmsg;
+ struct spi_transfer *rmsg_xfr; /* size 1 */
+ u8 *r_tx_buf;
+ u8 *r_rx_buf;
+
+ struct spi_message *setup_msg;
+ struct spi_transfer *setup_xfr; /* size 2 */
+ u8 *setup_buf; /* size 1 for SUBUF */
+
+ struct spi_message *in_msg;
+ struct spi_transfer *in_xfr; /* size 2 */
+ u8 *in_buf; /* size 2 for reading from RBUF0 */
+
+ struct spi_message *out_msg;
+ struct spi_transfer *out_xfr; /* size 2 */
+ u8 *out_buf; /* size 1 for writing WBUF0 */
+};
+
+static char fw_name[16] = "ice40.bin";
+module_param_string(fw, fw_name, sizeof(fw_name), S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(fw, "firmware blob file name");
+
+static bool debugger;
+module_param(debugger, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debugger, "true to use the debug port");
+
+static inline struct ice40_hcd *hcd_to_ihcd(struct usb_hcd *hcd)
+{
+ return *((struct ice40_hcd **) hcd->hcd_priv);
+}
+
+static void ice40_spi_reg_write(struct ice40_hcd *ihcd, u8 val, u8 addr)
+{
+ int ret;
+
+ /*
+ * Register Write Pattern:
+ * TX: 1st byte is CMD (register + write), 2nd byte is value
+ * RX: Ignore
+ *
+ * The Mutex is to protect concurrent register writes as
+ * we have only 1 SPI message struct.
+ */
+
+ mutex_lock(&ihcd->wlock);
+
+ ihcd->w_tx_buf[0] = WRITE_CMD(addr);
+ ihcd->w_tx_buf[1] = val;
+ ret = spi_sync(ihcd->spi, ihcd->wmsg);
+ if (ret < 0) /* should not happen */
+ pr_err("failed. val = %d addr = %d\n", val, addr);
+
+ trace_ice40_reg_write(addr, val, ihcd->w_tx_buf[0],
+ ihcd->w_tx_buf[1], ret);
+
+ mutex_unlock(&ihcd->wlock);
+}
+
+static int ice40_spi_reg_read(struct ice40_hcd *ihcd, u8 addr)
+{
+ int ret;
+
+ /*
+ * Register Read Pattern:
+ * TX: 1st byte is CMD (register + read)
+ * RX: 1st, 2nd byte Ignore, 3rd byte value.
+ *
+ * The Mutex is to protect concurrent register reads as
+ * we have only 1 SPI message struct.
+ */
+
+ mutex_lock(&ihcd->rlock);
+
+ ihcd->r_tx_buf[0] = READ_CMD(addr);
+ ret = spi_sync(ihcd->spi, ihcd->rmsg);
+ if (ret < 0)
+ pr_err("failed. addr = %d\n", addr);
+ else
+ ret = ihcd->r_rx_buf[2];
+
+ trace_ice40_reg_read(addr, ihcd->r_tx_buf[0], ret);
+
+ mutex_unlock(&ihcd->rlock);
+
+ return ret;
+}
+
+static int ice40_poll_xfer(struct ice40_hcd *ihcd, int usecs)
+{
+ ktime_t start = ktime_get();
+ u8 val, retry = 0;
+ u8 ret = ~0; /* time out */
+
+again:
+
+ /*
+ * The SPI transaction may take tens of usec. Use ktime
+ * based checks rather than loop count.
+ */
+ do {
+ val = ice40_spi_reg_read(ihcd, XFRST_REG);
+
+ if (XFR_MASK(val) != XFR_BUSY)
+ return val;
+
+ } while (ktime_us_delta(ktime_get(), start) < usecs);
+
+ /*
+ * The SPI transaction involves a context switch. For any
+ * reason, if we are scheduled out more than usecs after
+ * the 1st read, this extra read will help.
+ */
+ if (!retry) {
+ retry = 1;
+ goto again;
+ }
+
+ return ret;
+}
+
+static int
+ice40_handshake(struct ice40_hcd *ihcd, u8 reg, u8 mask, u8 done, int usecs)
+{
+ ktime_t start = ktime_get();
+ u8 val, retry = 0;
+
+again:
+ do {
+ val = ice40_spi_reg_read(ihcd, reg);
+ val &= mask;
+
+ if (val == done)
+ return 0;
+
+ } while (ktime_us_delta(ktime_get(), start) < usecs);
+
+ if (!retry) {
+ retry = 1;
+ goto again;
+ }
+
+ return -ETIMEDOUT;
+}
+
+
+static const char hcd_name[] = "ice40-hcd";
+
+static int ice40_reset(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ u8 ctrl, status;
+ int ret = 0;
+
+ /*
+ * Program the defualt address 0. The device address is
+ * re-programmed after SET_ADDRESS in URB handling path.
+ */
+ ihcd->devnum = 0;
+ ice40_spi_reg_write(ihcd, 0, FADDR_REG);
+
+ ihcd->wblen0 = ~0;
+ /*
+ * Read the line state. This driver is loaded after the
+ * UICC card insertion. So the line state should indicate
+ * that a Full-speed device is connected. Return error
+ * if there is no device connected.
+ *
+ * There can be no device connected during debug. A debugfs
+ * file is provided to sample the bus line and update the
+ * port flags accordingly.
+ */
+
+ if (debugger)
+ goto out;
+
+ ctrl = ice40_spi_reg_read(ihcd, CTRL0_REG);
+ ice40_spi_reg_write(ihcd, ctrl | DET_BUS_CTRL, CTRL0_REG);
+
+ ret = ice40_handshake(ihcd, CTRL0_REG, DET_BUS_CTRL, 0, 5000);
+ if (ret) {
+ pr_err("bus detection failed\n");
+ goto out;
+ }
+
+ status = ice40_spi_reg_read(ihcd, XFRST_REG);
+ pr_debug("line state (D+, D-) is %d\n", LINE_STATE(status));
+
+ if (status & DPST) {
+ pr_debug("Full speed device connected\n");
+ ihcd->port_flags |= USB_PORT_STAT_CONNECTION;
+ } else {
+ pr_err("No device connected\n");
+ ret = -ENODEV;
+ }
+out:
+ return ret;
+}
+
+static int ice40_run(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ /*
+ * HCD_FLAG_POLL_RH flag is not set by us. Core will not poll
+ * for the port status periodically. This uses_new_polling
+ * flag tells core that this hcd will call usb_hcd_poll_rh_status
+ * upon port change.
+ */
+ hcd->uses_new_polling = 1;
+
+ /*
+ * Cache the ctrl0 register to avoid multiple reads. This register
+ * is written during reset and resume.
+ */
+ ihcd->ctrl0 = ice40_spi_reg_read(ihcd, CTRL0_REG);
+ ihcd->ctrl0 |= SOFEN_CTRL;
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0, CTRL0_REG);
+
+ return 0;
+}
+
+static void ice40_stop(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ cancel_work_sync(&ihcd->async_work);
+}
+
+/*
+ * The _Error looks odd. But very helpful when looking for
+ * any errors in logs.
+ */
+static char __maybe_unused *xfr_status_string(int status)
+{
+ switch (XFR_MASK(status)) {
+ case XFR_SUCCESS:
+ return "Ack";
+ case XFR_BUSY:
+ return "Busy_Error";
+ case XFR_PKTERR:
+ return "Pkt_Error";
+ case XFR_PIDERR:
+ return "PID_Error";
+ case XFR_NAK:
+ return "Nak";
+ case XFR_STALL:
+ return "Stall_Error";
+ case XFR_WRONGPID:
+ return "WrongPID_Error";
+ case XFR_CRCERR:
+ return "CRC_Error";
+ case XFR_TOGERR:
+ return "Togg_Error";
+ case XFR_BADLEN:
+ return "BadLen_Error";
+ case XFR_TIMEOUT:
+ return "Timeout_Error";
+ default:
+ return "Unknown_Error";
+ }
+}
+
+static int ice40_xfer_setup(struct ice40_hcd *ihcd, struct urb *urb)
+{
+ struct usb_host_endpoint *ep = urb->ep;
+ struct ice40_ep *iep = ep->hcpriv;
+ void *buf = urb->setup_packet;
+ int ret, status;
+ u8 cmd;
+
+ /*
+ * SETUP transaction Handling:
+ * - copy the setup buffer to SUBUF fifo
+ * - Program HCMD register to initiate the SETP transaction.
+ * - poll for completion by reading XFRST register.
+ * - Interpret the result.
+ */
+
+ ihcd->setup_buf[0] = WRITE_CMD(SUBUF_REG);
+ ihcd->setup_xfr[1].tx_buf = buf;
+ ihcd->setup_xfr[1].len = sizeof(struct usb_ctrlrequest);
+
+ ret = spi_sync(ihcd->spi, ihcd->setup_msg);
+ if (ret < 0) {
+ pr_err("SPI transfer failed\n");
+ status = ret = -EIO;
+ goto out;
+ }
+
+ cmd = HCMD_PT(2) | HCMD_TOGV(0) | HCMD_BSEL(0) | HCMD_EP(0);
+ ice40_spi_reg_write(ihcd, cmd, HCMD_REG);
+
+ status = ice40_poll_xfer(ihcd, 1000);
+ switch (XFR_MASK(status)) {
+ case XFR_SUCCESS:
+ iep->xcat_err = 0;
+ ret = 0;
+ break;
+ case XFR_NAK: /* Device should not return Nak for SETUP */
+ case XFR_STALL:
+ iep->xcat_err = 0;
+ ret = -EPIPE;
+ break;
+ case XFR_PKTERR:
+ case XFR_PIDERR:
+ case XFR_WRONGPID:
+ case XFR_CRCERR:
+ case XFR_TIMEOUT:
+ if (++iep->xcat_err < 8)
+ ret = -EINPROGRESS;
+ else
+ ret = -EPROTO;
+ break;
+ default:
+ pr_err("transaction timed out\n");
+ ret = -EIO;
+ }
+
+out:
+ trace_ice40_setup(xfr_status_string(status), ret);
+ return ret;
+}
+
+static int ice40_xfer_in(struct ice40_hcd *ihcd, struct urb *urb)
+{
+ struct usb_host_endpoint *ep = urb->ep;
+ struct usb_device *udev = urb->dev;
+ u32 total_len = urb->transfer_buffer_length;
+ u16 maxpacket = usb_endpoint_maxp(&ep->desc);
+ u8 epnum = usb_pipeendpoint(urb->pipe);
+ bool is_out = usb_pipeout(urb->pipe);
+ struct ice40_ep *iep = ep->hcpriv;
+ u8 cmd, status, len = 0, t, expected_len;
+ void *buf;
+ int ret;
+ bool short_packet = true;
+
+ if (epnum == 0 && ihcd->ep0_state == STATUS_PHASE) {
+ expected_len = 0;
+ buf = NULL;
+ t = 1; /* STATUS PHASE is always DATA1 */
+ } else {
+ expected_len = min_t(u32, maxpacket,
+ total_len - urb->actual_length);
+ buf = urb->transfer_buffer + urb->actual_length;
+ t = usb_gettoggle(udev, epnum, is_out);
+ }
+
+ /*
+ * IN transaction Handling:
+ * - Program HCMD register to initiate the IN transaction.
+ * - poll for completion by reading XFRST register.
+ * - Interpret the result.
+ * - If ACK is received and we expect some data, read RBLEN
+ * - Read the data from RBUF
+ */
+
+ cmd = HCMD_PT(0) | HCMD_TOGV(t) | HCMD_BSEL(0) | HCMD_EP(epnum);
+ ice40_spi_reg_write(ihcd, cmd, HCMD_REG);
+
+ status = ice40_poll_xfer(ihcd, 1000);
+ switch (XFR_MASK(status)) {
+ case XFR_SUCCESS:
+ usb_dotoggle(udev, epnum, is_out);
+ iep->xcat_err = 0;
+ ret = 0;
+ if ((expected_len == 64) && (status & R64B))
+ short_packet = false;
+ break;
+ case XFR_NAK:
+ iep->xcat_err = 0;
+ ret = -EINPROGRESS;
+ break;
+ case XFR_TOGERR:
+ /*
+ * Peripheral had missed the previous Ack and sent
+ * the same packet again. Ack is sent by the hardware.
+ * As the data is received already, ignore this
+ * event.
+ */
+ ret = -EINPROGRESS;
+ break;
+ case XFR_PKTERR:
+ case XFR_PIDERR:
+ case XFR_WRONGPID:
+ case XFR_CRCERR:
+ case XFR_TIMEOUT:
+ if (++iep->xcat_err < 8)
+ ret = -EINPROGRESS;
+ else
+ ret = -EPROTO;
+ break;
+ case XFR_STALL:
+ ret = -EPIPE;
+ break;
+ case XFR_BADLEN:
+ ret = -EOVERFLOW;
+ break;
+ default:
+ pr_err("transaction timed out\n");
+ ret = -EIO;
+ }
+
+ /*
+ * Proceed further only if Ack is received and
+ * we are expecting some data.
+ */
+ if (ret || !expected_len)
+ goto out;
+
+ if (short_packet)
+ len = ice40_spi_reg_read(ihcd, RBLEN_REG);
+ else
+ len = 64;
+
+ /* babble condition */
+ if (len > expected_len) {
+ pr_err("overflow condition\n");
+ ret = -EOVERFLOW;
+ goto out;
+ }
+
+ /*
+ * zero len packet received. nothing to read from
+ * FIFO.
+ */
+ if (len == 0) {
+ ret = 0;
+ goto out;
+ }
+
+ ihcd->in_buf[0] = READ_CMD(RBUF0_REG);
+
+ ihcd->in_xfr[1].rx_buf = buf;
+ ihcd->in_xfr[1].len = len;
+
+ ret = spi_sync(ihcd->spi, ihcd->in_msg);
+ if (ret < 0) {
+ pr_err("SPI transfer failed\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ urb->actual_length += len;
+ if ((urb->actual_length == total_len) ||
+ (len < expected_len))
+ ret = 0; /* URB completed */
+ else
+ ret = -EINPROGRESS; /* still pending */
+out:
+ trace_ice40_in(epnum, xfr_status_string(status), len,
+ expected_len, ret);
+ return ret;
+}
+
+static int ice40_xfer_out(struct ice40_hcd *ihcd, struct urb *urb)
+{
+ struct usb_host_endpoint *ep = urb->ep;
+ struct usb_device *udev = urb->dev;
+ u32 total_len = urb->transfer_buffer_length;
+ u16 maxpacket = usb_endpoint_maxp(&ep->desc);
+ u8 epnum = usb_pipeendpoint(urb->pipe);
+ bool is_out = usb_pipeout(urb->pipe);
+ struct ice40_ep *iep = ep->hcpriv;
+ u8 cmd, status, len, t;
+ void *buf;
+ int ret;
+
+ if (epnum == 0 && ihcd->ep0_state == STATUS_PHASE) {
+ len = 0;
+ buf = NULL;
+ t = 1; /* STATUS PHASE is always DATA1 */
+ } else {
+ len = min_t(u32, maxpacket, total_len - urb->actual_length);
+ buf = urb->transfer_buffer + urb->actual_length;
+ t = usb_gettoggle(udev, epnum, is_out);
+ }
+
+ /*
+ * OUT transaction Handling:
+ * - If we need to send data, write the data to WBUF Fifo
+ * - Program the WBLEN register
+ * - Program HCMD register to initiate the OUT transaction.
+ * - poll for completion by reading XFRST register.
+ * - Interpret the result.
+ */
+
+
+ if (!len)
+ goto no_data;
+
+ ihcd->out_buf[0] = WRITE_CMD(WBUF0_REG);
+
+ ihcd->out_xfr[1].tx_buf = buf;
+ ihcd->out_xfr[1].len = len;
+
+ ret = spi_sync(ihcd->spi, ihcd->out_msg);
+ if (ret < 0) {
+ pr_err("SPI transaction failed\n");
+ status = ret = -EIO;
+ goto out;
+ }
+
+no_data:
+ /*
+ * Cache the WBLEN register and update it only if it
+ * is changed from the previous value.
+ */
+ if (len != ihcd->wblen0) {
+ ice40_spi_reg_write(ihcd, len, WBLEN_REG);
+ ihcd->wblen0 = len;
+ }
+
+ cmd = HCMD_PT(1) | HCMD_TOGV(t) | HCMD_BSEL(0) | HCMD_EP(epnum);
+ ice40_spi_reg_write(ihcd, cmd, HCMD_REG);
+
+ status = ice40_poll_xfer(ihcd, 1000);
+ switch (XFR_MASK(status)) {
+ case XFR_SUCCESS:
+ usb_dotoggle(udev, epnum, is_out);
+ urb->actual_length += len;
+ iep->xcat_err = 0;
+ if (!len || (urb->actual_length == total_len))
+ ret = 0; /* URB completed */
+ else
+ ret = -EINPROGRESS; /* pending */
+ break;
+ case XFR_NAK:
+ iep->xcat_err = 0;
+ ret = -EINPROGRESS;
+ break;
+ case XFR_PKTERR:
+ case XFR_PIDERR:
+ case XFR_WRONGPID:
+ case XFR_CRCERR:
+ case XFR_TIMEOUT:
+ if (++iep->xcat_err < 8)
+ ret = -EINPROGRESS;
+ else
+ ret = -EPROTO;
+ break;
+ case XFR_STALL:
+ ret = -EPIPE;
+ break;
+ case XFR_BADLEN:
+ ret = -EOVERFLOW;
+ break;
+ default:
+ pr_err("transaction timed out\n");
+ ret = -EIO;
+ }
+
+out:
+ trace_ice40_out(epnum, xfr_status_string(status), len, ret);
+ return ret;
+}
+
+static int ice40_process_urb(struct ice40_hcd *ihcd, struct urb *urb)
+{
+ struct usb_device *udev = urb->dev;
+ u8 devnum = usb_pipedevice(urb->pipe);
+ bool is_out = usb_pipeout(urb->pipe);
+ u32 total_len = urb->transfer_buffer_length;
+ int ret = 0;
+
+ /*
+ * The USB device address can be reset to 0 by core temporarily
+ * during reset recovery process. Don't assume anything about
+ * device address. The device address is programmed as 0 by
+ * default. If the device address is different to the previous
+ * cached value, re-program it here before proceeding. The device
+ * address register (FADDR) holds the value across multiple
+ * transactions and we support only one device.
+ */
+ if (ihcd->devnum != devnum) {
+ ice40_spi_reg_write(ihcd, devnum, FADDR_REG);
+ ihcd->devnum = devnum;
+ }
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ switch (ihcd->ep0_state) {
+ case SETUP_PHASE:
+ trace_ice40_ep0("SETUP");
+ ret = ice40_xfer_setup(ihcd, urb);
+ if (ret)
+ break;
+ if (total_len) {
+ ihcd->ep0_state = DATA_PHASE;
+ /*
+ * Data stage always begin with
+ * DATA1 PID.
+ */
+ usb_settoggle(udev, 0, is_out, 1);
+ } else {
+ ihcd->ep0_state = STATUS_PHASE;
+ goto do_status;
+ }
+ /* fall through */
+ case DATA_PHASE:
+ trace_ice40_ep0("DATA");
+ if (is_out)
+ ret = ice40_xfer_out(ihcd, urb);
+ else
+ ret = ice40_xfer_in(ihcd, urb);
+ if (ret)
+ break;
+ /* DATA Phase is completed successfully */
+ ihcd->ep0_state = STATUS_PHASE;
+ /* fall through */
+ case STATUS_PHASE:
+do_status:
+ trace_ice40_ep0("STATUS");
+ /* zero len DATA transfers have IN status */
+ if (!total_len || is_out)
+ ret = ice40_xfer_in(ihcd, urb);
+ else
+ ret = ice40_xfer_out(ihcd, urb);
+ if (ret)
+ break;
+ ihcd->ep0_state = SETUP_PHASE;
+ break;
+ default:
+ pr_err("unknown stage for a control transfer\n");
+ break;
+ }
+ break;
+ case PIPE_BULK:
+ if (is_out)
+ ret = ice40_xfer_out(ihcd, urb);
+ else
+ ret = ice40_xfer_in(ihcd, urb);
+ /*
+ * We may have to support zero len packet terminations
+ * for URB_ZERO_PACKET URBs.
+ */
+ break;
+ default:
+ pr_err("IN/ISO transfers not supported\n");
+ break;
+ }
+
+ return ret;
+}
+
+/* Must be called with spin lock and interrupts disabled */
+static void ice40_complete_urb(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ struct usb_host_endpoint *ep = urb->ep;
+ struct ice40_ep *iep = ep->hcpriv;
+ struct urb *first_urb;
+ bool needs_update = false;
+ bool control = usb_pipecontrol(urb->pipe);
+
+ /*
+ * If the active URB i.e the first URB in the ep list is being
+ * removed, clear the transaction error count. If it is a control
+ * URB ep0_state needs to be reset to SETUP_PHASE.
+ */
+ first_urb = list_first_entry(&ep->urb_list, struct urb, urb_list);
+ if (urb == first_urb)
+ needs_update = true;
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock(&ihcd->lock);
+ trace_ice40_urb_done(urb, status);
+ usb_hcd_giveback_urb(ihcd->hcd, urb, status);
+ spin_lock(&ihcd->lock);
+
+ if (needs_update) {
+ iep->xcat_err = 0;
+ if (control)
+ ihcd->ep0_state = SETUP_PHASE;
+ }
+}
+
+static void ice40_async_work(struct work_struct *work)
+{
+ struct ice40_hcd *ihcd = container_of(work,
+ struct ice40_hcd, async_work);
+ struct usb_hcd *hcd = ihcd->hcd;
+ struct list_head *tmp, *uent, *utmp;
+ struct ice40_ep *iep;
+ struct usb_host_endpoint *ep;
+ struct urb *urb;
+ unsigned long flags;
+ int status;
+
+ /*
+ * Traverse the active endpoints circularly and process URBs.
+ * If any endpoint is marked for unlinking, the URBs are
+ * completed here. The endpoint is removed from active list
+ * if a URB is retired with -EPIPE/-EPROTO errors.
+ */
+
+ spin_lock_irqsave(&ihcd->lock, flags);
+
+ if (list_empty(&ihcd->async_list))
+ goto out;
+
+ iep = list_first_entry(&ihcd->async_list, struct ice40_ep, ep_list);
+ while (1) {
+ ep = iep->ep;
+
+ urb = list_first_entry(&ep->urb_list, struct urb, urb_list);
+ if (urb->unlinked) {
+ status = urb->unlinked;
+ } else {
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+ status = ice40_process_urb(ihcd, urb);
+ spin_lock_irqsave(&ihcd->lock, flags);
+ }
+
+ if ((status == -EPIPE) || (status == -EPROTO))
+ iep->halted = true;
+
+ if (status != -EINPROGRESS)
+ ice40_complete_urb(hcd, urb, status);
+
+ if (iep->unlinking) {
+ list_for_each_safe(uent, utmp, &ep->urb_list) {
+ urb = list_entry(uent, struct urb, urb_list);
+ if (urb->unlinked)
+ ice40_complete_urb(hcd, urb, 0);
+ }
+ iep->unlinking = false;
+ }
+
+ tmp = iep->ep_list.next;
+ if (list_empty(&ep->urb_list) || iep->halted) {
+ list_del_init(&iep->ep_list);
+
+ if (list_empty(&ihcd->async_list))
+ break;
+ }
+
+ if (tmp == &ihcd->async_list)
+ tmp = tmp->next;
+ iep = list_entry(tmp, struct ice40_ep, ep_list);
+ }
+out:
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+}
+
+static int
+ice40_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ struct usb_device *udev = urb->dev;
+ struct usb_host_endpoint *ep = urb->ep;
+ bool is_out = usb_pipeout(urb->pipe);
+ u8 epnum = usb_pipeendpoint(urb->pipe);
+ struct ice40_ep *iep;
+ unsigned long flags;
+ int ret;
+
+ /*
+ * This bridge chip supports only Full-speed. So ISO is not
+ * supported. Interrupt support is not implemented as there
+ * is no use case.
+ */
+ if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
+ pr_debug("iso and int xfers not supported\n");
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ spin_lock_irqsave(&ihcd->lock, flags);
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto rel_lock;
+
+ trace_ice40_urb_enqueue(urb);
+
+ iep = ep->hcpriv;
+ if (!iep) {
+ iep = kzalloc(sizeof(struct ice40_ep), GFP_ATOMIC);
+ if (!iep) {
+ pr_debug("fail to allocate iep\n");
+ ret = -ENOMEM;
+ goto unlink;
+ }
+ ep->hcpriv = iep;
+ INIT_LIST_HEAD(&iep->ep_list);
+ iep->ep = ep;
+ usb_settoggle(udev, epnum, is_out, 0);
+ if (usb_pipecontrol(urb->pipe))
+ ihcd->ep0_state = SETUP_PHASE;
+ }
+
+ /*
+ * We expect the interface driver to clear the stall condition
+ * before queueing another URB. For example mass storage
+ * device may STALL a bulk endpoint for un-supported command.
+ * The storage driver clear the STALL condition before queueing
+ * another URB.
+ */
+ iep->halted = false;
+ if (list_empty(&iep->ep_list))
+ list_add_tail(&iep->ep_list, &ihcd->async_list);
+
+ queue_work(ihcd->wq, &ihcd->async_work);
+
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+
+ return 0;
+unlink:
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+rel_lock:
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+out:
+ return ret;
+}
+
+static int
+ice40_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ struct usb_host_endpoint *ep = urb->ep;
+ struct ice40_ep *iep;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ihcd->lock, flags);
+
+ ret = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (ret)
+ goto rel_lock;
+
+ trace_ice40_urb_dequeue(urb);
+ iep = ep->hcpriv;
+
+ /*
+ * If the endpoint is not in asynchronous schedule, complete
+ * the URB immediately. Otherwise mark it as being unlinked.
+ * The asynchronous schedule work will take care of completing
+ * the URB when this endpoint is encountered during traversal.
+ */
+ if (list_empty(&iep->ep_list))
+ ice40_complete_urb(hcd, urb, status);
+ else
+ iep->unlinking = true;
+
+rel_lock:
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+ return ret;
+}
+
+static void
+ice40_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct ice40_ep *iep = ep->hcpriv;
+
+ /*
+ * If there is no I/O on this endpoint before, ep->hcpriv
+ * will be NULL. nothing to do in this case.
+ */
+ if (!iep)
+ return;
+
+ if (!list_empty(&ep->urb_list))
+ pr_err("trying to disable an non-empty endpoint\n");
+
+ kfree(iep);
+ ep->hcpriv = NULL;
+}
+
+
+static int ice40_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ int ret = 0;
+
+ /*
+ * core calls hub_status_method during suspend/resume.
+ * return 0 if there is no port change. pcd_pending
+ * is set to true when a device is connected and line
+ * state is sampled via debugfs command. clear this
+ * flag after returning the port change status.
+ */
+ if (ihcd->pcd_pending) {
+ *buf = (1 << 1);
+ ret = 1;
+ ihcd->pcd_pending = false;
+ }
+
+ return ret;
+}
+
+static void ice40_hub_descriptor(struct usb_hub_descriptor *desc)
+{
+ /* There is nothing special about us!! */
+ desc->bDescLength = 9;
+ desc->bDescriptorType = 0x29;
+ desc->bNbrPorts = 1;
+ desc->wHubCharacteristics = cpu_to_le16(HUB_CHAR_NO_LPSM |
+ HUB_CHAR_NO_OCPM);
+ desc->bPwrOn2PwrGood = 0;
+ desc->bHubContrCurrent = 0;
+ desc->u.hs.DeviceRemovable[0] = 0;
+ desc->u.hs.DeviceRemovable[1] = ~0;
+}
+
+static int
+ice40_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ int ret = 0;
+ u8 ctrl;
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ /*
+ * We have only 1 port. No special locking is required while
+ * handling root hub commands. The bridge chip does not maintain
+ * any port states. Maintain different port states in software.
+ */
+ switch (typeReq) {
+ case ClearPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ /*
+ * The device is resumed as part of the root hub
+ * resume to simplify the resume sequence. so
+ * we may simply return from here. If device is
+ * resumed before root hub is suspended, this
+ * flags will be cleared here.
+ */
+ if (!(ihcd->port_flags & USB_PORT_STAT_SUSPEND))
+ break;
+ ihcd->port_flags &= ~USB_PORT_STAT_SUSPEND;
+ break;
+ case USB_PORT_FEAT_ENABLE:
+ ihcd->port_flags &= ~USB_PORT_STAT_ENABLE;
+ break;
+ case USB_PORT_FEAT_POWER:
+ ihcd->port_flags &= ~USB_PORT_STAT_POWER;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ ihcd->port_flags &= ~(USB_PORT_STAT_C_CONNECTION << 16);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ /* nothing special here */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case GetHubDescriptor:
+ ice40_hub_descriptor((struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ put_unaligned_le32(0, buf);
+ break;
+ case GetPortStatus:
+ if (wIndex != 1)
+ goto error;
+
+ /*
+ * Core resets the device and requests port status to
+ * stop the reset signaling. If there is a reset in
+ * progress, finish it here.
+ */
+ ctrl = ice40_spi_reg_read(ihcd, CTRL0_REG);
+ if (!(ctrl & RESET_CTRL))
+ ihcd->port_flags &= ~USB_PORT_STAT_RESET;
+
+ put_unaligned_le32(ihcd->port_flags, buf);
+ break;
+ case SetPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (ihcd->port_flags & USB_PORT_STAT_RESET)
+ goto error;
+ if (!(ihcd->port_flags & USB_PORT_STAT_ENABLE))
+ goto error;
+ /* SOFs will be stopped during root hub suspend */
+ ihcd->port_flags |= USB_PORT_STAT_SUSPEND;
+ break;
+ case USB_PORT_FEAT_POWER:
+ ihcd->port_flags |= USB_PORT_STAT_POWER;
+ break;
+ case USB_PORT_FEAT_RESET:
+ /* Good time to enable the port */
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0 |
+ RESET_CTRL, CTRL0_REG);
+ ihcd->port_flags |= USB_PORT_STAT_RESET;
+ ihcd->port_flags |= USB_PORT_STAT_ENABLE;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ default:
+error:
+ /* "protocol stall" on error */
+ ret = -EPIPE;
+ }
+
+ trace_ice40_hub_control(typeReq, wValue, wIndex, wLength, ret);
+ return ret;
+}
+
+static void ice40_spi_power_off(struct ice40_hcd *ihcd);
+static int ice40_bus_suspend(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ trace_ice40_bus_suspend(0); /* start */
+
+ /* This happens only during debugging */
+ if (!ihcd->devnum) {
+ pr_debug("device still not connected. abort suspend\n");
+ trace_ice40_bus_suspend(2); /* failure */
+ return -EAGAIN;
+ }
+ /*
+ * Stop sending the SOFs on downstream port. The device
+ * finds the bus idle and enter suspend. The device
+ * takes ~3 msec to enter suspend.
+ */
+ ihcd->ctrl0 &= ~SOFEN_CTRL;
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0, CTRL0_REG);
+ usleep_range(4500, 5000);
+
+ /*
+ * Power collapse the bridge chip to avoid the leakage
+ * current.
+ */
+ ice40_spi_power_off(ihcd);
+
+ trace_ice40_bus_suspend(1); /* successful */
+ pm_relax(&ihcd->spi->dev);
+ return 0;
+}
+
+static int ice40_spi_load_fw(struct ice40_hcd *ihcd);
+static int ice40_bus_resume(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ u8 ctrl0;
+ int ret;
+
+ pm_stay_awake(&ihcd->spi->dev);
+ trace_ice40_bus_resume(0); /* start */
+ /*
+ * Power up the bridge chip and load the configuration file.
+ * Re-program the previous settings. For now we need to
+ * update the device address only.
+ */
+ ice40_spi_load_fw(ihcd);
+ ice40_spi_reg_write(ihcd, ihcd->devnum, FADDR_REG);
+ ihcd->wblen0 = ~0;
+
+ /*
+ * Program the bridge chip to drive resume signaling. The SOFs
+ * are automatically transmitted after resume completion. It
+ * will take ~20 msec for resume completion.
+ */
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0 | RESUME_CTRL, CTRL0_REG);
+ usleep_range(20000, 21000);
+ ret = ice40_handshake(ihcd, CTRL0_REG, RESUME_CTRL, 0, 5000);
+ if (ret) {
+ pr_err("resume failed\n");
+ trace_ice40_bus_resume(2); /* failure */
+ return -ENODEV;
+ }
+
+ ctrl0 = ice40_spi_reg_read(ihcd, CTRL0_REG);
+ if (!(ctrl0 & SOFEN_CTRL)) {
+ pr_err("SOFs are not transmitted after resume\n");
+ trace_ice40_bus_resume(3); /* failure */
+ return -ENODEV;
+ }
+
+ ihcd->port_flags &= ~USB_PORT_STAT_SUSPEND;
+ ihcd->ctrl0 |= SOFEN_CTRL;
+
+ trace_ice40_bus_resume(1); /* success */
+ return 0;
+}
+
+static void ice40_set_autosuspend_delay(struct usb_device *dev)
+{
+ /*
+ * Immediate suspend for root hub and 500 msec auto-suspend
+ * timeout for the card.
+ */
+ if (!dev->parent)
+ pm_runtime_set_autosuspend_delay(&dev->dev, 0);
+ else
+ pm_runtime_set_autosuspend_delay(&dev->dev, 500);
+}
+
+static const struct hc_driver ice40_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "ICE40 SPI Host Controller",
+ .hcd_priv_size = sizeof(struct ice40_hcd *),
+ .flags = HCD_USB11,
+
+ /* setup and clean up */
+ .reset = ice40_reset,
+ .start = ice40_run,
+ .stop = ice40_stop,
+
+ /* endpoint and I/O routines */
+ .urb_enqueue = ice40_urb_enqueue,
+ .urb_dequeue = ice40_urb_dequeue,
+ .endpoint_disable = ice40_endpoint_disable,
+
+ /* Root hub operations */
+ .hub_status_data = ice40_hub_status_data,
+ .hub_control = ice40_hub_control,
+ .bus_suspend = ice40_bus_suspend,
+ .bus_resume = ice40_bus_resume,
+
+ .set_autosuspend_delay = ice40_set_autosuspend_delay,
+};
+
+static int ice40_spi_parse_dt(struct ice40_hcd *ihcd)
+{
+ struct device_node *node = ihcd->spi->dev.of_node;
+ int ret = 0;
+
+ if (!node) {
+ pr_err("device specific info missing\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ihcd->reset_gpio = of_get_named_gpio(node, "lattice,reset-gpio", 0);
+ if (ihcd->reset_gpio < 0) {
+ pr_err("reset gpio is missing\n");
+ ret = ihcd->reset_gpio;
+ goto out;
+ }
+
+ ihcd->slave_select_gpio = of_get_named_gpio(node,
+ "lattice,slave-select-gpio", 0);
+ if (ihcd->slave_select_gpio < 0) {
+ pr_err("slave select gpio is missing\n");
+ ret = ihcd->slave_select_gpio;
+ goto out;
+ }
+
+ ihcd->config_done_gpio = of_get_named_gpio(node,
+ "lattice,config-done-gpio", 0);
+ if (ihcd->config_done_gpio < 0) {
+ pr_err("config done gpio is missing\n");
+ ret = ihcd->config_done_gpio;
+ goto out;
+ }
+
+ ihcd->vcc_en_gpio = of_get_named_gpio(node, "lattice,vcc-en-gpio", 0);
+ if (ihcd->vcc_en_gpio < 0) {
+ pr_err("vcc enable gpio is missing\n");
+ ret = ihcd->vcc_en_gpio;
+ goto out;
+ }
+
+ /*
+ * When clk-en-gpio is present, it is used to enable the 19.2 MHz
+ * clock from MSM to the bridge chip. Otherwise on-board clock
+ * is used.
+ */
+ ihcd->clk_en_gpio = of_get_named_gpio(node, "lattice,clk-en-gpio", 0);
+ if (ihcd->clk_en_gpio < 0)
+ ihcd->clk_en_gpio = 0;
+out:
+ return ret;
+}
+
+static void ice40_spi_power_off(struct ice40_hcd *ihcd)
+{
+ if (!ihcd->powered)
+ return;
+
+ gpio_direction_output(ihcd->vcc_en_gpio, 0);
+ regulator_disable(ihcd->core_vcc);
+ regulator_disable(ihcd->spi_vcc);
+ if (ihcd->gpio_vcc)
+ regulator_disable(ihcd->gpio_vcc);
+ if (ihcd->clk_en_gpio)
+ gpio_direction_output(ihcd->clk_en_gpio, 0);
+
+ ihcd->powered = false;
+}
+
+static int ice40_spi_power_up(struct ice40_hcd *ihcd)
+{
+ int ret;
+
+ if (ihcd->clk_en_gpio) {
+ ret = gpio_direction_output(ihcd->clk_en_gpio, 1);
+ if (ret < 0) {
+ pr_err("fail to enabel clk %d\n", ret);
+ goto out;
+ }
+ }
+
+ if (ihcd->gpio_vcc) {
+ ret = regulator_enable(ihcd->gpio_vcc); /* 1.8 V */
+ if (ret < 0) {
+ pr_err("fail to enable gpio vcc\n");
+ goto disable_clk;
+ }
+ }
+
+ ret = regulator_enable(ihcd->spi_vcc); /* 1.8 V */
+ if (ret < 0) {
+ pr_err("fail to enable spi vcc\n");
+ goto disable_gpio_vcc;
+ }
+
+ ret = regulator_enable(ihcd->core_vcc); /* 1.2 V */
+ if (ret < 0) {
+ pr_err("fail to enable core vcc\n");
+ goto disable_spi_vcc;
+ }
+
+ ret = gpio_direction_output(ihcd->vcc_en_gpio, 1);
+ if (ret < 0) {
+ pr_err("fail to assert vcc gpio\n");
+ goto disable_core_vcc;
+ }
+
+ ihcd->powered = true;
+
+ return 0;
+
+disable_core_vcc:
+ regulator_disable(ihcd->core_vcc);
+disable_spi_vcc:
+ regulator_disable(ihcd->spi_vcc);
+disable_gpio_vcc:
+ if (ihcd->gpio_vcc)
+ regulator_disable(ihcd->gpio_vcc);
+disable_clk:
+ if (ihcd->clk_en_gpio)
+ gpio_direction_output(ihcd->clk_en_gpio, 0);
+out:
+ return ret;
+}
+
+static struct gpiomux_setting slave_select_setting = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
+};
+
+static int ice40_spi_cache_fw(struct ice40_hcd *ihcd)
+{
+ const struct firmware *fw;
+ void *buf;
+ size_t buf_len;
+ int ret;
+
+ ret = request_firmware(&fw, fw_name, &ihcd->spi->dev);
+ if (ret < 0) {
+ pr_err("fail to get the firmware\n");
+ goto out;
+ }
+
+ pr_debug("received firmware size = %zu\n", fw->size);
+
+ /*
+ * The bridge expects additional clock cycles after
+ * receiving the configuration data. We don't have a
+ * direct control over SPI clock. Add extra bytes
+ * to the confiration data.
+ */
+ buf_len = fw->size + 16;
+ buf = devm_kzalloc(&ihcd->spi->dev, buf_len, GFP_KERNEL);
+ if (!buf) {
+ pr_err("fail to allocate firmware buffer\n");
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ /*
+ * The firmware buffer can not be used for DMA as it
+ * is not physically contiguous. We copy the data
+ * in kmalloc buffer. This buffer will be freed only
+ * during unbind or rmmod.
+ */
+ memcpy(buf, fw->data, fw->size);
+ release_firmware(fw);
+
+ /*
+ * The bridge supports only 25 MHz during configuration
+ * file loading.
+ */
+ ihcd->fmsg_xfr[0].tx_buf = buf;
+ ihcd->fmsg_xfr[0].len = buf_len;
+ ihcd->fmsg_xfr[0].speed_hz = 25000000;
+
+ return 0;
+
+release:
+ release_firmware(fw);
+out:
+ return ret;
+}
+
+static int ice40_spi_load_fw(struct ice40_hcd *ihcd)
+{
+ int ret, i;
+ struct gpiomux_setting old_setting;
+
+ ret = gpio_direction_output(ihcd->reset_gpio, 0);
+ if (ret < 0) {
+ pr_err("fail to assert reset %d\n", ret);
+ goto out;
+ }
+
+ ret = gpio_direction_output(ihcd->vcc_en_gpio, 0);
+ if (ret < 0) {
+ pr_err("fail to de-assert vcc_en gpio %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * The bridge chip samples the chip select signal during
+ * power-up. If it is low, it enters SPI slave mode and
+ * accepts the configuration data from us. The chip
+ * select signal is managed by the SPI controller driver.
+ * We temporarily override the chip select config to
+ * drive it low. The SPI bus needs to be locked down during
+ * this period to avoid other slave data going to our
+ * bridge chip.
+ *
+ */
+ spi_bus_lock(ihcd->spi->master);
+
+ ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_SUSPENDED,
+ &slave_select_setting, &old_setting);
+ if (ret < 0) {
+ pr_err("fail to select the slave %d\n", ret);
+ goto out;
+ }
+
+ ret = ice40_spi_power_up(ihcd);
+ if (ret < 0) {
+ pr_err("fail to power up the chip\n");
+ goto out;
+ }
+
+
+ /*
+ * The databook says 1200 usec is required before the
+ * chip becomes ready for the SPI transfer.
+ */
+ usleep_range(1200, 1250);
+
+ ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_SUSPENDED,
+ &old_setting, NULL);
+ if (ret < 0) {
+ pr_err("fail to de-select the slave %d\n", ret);
+ goto power_off;
+ }
+
+ ret = spi_sync_locked(ihcd->spi, ihcd->fmsg);
+
+ spi_bus_unlock(ihcd->spi->master);
+
+ if (ret < 0) {
+ pr_err("spi write failed\n");
+ goto power_off;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ ret = gpio_get_value(ihcd->config_done_gpio);
+ if (ret) {
+ pr_debug("config done asserted %d\n", i);
+ break;
+ }
+ udelay(1);
+ }
+
+ if (ret <= 0) {
+ pr_err("config done not asserted\n");
+ ret = -ENODEV;
+ goto power_off;
+ }
+
+ ret = gpio_direction_output(ihcd->reset_gpio, 1);
+ if (ret < 0) {
+ pr_err("fail to assert reset %d\n", ret);
+ goto power_off;
+ }
+ udelay(50);
+
+ ret = ice40_spi_reg_read(ihcd, XFRST_REG);
+ pr_debug("XFRST val is %x\n", ret);
+ if (!(ret & PLLOK)) {
+ pr_err("The PLL2 is not synchronized\n");
+ goto power_off;
+ }
+
+ pr_info("Firmware load success\n");
+
+ return 0;
+
+power_off:
+ ice40_spi_power_off(ihcd);
+out:
+ return ret;
+}
+
+static int ice40_spi_init_regulators(struct ice40_hcd *ihcd)
+{
+ int ret;
+
+ ihcd->spi_vcc = devm_regulator_get(&ihcd->spi->dev, "spi-vcc");
+ if (IS_ERR(ihcd->spi_vcc)) {
+ ret = PTR_ERR(ihcd->spi_vcc);
+ if (ret != -EPROBE_DEFER)
+ pr_err("fail to get spi-vcc %d\n", ret);
+ goto out;
+ }
+
+ ret = regulator_set_voltage(ihcd->spi_vcc, 1800000, 1800000);
+ if (ret < 0) {
+ pr_err("fail to set spi-vcc %d\n", ret);
+ goto out;
+ }
+
+ ihcd->core_vcc = devm_regulator_get(&ihcd->spi->dev, "core-vcc");
+ if (IS_ERR(ihcd->core_vcc)) {
+ ret = PTR_ERR(ihcd->core_vcc);
+ if (ret != -EPROBE_DEFER)
+ pr_err("fail to get core-vcc %d\n", ret);
+ goto out;
+ }
+
+ ret = regulator_set_voltage(ihcd->core_vcc, 1200000, 1200000);
+ if (ret < 0) {
+ pr_err("fail to set core-vcc %d\n", ret);
+ goto out;
+ }
+
+ if (!of_get_property(ihcd->spi->dev.of_node, "gpio-supply", NULL))
+ goto out;
+
+ ihcd->gpio_vcc = devm_regulator_get(&ihcd->spi->dev, "gpio");
+ if (IS_ERR(ihcd->gpio_vcc)) {
+ ret = PTR_ERR(ihcd->gpio_vcc);
+ if (ret != -EPROBE_DEFER)
+ pr_err("fail to get gpio_vcc %d\n", ret);
+ goto out;
+ }
+
+ ret = regulator_set_voltage(ihcd->gpio_vcc, 1800000, 1800000);
+ if (ret < 0) {
+ pr_err("fail to set gpio_vcc %d\n", ret);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static int ice40_spi_request_gpios(struct ice40_hcd *ihcd)
+{
+ int ret;
+
+ ret = devm_gpio_request(&ihcd->spi->dev, ihcd->reset_gpio,
+ "ice40_reset");
+ if (ret < 0) {
+ pr_err("fail to request reset gpio\n");
+ goto out;
+ }
+
+ ret = devm_gpio_request(&ihcd->spi->dev, ihcd->config_done_gpio,
+ "ice40_config_done");
+ if (ret < 0) {
+ pr_err("fail to request config_done gpio\n");
+ goto out;
+ }
+
+ ret = devm_gpio_request(&ihcd->spi->dev, ihcd->vcc_en_gpio,
+ "ice40_vcc_en");
+ if (ret < 0) {
+ pr_err("fail to request vcc_en gpio\n");
+ goto out;
+ }
+
+ if (ihcd->clk_en_gpio) {
+
+ ret = devm_gpio_request(&ihcd->spi->dev, ihcd->clk_en_gpio,
+ "ice40_clk_en");
+ if (ret < 0)
+ pr_err("fail to request clk_en gpio\n");
+ }
+
+out:
+ return ret;
+}
+
+static int
+ice40_spi_init_one_xfr(struct ice40_hcd *ihcd, enum ice40_xfr_type type)
+{
+ struct spi_message **m;
+ struct spi_transfer **t;
+ int n;
+
+ switch (type) {
+ case FIRMWARE_XFR:
+ m = &ihcd->fmsg;
+ t = &ihcd->fmsg_xfr;
+ n = 1;
+ break;
+ case REG_WRITE_XFR:
+ m = &ihcd->wmsg;
+ t = &ihcd->wmsg_xfr;
+ n = 1;
+ break;
+ case REG_READ_XFR:
+ m = &ihcd->rmsg;
+ t = &ihcd->rmsg_xfr;
+ n = 1;
+ break;
+ case SETUP_XFR:
+ m = &ihcd->setup_msg;
+ t = &ihcd->setup_xfr;
+ n = 2;
+ break;
+ case DATA_IN_XFR:
+ m = &ihcd->in_msg;
+ t = &ihcd->in_xfr;
+ n = 2;
+ break;
+ case DATA_OUT_XFR:
+ m = &ihcd->out_msg;
+ t = &ihcd->out_xfr;
+ n = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *m = devm_kzalloc(&ihcd->spi->dev, sizeof(**m), GFP_KERNEL);
+ if (*m == NULL)
+ goto out;
+
+ *t = devm_kzalloc(&ihcd->spi->dev, n * sizeof(**t), GFP_KERNEL);
+ if (*t == NULL)
+ goto out;
+
+ spi_message_init_with_transfers(*m, *t, n);
+
+ return 0;
+out:
+ return -ENOMEM;
+}
+
+static int ice40_spi_init_xfrs(struct ice40_hcd *ihcd)
+{
+ int ret = -ENOMEM;
+
+ ret = ice40_spi_init_one_xfr(ihcd, FIRMWARE_XFR);
+ if (ret < 0)
+ goto out;
+
+ ret = ice40_spi_init_one_xfr(ihcd, REG_WRITE_XFR);
+ if (ret < 0)
+ goto out;
+
+ ihcd->w_tx_buf = devm_kzalloc(&ihcd->spi->dev, 2, GFP_KERNEL);
+ if (!ihcd->w_tx_buf)
+ goto out;
+
+ ihcd->w_rx_buf = devm_kzalloc(&ihcd->spi->dev, 2, GFP_KERNEL);
+ if (!ihcd->w_rx_buf)
+ goto out;
+
+ ihcd->wmsg_xfr[0].tx_buf = ihcd->w_tx_buf;
+ ihcd->wmsg_xfr[0].rx_buf = ihcd->w_rx_buf;
+ ihcd->wmsg_xfr[0].len = 2;
+
+ ret = ice40_spi_init_one_xfr(ihcd, REG_READ_XFR);
+ if (ret < 0)
+ goto out;
+
+ ihcd->r_tx_buf = devm_kzalloc(&ihcd->spi->dev, 3, GFP_KERNEL);
+ if (!ihcd->r_tx_buf)
+ goto out;
+
+ ihcd->r_rx_buf = devm_kzalloc(&ihcd->spi->dev, 3, GFP_KERNEL);
+ if (!ihcd->r_rx_buf)
+ goto out;
+
+ ihcd->rmsg_xfr[0].tx_buf = ihcd->r_tx_buf;
+ ihcd->rmsg_xfr[0].rx_buf = ihcd->r_rx_buf;
+ ihcd->rmsg_xfr[0].len = 3;
+
+ ret = ice40_spi_init_one_xfr(ihcd, SETUP_XFR);
+ if (ret < 0)
+ goto out;
+
+ ihcd->setup_buf = devm_kzalloc(&ihcd->spi->dev, 1, GFP_KERNEL);
+ if (!ihcd->setup_buf)
+ goto out;
+ ihcd->setup_xfr[0].tx_buf = ihcd->setup_buf;
+ ihcd->setup_xfr[0].len = 1;
+
+ ret = ice40_spi_init_one_xfr(ihcd, DATA_IN_XFR);
+ if (ret < 0)
+ goto out;
+ ihcd->in_buf = devm_kzalloc(&ihcd->spi->dev, 2, GFP_KERNEL);
+ if (!ihcd->in_buf)
+ goto out;
+ ihcd->in_xfr[0].tx_buf = ihcd->in_buf;
+ ihcd->in_xfr[0].len = 2;
+
+ ret = ice40_spi_init_one_xfr(ihcd, DATA_OUT_XFR);
+ if (ret < 0)
+ goto out;
+ ihcd->out_buf = devm_kzalloc(&ihcd->spi->dev, 1, GFP_KERNEL);
+ if (!ihcd->out_buf)
+ goto out;
+ ihcd->out_xfr[0].tx_buf = ihcd->out_buf;
+ ihcd->out_xfr[0].len = 1;
+
+ return 0;
+
+out:
+ return -ENOMEM;
+}
+
+static int ice40_dbg_cmd_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, NULL, inode->i_private);
+}
+
+static ssize_t ice40_dbg_cmd_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct ice40_hcd *ihcd = s->private;
+ char buf[32];
+ int ret;
+ u8 status, addr;
+
+ memset(buf, 0x00, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (!strcmp(buf, "poll")) {
+ if (!HCD_RH_RUNNING(ihcd->hcd)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ /*
+ * The bridge chip supports interrupt for device
+ * connect and disconnect. We don;t have a real
+ * use case of connect/disconnect. This debugfs
+ * interface provides a way to enumerate the
+ * attached device.
+ */
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0 |
+ DET_BUS_CTRL, CTRL0_REG);
+ ice40_handshake(ihcd, CTRL0_REG, DET_BUS_CTRL, 0, 5000);
+ status = ice40_spi_reg_read(ihcd, XFRST_REG);
+ if ((status & DPST)) {
+ ihcd->port_flags |= USB_PORT_STAT_CONNECTION;
+ ihcd->port_flags |= USB_PORT_STAT_C_CONNECTION << 16;
+ ihcd->pcd_pending = true;
+ usb_hcd_poll_rh_status(ihcd->hcd);
+ } else if (ihcd->port_flags & USB_PORT_STAT_CONNECTION) {
+ ihcd->port_flags &= ~USB_PORT_STAT_ENABLE;
+ ihcd->port_flags &= ~USB_PORT_STAT_CONNECTION;
+ ihcd->port_flags |= (USB_PORT_STAT_C_CONNECTION << 16);
+ ihcd->pcd_pending = true;
+ usb_hcd_poll_rh_status(ihcd->hcd);
+ }
+ } else if (!strcmp(buf, "rwtest")) {
+ ihcd->devnum = 1;
+ ice40_spi_reg_write(ihcd, 0x1, FADDR_REG);
+ addr = ice40_spi_reg_read(ihcd, FADDR_REG);
+ pr_info("addr written was 0x1 read as %x\n", addr);
+ } else if (!strcmp(buf, "force_disconnect")) {
+ if (!HCD_RH_RUNNING(ihcd->hcd)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ /*
+ * Forcfully disconnect the device. This is required
+ * for simulating the disconnect on a USB port which
+ * does not have pull-down resistors.
+ */
+ ihcd->port_flags &= ~USB_PORT_STAT_ENABLE;
+ ihcd->port_flags &= ~USB_PORT_STAT_CONNECTION;
+ ihcd->port_flags |= (USB_PORT_STAT_C_CONNECTION << 16);
+ ihcd->pcd_pending = true;
+ usb_hcd_poll_rh_status(ihcd->hcd);
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = count;
+out:
+ return ret;
+}
+
+const struct file_operations ice40_dbg_cmd_ops = {
+ .open = ice40_dbg_cmd_open,
+ .write = ice40_dbg_cmd_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ice40_debugfs_init(struct ice40_hcd *ihcd)
+{
+ struct dentry *dir;
+ int ret = 0;
+
+ dir = debugfs_create_dir("ice40_hcd", NULL);
+
+ if (!dir || IS_ERR(dir)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ihcd->dbg_root = dir;
+
+ dir = debugfs_create_file("command", S_IWUSR, ihcd->dbg_root, ihcd,
+ &ice40_dbg_cmd_ops);
+
+ if (!dir) {
+ debugfs_remove_recursive(ihcd->dbg_root);
+ ihcd->dbg_root = NULL;
+ ret = -ENODEV;
+ }
+
+out:
+ return ret;
+}
+
+static int ice40_spi_probe(struct spi_device *spi)
+{
+ struct ice40_hcd *ihcd;
+ int ret;
+
+ ihcd = devm_kzalloc(&spi->dev, sizeof(*ihcd), GFP_KERNEL);
+ if (!ihcd) {
+ pr_err("fail to allocate ihcd\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ ihcd->spi = spi;
+
+ ret = ice40_spi_parse_dt(ihcd);
+ if (ret) {
+ pr_err("fail to parse dt node\n");
+ goto out;
+ }
+
+ ret = ice40_spi_init_regulators(ihcd);
+ if (ret) {
+ pr_err("fail to init regulators\n");
+ goto out;
+ }
+
+ ret = ice40_spi_request_gpios(ihcd);
+ if (ret) {
+ pr_err("fail to request gpios\n");
+ goto out;
+ }
+
+ spin_lock_init(&ihcd->lock);
+ INIT_LIST_HEAD(&ihcd->async_list);
+ INIT_WORK(&ihcd->async_work, ice40_async_work);
+ mutex_init(&ihcd->wlock);
+ mutex_init(&ihcd->rlock);
+
+ /*
+ * Enable all our trace points. Useful in debugging card
+ * enumeration issues.
+ */
+ ret = trace_set_clr_event(__stringify(TRACE_SYSTEM), NULL, 1);
+ if (ret < 0)
+ pr_err("fail to enable trace points with %d\n", ret);
+
+ ihcd->wq = create_singlethread_workqueue("ice40_wq");
+ if (!ihcd->wq) {
+ pr_err("fail to create workqueue\n");
+ ret = -ENOMEM;
+ goto destroy_mutex;
+ }
+
+ ret = ice40_spi_init_xfrs(ihcd);
+ if (ret) {
+ pr_err("fail to init spi xfrs %d\n", ret);
+ goto destroy_wq;
+ }
+
+ ret = ice40_spi_cache_fw(ihcd);
+ if (ret) {
+ pr_err("fail to cache fw %d\n", ret);
+ goto destroy_wq;
+ }
+
+ ret = ice40_spi_load_fw(ihcd);
+ if (ret) {
+ pr_err("fail to load fw %d\n", ret);
+ goto destroy_wq;
+ }
+
+ ihcd->hcd = usb_create_hcd(&ice40_hc_driver, &spi->dev, "ice40");
+ if (!ihcd->hcd) {
+ pr_err("fail to alloc hcd\n");
+ ret = -ENOMEM;
+ goto power_off;
+ }
+ *((struct ice40_hcd **) ihcd->hcd->hcd_priv) = ihcd;
+
+ ret = usb_add_hcd(ihcd->hcd, 0, 0);
+
+ if (ret < 0) {
+ pr_err("fail to add HCD\n");
+ goto put_hcd;
+ }
+
+ ice40_debugfs_init(ihcd);
+
+ /*
+ * We manage the power states of the bridge chip
+ * as part of root hub suspend/resume. We don't
+ * need to implement any additional runtime PM
+ * methods.
+ */
+ pm_runtime_no_callbacks(&spi->dev);
+ pm_runtime_set_active(&spi->dev);
+ pm_runtime_enable(&spi->dev);
+
+ /*
+ * This does not mean bridge chip can wakeup the
+ * system from sleep. It's activity can prevent
+ * or abort the system sleep. The device_init_wakeup
+ * creates the wakeup source for us which we will
+ * use to control system sleep.
+ */
+ device_init_wakeup(&spi->dev, 1);
+ pm_stay_awake(&spi->dev);
+
+ pr_debug("success\n");
+
+ return 0;
+
+put_hcd:
+ usb_put_hcd(ihcd->hcd);
+power_off:
+ ice40_spi_power_off(ihcd);
+destroy_wq:
+ destroy_workqueue(ihcd->wq);
+destroy_mutex:
+ mutex_destroy(&ihcd->rlock);
+ mutex_destroy(&ihcd->wlock);
+out:
+ pr_info("ice40_spi_probe failed\n");
+ return ret;
+}
+
+static int ice40_spi_remove(struct spi_device *spi)
+{
+ struct usb_hcd *hcd = spi_get_drvdata(spi);
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ debugfs_remove_recursive(ihcd->dbg_root);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ destroy_workqueue(ihcd->wq);
+ ice40_spi_power_off(ihcd);
+
+ pm_runtime_disable(&spi->dev);
+ pm_relax(&spi->dev);
+
+ return 0;
+}
+
+static struct of_device_id ice40_spi_of_match_table[] = {
+ { .compatible = "lattice,ice40-spi-usb", },
+ {},
+};
+
+static struct spi_driver ice40_spi_driver = {
+ .driver = {
+ .name = "ice40_spi",
+ .owner = THIS_MODULE,
+ .of_match_table = ice40_spi_of_match_table,
+ },
+ .probe = ice40_spi_probe,
+ .remove = ice40_spi_remove,
+};
+
+module_spi_driver(ice40_spi_driver);
+
+MODULE_DESCRIPTION("ICE40 FPGA based SPI-USB bridge HCD");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index 1111aeb..da714ad 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -897,9 +897,7 @@
dma->source_config.stride = stride;
dma->output_config.pack_pattern =
mdp3_ctrl_get_pack_pattern(req->src.format);
- mdp3_clk_enable(1, 0);
- mdp3_session->dma->dma_config_source(dma);
- mdp3_clk_enable(0, 0);
+ dma->update_src_cfg = true;
}
mdp3_session->overlay.id = 1;
req->id = 1;
@@ -923,14 +921,6 @@
mutex_lock(&mdp3_session->lock);
if (mdp3_session->overlay.id == ndx && ndx == 1) {
- struct mdp3_dma *dma = mdp3_session->dma;
- dma->source_config.format = format;
- dma->source_config.stride = fix->line_length;
- dma->output_config.pack_pattern =
- mdp3_ctrl_get_pack_pattern(mfd->fb_imgType);
- mdp3_clk_enable(1, 0);
- mdp3_session->dma->dma_config_source(dma);
- mdp3_clk_enable(0, 0);
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
mdp3_bufq_deinit(&mdp3_session->bufq_in);
} else {
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 993a36f..8a13de2 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -605,6 +605,13 @@
}
}
}
+ if (dma->update_src_cfg) {
+ if (dma->output_config.out_sel ==
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO && intf->active)
+ pr_err("configuring dma source while dma is active\n");
+ dma->dma_config_source(dma);
+ dma->update_src_cfg = false;
+ }
spin_lock_irqsave(&dma->dma_lock, flag);
MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf);
dma->source_config.buf = buf;
@@ -961,6 +968,7 @@
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+ dma->update_src_cfg = false;
memset(&dma->cursor, 0, sizeof(dma->cursor));
memset(&dma->ccs_config, 0, sizeof(dma->ccs_config));
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index 207168f..80ebb9b 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. 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
@@ -259,6 +259,7 @@
int histo_state;
struct mdp3_dma_histogram_data histo_data;
unsigned int vsync_status;
+ bool update_src_cfg;
int (*dma_config)(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config,
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index c2fbc1a..f342c56 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -90,6 +90,8 @@
ctrl->ndx = DSI_CTRL_1;
}
+ ctrl->panel_mode = ctrl->panel_data.panel_info.mipi.mode;
+
ctrl_list[ctrl->ndx] = ctrl; /* keep it */
if (ctrl->shared_pdata.broadcast_enable)
@@ -269,8 +271,6 @@
pinfo->rgb_swap = DSI_RGB_SWAP_RGB;
- ctrl_pdata->panel_mode = pinfo->mode;
-
if (pinfo->mode == DSI_VIDEO_MODE) {
data = 0;
if (pinfo->pulse_mode_hsa_he)
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index cdf9d73..2b409f5 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, The Linux Foundation. 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
@@ -1127,6 +1127,17 @@
DEV_DBG("%s: Got HPD interrupt\n", __func__);
if (hdmi_ctrl->hpd_state) {
+ /*
+ * If a down stream device or bridge chip is attached to hdmi
+ * Tx core output, it is likely that it might be powering the
+ * hpd module ON/OFF on cable connect/disconnect as it would
+ * have its own mechanism of detecting cable. Flush power off
+ * work is needed in case there is any race condidtion between
+ * power off and on during fast cable plug in/out.
+ */
+ if (hdmi_ctrl->ds_registered)
+ flush_work(&hdmi_ctrl->power_off_work);
+
if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) {
DEV_ERR("%s: Failed to enable ddc power\n", __func__);
return;
@@ -2351,6 +2362,8 @@
ops->set_mhl_max_pclk = hdmi_tx_set_mhl_max_pclk;
ops->set_upstream_hpd = hdmi_tx_set_mhl_hpd;
+ hdmi_ctrl->ds_registered = true;
+
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index 8233ba8..54d80dc 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -83,6 +83,7 @@
struct work_struct cable_notify_work;
bool hdcp_feature_on;
+ bool ds_registered;
u32 present_hdcp;
u8 spd_vendor_name[9];
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 9a469a4..f3b7ce1 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -452,6 +452,16 @@
};
#define is_vig_pipe(_pipe_id_) ((_pipe_id_) <= MDSS_MDP_SSPP_VIG2)
+
+static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl(
+ struct mdss_mdp_ctl *ctl)
+{
+ if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl))
+ return ctl->mixer_right->ctl;
+
+ return NULL;
+}
+
static inline void mdss_mdp_ctl_write(struct mdss_mdp_ctl *ctl,
u32 reg, u32 val)
{
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index fa64448..fe42669 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -1133,15 +1133,6 @@
return 0;
}
-static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl(
- struct mdss_mdp_ctl *ctl)
-{
- if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl))
- return ctl->mixer_right->ctl;
-
- return NULL;
-}
-
int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff)
{
struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl);
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
index 8c7dc29..78ecf16 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
@@ -418,6 +418,7 @@
{
struct mdss_mdp_cmd_ctx *ctx;
unsigned long flags;
+ bool enable_rdptr = false;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
@@ -429,12 +430,14 @@
if (!handle->enabled) {
handle->enabled = true;
list_add(&handle->list, &ctx->vsync_handlers);
- if (!handle->cmd_post_flush)
- ctx->vsync_enabled = 1;
+
+ enable_rdptr = !handle->cmd_post_flush;
+ if (enable_rdptr)
+ ctx->vsync_enabled++;
}
spin_unlock_irqrestore(&ctx->clk_lock, flags);
- if (!handle->cmd_post_flush)
+ if (enable_rdptr)
mdss_mdp_cmd_clk_on(ctx);
return 0;
@@ -443,11 +446,8 @@
static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_vsync_handler *handle)
{
-
struct mdss_mdp_cmd_ctx *ctx;
unsigned long flags;
- struct mdss_mdp_vsync_handler *tmp;
- int num_rdptr_vsync = 0;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
@@ -455,19 +455,17 @@
return -ENODEV;
}
-
spin_lock_irqsave(&ctx->clk_lock, flags);
if (handle->enabled) {
handle->enabled = false;
list_del_init(&handle->list);
- }
- list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
- if (!tmp->cmd_post_flush)
- num_rdptr_vsync++;
- }
- if (!num_rdptr_vsync) {
- ctx->vsync_enabled = 0;
- ctx->rdptr_enabled = VSYNC_EXPIRE_TICK;
+
+ if (!handle->cmd_post_flush) {
+ if (ctx->vsync_enabled)
+ ctx->vsync_enabled--;
+ else
+ WARN(1, "unbalanced vsync disable");
+ }
}
spin_unlock_irqrestore(&ctx->clk_lock, flags);
return 0;
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 55a4a4d..f8bdc04 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -298,6 +298,7 @@
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_mdp_vsync_handler *tmp, *handle;
+ struct mdss_mdp_ctl *sctl;
int rc;
u32 frame_rate = 0;
@@ -335,6 +336,10 @@
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
ctl->intf_num);
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (sctl)
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
+ sctl->intf_num);
}
list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list)
@@ -641,6 +646,7 @@
static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_ctl *sctl;
int rc;
pr_debug("kickoff ctl=%d\n", ctl->num);
@@ -674,6 +680,11 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN, ctl->intf_num);
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (sctl)
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
+ sctl->intf_num);
+
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
wmb();
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 7420f95..34cfe23 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -974,6 +974,11 @@
int ret = 0;
int sd_in_pipe = 0;
+ if (!ctl) {
+ pr_warn("kickoff on fb=%d without a ctl attched\n", mfd->index);
+ return ret;
+ }
+
if (ctl->shared_lock)
mutex_lock(ctl->shared_lock);
@@ -2693,6 +2698,56 @@
return 0;
}
+static int __mdss_mdp_ctl_handoff(struct mdss_mdp_ctl *ctl,
+ struct mdss_data_type *mdata)
+{
+ int rc = 0;
+ int i, j;
+ u32 mixercfg;
+ struct mdss_mdp_pipe *pipe = NULL;
+
+ if (!ctl || !mdata)
+ return -EINVAL;
+
+ for (i = 0; i < mdata->nmixers_intf; i++) {
+ mixercfg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i));
+ pr_debug("for lm%d mixercfg = 0x%09x\n", i, mixercfg);
+
+ j = MDSS_MDP_SSPP_VIG0;
+ for (; j < MDSS_MDP_MAX_SSPP && mixercfg; j++) {
+ u32 cfg = j * 3;
+ if ((j == MDSS_MDP_SSPP_VIG3) ||
+ (j == MDSS_MDP_SSPP_RGB3)) {
+ /* Add 2 to account for Cursor & Border bits */
+ cfg += 2;
+ }
+ if (mixercfg & (0x7 << cfg)) {
+ pr_debug("Pipe %d staged\n", j);
+ pipe = mdss_mdp_pipe_search(mdata, BIT(j));
+ if (!pipe) {
+ pr_warn("Invalid pipe %d staged\n", j);
+ continue;
+ }
+
+ rc = mdss_mdp_pipe_handoff(pipe);
+ if (rc) {
+ pr_err("Failed to handoff pipe%d\n",
+ pipe->num);
+ goto exit;
+ }
+
+ rc = mdss_mdp_mixer_handoff(ctl, i, pipe);
+ if (rc) {
+ pr_err("failed to handoff mix%d\n", i);
+ goto exit;
+ }
+ }
+ }
+ }
+exit:
+ return rc;
+}
+
/**
* mdss_mdp_overlay_handoff() - Read MDP registers to handoff an active ctl path
* @mfd: Msm frame buffer structure associated with the fb device.
@@ -2708,10 +2763,8 @@
int rc = 0;
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- int i, j;
- u32 reg;
- struct mdss_mdp_pipe *pipe = NULL;
struct mdss_mdp_ctl *ctl = NULL;
+ struct mdss_mdp_ctl *sctl = NULL;
if (!mdp5_data->ctl) {
ctl = __mdss_mdp_overlay_ctl_init(mfd);
@@ -2735,38 +2788,23 @@
ctl->clk_rate = mdss_mdp_get_clk_rate(MDSS_CLK_MDP_SRC);
pr_debug("Set the ctl clock rate to %d Hz\n", ctl->clk_rate);
- for (i = 0; i < mdata->nmixers_intf; i++) {
- reg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i));
- pr_debug("for lm%d reg = 0x%09x\n", i, reg);
- for (j = MDSS_MDP_SSPP_VIG0; j < MDSS_MDP_MAX_SSPP; j++) {
- u32 cfg = j * 3;
- if ((j == MDSS_MDP_SSPP_VIG3) ||
- (j == MDSS_MDP_SSPP_RGB3)) {
- /* Add 2 to account for Cursor & Border bits */
- cfg += 2;
- }
- if (reg & (0x7 << cfg)) {
- pr_debug("Pipe %d staged\n", j);
- pipe = mdss_mdp_pipe_search(mdata, BIT(j));
- if (!pipe) {
- pr_warn("Invalid pipe %d staged\n", j);
- continue;
- }
+ rc = __mdss_mdp_ctl_handoff(ctl, mdata);
+ if (rc) {
+ pr_err("primary ctl handoff failed. rc=%d\n", rc);
+ goto error;
+ }
- rc = mdss_mdp_pipe_handoff(pipe);
- if (rc) {
- pr_err("Failed to handoff pipe num %d\n"
- , pipe->num);
- goto error;
- }
-
- rc = mdss_mdp_mixer_handoff(ctl, i, pipe);
- if (rc) {
- pr_err("failed to handoff mixer num %d\n"
- , i);
- goto error;
- }
- }
+ if (mfd->split_display) {
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (!sctl) {
+ pr_err("cannot get secondary ctl. fail the handoff\n");
+ rc = -EPERM;
+ goto error;
+ }
+ rc = __mdss_mdp_ctl_handoff(sctl, mdata);
+ if (rc) {
+ pr_err("secondary ctl handoff failed. rc=%d\n", rc);
+ goto error;
}
}
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index fa702ae..9d13091 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -588,6 +588,26 @@
list_del(&t->transfer_list);
}
+/**
+ * spi_message_init_with_transfers - Initialize spi_message and append transfers
+ * @m: spi_message to be initialized
+ * @xfers: An array of spi transfers
+ * @num_xfers: Number of items in the xfer array
+ *
+ * This function initializes the given spi_message and adds each spi_transfer in
+ * the given array to the message.
+ */
+static inline void
+spi_message_init_with_transfers(struct spi_message *m,
+struct spi_transfer *xfers, unsigned int num_xfers)
+{
+ unsigned int i;
+
+ spi_message_init(m);
+ for (i = 0; i < num_xfers; ++i)
+ spi_message_add_tail(&xfers[i], m);
+}
+
/* It's fine to embed message and transaction structures in other data
* structures so long as you don't free them while they're in use.
*/
@@ -680,6 +700,30 @@
return spi_sync(spi, &m);
}
+/**
+ * spi_sync_transfer - synchronous SPI data transfer
+ * @spi: device with which data will be exchanged
+ * @xfers: An array of spi_transfers
+ * @num_xfers: Number of items in the xfer array
+ * Context: can sleep
+ *
+ * Does a synchronous SPI data transfer of the given spi_transfer array.
+ *
+ * For more specific semantics see spi_sync().
+ *
+ * It returns zero on success, else a negative error code.
+ */
+static inline int
+spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
+ unsigned int num_xfers)
+{
+ struct spi_message msg;
+
+ spi_message_init_with_transfers(&msg, xfers, num_xfers);
+
+ return spi_sync(spi, &msg);
+}
+
/* this copies txbuf and rxbuf data; for small transfers only! */
extern int spi_write_then_read(struct spi_device *spi,
const void *txbuf, unsigned n_tx,
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 50660b3..dce56a6 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -131,6 +131,8 @@
* most likely due to retrans in 3WHS.
*/
+#define TCP_DELACK_SEG 1 /*Number of full MSS to receive before Acking RFC2581*/
+
#define TCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ/2U)) /* Maximal interval between probes
* for local resources.
*/
@@ -253,6 +255,10 @@
extern int sysctl_tcp_thin_linear_timeouts;
extern int sysctl_tcp_thin_dupack;
+/* sysctl variables for controlling various tcp parameters */
+extern int sysctl_tcp_delack_seg;
+extern int sysctl_tcp_use_userconfig;
+
extern atomic_long_t tcp_memory_allocated;
extern struct percpu_counter tcp_sockets_allocated;
extern int tcp_memory_pressure;
@@ -346,6 +352,10 @@
struct pipe_inode_info *pipe, size_t len,
unsigned int flags);
+extern int tcp_use_userconfig_sysctl_handler(struct ctl_table *, int,
+ void __user *, size_t *, loff_t *);
+extern int tcp_proc_delayed_ack_control(struct ctl_table *, int,
+ void __user *, size_t *, loff_t *);
static inline void tcp_dec_quickack_mode(struct sock *sk,
const unsigned int pkts)
{
diff --git a/include/sound/Kbuild b/include/sound/Kbuild
index 60847b0..aeccfed 100644
--- a/include/sound/Kbuild
+++ b/include/sound/Kbuild
@@ -13,3 +13,4 @@
header-y += compress_offload.h
header-y += lsm_params.h
header-y += voice_params.h
+header-y += voice_svc.h
diff --git a/include/sound/voice_svc.h b/include/sound/voice_svc.h
new file mode 100644
index 0000000..7045018
--- /dev/null
+++ b/include/sound/voice_svc.h
@@ -0,0 +1,46 @@
+#ifndef __VOICE_SVC_H__
+#define __VOICE_SVC_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define VOICE_SVC_DRIVER_NAME "voice_svc"
+
+#define VOICE_SVC_MVM_STR "MVM"
+#define VOICE_SVC_CVS_STR "CVS"
+#define MAX_APR_SERVICE_NAME_LEN 64
+
+struct voice_svc_register {
+ char svc_name[MAX_APR_SERVICE_NAME_LEN];
+ __u32 src_port;
+ __u8 reg_flag;
+};
+
+struct voice_svc_cmd_response {
+ __u32 src_port;
+ __u32 dest_port;
+ __u32 token;
+ __u32 opcode;
+ __u32 payload_size;
+ __u8 payload[0];
+};
+
+struct voice_svc_cmd_request {
+ char svc_name[MAX_APR_SERVICE_NAME_LEN];
+ __u32 src_port;
+ __u32 dest_port;
+ __u32 token;
+ __u32 opcode;
+ __u32 payload_size;
+ __u8 payload[0];
+};
+
+#define VOICE_SVC_MAGIC 'N'
+
+#define SNDRV_VOICE_SVC_REGISTER_SVC _IOWR(VOICE_SVC_MAGIC, \
+ 0x01, struct voice_svc_register)
+#define SNDRV_VOICE_SVC_CMD_RESPONSE _IOWR(VOICE_SVC_MAGIC, \
+ 0x02, struct voice_svc_cmd_response)
+#define SNDRV_VOICE_SVC_CMD_REQUEST _IOWR(VOICE_SVC_MAGIC, \
+ 0x03, struct voice_svc_cmd_request)
+#endif
diff --git a/include/trace/events/ice40.h b/include/trace/events/ice40.h
new file mode 100644
index 0000000..c0649a8
--- /dev/null
+++ b/include/trace/events/ice40.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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
+ * only 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ice40
+
+#if !defined(_TRACE_ICE40_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ICE40_H
+
+#include <linux/tracepoint.h>
+#include <linux/usb.h>
+
+TRACE_EVENT(ice40_reg_write,
+
+ TP_PROTO(u8 addr, u8 val, u8 cmd0, u8 cmd1, int ret),
+
+ TP_ARGS(addr, val, cmd0, cmd1, ret),
+
+ TP_STRUCT__entry(
+ __field(u8, addr)
+ __field(u8, val)
+ __field(u8, cmd0)
+ __field(u8, cmd1)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->addr = addr;
+ __entry->val = val;
+ __entry->cmd0 = cmd0;
+ __entry->cmd1 = cmd1;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("addr = %x val = %x cmd0 = %x cmd1 = %x ret = %d",
+ __entry->addr, __entry->val, __entry->cmd0,
+ __entry->cmd1, __entry->ret)
+);
+
+TRACE_EVENT(ice40_reg_read,
+
+ TP_PROTO(u8 addr, u8 cmd0, int ret),
+
+ TP_ARGS(addr, cmd0, ret),
+
+ TP_STRUCT__entry(
+ __field(u8, addr)
+ __field(u8, cmd0)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->addr = addr;
+ __entry->cmd0 = cmd0;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("addr = %x cmd0 = %x ret = %x", __entry->addr,
+ __entry->cmd0, __entry->ret)
+);
+
+TRACE_EVENT(ice40_hub_control,
+
+ TP_PROTO(u16 req, u16 val, u16 index, u16 len, int ret),
+
+ TP_ARGS(req, val, index, len, ret),
+
+ TP_STRUCT__entry(
+ __field(u16, req)
+ __field(u16, val)
+ __field(u16, index)
+ __field(u16, len)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->req = req;
+ __entry->val = val;
+ __entry->index = index;
+ __entry->len = len;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("req = %x val = %x index = %x len = %x ret = %d",
+ __entry->req, __entry->val, __entry->index,
+ __entry->len, __entry->ret)
+);
+
+TRACE_EVENT(ice40_ep0,
+
+ TP_PROTO(const char *state),
+
+ TP_ARGS(state),
+
+ TP_STRUCT__entry(
+ __string(state, state)
+ ),
+
+ TP_fast_assign(
+ __assign_str(state, state);
+ ),
+
+ TP_printk("ep0 state: %s", __get_str(state))
+);
+
+TRACE_EVENT(ice40_urb_enqueue,
+
+ TP_PROTO(struct urb *urb),
+
+ TP_ARGS(urb),
+
+ TP_STRUCT__entry(
+ __field(u16, epnum)
+ __field(u8, dir)
+ __field(u8, type)
+ __field(u32, len)
+ ),
+
+ TP_fast_assign(
+ __entry->epnum = usb_pipeendpoint(urb->pipe);
+ __entry->dir = usb_urb_dir_in(urb);
+ __entry->type = usb_pipebulk(urb->pipe);
+ __entry->len = urb->transfer_buffer_length;
+ ),
+
+ TP_printk("URB_LOG: E: ep %d %s %s len %d", __entry->epnum,
+ __entry->dir ? "In" : "Out",
+ __entry->type ? "Bulk" : "ctrl",
+ __entry->len)
+);
+
+TRACE_EVENT(ice40_urb_dequeue,
+
+ TP_PROTO(struct urb *urb),
+
+ TP_ARGS(urb),
+
+ TP_STRUCT__entry(
+ __field(u16, epnum)
+ __field(u8, dir)
+ __field(u8, type)
+ __field(u32, len)
+ __field(int, reason)
+ ),
+
+ TP_fast_assign(
+ __entry->epnum = usb_pipeendpoint(urb->pipe);
+ __entry->dir = usb_urb_dir_in(urb);
+ __entry->type = usb_pipebulk(urb->pipe);
+ __entry->len = urb->transfer_buffer_length;
+ __entry->reason = urb->unlinked;
+ ),
+
+ TP_printk("URB_LOG: D: ep %d %s %s len %d reason %d",
+ __entry->epnum,
+ __entry->dir ? "In" : "Out",
+ __entry->type ? "Bulk" : "ctrl",
+ __entry->len, __entry->reason)
+);
+
+TRACE_EVENT(ice40_urb_done,
+
+ TP_PROTO(struct urb *urb, int result),
+
+ TP_ARGS(urb, result),
+
+ TP_STRUCT__entry(
+ __field(int, result)
+ __field(u16, epnum)
+ __field(u8, dir)
+ __field(u8, type)
+ __field(u32, len)
+ __field(u32, actual)
+ ),
+
+ TP_fast_assign(
+ __entry->result = result;
+ __entry->epnum = usb_pipeendpoint(urb->pipe);
+ __entry->dir = usb_urb_dir_in(urb);
+ __entry->type = usb_pipebulk(urb->pipe);
+ __entry->len = urb->transfer_buffer_length;
+ __entry->actual = urb->actual_length;
+ ),
+
+ TP_printk("URB_LOG: C: ep %d %s %s len %d actual %d result %d",
+ __entry->epnum, __entry->dir ? "In" : "Out",
+ __entry->type ? "Bulk" : "ctrl", __entry->len,
+ __entry->actual, __entry->result)
+);
+
+TRACE_EVENT(ice40_bus_suspend,
+
+ TP_PROTO(u8 status),
+
+ TP_ARGS(status),
+
+ TP_STRUCT__entry(
+ __field(u8, status)
+ ),
+
+ TP_fast_assign(
+ __entry->status = status;
+ ),
+
+ TP_printk("bus_suspend status %d", __entry->status)
+);
+
+TRACE_EVENT(ice40_bus_resume,
+
+ TP_PROTO(u8 status),
+
+ TP_ARGS(status),
+
+ TP_STRUCT__entry(
+ __field(u8, status)
+ ),
+
+ TP_fast_assign(
+ __entry->status = status;
+ ),
+
+ TP_printk("bus_resume status %d", __entry->status)
+);
+
+TRACE_EVENT(ice40_setup,
+
+ TP_PROTO(const char *token, int ret),
+
+ TP_ARGS(token, ret),
+
+ TP_STRUCT__entry(
+ __string(token, token)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __assign_str(token, token);
+ __entry->ret = ret;
+ ),
+
+ TP_printk("Trace: SETUP %s ret %d",
+ __get_str(token), __entry->ret)
+);
+
+TRACE_EVENT(ice40_in,
+
+ TP_PROTO(u16 ep, const char *token, u8 len, u8 expected, int ret),
+
+ TP_ARGS(ep, token, len, expected, ret),
+
+ TP_STRUCT__entry(
+ __field(u16, ep)
+ __string(token, token)
+ __field(u8, len)
+ __field(u8, expected)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->ep = ep;
+ __assign_str(token, token);
+ __entry->len = len;
+ __entry->expected = expected;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("Trace: %d IN %s len %d expected %d ret %d",
+ __entry->ep, __get_str(token),
+ __entry->len, __entry->expected,
+ __entry->ret)
+);
+
+TRACE_EVENT(ice40_out,
+
+ TP_PROTO(u16 ep, const char *token, u8 len, int ret),
+
+ TP_ARGS(ep, token, len, ret),
+
+ TP_STRUCT__entry(
+ __field(u16, ep)
+ __string(token, token)
+ __field(u8, len)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->ep = ep;
+ __assign_str(token, token);
+ __entry->len = len;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("Trace: %d OUT %s len %d ret %d",
+ __entry->ep, __get_str(token),
+ __entry->len, __entry->ret)
+);
+#endif /* if !defined(_TRACE_ICE40_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/mm/compaction.c b/mm/compaction.c
index 673142d..35bb243 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -243,7 +243,6 @@
{
int nr_scanned = 0, total_isolated = 0;
struct page *cursor, *valid_page = NULL;
- unsigned long nr_strict_required = end_pfn - blockpfn;
unsigned long flags;
bool locked = false;
@@ -256,11 +255,12 @@
nr_scanned++;
if (!pfn_valid_within(blockpfn))
- continue;
+ goto isolate_fail;
+
if (!valid_page)
valid_page = page;
if (!PageBuddy(page))
- continue;
+ goto isolate_fail;
/*
* The zone lock must be held to isolate freepages.
@@ -281,12 +281,10 @@
/* Recheck this is a buddy page under lock */
if (!PageBuddy(page))
- continue;
+ goto isolate_fail;
/* Found a free page, break it into order-0 pages */
isolated = split_free_page(page);
- if (!isolated && strict)
- break;
total_isolated += isolated;
for (i = 0; i < isolated; i++) {
list_add(&page->lru, freelist);
@@ -297,7 +295,13 @@
if (isolated) {
blockpfn += isolated - 1;
cursor += isolated - 1;
+ continue;
}
+
+isolate_fail:
+ if (strict)
+ break;
+
}
trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
@@ -307,7 +311,7 @@
* pages requested were isolated. If there were any failures, 0 is
* returned and CMA will fail.
*/
- if (strict && nr_strict_required > total_isolated)
+ if (strict && blockpfn < end_pfn)
total_isolated = 0;
if (locked)
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 7f38d35..a8d7ed0 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -896,13 +896,13 @@
sin6->sin6_port = 0;
sin6->sin6_addr = ip6->saddr;
+ sin6->sin6_flowinfo = 0;
if (np->sndflow)
sin6->sin6_flowinfo =
*(__be32 *)ip6 & IPV6_FLOWINFO_MASK;
- if (__ipv6_addr_needs_scope_id(
- ipv6_addr_type(&sin6->sin6_addr)))
- sin6->sin6_scope_id = IP6CB(skb)->iif;
+ sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
+ IP6CB(skb)->iif);
if (inet6_sk(sk)->rxopt.all)
pingv6_ops.datagram_recv_ctl(sk, msg, skb);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 7a7724d..6bd622f 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -36,6 +36,10 @@
static int ip_ttl_max = 255;
static int ip_ping_group_range_min[] = { 0, 0 };
static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
+static int tcp_delack_seg_min = TCP_DELACK_MIN;
+static int tcp_delack_seg_max = 60;
+static int tcp_use_userconfig_min;
+static int tcp_use_userconfig_max = 1;
/* Update system visible IP port range */
static void set_local_port_range(int range[2])
@@ -699,6 +703,25 @@
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero
},
+ {
+ .procname = "tcp_delack_seg",
+ .data = &sysctl_tcp_delack_seg,
+ .maxlen = sizeof(sysctl_tcp_delack_seg),
+ .mode = 0644,
+ .proc_handler = tcp_proc_delayed_ack_control,
+ .extra1 = &tcp_delack_seg_min,
+ .extra2 = &tcp_delack_seg_max,
+ },
+ {
+ .procname = "tcp_use_userconfig",
+ .data = &sysctl_tcp_use_userconfig,
+ .maxlen = sizeof(sysctl_tcp_use_userconfig),
+ .mode = 0644,
+ .proc_handler = tcp_use_userconfig_sysctl_handler,
+ .extra1 = &tcp_use_userconfig_min,
+ .extra2 = &tcp_use_userconfig_max,
+ },
+
{ }
};
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 74a286c..706899e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -294,6 +294,12 @@
EXPORT_SYMBOL(sysctl_tcp_rmem);
EXPORT_SYMBOL(sysctl_tcp_wmem);
+int sysctl_tcp_delack_seg __read_mostly = TCP_DELACK_SEG;
+EXPORT_SYMBOL(sysctl_tcp_delack_seg);
+
+int sysctl_tcp_use_userconfig __read_mostly;
+EXPORT_SYMBOL(sysctl_tcp_use_userconfig);
+
atomic_long_t tcp_memory_allocated; /* Current allocated memory. */
EXPORT_SYMBOL(tcp_memory_allocated);
@@ -1213,8 +1219,11 @@
/* Delayed ACKs frequently hit locked sockets during bulk
* receive. */
if (icsk->icsk_ack.blocked ||
- /* Once-per-two-segments ACK was not sent by tcp_input.c */
- tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss ||
+ /* Once-per-sysctl_tcp_delack_seg segments
+ * ACK was not sent by tcp_input.c
+ */
+ tp->rcv_nxt - tp->rcv_wup > (icsk->icsk_ack.rcv_mss) *
+ sysctl_tcp_delack_seg ||
/*
* If this read emptied read buffer, we send ACK, if
* connection is not bidirectional, user drained
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 257b617..7c3612b 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5047,7 +5047,8 @@
struct tcp_sock *tp = tcp_sk(sk);
/* More than one full frame received... */
- if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss &&
+ if (((tp->rcv_nxt - tp->rcv_wup) > (inet_csk(sk)->icsk_ack.rcv_mss) *
+ sysctl_tcp_delack_seg &&
/* ... and right edge of window advances far enough.
* (tcp_recvmsg() will send ACK otherwise). Or...
*/
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 34d4a02..d1b4792 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -34,7 +34,39 @@
static void tcp_write_timer(unsigned long);
static void tcp_delack_timer(unsigned long);
-static void tcp_keepalive_timer (unsigned long data);
+static void tcp_keepalive_timer(unsigned long data);
+
+/*Function to reset tcp_ack related sysctl on resetting master control */
+void set_tcp_default(void)
+{
+ sysctl_tcp_delack_seg = TCP_DELACK_SEG;
+}
+
+/*sysctl handler for tcp_ack realted master control */
+int tcp_proc_delayed_ack_control(ctl_table *table, int write,
+ void __user *buffer, size_t *length, loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
+
+ /* The ret value will be 0 if the input validation is successful
+ * and the values are written to sysctl table. If not, the stack
+ * will continue to work with currently configured values
+ */
+ return ret;
+}
+
+/*sysctl handler for tcp_ack realted master control */
+int tcp_use_userconfig_sysctl_handler(ctl_table *table, int write,
+ void __user *buffer, size_t *length, loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
+
+ if (write && ret == 0) {
+ if (!sysctl_tcp_use_userconfig)
+ set_tcp_default();
+ }
+ return ret;
+}
void tcp_init_xmit_timers(struct sock *sk)
{
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 1434970..8b2c443 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -57,6 +57,7 @@
struct msm_dai_q6_dai_data {
DECLARE_BITMAP(status_mask, STATUS_MAX);
+ DECLARE_BITMAP(hwfree_status, STATUS_MAX);
u32 rate;
u32 channels;
u32 bitwidth;
@@ -1510,6 +1511,11 @@
set_bit(STATUS_PORT_STARTED,
dai_data->status_mask);
}
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+ set_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+ dev_dbg(dai->dev, "%s: set hwfree_status to started\n",
+ __func__);
+ }
return rc;
}
@@ -1525,7 +1531,6 @@
struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data;
struct afe_param_id_i2s_cfg *i2s = &dai_data->port_config.i2s;
-
dai_data->channels = params_channels(params);
switch (dai_data->channels) {
case 8:
@@ -1602,10 +1607,14 @@
dai_data->port_config.i2s.i2s_cfg_minor_version =
AFE_API_VERSION_I2S_CONFIG;
dai_data->port_config.i2s.sample_rate = dai_data->rate;
- if (test_bit(STATUS_PORT_STARTED,
- mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) ||
+ if ((test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) &&
test_bit(STATUS_PORT_STARTED,
- mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+ mi2s_dai_data->rx_dai.mi2s_dai_data.hwfree_status)) ||
+ (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask) &&
+ test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.hwfree_status))) {
if ((mi2s_dai_data->tx_dai.mi2s_dai_data.rate !=
mi2s_dai_data->rx_dai.mi2s_dai_data.rate) ||
(mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth !=
@@ -1669,6 +1678,23 @@
return 0;
}
+static int msm_dai_q6_mi2s_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+ clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+ dev_dbg(dai->dev, "%s: clear hwfree_status\n", __func__);
+ }
+ return 0;
+}
+
static void msm_dai_q6_mi2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -1696,12 +1722,15 @@
dev_err(dai->dev, "fail to close AFE port\n");
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
}
+ if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status))
+ clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
}
static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
.startup = msm_dai_q6_mi2s_startup,
.prepare = msm_dai_q6_mi2s_prepare,
.hw_params = msm_dai_q6_mi2s_hw_params,
+ .hw_free = msm_dai_q6_mi2s_hw_free,
.set_fmt = msm_dai_q6_mi2s_set_fmt,
.shutdown = msm_dai_q6_mi2s_shutdown,
};
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
index e3c8944..6b32064 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
@@ -47,8 +47,27 @@
#define MODE_AMR_WB 0xD
#define MODE_PCM 0xC
#define MODE_4GV_NW 0xE
+#define MODE_G711 0xA
+#define MODE_G711A 0xF
-#define VOIP_MODE_MAX MODE_4GV_NW
+enum msm_audio_g711a_frame_type {
+ MVS_G711A_SPEECH_GOOD,
+ MVS_G711A_SID,
+ MVS_G711A_NO_DATA,
+ MVS_G711A_ERASURE
+};
+
+enum msm_audio_g711a_mode {
+ MVS_G711A_MODE_MULAW,
+ MVS_G711A_MODE_ALAW
+};
+
+enum msm_audio_g711_mode {
+ MVS_G711_MODE_MULAW,
+ MVS_G711_MODE_ALAW
+};
+
+#define VOIP_MODE_MAX MODE_G711A
#define VOIP_RATE_MAX 23850
enum format {
@@ -153,7 +172,7 @@
uint32_t evrc_max_rate;
};
-static int voip_get_media_type(uint32_t mode,
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
unsigned int samp_rate,
unsigned int *media_type);
static int voip_get_rate_type(uint32_t mode,
@@ -358,6 +377,81 @@
list_add_tail(&buf_node->list, &prtd->out_queue);
break;
}
+ case MODE_G711:
+ case MODE_G711A:{
+ /* G711 frames are 10ms each, but the DSP works with
+ * 20ms frames and sends two 10ms frames per buffer.
+ * Extract the two frames and put them in separate
+ * buffers.
+ */
+ /* Remove the first DSP frame info header.
+ * Header format: G711A
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ *
+ * Header format: G711
+ * Bits 2-3: Frame rate
+ */
+ if (prtd->mode == MODE_G711A)
+ buf_node->frame.frm_hdr.frame_type =
+ (*voc_pkt) & 0x03;
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ /* There are two frames in the buffer. Length of the
+ * first frame:
+ */
+ buf_node->frame.pktlen = (pkt_len -
+ 2 * DSP_FRAME_HDR_LEN) / 2;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+ voc_pkt = voc_pkt + buf_node->frame.pktlen;
+
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+
+ /* Get another buffer from the free Q and fill in the
+ * second frame.
+ */
+ if (!list_empty(&prtd->free_out_queue)) {
+ buf_node =
+ list_first_entry(&prtd->free_out_queue,
+ struct voip_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ /* Remove the second DSP frame info header.
+ * Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+
+ if (prtd->mode == MODE_G711A)
+ buf_node->frame.frm_hdr.frame_type =
+ (*voc_pkt) & 0x03;
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ /* There are two frames in the buffer. Length
+ * of the second frame:
+ */
+ buf_node->frame.pktlen = (pkt_len -
+ 2 * DSP_FRAME_HDR_LEN) / 2;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list,
+ &prtd->out_queue);
+ } else {
+ /* Drop the second frame */
+ pr_err("%s: UL data dropped, read is slow\n",
+ __func__);
+ }
+ break;
+ }
default: {
buf_node->frame.frm_hdr.timestamp = timestamp;
buf_node->frame.pktlen = pkt_len;
@@ -389,6 +483,8 @@
unsigned long dsp_flags;
uint32_t rate_type;
uint32_t frame_rate;
+ u32 pkt_len;
+ u8 *voc_addr = NULL;
if (prtd->playback_substream == NULL)
return;
@@ -454,6 +550,70 @@
list_add_tail(&buf_node->list, &prtd->free_in_queue);
break;
}
+ case MODE_G711:
+ case MODE_G711A:{
+ /* G711 frames are 10ms each but the DSP expects 20ms
+ * worth of data, so send two 10ms frames per buffer.
+ */
+ /* Add the first DSP frame info header. Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+ voc_addr = voc_pkt;
+ voc_pkt = voc_pkt + sizeof(uint32_t);
+
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (buf_node->frame.frm_hdr.frame_type & 0x03);
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ pkt_len = buf_node->frame.pktlen + DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+ voc_pkt = voc_pkt + buf_node->frame.pktlen;
+
+ list_add_tail(&buf_node->list, &prtd->free_in_queue);
+
+ if (!list_empty(&prtd->in_queue)) {
+ /* Get the second buffer. */
+ buf_node = list_first_entry(&prtd->in_queue,
+ struct voip_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ /* Add the second DSP frame info header.
+ * Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (buf_node->frame.frm_hdr.frame_type & 0x03);
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ pkt_len = pkt_len + buf_node->frame.pktlen +
+ DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list,
+ &prtd->free_in_queue);
+ } else {
+ /* Only 10ms worth of data is available, signal
+ * erasure frame.
+ */
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (MVS_G711A_ERASURE & 0x03);
+
+ pkt_len = pkt_len + DSP_FRAME_HDR_LEN;
+ pr_debug("%s, Only 10ms read, erase 2nd frame\n",
+ __func__);
+ }
+ *((uint32_t *)voc_addr) = pkt_len;
+ break;
+ }
default: {
*((uint32_t *)voc_pkt) = buf_node->frame.pktlen;
voc_pkt = voc_pkt + sizeof(uint32_t);
@@ -829,10 +989,12 @@
pr_debug("%s(): mode=%d, playback sample rate=%d, capture sample rate=%d\n",
__func__, prtd->mode, prtd->play_samp_rate, prtd->cap_samp_rate);
- if ((runtime->format != FORMAT_S16_LE) && ((prtd->mode == MODE_PCM) ||
- (prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) ||
+ if ((runtime->format != FORMAT_S16_LE &&
+ runtime->format != FORMAT_SPECIAL) &&
+ ((prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) ||
(prtd->mode == MODE_IS127) || (prtd->mode == MODE_4GV_NB) ||
- (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_4GV_NW))) {
+ (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_4GV_NW) ||
+ (prtd->mode == MODE_G711) || (prtd->mode == MODE_G711A))) {
pr_err("%s(): mode:%d and format:%u are not matched\n",
__func__, prtd->mode, (uint32_t)runtime->format);
@@ -840,21 +1002,19 @@
goto done;
}
- ret = voip_get_media_type(prtd->mode,
- prtd->play_samp_rate,
- &media_type);
- if (ret < 0) {
- pr_err("%s(): fail at getting media_type, ret=%d\n",
- __func__, ret);
+ if (runtime->format != FORMAT_S16_LE && (prtd->mode == MODE_PCM)) {
+ pr_err("%s(): mode:%d and format:%u are not matched\n",
+ __func__, prtd->mode, runtime->format);
- ret = -EINVAL;
+ ret = -EINVAL;
goto done;
}
- pr_debug("%s(): media_type=%d\n", __func__, media_type);
if ((prtd->mode == MODE_PCM) ||
(prtd->mode == MODE_AMR) ||
- (prtd->mode == MODE_AMR_WB)) {
+ (prtd->mode == MODE_AMR_WB) ||
+ (prtd->mode == MODE_G711) ||
+ (prtd->mode == MODE_G711A)) {
ret = voip_get_rate_type(prtd->mode,
prtd->rate,
&rate_type);
@@ -909,6 +1069,19 @@
pr_debug("%s(): min rate=%d, max rate=%d\n",
__func__, evrc_min_rate_type, evrc_max_rate_type);
}
+ ret = voip_get_media_type(prtd->mode,
+ prtd->rate_type,
+ prtd->play_samp_rate,
+ &media_type);
+ if (ret < 0) {
+ pr_err("%s(): fail at getting media_type, ret=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ pr_debug("%s(): media_type=%d\n", __func__, media_type);
+
if ((prtd->play_samp_rate == 8000) &&
(prtd->cap_samp_rate == 8000))
voc_config_vocoder(media_type, rate_type,
@@ -1285,6 +1458,10 @@
}
break;
}
+ case MODE_G711:
+ case MODE_G711A:
+ *rate_type = rate;
+ break;
default:
pr_err("wrong mode type.\n");
ret = -EINVAL;
@@ -1294,9 +1471,9 @@
return ret;
}
-static int voip_get_media_type(uint32_t mode,
- unsigned int samp_rate,
- unsigned int *media_type)
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
+ unsigned int samp_rate,
+ unsigned int *media_type)
{
int ret = 0;
@@ -1327,6 +1504,13 @@
case MODE_4GV_NW: /* EVRC-NW */
*media_type = VSS_MEDIA_ID_4GV_NW_MODEM;
break;
+ case MODE_G711:
+ case MODE_G711A:
+ if (rate_type == MVS_G711A_MODE_MULAW)
+ *media_type = VSS_MEDIA_ID_G711_MULAW;
+ else
+ *media_type = VSS_MEDIA_ID_G711_ALAW;
+ break;
default:
pr_debug(" input mode is not supported\n");
ret = -EINVAL;
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index c36b53a..badc3c3 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -453,7 +453,7 @@
q6asm_add_hdr_custom_topology(ac, &asm_top.hdr,
APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(asm_top)), TRUE);
-
+ atomic_set(&ac->cmd_state, 1);
asm_top.hdr.opcode = ASM_CMD_ADD_TOPOLOGIES;
asm_top.payload_addr_lsw = cal_block.cal_paddr;
asm_top.payload_addr_msw = 0;
@@ -1626,7 +1626,6 @@
hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
if (cmd_flg) {
hdr->token = ac->session;
- atomic_set(&ac->cmd_state, 1);
}
hdr->pkt_size = pkt_size;
mutex_unlock(&ac->cmd_lock);
@@ -1667,7 +1666,6 @@
hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
if (cmd_flg) {
hdr->token = ac->session;
- atomic_set(&ac->cmd_state, 1);
}
hdr->pkt_size = pkt_size;
return;
@@ -1711,7 +1709,6 @@
hdr->dest_port = 0;
if (cmd_flg) {
hdr->token = ((ac->session << 8) | 0x0001) ;
- atomic_set(&ac->cmd_state, 1);
}
hdr->pkt_size = pkt_size;
mutex_unlock(&ac->cmd_lock);
@@ -1728,7 +1725,6 @@
hdr->dest_port = 0;
if (cmd_flg) {
hdr->token = token;
- atomic_set(&ac->cmd_state, 1);
}
hdr->pkt_size = pkt_size;
return;
@@ -1748,6 +1744,7 @@
pr_debug("%s:session[%d]", __func__, ac->session);
q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, 1);
open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3;
/* Stream prio : High, provide meta info with encoded frames */
open.src_endpointype = ASM_END_POINT_DEVICE_MATRIX;
@@ -1841,7 +1838,7 @@
format);
q6asm_stream_add_hdr(ac, &open.hdr, sizeof(open), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
/*
* Updated the token field with stream/session for compressed playback
* Platform driver must know the the stream with which the command is
@@ -1959,6 +1956,7 @@
ac->io_mode |= NT_MODE;
q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, 1);
open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE_V2;
open.mode_flags = BUFFER_META_ENABLE;
@@ -2068,6 +2066,7 @@
pr_debug("%s: session[%d]", __func__, ac->session);
q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, 1);
open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK_V2;
open.mode_flags = 0;
@@ -2110,6 +2109,7 @@
}
pr_debug("%s session[%d]", __func__, ac->session);
q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE);
+ atomic_set(&ac->cmd_state, 1);
run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
run.flags = flags;
@@ -2147,7 +2147,7 @@
}
pr_debug("session[%d]", ac->session);
q6asm_stream_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
run.flags = flags;
run.time_lsw = lsw_ts;
@@ -2189,6 +2189,7 @@
sample_rate, channels, bit_rate, mode, format);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
@@ -2229,6 +2230,7 @@
pr_debug("%s: Session %d, num_channels = %d\n",
__func__, ac->session, num_channels);
q6asm_add_hdr(ac, &chan_map.hdr, sizeof(chan_map), TRUE);
+ atomic_set(&ac->cmd_state, 1);
chan_map.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
chan_map.encdec.param_id = ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP;
chan_map.encdec.param_size = sizeof(struct asm_dec_out_chan_map_param) -
@@ -2273,6 +2275,7 @@
ac->session, rate, channels);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
@@ -2334,7 +2337,7 @@
ac->session, rate, channels);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
-
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
@@ -2433,6 +2436,7 @@
pr_debug("%s: Session %d\n", __func__, ac->session);
q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE);
+ atomic_set(&ac->cmd_state, 1);
sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
sbrps.encdec.param_id = ASM_PARAM_ID_AAC_SBR_PS_FLAG;
@@ -2474,6 +2478,7 @@
__func__, ac->session, sce_left, sce_right);
q6asm_add_hdr(ac, &dual_mono.hdr, sizeof(dual_mono), TRUE);
+ atomic_set(&ac->cmd_state, 1);
dual_mono.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
dual_mono.encdec.param_id = ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING;
@@ -2509,6 +2514,7 @@
int rc = 0;
q6asm_add_hdr(ac, &aac_mix_coeff.hdr, sizeof(aac_mix_coeff), TRUE);
+ atomic_set(&ac->cmd_state, 1);
aac_mix_coeff.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
aac_mix_coeff.param_id =
ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2;
@@ -2549,6 +2555,7 @@
reduced_rate_level, rate_modulation_cmd);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(struct asm_v13k_enc_cfg) -
@@ -2590,6 +2597,7 @@
frames_per_buf, min_rate, max_rate, rate_modulation_cmd);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(struct asm_evrc_enc_cfg) -
@@ -2629,6 +2637,7 @@
__func__, ac->session, frames_per_buf, band_mode, dtx_enable);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(struct asm_amrnb_enc_cfg) -
@@ -2666,6 +2675,7 @@
__func__, ac->session, frames_per_buf, band_mode, dtx_enable);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(struct asm_amrwb_enc_cfg) -
@@ -2706,6 +2716,7 @@
channels);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -2766,6 +2777,7 @@
channels);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -2834,7 +2846,7 @@
cfg->sample_rate, cfg->ch_cfg);
q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
/*
* Updated the token field with stream/session for compressed playback
* Platform driver must know the the stream with which the command is
@@ -2910,6 +2922,7 @@
wma_cfg->ch_mask, wma_cfg->encode_opt);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -2955,6 +2968,7 @@
wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -3002,6 +3016,7 @@
cfg->num_channels);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -3033,6 +3048,7 @@
pr_debug("%s: session[%d]param_id[%d]param_value[%d]", __func__,
ac->session, param_id, param_value);
q6asm_add_hdr(ac, &ddp_cfg.hdr, sizeof(ddp_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
ddp_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
ddp_cfg.encdec.param_id = param_id;
ddp_cfg.encdec.param_size = sizeof(struct asm_dec_ddp_endp_param_v2) -
@@ -3093,6 +3109,7 @@
mmap_region_cmd;
q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size,
TRUE, ((ac->session << 8) | dir));
+ atomic_set(&ac->cmd_state, 1);
mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;
mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
mmap_regions->num_regions = bufcnt & 0x00ff;
@@ -3156,7 +3173,7 @@
q6asm_add_mmaphdr(ac, &mem_unmap.hdr,
sizeof(struct avs_cmd_shared_mem_unmap_regions),
TRUE, ((ac->session << 8) | dir));
-
+ atomic_set(&ac->cmd_state, 1);
mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
buf_node = list_entry(ptr, struct asm_buffer_node,
@@ -3254,6 +3271,7 @@
mmap_region_cmd;
q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, TRUE,
((ac->session << 8) | dir));
+ atomic_set(&ac->cmd_state, 1);
pr_debug("mmap_region=0x%p token=0x%x\n",
mmap_regions, ((ac->session << 8) | dir));
@@ -3335,6 +3353,7 @@
cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
q6asm_add_mmaphdr(ac, &mem_unmap.hdr, cmd_size,
TRUE, ((ac->session << 8) | dir));
+ atomic_set(&ac->cmd_state, 1);
port = &ac->port[dir];
buf_add = (uint32_t)port->buf->phys;
mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
@@ -3392,6 +3411,7 @@
sz = sizeof(struct asm_volume_ctrl_lr_chan_gain);
q6asm_add_hdr_async(ac, &lrgain.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
lrgain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
lrgain.param.data_payload_addr_lsw = 0;
lrgain.param.data_payload_addr_msw = 0;
@@ -3440,6 +3460,7 @@
sz = sizeof(struct asm_volume_ctrl_mute_config);
q6asm_add_hdr_async(ac, &mute.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
mute.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
mute.param.data_payload_addr_lsw = 0;
mute.param.data_payload_addr_msw = 0;
@@ -3487,6 +3508,7 @@
sz = sizeof(struct asm_volume_ctrl_master_gain);
q6asm_add_hdr_async(ac, &vol.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
vol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
vol.param.data_payload_addr_lsw = 0;
vol.param.data_payload_addr_msw = 0;
@@ -3535,6 +3557,7 @@
sz = sizeof(struct asm_soft_pause_params);
q6asm_add_hdr_async(ac, &softpause.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
softpause.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
softpause.param.data_payload_addr_lsw = 0;
@@ -3588,6 +3611,7 @@
sz = sizeof(struct asm_soft_step_volume_params);
q6asm_add_hdr_async(ac, &softvol.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
softvol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
softvol.param.data_payload_addr_lsw = 0;
softvol.param.data_payload_addr_msw = 0;
@@ -3646,6 +3670,7 @@
sz = sizeof(struct asm_eq_params);
eq_params = (struct msm_audio_eq_stream_config *) eq_p;
q6asm_add_hdr(ac, &eq.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
eq.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
eq.param.data_payload_addr_lsw = 0;
@@ -3848,7 +3873,6 @@
q6asm_stream_add_hdr_async(
ac, &write.hdr, sizeof(write), FALSE, ac->stream_id);
-
port = &ac->port[IN];
ab = &port->buf[port->dsp_buf];
@@ -4157,6 +4181,7 @@
q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) +
sizeof(struct asm_stream_cmd_set_pp_params_v2) +
params_length), TRUE);
+ atomic_set(&ac->cmd_state, 1);
hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
payload_params.data_payload_addr_lsw = 0;
payload_params.data_payload_addr_msw = 0;
@@ -4197,7 +4222,7 @@
return -EINVAL;
}
q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
/*
* Updated the token field with stream/session for compressed playback
* Platform driver must know the the stream with which the command is
@@ -4306,7 +4331,7 @@
return -EINVAL;
}
q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
/*
* Updated the token field with stream/session for compressed playback
* Platform driver must know the the stream with which the command is
@@ -4464,6 +4489,7 @@
pr_debug("%s:session[%d]enable[%d]\n", __func__,
ac->session, enable);
q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE);
+ atomic_set(&ac->cmd_state, 1);
tx_overflow.hdr.opcode = \
ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 2f9e319..56efb97 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2308,6 +2308,7 @@
if (ret < 0) {
dev_err(dapm->dev, "Failed to add route %s->%s\n",
route->source, route->sink);
+ mutex_unlock(&dapm->card->dapm_mutex);
return ret;
}
route++;
@@ -3017,6 +3018,7 @@
dev_err(dapm->dev,
"ASoC: Failed to create DAPM control %s: %d\n",
widget->name, ret);
+ mutex_unlock(&dapm->card->dapm_mutex);
return ret;
}
widget++;