Merge "msm: camera: Create v4l2 subdevice for flash modules"
diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-stats.txt b/Documentation/devicetree/bindings/arm/msm/rpm-stats.txt
index 1b21007..3137e91 100644
--- a/Documentation/devicetree/bindings/arm/msm/rpm-stats.txt
+++ b/Documentation/devicetree/bindings/arm/msm/rpm-stats.txt
@@ -14,7 +14,7 @@
Example:
-qcom,rpm-stats@0xfc19dbd0{
+qcom,rpm-stats@fc19dbd0 {
compatible = "qcom,rpm-stats";
reg = <0xfc19dbd0 0x1000>;
reg-names = "phys_addr_base";
diff --git a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
index a981eed..46173a0 100644
--- a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
+++ b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
@@ -12,41 +12,41 @@
- interrupt-names : indicates interrupts passed to driver (via interrupts property) by name.
"core_irq" is mandatory, "bam_irq" is mandatory only when BAM DMA engine is used,
"status_irq" and "sdiowakeup_irq" are optional.
- - qcom,sdcc-clk-rates : specifies supported SDCC clock frequencies, Units - Hz.
- - qcom,sdcc-sup-voltages: specifies supported voltage ranges for card. Should always be
+ - qcom,clk-rates : specifies supported SDCC clock frequencies, Units - Hz.
+ - qcom,sup-voltages: specifies supported voltage ranges for card. Should always be
specified in pairs (min, max), Units - mV.
- <supply-name>-supply: phandle to the regulator device tree node
"supply-name" examples are "vdd", "vdd-io".
Optional Properties:
- cell-index - defines slot ID.
- - qcom,sdcc-bus-width - defines the bus I/O width that controller supports.
+ - qcom,bus-width - defines the bus I/O width that controller supports.
- wp-gpios - specify GPIO for write protect switch detection.
- cd-gpios - specify GPIO for card detection.
- - qcom,sdcc-nonremovable - specifies whether the card in slot is
+ - qcom,nonremovable - specifies whether the card in slot is
hot pluggable or hard wired.
- - qcom,sdcc-disable_cmd23 - disable sending CMD23 to card when controller can't support it.
- - qcom,sdcc-bus-speed-mode - specifies supported bus speed modes by host.
- - qcom,sdcc-current-limit - specifies max. current the host can drive.
- - qcom,sdcc-xpc - specifies if the host can supply more than 150mA for SDXC cards.
+ - qcom,disable-cmd23 - disable sending CMD23 to card when controller can't support it.
+ - qcom,bus-speed-mode - specifies supported bus speed modes by host.
+ - qcom,current-limit - specifies max. current the host can drive.
+ - qcom,xpc - specifies if the host can supply more than 150mA for SDXC cards.
- qcom,dat1-mpm-int - specifies MPM interrupt number corresponding to DAT1 line of SDCC
(used only if slot has dedicated DAT1 MSM pin (not GPIO))
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
- - qcom,sdcc-<supply>-always_on - specifies whether supply should be kept "on" always.
- - qcom,sdcc-<supply>-lpm_sup - specifies whether supply can be kept in low power mode (lpm).
- - qcom,sdcc-<supply>-voltage_level - specifies voltage levels for supply. Should be
+ - qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
+ - qcom,<supply>-lpm-sup - specifies whether supply can be kept in low power mode (lpm).
+ - qcom,<supply>-voltage-level - specifies voltage levels for supply. Should be
specified in pairs (min, max), units uV.
- - qcom,sdcc-<supply>-current_level - specifies load levels for supply in lpm or
+ - qcom,<supply>-current-level - specifies load levels for supply in lpm or
high power mode (hpm). Should be specified in pairs (lpm, hpm), units uA.
- gpios - specifies gpios assigned for sdcc slot.
- - qcom,sdcc-gpio-names - a list of strings that map in order to the list of gpios
+ - qcom,gpio-names - a list of strings that map in order to the list of gpios
A slot has either gpios or dedicated tlmm pins as represented below.
- - qcom,sdcc-pad-pull-on - Active pull configuration for sdc tlmm pins
- - qcom,sdcc-pad-pull-off - Suspend pull configuration for sdc tlmm pins.
- - qcom,sdcc-pad-drv-on - Active drive strength configuration for sdc tlmm pins.
- - qcom,sdcc-pad-drv-off - Suspend drive strength configuration for sdc tlmm pins.
+ - qcom,pad-pull-on - Active pull configuration for sdc tlmm pins
+ - qcom,pad-pull-off - Suspend pull configuration for sdc tlmm pins.
+ - qcom,pad-drv-on - Active drive strength configuration for sdc tlmm pins.
+ - qcom,pad-drv-off - Suspend drive strength configuration for sdc tlmm pins.
Tlmm pins are specified as <clk cmd data>
- qcom,bus-bw-vectors-bps - specifies array of throughput values in Bytes/sec. The
@@ -71,10 +71,10 @@
0xf9602000 0x2000> // BAM register interface
interrupts = <123>;
- qcom,sdcc-clk-rates = <400000 24000000 48000000>;
- qcom,sdcc-sup-voltages = <2700 3300>;
- qcom,sdcc-bus-width = <8>; //8-bit wide
- qcom,sdcc-nonremovable;
+ qcom,clk-rates = <400000 24000000 48000000>;
+ qcom,sup-voltages = <2700 3300>;
+ qcom,bus-width = <8>; //8-bit wide
+ qcom,nonremovable;
qcom,msm-bus,name = "sdcc2";
qcom,msm-bus,num-cases = <7>;
diff --git a/Documentation/devicetree/bindings/power/smb137c-charger.txt b/Documentation/devicetree/bindings/power/smb137c-charger.txt
new file mode 100644
index 0000000..c0c4333
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/smb137c-charger.txt
@@ -0,0 +1,86 @@
+Summit SMB137C Battery Charger
+
+The SMB137C is controlled via an I2C bus. Its 7-bit I2C slave address is
+programmed during manufacturing.
+
+Required properties:
+- compatible: Must be "summit,smb137c".
+- reg: The device's 7-bit I2C address.
+
+Optional properties:
+- summit,chg-current-ma Maximum battery charging current in milliamps.
+ Supported values are: 500, 650, 750, 850, 950,
+ 1100, 1300, and 1500.
+- summit,term-current-ma Charging terminaton current in milliamps.
+ Supported values are: 0, 35, 50, 100, and 150.
+ A value of 0 means no termination current is
+ used.
+- summit,pre-chg-current-ma Maximum battery pre-charging current in
+ milliamps. This current limit is applied while
+ the battery voltage is below the pre-charge /
+ fast-charge threshold. Supported values are:
+ 50, 100, 150, and 200.
+- summit,float-voltage-mv Battery voltage threshold in millivolts at which
+ point charging switches from constant current to
+ constant voltage. Supported values are: 3460 up
+ through 4730 in 10 mV steps.
+- summit,thresh-voltage-mv Threshold voltage in millivolts which is used to
+ switch between pre-charge and fast-charge
+ current limits. Supported values are: 2400 up
+ to 3100 in 100 mV steps.
+- summit,recharge-thresh-mv Specifies the minimum voltage drop in millivolts
+ below the float voltage that is required in
+ order to initiate a new charging cycle.
+ Supported values are: 75 and 120.
+- summit,system-voltage-mv Regulated voltage output on the VOUTL pin in
+ millivolts. Supported values are 4250 and 4460.
+- summit,charging-timeout Maximum duration in minutes that a single charge
+ cycle may last. Supported values are: 0, 382,
+ 764, and 1527. A value of 0 means that no
+ charge cycle timeout is used and charging can
+ continue indefinitely.
+- summit,pre-charge-timeout Maximum time in minutes spent in the pre-charge
+ state in any given charge cycle. Supports
+ values are: 0, 48, 95, and 191. A value of 0
+ means that there is no limit to the amount of
+ time that may be spent in the pre-charge state.
+- summit,therm-current-ua Thermistor current in microamps to be used for
+ battery temperature monitoring. Supported
+ values are 10, 20, 40, and 100. These values
+ correspond to 100, 50, 25, and 10 kohm NTC
+ thermistors respectively.
+- summit,temperature-min Specifies the minimum temperature at which
+ charging is allowed. Supported values are
+ 0 to 7. These values correspond to -20 C to
+ +15 C in 5 C increments for an NTC thermistor
+ with beta = 4400.
+- summit,temperature-max Specifies the maximum temperature at which
+ charging is allowed. Supported values are
+ 0 to 7. These values correspond to +30 C to
+ +65 C in 5 C increments for an NTC thermistor
+ with beta = 4400.
+
+Note: If an optional property is not specified, then the hardware default value
+will be used.
+
+Example:
+/ {
+ i2c@f9925000 {
+ charger@57 {
+ compatible = "summit,smb137c";
+ reg = <0x57>;
+ summit,chg-current-ma = <1500>;
+ summit,term-current-ma = <50>;
+ summit,pre-chg-current-ma = <100>;
+ summit,float-voltage-mv = <4200>;
+ summit,thresh-voltage-mv = <3000>;
+ summit,recharge-thresh-mv = <75>;
+ summit,system-voltage-mv = <4250>;
+ summit,charging-timeout = <382>;
+ summit,pre-charge-timeout = <48>;
+ summit,therm-current-ua = <10>;
+ summit,temperature-min = <4>; /* 0 C */
+ summit,temperature-max = <3>; /* 45 C */
+ };
+ };
+};
diff --git a/Documentation/trace/events-msm-low-power.txt b/Documentation/trace/events-msm-low-power.txt
new file mode 100644
index 0000000..5414146
--- /dev/null
+++ b/Documentation/trace/events-msm-low-power.txt
@@ -0,0 +1,57 @@
+Subsystem Trace Points: msm_low_power
+
+The msm_low_power tracing system captures the events during the entry
+and exit of various low power modes like power collapse, standalone
+power collapse, retention and wfi. The tracing system adds the following
+events to capture the state of the low power mode.
+
+1) msm_pm_enter
+===================
+msm_pm_enter: cpu: %u latency: %uus sleep: %uus
+msm_pm_enter_pc: cpu: %u latency: %uus sleep: %uus wake_up: %u
+msm_pm_enter_ret: cpu: %u latency: %uus sleep: %uus wake_up: %u
+msm_pm_enter_spc: cpu: %u latency: %uus sleep: %uus wake_up: %u
+msm_pm_enter_wfi: cpu: %u latency: %uus sleep: %uus wake_up: %u
+
+The event captures various parameters during the entry into low power
+modes.
+
+The 'cpu' parameter represents the cpu on which the low power mode is
+chosen.
+
+The 'latency_us' parameter represents the system latency at the time of
+choosing the low power mode.
+
+The 'sleep_us' parameter tells the maximum amount of time the kernel can
+sleep in this low power mode.
+
+The 'wake_up' parameter tells if there was any immediate wakeup required
+before entering low power mode.
+
+2) msm_pm_exit
+=================
+msm_pm_exit: cpu:%u success:%d
+msm_pm_exit_pc: cpu:%u success:%d
+msm_pm_exit_ret: cpu:%u success:%d
+msm_pm_exit_spc: cpu:%u success:%d
+msm_pm_exit_wfi: cpu:%u success:%d
+
+The event captures parameters during the exit of the low power modes.
+
+The 'cpu' parameter represents the cpu on which the low power mode is chosen.
+
+The 'success' parameter shows the state of power collapse/standalone power
+collapse. It will be set if power collapse/standalone power collapse were
+successful. For the rest of the low power modes it is set to one.
+
+3) lpm_resources
+=================
+lpm_resources: name:%s sleep_value:%d
+
+This event captures parameters for each of the lpm resources.
+
+The 'name' parameter represents the name of the lpm resource and it can hold
+l2, pxo, vdd mem, vdd dig depending on the resource chosen during power
+collapse.
+
+The 'sleep_value' parameter corresponds to the sleep value set for the resource.
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index 7398b25..7961b78 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -238,16 +238,16 @@
interrupts = <0 123 0>;
interrupt-names = "core_irq";
- qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
- qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
- qcom,sdcc-sup-voltages = <2950 2950>;
- qcom,sdcc-bus-width = <8>;
- qcom,sdcc-nonremovable;
- qcom,sdcc-bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,bus-width = <8>;
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
};
sdcc2: qcom,sdcc@f98a4000 {
@@ -258,17 +258,17 @@
interrupts = <0 125 0>;
interrupt-names = "core_irq";
- qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
- qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
- qcom,sdcc-sup-voltages = <2950 2950>;
- qcom,sdcc-bus-width = <4>;
- qcom,sdcc-xpc;
- qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
- qcom,sdcc-current-limit = <800>;
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,bus-width = <4>;
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <800>;
};
};
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 21d8c8c..4ec5684 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -136,14 +136,14 @@
<0x0 0x10 0x6>,
<0x0 0x10 0x7>;
- interrupt-names = "chg-done",
- "chg-failed",
- "fast-chg-on",
- "trkl-chg-on",
- "state-change",
- "chgwdog",
+ interrupt-names = "vbat-det-lo",
"vbat-det-hi",
- "vbat-det-lo";
+ "chgwdog",
+ "state-change",
+ "trkl-chg-on",
+ "fast-chg-on",
+ "chg-failed",
+ "chg-done";
};
qcom,chg-buck@1100 {
@@ -157,13 +157,13 @@
<0x0 0x11 0x5>,
<0x0 0x11 0x6>;
- interrupt-names = "vdd-loop",
- "ibat-loop",
- "ichg-loop",
- "vchg-loop",
+ interrupt-names = "vbat-ov",
+ "vreg-ov",
"overtemp",
- "vref-ov",
- "vbat-ov";
+ "vchg-loop",
+ "ichg-loop",
+ "ibat-loop",
+ "vdd-loop";
};
qcom,chg-bat-if@1200 {
@@ -175,11 +175,12 @@
<0x0 0x12 0x3>,
<0x0 0x12 0x4>;
- interrupt-names = "psi",
- "vcp-on",
- "bat-fet-on",
+ interrupt-names = "batt-pres",
"bat-temp-ok",
- "batt-pres";
+ "bat-fet-on",
+ "vcp-on",
+ "psi";
+
};
qcom,chg-usb-chgpth@1300 {
@@ -200,8 +201,8 @@
interrupts = <0x0 0x14 0x0>,
<0x0 0x14 0x1>;
- interrupt-names = "dcin-valid",
- "coarse-det-dc";
+ interrupt-names = "coarse-det-dc",
+ "dcin-valid";
};
qcom,chg-boost@1500 {
@@ -210,8 +211,8 @@
interrupts = <0x0 0x15 0x0>,
<0x0 0x15 0x1>;
- interrupt-names = "limit-error",
- "boost-pwr-ok";
+ interrupt-names = "boost-pwr-ok",
+ "limit-error";
};
qcom,chg-misc@1600 {
diff --git a/arch/arm/boot/dts/msm8226-ion.dtsi b/arch/arm/boot/dts/msm8226-ion.dtsi
index beaffe5..b06ad42 100644
--- a/arch/arm/boot/dts/msm8226-ion.dtsi
+++ b/arch/arm/boot/dts/msm8226-ion.dtsi
@@ -19,5 +19,33 @@
qcom,ion-heap@30 { /* SYSTEM HEAP */
reg = <30>;
};
+
+ qcom,ion-heap@8 { /* CP_MM HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <8>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x3800000>;
+ };
+
+ qcom,ion-heap@25 { /* IOMMU HEAP */
+ reg = <25>;
+ };
+
+ qcom,ion-heap@27 { /* QSECOM HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <27>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x780000>;
+ };
+
+ qcom,ion-heap@28 { /* AUDIO HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <28>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x314000>;
+ };
};
};
diff --git a/arch/arm/boot/dts/msm8226-sim.dts b/arch/arm/boot/dts/msm8226-sim.dts
index 7c25680..41ac69d 100644
--- a/arch/arm/boot/dts/msm8226-sim.dts
+++ b/arch/arm/boot/dts/msm8226-sim.dts
@@ -12,7 +12,6 @@
/dts-v1/;
/include/ "msm8226.dtsi"
-/include/ "msm8226-ion.dtsi"
/include/ "msm8226-camera.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index db2bfb3..f0e950a 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -11,6 +11,7 @@
*/
/include/ "skeleton.dtsi"
+/include/ "msm8226-ion.dtsi"
/ {
model = "Qualcomm MSM 8226";
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8910.dtsi
index a2e1338..1f3c1d8 100644
--- a/arch/arm/boot/dts/msm8910.dtsi
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -51,6 +51,9 @@
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0>;
interrupt-names = "core_irq";
+ HSUSB_VDDCX-supply = <&pm8110_s1>;
+ HSUSB_1p8-supply = <&pm8110_l10>;
+ HSUSB_3p3-supply = <&pm8110_l20>;
qcom,hsusb-otg-phy-type = <2>;
qcom,hsusb-otg-mode = <1>;
@@ -70,16 +73,16 @@
interrupts = <0 123 0>;
interrupt-names = "core_irq";
- qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
- qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
- qcom,sdcc-sup-voltages = <2950 2950>;
- qcom,sdcc-bus-width = <8>;
- qcom,sdcc-nonremovable;
- qcom,sdcc-bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,bus-width = <8>;
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
};
sdcc2: qcom,sdcc@f98a4000 {
@@ -90,17 +93,17 @@
interrupts = <0 125 0>;
interrupt-names = "core_irq";
- qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
- qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
- qcom,sdcc-sup-voltages = <2950 2950>;
- qcom,sdcc-bus-width = <4>;
- qcom,sdcc-xpc;
- qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
- qcom,sdcc-current-limit = <800>;
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,bus-width = <4>;
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <800>;
};
};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 93f92c7..938bc22 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -155,7 +155,7 @@
};
&sdcc1 {
- qcom,sdcc-bus-width = <4>;
+ qcom,bus-width = <4>;
};
&sdcc2 {
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 002332b..68207af 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -180,6 +180,10 @@
};
};
+&usb3 {
+ qcom,charging-disabled;
+};
+
&pm8941_mvs1 {
parent-supply = <&ext_5v>;
};
diff --git a/arch/arm/boot/dts/msm8974-rumi.dtsi b/arch/arm/boot/dts/msm8974-rumi.dtsi
index d4b7793..5a16be7 100644
--- a/arch/arm/boot/dts/msm8974-rumi.dtsi
+++ b/arch/arm/boot/dts/msm8974-rumi.dtsi
@@ -24,11 +24,11 @@
};
qcom,sdcc@f9824000 {
- qcom,sdcc-clk-rates = <400000 19200000>;
+ qcom,clk-rates = <400000 19200000>;
};
qcom,sdcc@f98a4000 {
- qcom,sdcc-clk-rates = <400000 19200000>;
+ qcom,clk-rates = <400000 19200000>;
};
qcom,sps@f998000 {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 83e1141..98f0b3b 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -137,25 +137,25 @@
vdd-supply = <&pm8941_l20>;
vdd-io-supply = <&pm8941_s3>;
- qcom,sdcc-vdd-always_on;
- qcom,sdcc-vdd-lpm_sup;
- qcom,sdcc-vdd-voltage_level = <2950000 2950000>;
- qcom,sdcc-vdd-current_level = <800 500000>;
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
- qcom,sdcc-vdd-io-always_on;
- qcom,sdcc-vdd-io-voltage_level = <1800000 1800000>;
- qcom,sdcc-vdd-io-current_level = <250 154000>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
- qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
- qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
- qcom,sdcc-sup-voltages = <2950 2950>;
- qcom,sdcc-bus-width = <8>;
- qcom,sdcc-nonremovable;
- qcom,sdcc-bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,bus-width = <8>;
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
qcom,msm-bus,name = "sdcc1";
qcom,msm-bus,num-cases = <7>;
@@ -184,25 +184,25 @@
vdd-supply = <&pm8941_l21>;
vdd-io-supply = <&pm8941_l13>;
- qcom,sdcc-vdd-voltage_level = <2950000 2950000>;
- qcom,sdcc-vdd-current_level = <9000 800000>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
- qcom,sdcc-vdd-io-always_on;
- qcom,sdcc-vdd-io-lpm_sup;
- qcom,sdcc-vdd-io-voltage_level = <1800000 2950000>;
- qcom,sdcc-vdd-io-current_level = <6 22000>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <6 22000>;
- qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
- qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
- qcom,sdcc-sup-voltages = <2950 2950>;
- qcom,sdcc-bus-width = <4>;
- qcom,sdcc-xpc;
- qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
- qcom,sdcc-current-limit = <800>;
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,bus-width = <4>;
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <800>;
qcom,msm-bus,name = "sdcc2";
qcom,msm-bus,num-cases = <7>;
@@ -242,12 +242,12 @@
<&msmgpio 37 0>, /* DATA1 */
<&msmgpio 36 0>, /* DATA2 */
<&msmgpio 35 0>; /* DATA3 */
- qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+ qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
- qcom,sdcc-sup-voltages = <1800 1800>;
- qcom,sdcc-bus-width = <4>;
- qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+ qcom,clk-rates = <400000 25000000 50000000 100000000>;
+ qcom,sup-voltages = <1800 1800>;
+ qcom,bus-width = <4>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
qcom,msm-bus,name = "sdcc3";
qcom,msm-bus,num-cases = <7>;
@@ -287,12 +287,12 @@
<&msmgpio 95 0>, /* DATA1 */
<&msmgpio 94 0>, /* DATA2 */
<&msmgpio 92 0>; /* DATA3 */
- qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+ qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
- qcom,sdcc-sup-voltages = <1800 1800>;
- qcom,sdcc-bus-width = <4>;
- qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+ qcom,clk-rates = <400000 25000000 50000000 100000000>;
+ qcom,sup-voltages = <1800 1800>;
+ qcom,bus-width = <4>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
qcom,msm-bus,name = "sdcc4";
qcom,msm-bus,num-cases = <7>;
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 86e0cf7..d62f7e7 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -204,4 +204,11 @@
qcom,pc-mode = <2>; /*MSM_PC_TZ_L2_EXT */
qcom,use-sync-timer;
};
+
+ qcom,rpm-stats@fc19dbd0 {
+ compatible = "qcom,rpm-stats";
+ reg = <0xfc19dbd0 0x1000>;
+ reg-names = "phys_addr_base";
+ qcom,sleep-stats-version = <2>;
+ };
};
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index dccc723..ec8864f 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -215,7 +215,7 @@
status = "okay";
pm8019_l12: regulator-l12 {
parent-supply = <&pm8019_s3>;
- regulator-min-microvolt = <750000>;
+ regulator-min-microvolt = <675000>;
regulator-max-microvolt = <1050000>;
status = "okay";
};
@@ -224,10 +224,20 @@
regulator-name = "8019_l12_ao";
qcom,set = <1>;
parent-supply = <&pm8019_s3_ao>;
- regulator-min-microvolt = <750000>;
+ regulator-min-microvolt = <675000>;
regulator-max-microvolt = <1050000>;
status = "okay";
};
+ pm8019_l12_so: regulator-l12-so {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_l12_so";
+ qcom,set = <2>;
+ parent-supply = <&pm8019_s3>;
+ regulator-min-microvolt = <675000>;
+ regulator-max-microvolt = <1050000>;
+ qcom,init-voltage = <675000>;
+ status = "okay";
+ };
};
rpm-regulator-ldoa13 {
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 3d2515d..c54b3f6 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -184,22 +184,22 @@
vdd-supply = <&ext_2p95v>;
vdd-io-supply = <&pm8019_l13>;
- qcom,sdcc-vdd-io-always_on;
- qcom,sdcc-vdd-io-lpm_sup;
- qcom,sdcc-vdd-io-voltage_level = <1800000 2950000>;
- qcom,sdcc-vdd-io-current_level = <6 22000>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <6 22000>;
- qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>;
- qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>;
- qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>;
- qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>;
+ qcom,pad-pull-on = <0x0 0x3 0x3>;
+ qcom,pad-pull-off = <0x0 0x3 0x3>;
+ qcom,pad-drv-on = <0x7 0x4 0x4>;
+ qcom,pad-drv-off = <0x0 0x0 0x0>;
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
- qcom,sdcc-sup-voltages = <2950 2950>;
- qcom,sdcc-bus-width = <4>;
- qcom,sdcc-xpc;
- qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
- qcom,sdcc-current-limit = <800>;
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,bus-width = <4>;
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <800>;
interrupt-parent = <&sdcc2>;
#address-cells = <0>;
@@ -229,12 +229,12 @@
<&msmgpio 17 0>,
<&msmgpio 18 0>,
<&msmgpio 19 0>;
- qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+ qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
- qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
- qcom,sdcc-sup-voltages = <2950 2950>;
- qcom,sdcc-bus-width = <4>;
- qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+ qcom,clk-rates = <400000 25000000 50000000 100000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,bus-width = <4>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
};
qcom,bam_dmux@fc834000 {
@@ -328,4 +328,10 @@
qcom,hw-settle-time = <0>;
qcom,fast-avg-setup = <0>;
};
+
+ qcom,msm-rng@f9bff000 {
+ compatible = "qcom,msm-rng";
+ reg = <0xf9bff000 0x200>;
+ qcom,msm-rng-iface-clk;
+ };
};
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
index fe6a7da..5876a0f 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8910_defconfig
@@ -102,6 +102,8 @@
# CONFIG_HWMON is not set
CONFIG_REGULATOR=y
CONFIG_REGULATOR_STUB=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
CONFIG_FB=y
CONFIG_FB_VIRTUAL=y
CONFIG_SOUND=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index a613932..33c7718 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -464,6 +464,7 @@
CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y
CONFIG_MSM_SSBI=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_BAMDMA=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 0d63836..8c11368 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -467,6 +467,7 @@
CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y
CONFIG_MSM_SSBI=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_BAMDMA=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 7bfe3fe..5e1fa4a 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -48,6 +48,7 @@
CONFIG_MSM_BAM_DMUX=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
# CONFIG_MSM_HW3D is not set
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_SYSMON_COMM=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index af832cb..4d68a72 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -46,6 +46,7 @@
CONFIG_MSM_BAM_DMUX=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
# CONFIG_MSM_HW3D is not set
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_SYSMON_COMM=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 176696c..dbb4328 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -266,6 +266,7 @@
select MSM_RUN_QUEUE_STATS
select MEMORY_HOLE_CARVEOUT
select MSM_RPM_STATS_LOG
+ select QMI_ENCDEC
config ARCH_MPQ8092
bool "MPQ8092"
@@ -347,6 +348,7 @@
select MAY_HAVE_SPARSE_IRQ
select SPARSE_IRQ
select MSM_MULTIMEDIA_USE_ION
+ select MSM_RPM_STATS_LOG
config ARCH_MSM8910
bool "MSM8910"
@@ -1516,6 +1518,17 @@
help
SMD Transport Layer for IPC Router
+config MSM_QMI_INTERFACE
+ depends on MSM_IPC_ROUTER
+ depends on QMI_ENCDEC
+ default n
+ bool "MSM QMI Interface Library"
+ help
+ Library to send and receive QMI messages over IPC Router.
+ This library provides interface functions to the kernel drivers
+ to perform QMI message marshaling and transport them over IPC
+ Router.
+
config MSM_ONCRPCROUTER_DEBUG
depends on MSM_ONCRPCROUTER
default y
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 403c32c..88ed433 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -138,6 +138,7 @@
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o
obj-$(CONFIG_MSM_IPC_ROUTER) += ipc_router.o
obj-$(CONFIG_MSM_IPC_ROUTER)+= ipc_socket.o
+obj-$(CONFIG_MSM_QMI_INTERFACE) += msm_qmi_interface.o
obj-$(CONFIG_DEBUG_FS) += smd_rpc_sym.o
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_clients.o
diff --git a/arch/arm/mach-msm/acpuclock-9625.c b/arch/arm/mach-msm/acpuclock-9625.c
index 7fd00e6..b0556c3 100644
--- a/arch/arm/mach-msm/acpuclock-9625.c
+++ b/arch/arm/mach-msm/acpuclock-9625.c
@@ -386,7 +386,7 @@
{
unsigned long max_cpu_khz = 0;
struct resource *res;
- int i;
+ int i, rc;
u32 regval;
mutex_init(&drv_data.lock);
@@ -447,12 +447,43 @@
acpu_freq_tbl[i].use_for_scaling; i++)
max_cpu_khz = acpu_freq_tbl[i].khz;
+ /* Initialize regulators */
+ rc = increase_vdd(acpu_freq_tbl[i].vdd_cpu, acpu_freq_tbl[i].vdd_mem);
+ if (rc)
+ goto err_vdd;
+
+ rc = regulator_enable(drv_data.vdd_mem);
+ if (rc) {
+ dev_err(&pdev->dev, "regulator_enable for a5_mem failed\n");
+ goto err_vdd;
+ }
+
+ rc = regulator_enable(drv_data.vdd_cpu);
+ if (rc) {
+ dev_err(&pdev->dev, "regulator_enable for a5_cpu failed\n");
+ goto err_vdd_cpu;
+ }
+
acpuclk_9625_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT);
acpuclk_register(&acpuclk_9625_data);
cpufreq_table_init();
return 0;
+
+err_vdd_cpu:
+ regulator_disable(drv_data.vdd_mem);
+err_vdd:
+ regulator_put(drv_data.vdd_mem);
+ regulator_put(drv_data.vdd_cpu);
+
+ for (i = 0; i < NUM_SRC; i++) {
+ if (!src_clocks[i].name)
+ continue;
+ clk_unprepare(src_clocks[i].clk);
+ clk_put(src_clocks[i].clk);
+ }
+ return rc;
}
static struct of_device_id acpuclk_9625_match_table[] = {
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 4ea9d8e..58f6f66 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -1388,7 +1388,7 @@
/* T6 Object */
0, 0, 0, 0, 0, 0,
/* T38 Object */
- 14, 3, 0, 5, 7, 12, 0, 0, 0, 0,
+ 14, 4, 0, 5, 11, 12, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1396,7 +1396,7 @@
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
/* T7 Object */
- 32, 10, 50,
+ 32, 8, 50,
/* T8 Object */
25, 0, 20, 20, 0, 0, 0, 0, 0, 0,
/* T9 Object */
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index 980f312..e5263c7 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -43,6 +43,21 @@
#include "board-dt.h"
#include "clock.h"
+static struct memtype_reserve msm8226_reserve_table[] __initdata = {
+ [MEMTYPE_SMI] = {
+ },
+ [MEMTYPE_EBI0] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+};
+
+static int msm8226_paddr_to_memtype(unsigned int paddr)
+{
+ return MEMTYPE_EBI1;
+}
static struct clk_lookup msm_clocks_dummy[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
@@ -55,6 +70,23 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
+static struct reserve_info msm8226_reserve_info __initdata = {
+ .memtype_reserve_table = msm8226_reserve_table,
+ .paddr_to_memtype = msm8226_paddr_to_memtype,
+};
+
+static void __init msm8226_early_memory(void)
+{
+ reserve_info = &msm8226_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msm8226_reserve_table);
+}
+
+static void __init msm8226_reserve(void)
+{
+ msm_reserve();
+}
+
+
void __init msm8226_init(void)
{
msm8226_init_gpiomux();
@@ -78,4 +110,6 @@
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
.dt_compat = msm8226_dt_match,
+ .reserve = msm8226_reserve,
+ .init_very_early = msm8226_early_memory
MACHINE_END
diff --git a/arch/arm/mach-msm/board-9615-gpiomux.c b/arch/arm/mach-msm/board-9615-gpiomux.c
index cef967c..e5b7678 100644
--- a/arch/arm/mach-msm/board-9615-gpiomux.c
+++ b/arch/arm/mach-msm/board-9615-gpiomux.c
@@ -37,7 +37,7 @@
static struct gpiomux_setting gsbi5 = {
.func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
};
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index 2f3ab7f..e0a6b4d 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -762,6 +762,7 @@
.disable_reset_on_disconnect = true,
.enable_lpm_on_dev_suspend = true,
.core_clk_always_on_workaround = true,
+ .delay_lpm_on_disconnect = true,
};
@@ -907,6 +908,7 @@
&msm_cpu_fe,
&msm_stub_codec,
&msm_voice,
+ &msm_dtmf,
&msm_voip,
&msm_i2s_cpudai0,
&msm_i2s_cpudai1,
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index c4e174b..cb977fa 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -36,6 +36,12 @@
.pull = GPIOMUX_PULL_NONE,
};
+static struct gpiomux_setting gpio_i2c_config = {
+ .func = GPIOMUX_FUNC_3,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
{
.gpio = 8, /* BLSP1 UART TX */
@@ -50,6 +56,18 @@
},
},
{
+ .gpio = 10, /* BLSP1 QUP3 I2C_DAT */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
+ .gpio = 11, /* BLSP1 QUP3 I2C_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
.gpio = 69, /* BLSP6 QUP SPI_CS_N */
.settings = {
[GPIOMUX_SUSPENDED] = &gpio_spi_cs_config,
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index a41fc06..3e5fc9d 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -35,6 +35,7 @@
#include <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
#include "board-dt.h"
+#include <mach/msm_bus_board.h>
#include "clock.h"
#include "modem_notifier.h"
#include "lpm_resources.h"
@@ -253,6 +254,97 @@
}
};
+#define BIMC_BASE 0xfc380000
+#define BIMC_SIZE 0x0006A000
+#define SYS_NOC_BASE 0xfc460000
+#define PERIPH_NOC_BASE 0xFC468000
+#define CONFIG_NOC_BASE 0xfc480000
+#define NOC_SIZE 0x00004000
+
+static struct resource bimc_res[] = {
+ {
+ .start = BIMC_BASE,
+ .end = BIMC_BASE + BIMC_SIZE,
+ .flags = IORESOURCE_MEM,
+ .name = "bimc_mem",
+ },
+};
+
+static struct resource sys_noc_res[] = {
+ {
+ .start = SYS_NOC_BASE,
+ .end = SYS_NOC_BASE + NOC_SIZE,
+ .flags = IORESOURCE_MEM,
+ .name = "sys_noc_mem",
+ },
+};
+
+static struct resource config_noc_res[] = {
+ {
+ .start = CONFIG_NOC_BASE,
+ .end = CONFIG_NOC_BASE + NOC_SIZE,
+ .flags = IORESOURCE_MEM,
+ .name = "config_noc_mem",
+ },
+};
+
+static struct resource periph_noc_res[] = {
+ {
+ .start = PERIPH_NOC_BASE,
+ .end = PERIPH_NOC_BASE + NOC_SIZE,
+ .flags = IORESOURCE_MEM,
+ .name = "periph_noc_mem",
+ },
+};
+
+static struct platform_device msm_bus_sys_noc = {
+ .name = "msm_bus_fabric",
+ .id = MSM_BUS_FAB_SYS_NOC,
+ .num_resources = ARRAY_SIZE(sys_noc_res),
+ .resource = sys_noc_res,
+};
+
+static struct platform_device msm_bus_bimc = {
+ .name = "msm_bus_fabric",
+ .id = MSM_BUS_FAB_BIMC,
+ .num_resources = ARRAY_SIZE(bimc_res),
+ .resource = bimc_res,
+};
+
+static struct platform_device msm_bus_periph_noc = {
+ .name = "msm_bus_fabric",
+ .id = MSM_BUS_FAB_PERIPH_NOC,
+ .num_resources = ARRAY_SIZE(periph_noc_res),
+ .resource = periph_noc_res,
+};
+
+static struct platform_device msm_bus_config_noc = {
+ .name = "msm_bus_fabric",
+ .id = MSM_BUS_FAB_CONFIG_NOC,
+ .num_resources = ARRAY_SIZE(config_noc_res),
+ .resource = config_noc_res,
+};
+
+static struct platform_device *msm_bus_9625_devices[] = {
+ &msm_bus_sys_noc,
+ &msm_bus_bimc,
+ &msm_bus_periph_noc,
+ &msm_bus_config_noc,
+};
+
+static void __init msm9625_init_buses(void)
+{
+#ifdef CONFIG_MSM_BUS_SCALING
+ msm_bus_sys_noc.dev.platform_data =
+ &msm_bus_9625_sys_noc_pdata;
+ msm_bus_bimc.dev.platform_data = &msm_bus_9625_bimc_pdata;
+ msm_bus_periph_noc.dev.platform_data = &msm_bus_9625_periph_noc_pdata;
+ msm_bus_config_noc.dev.platform_data = &msm_bus_9625_config_noc_pdata;
+#endif
+ platform_add_devices(msm_bus_9625_devices,
+ ARRAY_SIZE(msm_bus_9625_devices));
+}
+
void __init msm9625_add_devices(void)
{
platform_device_register(&msm_device_smd_9625);
@@ -273,6 +365,7 @@
rpm_regulator_smd_driver_init();
msm_spm_device_init();
msm_clock_init(&msm9625_clock_init_data);
+ msm9625_init_buses();
}
void __init msm9625_init(void)
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 3c417c3..a73246d 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -3510,24 +3510,26 @@
.ctl_val = CC_BANKED(9, 6, n), \
}
-static struct clk_freq_tbl clk_tbl_gfx3d_8960ab[] = {
- F_GFX3D( 0, gnd, 0, 0),
- F_GFX3D( 27000000, pxo, 0, 0),
- F_GFX3D( 48000000, pll8, 1, 8),
- F_GFX3D( 54857000, pll8, 1, 7),
- F_GFX3D( 64000000, pll8, 1, 6),
- F_GFX3D( 76800000, pll8, 1, 5),
- F_GFX3D( 96000000, pll8, 1, 4),
- F_GFX3D(128000000, pll8, 1, 3),
- F_GFX3D(145455000, pll2, 2, 11),
- F_GFX3D(160000000, pll2, 1, 5),
- F_GFX3D(177778000, pll2, 2, 9),
- F_GFX3D(200000000, pll2, 1, 4),
- F_GFX3D(228571000, pll2, 2, 7),
- F_GFX3D(266667000, pll2, 1, 3),
- F_GFX3D(320000000, pll2, 2, 5),
- F_GFX3D(325000000, pll3, 1, 2),
- F_GFX3D(400000000, pll2, 1, 2),
+/*Shared by 8064, 8930, and 8960ab*/
+static struct clk_freq_tbl clk_tbl_gfx3d[] = {
+ F_GFX3D( 0, gnd, 0, 0),
+ F_GFX3D( 27000000, pxo, 0, 0),
+ F_GFX3D( 48000000, pll8, 1, 8),
+ F_GFX3D( 54857000, pll8, 1, 7),
+ F_GFX3D( 64000000, pll8, 1, 6),
+ F_GFX3D( 76800000, pll8, 1, 5),
+ F_GFX3D( 96000000, pll8, 1, 4),
+ F_GFX3D(128000000, pll8, 1, 3),
+ F_GFX3D(145455000, pll2, 2, 11),
+ F_GFX3D(160000000, pll2, 1, 5),
+ F_GFX3D(177778000, pll2, 2, 9),
+ F_GFX3D(192000000, pll8, 1, 2),
+ F_GFX3D(200000000, pll2, 1, 4),
+ F_GFX3D(228571000, pll2, 2, 7),
+ F_GFX3D(266667000, pll2, 1, 3),
+ F_GFX3D(320000000, pll2, 2, 5),
+ F_GFX3D(400000000, pll2, 1, 2),
+ F_GFX3D(450000000, pll15, 1, 2),
F_END
};
@@ -3552,49 +3554,6 @@
F_END
};
-static struct clk_freq_tbl clk_tbl_gfx3d_8064[] = {
- F_GFX3D( 0, gnd, 0, 0),
- F_GFX3D( 27000000, pxo, 0, 0),
- F_GFX3D( 48000000, pll8, 1, 8),
- F_GFX3D( 54857000, pll8, 1, 7),
- F_GFX3D( 64000000, pll8, 1, 6),
- F_GFX3D( 76800000, pll8, 1, 5),
- F_GFX3D( 96000000, pll8, 1, 4),
- F_GFX3D(128000000, pll8, 1, 3),
- F_GFX3D(145455000, pll2, 2, 11),
- F_GFX3D(160000000, pll2, 1, 5),
- F_GFX3D(177778000, pll2, 2, 9),
- F_GFX3D(192000000, pll8, 1, 2),
- F_GFX3D(200000000, pll2, 1, 4),
- F_GFX3D(228571000, pll2, 2, 7),
- F_GFX3D(266667000, pll2, 1, 3),
- F_GFX3D(400000000, pll2, 1, 2),
- F_GFX3D(450000000, pll15, 1, 2),
- F_END
-};
-
-static struct clk_freq_tbl clk_tbl_gfx3d_8930[] = {
- F_GFX3D( 0, gnd, 0, 0),
- F_GFX3D( 27000000, pxo, 0, 0),
- F_GFX3D( 48000000, pll8, 1, 8),
- F_GFX3D( 54857000, pll8, 1, 7),
- F_GFX3D( 64000000, pll8, 1, 6),
- F_GFX3D( 76800000, pll8, 1, 5),
- F_GFX3D( 96000000, pll8, 1, 4),
- F_GFX3D(128000000, pll8, 1, 3),
- F_GFX3D(145455000, pll2, 2, 11),
- F_GFX3D(160000000, pll2, 1, 5),
- F_GFX3D(177778000, pll2, 2, 9),
- F_GFX3D(192000000, pll8, 1, 2),
- F_GFX3D(200000000, pll2, 1, 4),
- F_GFX3D(228571000, pll2, 2, 7),
- F_GFX3D(266667000, pll2, 1, 3),
- F_GFX3D(320000000, pll2, 2, 5),
- F_GFX3D(400000000, pll2, 1, 2),
- F_GFX3D(450000000, pll15, 1, 2),
- F_END
-};
-
static unsigned long fmax_gfx3d_8064ab[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 325000000,
@@ -3651,7 +3610,7 @@
.ns_reg = GFX3D_NS_REG,
.root_en_mask = BIT(2),
.set_rate = set_rate_mnd_banked,
- .freq_tbl = clk_tbl_gfx3d_8960,
+ .freq_tbl = clk_tbl_gfx3d,
.bank_info = &bmnd_info_gfx3d,
.current_freq = &rcg_dummy_freq,
.c = {
@@ -6558,7 +6517,6 @@
sizeof(msm_clocks_8960_common));
if (cpu_is_msm8960ab()) {
pll3_clk.c.rate = 650000000;
- gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8960ab;
gfx3d_clk.c.fmax[VDD_DIG_LOW] = 192000000;
gfx3d_clk.c.fmax[VDD_DIG_NOMINAL] = 325000000;
gfx3d_clk.c.fmax[VDD_DIG_HIGH] = 400000000;
@@ -6573,6 +6531,7 @@
gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
} else if (cpu_is_msm8960()) {
+ gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8960;
memcpy(msm_clocks_8960 + ARRAY_SIZE(msm_clocks_8960_common),
msm_clocks_8960_only, sizeof(msm_clocks_8960_only));
msm8960_clock_init_data.size -=
@@ -6583,11 +6542,9 @@
* clocks which differ between chips.
*/
if (cpu_is_apq8064()) {
- gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064;
gfx3d_clk.c.fmax = fmax_gfx3d_8064;
}
if (cpu_is_apq8064ab()) {
- gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064;
gfx3d_clk.c.fmax = fmax_gfx3d_8064ab;
}
if ((cpu_is_apq8064() &&
@@ -6616,7 +6573,6 @@
gfx3d_clk.c.fmax = fmax_gfx3d_8930aa;
}
if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627()) {
- gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8930;
pll15_clk.c.rate = 900000000;
gmem_axi_clk.c.depends = &gfx3d_axi_clk_8930.c;
}
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index 6c9b413..e0d98b7 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -52,8 +52,11 @@
static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq)
{
int ret = 0;
+ int saved_sched_policy = -EINVAL;
+ int saved_sched_rt_prio = -EINVAL;
struct cpufreq_freqs freqs;
struct cpu_freq *limit = &per_cpu(cpu_freq_info, policy->cpu);
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
if (limit->limits_init) {
if (new_freq > limit->allowed_max) {
@@ -70,11 +73,29 @@
freqs.old = policy->cur;
freqs.new = new_freq;
freqs.cpu = policy->cpu;
+
+ /*
+ * Put the caller into SCHED_FIFO priority to avoid cpu starvation
+ * in the acpuclk_set_rate path while increasing frequencies
+ */
+
+ if (freqs.new > freqs.old && current->policy != SCHED_FIFO) {
+ saved_sched_policy = current->policy;
+ saved_sched_rt_prio = current->rt_priority;
+ sched_setscheduler_nocheck(current, SCHED_FIFO, ¶m);
+ }
+
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
ret = acpuclk_set_rate(policy->cpu, new_freq, SETRATE_CPUFREQ);
if (!ret)
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ /* Restore priority after clock ramp-up */
+ if (freqs.new > freqs.old && saved_sched_policy >= 0) {
+ param.sched_priority = saved_sched_rt_prio;
+ sched_setscheduler_nocheck(current, saved_sched_policy, ¶m);
+ }
return ret;
}
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 3b3425f..abc0e6a 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -1103,10 +1103,10 @@
.flags = IORESOURCE_IRQ,
},
{
- .start = 47,
- .end = 47,
+ .start = MSM_GPIO_TO_INT(47),
+ .end = MSM_GPIO_TO_INT(47),
.name = "wakeup",
- .flags = IORESOURCE_IO,
+ .flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index af8687e..1ba408f 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -584,6 +584,10 @@
.name = "msm-voip-dsp",
.id = -1,
};
+struct platform_device msm_dtmf = {
+ .name = "msm-pcm-dtmf",
+ .id = -1,
+};
struct platform_device msm_compr_dsp = {
.name = "msm-compr-dsp",
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index bcc9c94..cd99628 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -234,6 +234,7 @@
extern struct platform_device msm_stub_codec;
extern struct platform_device msm_voice;
extern struct platform_device msm_voip;
+extern struct platform_device msm_dtmf;
extern struct platform_device msm_lpa_pcm;
extern struct platform_device msm_pcm_hostless;
extern struct platform_device msm_cpudai_afe_01_rx;
diff --git a/arch/arm/mach-msm/include/mach/msm_bus_board.h b/arch/arm/mach-msm/include/mach/msm_bus_board.h
index 0a53b46..84a7dc0 100644
--- a/arch/arm/mach-msm/include/mach/msm_bus_board.h
+++ b/arch/arm/mach-msm/include/mach/msm_bus_board.h
@@ -92,6 +92,11 @@
extern struct msm_bus_fabric_registration msm_bus_8974_config_noc_pdata;
extern struct msm_bus_fabric_registration msm_bus_8974_ocmem_vnoc_pdata;
+extern struct msm_bus_fabric_registration msm_bus_9625_sys_noc_pdata;
+extern struct msm_bus_fabric_registration msm_bus_9625_bimc_pdata;
+extern struct msm_bus_fabric_registration msm_bus_9625_periph_noc_pdata;
+extern struct msm_bus_fabric_registration msm_bus_9625_config_noc_pdata;
+
void msm_bus_rpm_set_mt_mask(void);
int msm_bus_board_rpm_get_il_ids(uint16_t *id);
int msm_bus_board_get_iid(int id);
@@ -291,8 +296,10 @@
MSM_BUS_MASTER_USB_HS,
MSM_BUS_MASTER_PNOC_CFG,
MSM_BUS_MASTER_V_OCMEM_GFX3D,
+ MSM_BUS_MASTER_IPA,
+ MSM_BUS_MASTER_QPIC,
- MSM_BUS_MASTER_LAST = MSM_BUS_MASTER_V_OCMEM_GFX3D,
+ MSM_BUS_MASTER_LAST = MSM_BUS_MASTER_QPIC,
MSM_BUS_SYSTEM_FPB_MASTER_SYSTEM =
MSM_BUS_SYSTEM_MASTER_SYSTEM_FPB,
@@ -446,8 +453,10 @@
MSM_BUS_SLAVE_PHY_APU_CFG,
MSM_BUS_SLAVE_EBI1_PHY_CFG,
MSM_BUS_SLAVE_SERVICE_CNOC,
+ MSM_BUS_SLAVE_IPS_CFG,
+ MSM_BUS_SLAVE_QPIC,
- MSM_BUS_SLAVE_LAST = MSM_BUS_SLAVE_SERVICE_CNOC,
+ MSM_BUS_SLAVE_LAST = MSM_BUS_SLAVE_QPIC,
MSM_BUS_SYSTEM_FPB_SLAVE_SYSTEM =
MSM_BUS_SYSTEM_SLAVE_SYSTEM_FPB,
diff --git a/arch/arm/mach-msm/include/mach/msm_qmi_interface.h b/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
new file mode 100644
index 0000000..11867f3
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
@@ -0,0 +1,280 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#ifndef _MSM_QMI_INTERFACE_H_
+#define _MSM_QMI_INTERFACE_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+#include <linux/qmi_encdec.h>
+
+#define QMI_COMMON_TLV_TYPE 0
+
+enum qmi_event_type {
+ QMI_RECV_MSG = 1,
+ QMI_SERVER_ARRIVE,
+ QMI_SERVER_EXIT,
+};
+
+struct qmi_handle {
+ void *src_port;
+ void *dest_info;
+ uint16_t next_txn_id;
+ struct list_head txn_list;
+ struct mutex handle_lock;
+ spinlock_t notify_lock;
+ void (*notify)(struct qmi_handle *handle, enum qmi_event_type event,
+ void *notify_priv);
+ void *notify_priv;
+ void (*ind_cb)(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ unsigned int msg_len, void *ind_cb_priv);
+ void *ind_cb_priv;
+ int handle_reset;
+ wait_queue_head_t reset_waitq;
+};
+
+enum qmi_result_type_v01 {
+ /* To force a 32 bit signed enum. Do not change or use*/
+ QMI_RESULT_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+ QMI_RESULT_SUCCESS_V01 = 0,
+ QMI_RESULT_FAILURE_V01 = 1,
+ QMI_RESULT_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+enum qmi_error_type_v01 {
+ /* To force a 32 bit signed enum. Do not change or use*/
+ QMI_ERROR_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+ QMI_ERR_NONE_V01 = 0x0000,
+ QMI_ERROR_MALFORMED_MSG_V01 = 0x0001,
+ QMI_ERR_NO_MEMORY_V01 = 0x0002,
+ QMI_ERR_INTERNAL_V01 = 0x0003,
+ QMI_ERR_INVALID_ID_V01 = 0x0029,
+ QMI_ERR_INCOMPATIBLE_STATE_V01 = 0x005A,
+ QMI_ERROR_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+struct qmi_response_type_v01 {
+ enum qmi_result_type_v01 result;
+ enum qmi_error_type_v01 error;
+};
+
+#ifdef CONFIG_MSM_QMI_INTERFACE
+
+/* Element info array describing common qmi response structure */
+extern struct elem_info qmi_response_type_v01_ei[];
+#define get_qmi_response_type_v01_ei() qmi_response_type_v01_ei
+
+/**
+ * qmi_handle_create() - Create a QMI handle
+ * @notify: Callback to notify events on the handle created.
+ * @notify_priv: Private information to be passed along with the notification.
+ *
+ * @return: Valid QMI handle on success, NULL on error.
+ */
+struct qmi_handle *qmi_handle_create(
+ void (*notify)(struct qmi_handle *handle,
+ enum qmi_event_type event, void *notify_priv),
+ void *notify_priv);
+
+/**
+ * qmi_handle_destroy() - Destroy the QMI handle
+ * @handle: QMI handle to be destroyed.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_handle_destroy(struct qmi_handle *handle);
+
+/**
+ * qmi_register_ind_cb() - Register the indication callback function
+ * @handle: QMI handle with which the function is registered.
+ * @ind_cb: Callback function to be registered.
+ * @ind_cb_priv: Private data to be passed with the indication callback.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_register_ind_cb(struct qmi_handle *handle,
+ void (*ind_cb)(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ unsigned int msg_len, void *ind_cb_priv),
+ void *ind_cb_priv);
+
+/**
+ * qmi_send_req_wait() - Send a synchronous QMI request
+ * @handle: QMI handle through which the QMI request is sent.
+ * @request_desc: Structure describing the request data structure.
+ * @req: Buffer containing the request data structure.
+ * @req_len: Length of the request data structure.
+ * @resp_desc: Structure describing the response data structure.
+ * @resp: Buffer to hold the response data structure.
+ * @resp_len: Length of the response data structure.
+ * @timeout_ms: Timeout before a response is received.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_send_req_wait(struct qmi_handle *handle,
+ struct msg_desc *req_desc,
+ void *req, unsigned int req_len,
+ struct msg_desc *resp_desc,
+ void *resp, unsigned int resp_len,
+ unsigned long timeout_ms);
+
+/**
+ * qmi_send_req_nowait() - Send an asynchronous QMI request
+ * @handle: QMI handle through which the QMI request is sent.
+ * @request_desc: Structure describing the request data structure.
+ * @req: Buffer containing the request data structure.
+ * @req_len: Length of the request data structure.
+ * @resp_desc: Structure describing the response data structure.
+ * @resp: Buffer to hold the response data structure.
+ * @resp_len: Length of the response data structure.
+ * @resp_cb: Callback function to be invoked when the response arrives.
+ * @resp_cb_data: Private information to be passed along with the callback.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_send_req_nowait(struct qmi_handle *handle,
+ struct msg_desc *req_desc,
+ void *req, unsigned int req_len,
+ struct msg_desc *resp_desc,
+ void *resp, unsigned int resp_len,
+ void (*resp_cb)(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ void *resp_cb_data),
+ void *resp_cb_data);
+
+/**
+ * qmi_recv_msg() - Receive the QMI message
+ * @handle: Handle for which the QMI message has to be received.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_recv_msg(struct qmi_handle *handle);
+
+/**
+ * qmi_connect_to_service() - Connect the QMI handle with a QMI service
+ * @handle: QMI handle to be connected with the QMI service.
+ * @service_id: Service id to identify the QMI service.
+ * @instance_id: Instance id to identify the instance of the QMI service.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_connect_to_service(struct qmi_handle *handle,
+ uint32_t service_id, uint32_t instance_id);
+
+/**
+ * qmi_svc_event_notifier_register() - Register a notifier block to receive
+ * events regarding a QMI service
+ * @service_id: Service ID to identify the QMI service.
+ * @instance_id: Instance ID to identify the instance of the QMI service.
+ * @nb: Notifier block used to receive the event.
+ *
+ * @return: 0 if successfully registered, < 0 on error.
+ */
+int qmi_svc_event_notifier_register(uint32_t service_id,
+ uint32_t instance_id,
+ struct notifier_block *nb);
+
+/**
+ * qmi_svc_event_notifier_unregister() - Unregister service event
+ * notifier block
+ * @service_id: Service ID to identify the QMI service.
+ * @instance_id: Instance ID to identify the instance of the QMI service.
+ * @nb: Notifier block registered to receive the events.
+ *
+ * @return: 0 if successfully registered, < 0 on error.
+ */
+int qmi_svc_event_notifier_unregister(uint32_t service_id,
+ uint32_t instance_id,
+ struct notifier_block *nb);
+#else
+
+#define get_qmi_response_type_v01_ei() NULL
+
+static inline struct qmi_handle *qmi_handle_create(
+ void (*notify)(struct qmi_handle *handle,
+ enum qmi_event_type event, void *notify_priv),
+ void *notify_priv)
+{
+ return NULL;
+}
+
+static inline int qmi_handle_destroy(struct qmi_handle *handle)
+{
+ return -ENODEV;
+}
+
+static inline int qmi_register_ind_cb(struct qmi_handle *handle,
+ void (*ind_cb)(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ unsigned int msg_len, void *ind_cb_priv),
+ void *ind_cb_priv)
+{
+ return -ENODEV;
+}
+
+static inline int qmi_send_req_wait(struct qmi_handle *handle,
+ struct msg_desc *req_desc,
+ void *req, unsigned int req_len,
+ struct msg_desc *resp_desc,
+ void *resp, unsigned int resp_len,
+ unsigned long timeout_ms)
+{
+ return -ENODEV;
+}
+
+static inline int qmi_send_req_nowait(struct qmi_handle *handle,
+ struct msg_desc *req_desc,
+ void *req, unsigned int req_len,
+ struct msg_desc *resp_desc,
+ void *resp, unsigned int resp_len,
+ void (*resp_cb)(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ void *resp_cb_data),
+ void *resp_cb_data)
+{
+ return -ENODEV;
+}
+
+static inline int qmi_recv_msg(struct qmi_handle *handle)
+{
+ return -ENODEV;
+}
+
+static inline int qmi_connect_to_service(struct qmi_handle *handle,
+ uint32_t service_id,
+ uint32_t instance_id)
+{
+ return -ENODEV;
+}
+
+static inline int qmi_svc_event_notifier_register(uint32_t service_id,
+ uint32_t instance_id,
+ struct notifier_block *nb)
+{
+ return -ENODEV;
+}
+
+static inline int qmi_svc_event_notifier_unregister(uint32_t service_id,
+ uint32_t instance_id,
+ struct notifier_block *nb)
+{
+ return -ENODEV;
+}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h
index 44b52b6..c77c181 100644
--- a/arch/arm/mach-msm/include/mach/msm_smsm.h
+++ b/arch/arm/mach-msm/include/mach/msm_smsm.h
@@ -83,7 +83,7 @@
#define SMSM_WKUP_REASON_TIMER 0x00000008
#define SMSM_WKUP_REASON_ALARM 0x00000010
#define SMSM_WKUP_REASON_RESET 0x00000020
-#define SMSM_A2_FORCE_SHUTDOWN 0x00002000
+#define SMSM_USB_PLUG_UNPLUG 0x00002000
#define SMSM_A2_RESET_BAM 0x00004000
#define SMSM_VENDOR 0x00020000
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 4854fc48..61c2aa8 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, 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
@@ -145,6 +145,11 @@
if (time_param->latency_us < level->latency_us)
continue;
+ if ((MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE == sleep_mode)
+ || (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == sleep_mode))
+ if (!cpu && msm_rpm_waiting_for_ack())
+ break;
+
if (time_param->sleep_us <= 1) {
pwr = level->energy_overhead;
} else if (time_param->sleep_us <= level->time_overhead_us) {
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index 255cd46..c21ea33 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, 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
@@ -27,6 +27,7 @@
#include "lpm_resources.h"
#include "rpm-notifier.h"
#include "idle.h"
+#include "trace_msm_low_power.h"
/*Debug Definitions*/
enum {
@@ -417,6 +418,7 @@
if (rs->valid)
rs->sleep_value = limits->l2_cache;
+ trace_lpm_resources(rs->sleep_value, rs->name);
}
static void msm_lpm_flush_l2(int notify_rpm)
@@ -497,6 +499,7 @@
else
rs->sleep_value = vdd_buf;
}
+ trace_lpm_resources(rs->sleep_value, rs->name);
}
static void msm_lpm_flush_vdd_dig(int notify_rpm)
@@ -551,6 +554,7 @@
else
rs->sleep_value = vdd_buf;
}
+ trace_lpm_resources(rs->sleep_value, rs->name);
}
static void msm_lpm_flush_vdd_mem(int notify_rpm)
@@ -608,6 +612,7 @@
pr_info("%s: pxo buf %d sleep value %d\n",
__func__, pxo_buf, rs->sleep_value);
}
+ trace_lpm_resources(rs->sleep_value, rs->name);
}
static void msm_lpm_flush_pxo(int notify_rpm)
diff --git a/arch/arm/mach-msm/msm_bus/Makefile b/arch/arm/mach-msm/msm_bus/Makefile
index bdc6fac..dde25ab 100644
--- a/arch/arm/mach-msm/msm_bus/Makefile
+++ b/arch/arm/mach-msm/msm_bus/Makefile
@@ -11,4 +11,5 @@
obj-$(CONFIG_ARCH_APQ8064) += msm_bus_board_8064.o
obj-$(CONFIG_ARCH_MSM8930) += msm_bus_board_8930.o
obj-$(CONFIG_ARCH_MSM8974) += msm_bus_board_8974.o
+obj-$(CONFIG_ARCH_MSM9625) += msm_bus_board_9625.o
obj-$(CONFIG_DEBUG_FS) += msm_bus_dbg.o
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c
new file mode 100644
index 0000000..92cd255
--- /dev/null
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c
@@ -0,0 +1,1303 @@
+/* Copyright (c) 2012, 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/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/board.h>
+#include <mach/rpm.h>
+#include "msm_bus_core.h"
+#include "msm_bus_noc.h"
+#include "msm_bus_bimc.h"
+
+#define NMASTERS 120
+#define NSLAVES 150
+#define NFAB_9625 4
+
+enum msm_bus_9625_master_ports_type {
+ /* System NOC Masters */
+ MASTER_PORT_LPASS_AHB = 0,
+ MASTER_PORT_QDSS_BAM,
+ MASTER_PORT_SNOC_CFG,
+ MASTER_PORT_GW_BIMC_SNOC,
+ MASTER_PORT_GW_CNOC_SNOC,
+ MASTER_PORT_CRYPTO_CORE0,
+ MASTER_PORT_LPASS_PROC,
+ MASTER_PORT_MSS,
+ MASTER_PORT_MSS_NAV,
+ MASTER_PORT_IPA,
+ MASTER_PORT_GW_PNOC_SNOC,
+ MASTER_PORT_QDSS_ETR,
+
+ /* BIMC Masters */
+ MASTER_PORT_KMPSS_M0 = 0,
+ MASTER_PORT_MSS_PROC,
+ MASTER_PORT_GW_SNOC_BIMC_0,
+
+ /* Peripheral NOC Masters */
+ MASTER_PORT_QPIC = 0,
+ MASTER_PORT_SDCC_1,
+ MASTER_PORT_SDCC_3,
+ MASTER_PORT_SDCC_2,
+ MASTER_PORT_SDCC_4,
+ MASTER_PORT_TSIF,
+ MASTER_PORT_BAM_DMA,
+ MASTER_PORT_BLSP_2,
+ MASTER_PORT_USB_HSIC,
+ MASTER_PORT_BLSP_1,
+ MASTER_PORT_USB_HS1,
+ MASTER_PORT_USB_HS2,
+ MASTER_PORT_PNOC_CFG,
+ MASTER_PORT_GW_SNOC_PNOC,
+
+ /* Config NOC Masters */
+ MASTER_PORT_RPM_INST = 0,
+ MASTER_PORT_RPM_DATA,
+ MASTER_PORT_RPM_SYS,
+ MASTER_PORT_DEHR,
+ MASTER_PORT_QDSS_DAP,
+ MASTER_PORT_SPDM,
+ MASTER_PORT_TIC,
+ MASTER_PORT_GW_SNOC_CNOC,
+};
+
+enum msm_bus_9625_slave_ports_type {
+ /* System NOC Slaves */
+ SLAVE_PORT_KMPSS = 1,
+ SLAVE_PORT_LPASS,
+ SLAVE_PORT_GW_SNOC_BIMC_P0,
+ SLAVE_PORT_GW_SNOC_CNOC,
+ SLAVE_PORT_OCIMEM,
+ SLAVE_PORT_GW_SNOC_PNOC,
+ SLAVE_PORT_SERVICE_SNOC,
+ SLAVE_PORT_QDSS_STM,
+
+ /* BIMC Slaves */
+ SLAVE_PORT_EBI1_CH0 = 0,
+ SLAVE_PORT_GW_BIMC_SNOC,
+
+ /*Peripheral NOC Slaves */
+ SLAVE_PORT_QPIC = 0,
+ SLAVE_PORT_SDCC_1,
+ SLAVE_PORT_SDCC_3,
+ SLAVE_PORT_SDCC_2,
+ SLAVE_PORT_SDCC_4,
+ SLAVE_PORT_TSIF,
+ SLAVE_PORT_BAM_DMA,
+ SLAVE_PORT_BLSP_2,
+ SLAVE_PORT_USB_HSIC,
+ SLAVE_PORT_BLSP_1,
+ SLAVE_PORT_USB_HS1,
+ SLAVE_PORT_USB_HS2,
+ SLAVE_PORT_PDM,
+ SLAVE_PORT_PERIPH_APU_CFG,
+ SLAVE_PORT_PNOC_MPU_CFG,
+ SLAVE_PORT_PRNG,
+ SLAVE_PORT_GW_PNOC_SNOC,
+ SLAVE_PORT_SERVICE_PNOC,
+
+ /* Config NOC slaves */
+ SLAVE_PORT_CLK_CTL = 0,
+ SLAVE_PORT_CNOC_MSS,
+ SLAVE_PORT_SECURITY,
+ SLAVE_PORT_TCSR,
+ SLAVE_PORT_TLMM,
+ SLAVE_PORT_CRYPTO_0_CFG,
+ SLAVE_PORT_IMEM_CFG,
+ SLAVE_PORT_IPS_CFG,
+ SLAVE_PORT_MESSAGE_RAM,
+ SLAVE_PORT_BIMC_CFG,
+ SLAVE_PORT_BOOT_ROM,
+ SLAVE_PORT_PMIC_ARB,
+ SLAVE_PORT_SPDM_WRAPPER,
+ SLAVE_PORT_DEHR_CFG,
+ SLAVE_PORT_MPM,
+ SLAVE_PORT_QDSS_CFG,
+ SLAVE_PORT_RBCPR_CFG,
+ SLAVE_PORT_RBCPR_QDSS_APU_CFG,
+ SLAVE_PORT_SNOC_MPU_CFG,
+ SLAVE_PORT_PNOC_CFG,
+ SLAVE_PORT_SNOC_CFG,
+ SLAVE_PORT_PHY_APU_CFG,
+ SLAVE_PORT_EBI1_PHY_CFG,
+ SLAVE_PORT_RPM,
+ SLAVE_PORT_GW_CNOC_SNOC,
+ SLAVE_PORT_SERVICE_CNOC,
+};
+
+/* Hardware IDs for RPM */
+enum msm_bus_9625_mas_hw_id {
+ MAS_APPSS_PROC = 0,
+ MAS_AMSS_PROC,
+ MAS_MNOC_BIMC,
+ MAS_SNOC_BIMC,
+ MAS_CNOC_MNOC_MMSS_CFG,
+ MAS_CNOC_MNOC_CFG,
+ MAS_GFX3D,
+ MAS_JPEG,
+ MAS_MDP,
+ MAS_VIDEO_P0,
+ MAS_VIDEO_P1,
+ MAS_VFE,
+ MAS_CNOC_ONOC_CFG,
+ MAS_JPEG_OCMEM,
+ MAS_MDP_OCMEM,
+ MAS_VIDEO_P0_OCMEM,
+ MAS_VIDEO_P1_OCMEM,
+ MAS_VFE_OCMEM,
+ MAS_LPASS_AHB,
+ MAS_QDSS_BAM,
+ MAS_SNOC_CFG,
+ MAS_BIMC_SNOC,
+ MAS_CNOC_SNOC,
+ MAS_CRYPTO_CORE0,
+ MAS_CRYPTO_CORE1,
+ MAS_LPASS_PROC,
+ MAS_MSS,
+ MAS_MSS_NAV,
+ MAS_OCMEM_DMA,
+ MAS_PNOC_SNOC,
+ MAS_WCSS,
+ MAS_QDSS_ETR,
+ MAS_USB3,
+ MAS_SDCC_1,
+ MAS_SDCC_3,
+ MAS_SDCC_2,
+ MAS_SDCC_4,
+ MAS_TSIF,
+ MAS_BAM_DMA,
+ MAS_BLSP_2,
+ MAS_USB_HSIC,
+ MAS_BLSP_1,
+ MAS_USB_HS,
+ MAS_PNOC_CFG,
+ MAS_SNOC_PNOC,
+ MAS_RPM_INST,
+ MAS_RPM_DATA,
+ MAS_RPM_SYS,
+ MAS_DEHR,
+ MAS_QDSS_DAP,
+ MAS_SPDM,
+ MAS_TIC,
+ MAS_SNOC_CNOC,
+ MAS_OVNOC_SNOC,
+ MAS_OVNOC_ONOC,
+ MAS_V_OCMEM_GFX3D,
+ MAS_ONOC_OVNOC,
+ MAS_SNOC_OVNOC,
+ MAS_QPIC,
+ MAS_IPA,
+};
+
+enum msm_bus_9625_slv_hw_id {
+ SLV_EBI = 0,
+ SLV_APSS_L2,
+ SLV_BIMC_SNOC,
+ SLV_CAMERA_CFG,
+ SLV_DISPLAY_CFG,
+ SLV_OCMEM_CFG,
+ SLV_CPR_CFG,
+ SLV_CPR_XPU_CFG,
+ SLV_MISC_CFG,
+ SLV_MISC_XPU_CFG,
+ SLV_VENUS_CFG,
+ SLV_GFX3D_CFG,
+ SLV_MMSS_CLK_CFG,
+ SLV_MMSS_CLK_XPU_CFG,
+ SLV_MNOC_MPU_CFG,
+ SLV_ONOC_MPU_CFG,
+ SLV_MMSS_BIMC,
+ SLV_SERVICE_MNOC,
+ SLV_OCMEM,
+ SLV_SERVICE_ONOC,
+ SLV_APPSS,
+ SLV_LPASS,
+ SLV_USB3,
+ SLV_WCSS,
+ SLV_SNOC_BIMC,
+ SLV_SNOC_CNOC,
+ SLV_OCIMEM,
+ SLV_SNOC_OCMEM,
+ SLV_SNOC_PNOC,
+ SLV_SERVICE_SNOC,
+ SLV_QDSS_STM,
+ SLV_SDCC_1,
+ SLV_SDCC_3,
+ SLV_SDCC_2,
+ SLV_SDCC_4,
+ SLV_TSIF,
+ SLV_BAM_DMA,
+ SLV_BLSP_2,
+ SLV_USB_HSIC,
+ SLV_BLSP_1,
+ SLV_USB_HS,
+ SLV_PDM,
+ SLV_PERIPH_APU_CFG,
+ SLV_MPU_CFG,
+ SLV_PRNG,
+ SLV_PNOC_SNOC,
+ SLV_SERVICE_PNOC,
+ SLV_CLK_CTL,
+ SLV_CNOC_MSS,
+ SLV_SECURITY,
+ SLV_TCSR,
+ SLV_TLMM,
+ SLV_CRYPTO_0_CFG,
+ SLV_CRYPTO_1_CFG,
+ SLV_IMEM_CFG,
+ SLV_MESSAGE_RAM,
+ SLV_BIMC_CFG,
+ SLV_BOOT_ROM,
+ SLV_CNOC_MNOC_MMSS_CFG,
+ SLV_PMIC_ARB,
+ SLV_SPDM_WRAPPER,
+ SLV_DEHR_CFG,
+ SLV_MPM,
+ SLV_QDSS_CFG,
+ SLV_RBCPR_CFG,
+ SLV_RBCPR_QDSS_APU_CFG,
+ SLV_CNOC_MNOC_CFG,
+ SLV_SNOC_MPU_CFG,
+ SLV_CNOC_ONOC_CFG,
+ SLV_PNOC_CFG,
+ SLV_SNOC_CFG,
+ SLV_EBI1_DLL_CFG,
+ SLV_PHY_APU_CFG,
+ SLV_EBI1_PHY_CFG,
+ SLV_RPM,
+ SLV_CNOC_SNOC,
+ SLV_SERVICE_CNOC,
+ SLV_SNOC_OVNOC,
+ SLV_ONOC_OVNOC,
+ SLV_USB_HS2,
+ SLV_QPIC,
+ SLV_IPS_CFG,
+};
+
+static uint32_t master_iids[NMASTERS];
+static uint32_t slave_iids[NSLAVES];
+
+/* System NOC nodes */
+static int mport_lpass_ahb[] = {MASTER_PORT_LPASS_AHB,};
+static int mport_qdss_bam[] = {MASTER_PORT_QDSS_BAM,};
+static int mport_snoc_cfg[] = {MASTER_PORT_SNOC_CFG,};
+static int mport_gw_bimc_snoc[] = {MASTER_PORT_GW_BIMC_SNOC,};
+static int mport_gw_cnoc_snoc[] = {MASTER_PORT_GW_CNOC_SNOC,};
+static int mport_crypto_core0[] = {MASTER_PORT_CRYPTO_CORE0,};
+static int mport_lpass_proc[] = {MASTER_PORT_LPASS_PROC};
+static int mport_mss[] = {MASTER_PORT_MSS};
+static int mport_mss_nav[] = {MASTER_PORT_MSS_NAV};
+static int mport_ipa[] = {MASTER_PORT_IPA};
+static int mport_gw_pnoc_snoc[] = {MASTER_PORT_GW_PNOC_SNOC};
+static int mport_qdss_etr[] = {MASTER_PORT_QDSS_ETR};
+
+static int sport_kmpss[] = {SLAVE_PORT_KMPSS};
+static int sport_lpass[] = {SLAVE_PORT_LPASS};
+static int sport_gw_snoc_bimc[] = {SLAVE_PORT_GW_SNOC_BIMC_P0};
+static int sport_gw_snoc_cnoc[] = {SLAVE_PORT_GW_SNOC_CNOC};
+static int sport_ocimem[] = {SLAVE_PORT_OCIMEM};
+static int sport_gw_snoc_pnoc[] = {SLAVE_PORT_GW_SNOC_PNOC};
+static int sport_service_snoc[] = {SLAVE_PORT_SERVICE_SNOC};
+static int sport_qdss_stm[] = {SLAVE_PORT_QDSS_STM};
+
+/* BIMC Nodes */
+
+static int mport_kmpss_m0[] = {MASTER_PORT_KMPSS_M0,};
+static int mport_mss_proc[] = {MASTER_PORT_MSS_PROC};
+static int mport_gw_snoc_bimc[] = {MASTER_PORT_GW_SNOC_BIMC_0};
+
+static int sport_ebi1[] = {SLAVE_PORT_EBI1_CH0};
+static int sport_gw_bimc_snoc[] = {SLAVE_PORT_GW_BIMC_SNOC,};
+
+/* Peripheral NOC Nodes */
+static int mport_sdcc_1[] = {MASTER_PORT_SDCC_1,};
+static int mport_sdcc_3[] = {MASTER_PORT_SDCC_3,};
+static int mport_sdcc_2[] = {MASTER_PORT_SDCC_2,};
+static int mport_sdcc_4[] = {MASTER_PORT_SDCC_4,};
+static int mport_tsif[] = {MASTER_PORT_TSIF,};
+static int mport_bam_dma[] = {MASTER_PORT_BAM_DMA,};
+static int mport_blsp_2[] = {MASTER_PORT_BLSP_2,};
+static int mport_usb_hsic[] = {MASTER_PORT_USB_HSIC,};
+static int mport_blsp_1[] = {MASTER_PORT_BLSP_1,};
+static int mport_pnoc_cfg[] = {MASTER_PORT_PNOC_CFG,};
+static int mport_qpic[] = {MASTER_PORT_QPIC,};
+static int mport_gw_snoc_pnoc[] = {MASTER_PORT_GW_SNOC_PNOC,};
+
+static int sport_sdcc_1[] = {SLAVE_PORT_SDCC_1,};
+static int sport_sdcc_3[] = {SLAVE_PORT_SDCC_3,};
+static int sport_sdcc_2[] = {SLAVE_PORT_SDCC_2,};
+static int sport_sdcc_4[] = {SLAVE_PORT_SDCC_4,};
+static int sport_tsif[] = {SLAVE_PORT_TSIF,};
+static int sport_qpic[] = {SLAVE_PORT_QPIC,};
+static int sport_bam_dma[] = {SLAVE_PORT_BAM_DMA,};
+static int sport_blsp_2[] = {SLAVE_PORT_BLSP_2,};
+static int sport_usb_hsic[] = {SLAVE_PORT_USB_HSIC,};
+static int sport_blsp_1[] = {SLAVE_PORT_BLSP_1,};
+static int sport_pdm[] = {SLAVE_PORT_PDM,};
+static int sport_periph_apu_cfg[] = {
+ SLAVE_PORT_PERIPH_APU_CFG,
+};
+static int sport_pnoc_mpu_cfg[] = {SLAVE_PORT_PNOC_MPU_CFG,};
+static int sport_prng[] = {SLAVE_PORT_PRNG,};
+static int sport_gw_pnoc_snoc[] = {SLAVE_PORT_GW_PNOC_SNOC,};
+static int sport_service_pnoc[] = {SLAVE_PORT_SERVICE_PNOC,};
+
+/* Config NOC Nodes */
+static int mport_rpm_inst[] = {MASTER_PORT_RPM_INST,};
+static int mport_rpm_data[] = {MASTER_PORT_RPM_DATA,};
+static int mport_rpm_sys[] = {MASTER_PORT_RPM_SYS,};
+static int mport_dehr[] = {MASTER_PORT_DEHR,};
+static int mport_qdss_dap[] = {MASTER_PORT_QDSS_DAP,};
+static int mport_spdm[] = {MASTER_PORT_SPDM,};
+static int mport_tic[] = {MASTER_PORT_TIC,};
+static int mport_gw_snoc_cnoc[] = {MASTER_PORT_GW_SNOC_CNOC,};
+
+static int sport_clk_ctl[] = {SLAVE_PORT_CLK_CTL,};
+static int sport_cnoc_mss[] = {SLAVE_PORT_CNOC_MSS,};
+static int sport_security[] = {SLAVE_PORT_SECURITY,};
+static int sport_tcsr[] = {SLAVE_PORT_TCSR,};
+static int sport_tlmm[] = {SLAVE_PORT_TLMM,};
+static int sport_crypto_0_cfg[] = {SLAVE_PORT_CRYPTO_0_CFG,};
+static int sport_imem_cfg[] = {SLAVE_PORT_IMEM_CFG,};
+static int sport_ips_cfg[] = {SLAVE_PORT_IPS_CFG,};
+static int sport_message_ram[] = {SLAVE_PORT_MESSAGE_RAM,};
+static int sport_bimc_cfg[] = {SLAVE_PORT_BIMC_CFG,};
+static int sport_boot_rom[] = {SLAVE_PORT_BOOT_ROM,};
+static int sport_pmic_arb[] = {SLAVE_PORT_PMIC_ARB,};
+static int sport_spdm_wrapper[] = {SLAVE_PORT_SPDM_WRAPPER,};
+static int sport_dehr_cfg[] = {SLAVE_PORT_DEHR_CFG,};
+static int sport_mpm[] = {SLAVE_PORT_MPM,};
+static int sport_qdss_cfg[] = {SLAVE_PORT_QDSS_CFG,};
+static int sport_rbcpr_cfg[] = {SLAVE_PORT_RBCPR_CFG,};
+static int sport_rbcpr_qdss_apu_cfg[] = {SLAVE_PORT_RBCPR_QDSS_APU_CFG,};
+static int sport_snoc_mpu_cfg[] = {SLAVE_PORT_SNOC_MPU_CFG,};
+static int sport_pnoc_cfg[] = {SLAVE_PORT_PNOC_CFG,};
+static int sport_snoc_cfg[] = {SLAVE_PORT_SNOC_CFG,};
+static int sport_phy_apu_cfg[] = {SLAVE_PORT_PHY_APU_CFG,};
+static int sport_ebi1_phy_cfg[] = {SLAVE_PORT_EBI1_PHY_CFG,};
+static int sport_rpm[] = {SLAVE_PORT_RPM,};
+static int sport_gw_cnoc_snoc[] = {SLAVE_PORT_GW_CNOC_SNOC,};
+static int sport_service_cnoc[] = {SLAVE_PORT_SERVICE_CNOC,};
+
+static int tier2[] = {MSM_BUS_BW_TIER2,};
+
+/*
+ * QOS Ports defined only when qos ports are different than
+ * master ports
+ **/
+static int qports_crypto_c0[] = {2};
+static int qports_lpass_proc[] = {4};
+static int qports_gw_snoc_bimc[] = {2};
+static int qports_kmpss[] = {0};
+static int qports_lpass_ahb[] = {0};
+static int qports_qdss_bam[] = {1};
+static int qports_gw_pnoc_snoc[] = {8};
+static int qports_ipa[] = {7};
+static int qports_qdss_etr[] = {10};
+
+static struct msm_bus_node_info sys_noc_info[] = {
+ {
+ .id = MSM_BUS_MASTER_LPASS_AHB,
+ .masterp = mport_lpass_ahb,
+ .num_mports = ARRAY_SIZE(mport_lpass_ahb),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .qport = qports_lpass_ahb,
+ .mas_hw_id = MAS_LPASS_AHB,
+ .mode = NOC_QOS_MODE_FIXED,
+ .prio_rd = 2,
+ .prio_wr = 2,
+ },
+ {
+ .id = MSM_BUS_MASTER_QDSS_BAM,
+ .masterp = mport_qdss_bam,
+ .num_mports = ARRAY_SIZE(mport_qdss_bam),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .mode = NOC_QOS_MODE_FIXED,
+ .qport = qports_qdss_bam,
+ .mas_hw_id = MAS_QDSS_BAM,
+ },
+ {
+ .id = MSM_BUS_MASTER_SNOC_CFG,
+ .masterp = mport_snoc_cfg,
+ .num_mports = ARRAY_SIZE(mport_snoc_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .mas_hw_id = MAS_SNOC_CFG,
+ },
+ {
+ .id = MSM_BUS_FAB_BIMC,
+ .gateway = 1,
+ .slavep = sport_gw_snoc_bimc,
+ .num_sports = ARRAY_SIZE(sport_gw_snoc_bimc),
+ .masterp = mport_gw_bimc_snoc,
+ .num_mports = ARRAY_SIZE(mport_gw_bimc_snoc),
+ .buswidth = 8,
+ .mas_hw_id = MAS_BIMC_SNOC,
+ .slv_hw_id = SLV_SNOC_BIMC,
+ },
+ {
+ .id = MSM_BUS_FAB_CONFIG_NOC,
+ .gateway = 1,
+ .slavep = sport_gw_snoc_cnoc,
+ .num_sports = ARRAY_SIZE(sport_gw_snoc_cnoc),
+ .masterp = mport_gw_cnoc_snoc,
+ .num_mports = ARRAY_SIZE(mport_gw_cnoc_snoc),
+ .buswidth = 8,
+ .mas_hw_id = MAS_CNOC_SNOC,
+ .slv_hw_id = SLV_SNOC_CNOC,
+ },
+ {
+ .id = MSM_BUS_FAB_PERIPH_NOC,
+ .gateway = 1,
+ .slavep = sport_gw_snoc_pnoc,
+ .num_sports = ARRAY_SIZE(sport_gw_snoc_pnoc),
+ .masterp = mport_gw_pnoc_snoc,
+ .num_mports = ARRAY_SIZE(mport_gw_pnoc_snoc),
+ .buswidth = 8,
+ .qport = qports_gw_pnoc_snoc,
+ .mas_hw_id = MAS_PNOC_SNOC,
+ .slv_hw_id = SLV_SNOC_PNOC,
+ .mode = NOC_QOS_MODE_FIXED,
+ .prio_rd = 2,
+ .prio_wr = 2,
+ },
+ {
+ .id = MSM_BUS_MASTER_CRYPTO_CORE0,
+ .masterp = mport_crypto_core0,
+ .num_mports = ARRAY_SIZE(mport_crypto_core0),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .mode = NOC_QOS_MODE_FIXED,
+ .qport = qports_crypto_c0,
+ .mas_hw_id = MAS_CRYPTO_CORE0,
+ .hw_sel = MSM_BUS_NOC,
+ },
+ {
+ .id = MSM_BUS_MASTER_LPASS_PROC,
+ .masterp = mport_lpass_proc,
+ .num_mports = ARRAY_SIZE(mport_lpass_proc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .qport = qports_lpass_proc,
+ .mas_hw_id = MAS_LPASS_PROC,
+ .mode = NOC_QOS_MODE_FIXED,
+ .prio_rd = 2,
+ .prio_wr = 2,
+ },
+ {
+ .id = MSM_BUS_MASTER_MSS,
+ .masterp = mport_mss,
+ .num_mports = ARRAY_SIZE(mport_mss),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .mas_hw_id = MAS_MSS,
+ },
+ {
+ .id = MSM_BUS_MASTER_MSS_NAV,
+ .masterp = mport_mss_nav,
+ .num_mports = ARRAY_SIZE(mport_mss_nav),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .mas_hw_id = MAS_MSS_NAV,
+ },
+ {
+ .id = MSM_BUS_MASTER_IPA,
+ .masterp = mport_ipa,
+ .num_mports = ARRAY_SIZE(mport_ipa),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .mode = NOC_QOS_MODE_FIXED,
+ .qport = qports_ipa,
+ .mas_hw_id = MAS_IPA,
+ },
+ {
+ .id = MSM_BUS_MASTER_QDSS_ETR,
+ .masterp = mport_qdss_etr,
+ .num_mports = ARRAY_SIZE(mport_qdss_etr),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .qport = qports_qdss_etr,
+ .mode = NOC_QOS_MODE_FIXED,
+ .mas_hw_id = MAS_QDSS_ETR,
+ },
+ {
+ .id = MSM_BUS_SLAVE_AMPSS,
+ .slavep = sport_kmpss,
+ .num_sports = ARRAY_SIZE(sport_kmpss),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_APPSS,
+ },
+ {
+ .id = MSM_BUS_SLAVE_LPASS,
+ .slavep = sport_lpass,
+ .num_sports = ARRAY_SIZE(sport_lpass),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_LPASS,
+ },
+ {
+ .id = MSM_BUS_SLAVE_OCIMEM,
+ .slavep = sport_ocimem,
+ .num_sports = ARRAY_SIZE(sport_ocimem),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_OCIMEM,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SERVICE_SNOC,
+ .slavep = sport_service_snoc,
+ .num_sports = ARRAY_SIZE(sport_service_snoc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SERVICE_SNOC,
+ },
+ {
+ .id = MSM_BUS_SLAVE_QDSS_STM,
+ .slavep = sport_qdss_stm,
+ .num_sports = ARRAY_SIZE(sport_qdss_stm),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_QDSS_STM,
+ },
+};
+
+static struct msm_bus_node_info bimc_info[] = {
+ {
+ .id = MSM_BUS_MASTER_AMPSS_M0,
+ .masterp = mport_kmpss_m0,
+ .num_mports = ARRAY_SIZE(mport_kmpss_m0),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .hw_sel = MSM_BUS_BIMC,
+ .mode = NOC_QOS_MODE_FIXED,
+ .qport = qports_kmpss,
+ .ws = 10000,
+ .mas_hw_id = MAS_APPSS_PROC,
+ .prio_lvl = 0,
+ .prio_rd = 2,
+ .prio_wr = 2,
+ },
+ {
+ .id = MSM_BUS_MASTER_MSS_PROC,
+ .masterp = mport_mss_proc,
+ .num_mports = ARRAY_SIZE(mport_mss_proc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .hw_sel = MSM_BUS_RPM,
+ .mas_hw_id = MAS_AMSS_PROC,
+ },
+ {
+ .id = MSM_BUS_FAB_SYS_NOC,
+ .gateway = 1,
+ .slavep = sport_gw_bimc_snoc,
+ .num_sports = ARRAY_SIZE(sport_gw_bimc_snoc),
+ .masterp = mport_gw_snoc_bimc,
+ .num_mports = ARRAY_SIZE(mport_gw_snoc_bimc),
+ .qport = qports_gw_snoc_bimc,
+ .buswidth = 8,
+ .ws = 10000,
+ .mas_hw_id = MAS_SNOC_BIMC,
+ .slv_hw_id = SLV_BIMC_SNOC,
+ .mode = NOC_QOS_MODE_BYPASS,
+ },
+ {
+ .id = MSM_BUS_SLAVE_EBI_CH0,
+ .slavep = sport_ebi1,
+ .num_sports = ARRAY_SIZE(sport_ebi1),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_EBI,
+ .mode = NOC_QOS_MODE_BYPASS,
+ },
+};
+
+static struct msm_bus_node_info periph_noc_info[] = {
+ {
+ .id = MSM_BUS_MASTER_PNOC_CFG,
+ .masterp = mport_pnoc_cfg,
+ .num_mports = ARRAY_SIZE(mport_pnoc_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_PNOC_CFG,
+ },
+ {
+ .id = MSM_BUS_MASTER_QPIC,
+ .masterp = mport_qpic,
+ .num_mports = ARRAY_SIZE(mport_qpic),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_QPIC,
+ },
+ {
+ .id = MSM_BUS_MASTER_SDCC_1,
+ .masterp = mport_sdcc_1,
+ .num_mports = ARRAY_SIZE(mport_sdcc_1),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_SDCC_1,
+ },
+ {
+ .id = MSM_BUS_MASTER_SDCC_3,
+ .masterp = mport_sdcc_3,
+ .num_mports = ARRAY_SIZE(mport_sdcc_3),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_SDCC_3,
+ },
+ {
+ .id = MSM_BUS_MASTER_SDCC_4,
+ .masterp = mport_sdcc_4,
+ .num_mports = ARRAY_SIZE(mport_sdcc_4),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_SDCC_4,
+ },
+ {
+ .id = MSM_BUS_MASTER_SDCC_2,
+ .masterp = mport_sdcc_2,
+ .num_mports = ARRAY_SIZE(mport_sdcc_2),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_SDCC_2,
+ },
+ {
+ .id = MSM_BUS_MASTER_TSIF,
+ .masterp = mport_tsif,
+ .num_mports = ARRAY_SIZE(mport_tsif),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_TSIF,
+ },
+ {
+ .id = MSM_BUS_MASTER_BAM_DMA,
+ .masterp = mport_bam_dma,
+ .num_mports = ARRAY_SIZE(mport_bam_dma),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_BAM_DMA,
+ },
+ {
+ .id = MSM_BUS_MASTER_BLSP_2,
+ .masterp = mport_blsp_2,
+ .num_mports = ARRAY_SIZE(mport_blsp_2),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_BLSP_2,
+ },
+ {
+ .id = MSM_BUS_MASTER_USB_HSIC,
+ .masterp = mport_usb_hsic,
+ .num_mports = ARRAY_SIZE(mport_usb_hsic),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_USB_HSIC,
+ },
+ {
+ .id = MSM_BUS_MASTER_BLSP_1,
+ .masterp = mport_blsp_1,
+ .num_mports = ARRAY_SIZE(mport_blsp_1),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_BLSP_1,
+ },
+ {
+ .id = MSM_BUS_FAB_SYS_NOC,
+ .gateway = 1,
+ .slavep = sport_gw_pnoc_snoc,
+ .num_sports = ARRAY_SIZE(sport_gw_pnoc_snoc),
+ .masterp = mport_gw_snoc_pnoc,
+ .num_mports = ARRAY_SIZE(mport_gw_snoc_pnoc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_PNOC_SNOC,
+ .mas_hw_id = MAS_SNOC_PNOC,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SDCC_1,
+ .slavep = sport_sdcc_1,
+ .num_sports = ARRAY_SIZE(sport_sdcc_1),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SDCC_1,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SDCC_3,
+ .slavep = sport_sdcc_3,
+ .num_sports = ARRAY_SIZE(sport_sdcc_3),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SDCC_3,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SDCC_2,
+ .slavep = sport_sdcc_2,
+ .num_sports = ARRAY_SIZE(sport_sdcc_2),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SDCC_2,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SDCC_4,
+ .slavep = sport_sdcc_4,
+ .num_sports = ARRAY_SIZE(sport_sdcc_4),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SDCC_4,
+ },
+ {
+ .id = MSM_BUS_SLAVE_TSIF,
+ .slavep = sport_tsif,
+ .num_sports = ARRAY_SIZE(sport_tsif),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_TSIF,
+ },
+ {
+ .id = MSM_BUS_SLAVE_BAM_DMA,
+ .slavep = sport_bam_dma,
+ .num_sports = ARRAY_SIZE(sport_bam_dma),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_BAM_DMA,
+ },
+ {
+ .id = MSM_BUS_SLAVE_QPIC,
+ .masterp = sport_qpic,
+ .num_mports = ARRAY_SIZE(sport_qpic),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = SLV_QPIC,
+ },
+ {
+ .id = MSM_BUS_SLAVE_BLSP_2,
+ .slavep = sport_blsp_2,
+ .num_sports = ARRAY_SIZE(sport_blsp_2),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_BLSP_2,
+ },
+ {
+ .id = MSM_BUS_SLAVE_USB_HSIC,
+ .slavep = sport_usb_hsic,
+ .num_sports = ARRAY_SIZE(sport_usb_hsic),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_USB_HSIC,
+ },
+ {
+ .id = MSM_BUS_SLAVE_BLSP_1,
+ .slavep = sport_blsp_1,
+ .num_sports = ARRAY_SIZE(sport_blsp_1),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_BLSP_1,
+ },
+ {
+ .id = MSM_BUS_SLAVE_PDM,
+ .slavep = sport_pdm,
+ .num_sports = ARRAY_SIZE(sport_pdm),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_PDM,
+ },
+ {
+ .id = MSM_BUS_SLAVE_PERIPH_APU_CFG,
+ .slavep = sport_periph_apu_cfg,
+ .num_sports = ARRAY_SIZE(sport_periph_apu_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_PERIPH_APU_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_PNOC_MPU_CFG,
+ .slavep = sport_pnoc_mpu_cfg,
+ .num_sports = ARRAY_SIZE(sport_pnoc_mpu_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_MPU_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_PRNG,
+ .slavep = sport_prng,
+ .num_sports = ARRAY_SIZE(sport_prng),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_PRNG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SERVICE_PNOC,
+ .slavep = sport_service_pnoc,
+ .num_sports = ARRAY_SIZE(sport_service_pnoc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SERVICE_PNOC,
+ },
+};
+
+static struct msm_bus_node_info config_noc_info[] = {
+ {
+ .id = MSM_BUS_MASTER_RPM_INST,
+ .masterp = mport_rpm_inst,
+ .num_mports = ARRAY_SIZE(mport_rpm_inst),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_RPM_INST,
+ },
+ {
+ .id = MSM_BUS_MASTER_RPM_DATA,
+ .masterp = mport_rpm_data,
+ .num_mports = ARRAY_SIZE(mport_rpm_data),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_RPM_DATA,
+ },
+ {
+ .id = MSM_BUS_MASTER_RPM_SYS,
+ .masterp = mport_rpm_sys,
+ .num_mports = ARRAY_SIZE(mport_rpm_sys),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_RPM_SYS,
+ },
+ {
+ .id = MSM_BUS_MASTER_DEHR,
+ .masterp = mport_dehr,
+ .num_mports = ARRAY_SIZE(mport_dehr),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_DEHR,
+ },
+ {
+ .id = MSM_BUS_MASTER_QDSS_DAP,
+ .masterp = mport_qdss_dap,
+ .num_mports = ARRAY_SIZE(mport_qdss_dap),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_QDSS_DAP,
+ },
+ {
+ .id = MSM_BUS_MASTER_SPDM,
+ .masterp = mport_spdm,
+ .num_mports = ARRAY_SIZE(mport_spdm),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_SPDM,
+ },
+ {
+ .id = MSM_BUS_MASTER_TIC,
+ .masterp = mport_tic,
+ .num_mports = ARRAY_SIZE(mport_tic),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_TIC,
+ },
+ {
+ .id = MSM_BUS_SLAVE_CLK_CTL,
+ .slavep = sport_clk_ctl,
+ .num_sports = ARRAY_SIZE(sport_clk_ctl),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_CLK_CTL,
+ },
+ {
+ .id = MSM_BUS_SLAVE_CNOC_MSS,
+ .slavep = sport_cnoc_mss,
+ .num_sports = ARRAY_SIZE(sport_cnoc_mss),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_CNOC_MSS,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SECURITY,
+ .slavep = sport_security,
+ .num_sports = ARRAY_SIZE(sport_security),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SECURITY,
+ },
+ {
+ .id = MSM_BUS_SLAVE_TCSR,
+ .slavep = sport_tcsr,
+ .num_sports = ARRAY_SIZE(sport_tcsr),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_TCSR,
+ },
+ {
+ .id = MSM_BUS_SLAVE_TLMM,
+ .slavep = sport_tlmm,
+ .num_sports = ARRAY_SIZE(sport_tlmm),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_TLMM,
+ },
+ {
+ .id = MSM_BUS_SLAVE_CRYPTO_0_CFG,
+ .slavep = sport_crypto_0_cfg,
+ .num_sports = ARRAY_SIZE(sport_crypto_0_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_CRYPTO_0_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_IMEM_CFG,
+ .slavep = sport_imem_cfg,
+ .num_sports = ARRAY_SIZE(sport_imem_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_IMEM_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_IPS_CFG,
+ .slavep = sport_ips_cfg,
+ .num_sports = ARRAY_SIZE(sport_ips_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_IPS_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_MESSAGE_RAM,
+ .slavep = sport_message_ram,
+ .num_sports = ARRAY_SIZE(sport_message_ram),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_MESSAGE_RAM,
+ },
+ {
+ .id = MSM_BUS_SLAVE_BIMC_CFG,
+ .slavep = sport_bimc_cfg,
+ .num_sports = ARRAY_SIZE(sport_bimc_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_BIMC_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_BOOT_ROM,
+ .slavep = sport_boot_rom,
+ .num_sports = ARRAY_SIZE(sport_boot_rom),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_BOOT_ROM,
+ },
+ {
+ .id = MSM_BUS_SLAVE_PMIC_ARB,
+ .slavep = sport_pmic_arb,
+ .num_sports = ARRAY_SIZE(sport_pmic_arb),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_PMIC_ARB,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SPDM_WRAPPER,
+ .slavep = sport_spdm_wrapper,
+ .num_sports = ARRAY_SIZE(sport_spdm_wrapper),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SPDM_WRAPPER,
+ },
+ {
+ .id = MSM_BUS_SLAVE_DEHR_CFG,
+ .slavep = sport_dehr_cfg,
+ .num_sports = ARRAY_SIZE(sport_dehr_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_DEHR_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_MPM,
+ .slavep = sport_mpm,
+ .num_sports = ARRAY_SIZE(sport_mpm),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_MPM,
+ },
+ {
+ .id = MSM_BUS_SLAVE_QDSS_CFG,
+ .slavep = sport_qdss_cfg,
+ .num_sports = ARRAY_SIZE(sport_qdss_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_QDSS_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_RBCPR_CFG,
+ .slavep = sport_rbcpr_cfg,
+ .num_sports = ARRAY_SIZE(sport_rbcpr_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_RBCPR_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_RBCPR_QDSS_APU_CFG,
+ .slavep = sport_rbcpr_qdss_apu_cfg,
+ .num_sports = ARRAY_SIZE(sport_rbcpr_qdss_apu_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_RBCPR_QDSS_APU_CFG,
+ },
+ {
+ .id = MSM_BUS_FAB_SYS_NOC,
+ .gateway = 1,
+ .slavep = sport_gw_cnoc_snoc,
+ .num_sports = ARRAY_SIZE(sport_gw_cnoc_snoc),
+ .masterp = mport_gw_snoc_cnoc,
+ .num_mports = ARRAY_SIZE(mport_gw_snoc_cnoc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .mas_hw_id = MAS_SNOC_CNOC,
+ .slv_hw_id = SLV_CNOC_SNOC,
+ },
+ {
+ .id = MSM_BUS_SLAVE_PNOC_CFG,
+ .slavep = sport_pnoc_cfg,
+ .num_sports = ARRAY_SIZE(sport_pnoc_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_PNOC_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SNOC_MPU_CFG,
+ .slavep = sport_snoc_mpu_cfg,
+ .num_sports = ARRAY_SIZE(sport_snoc_mpu_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SNOC_MPU_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SNOC_CFG,
+ .slavep = sport_snoc_cfg,
+ .num_sports = ARRAY_SIZE(sport_snoc_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SNOC_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_PHY_APU_CFG,
+ .slavep = sport_phy_apu_cfg,
+ .num_sports = ARRAY_SIZE(sport_phy_apu_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_PHY_APU_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_EBI1_PHY_CFG,
+ .slavep = sport_ebi1_phy_cfg,
+ .num_sports = ARRAY_SIZE(sport_ebi1_phy_cfg),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_EBI1_PHY_CFG,
+ },
+ {
+ .id = MSM_BUS_SLAVE_RPM,
+ .slavep = sport_rpm,
+ .num_sports = ARRAY_SIZE(sport_rpm),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_RPM,
+ },
+ {
+ .id = MSM_BUS_SLAVE_SERVICE_CNOC,
+ .slavep = sport_service_cnoc,
+ .num_sports = ARRAY_SIZE(sport_service_cnoc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ .buswidth = 8,
+ .slv_hw_id = SLV_SERVICE_CNOC,
+ },
+};
+
+static void msm_bus_board_assign_iids(struct msm_bus_fabric_registration
+ *fabreg, int fabid)
+{
+ int i;
+ for (i = 0; i < fabreg->len; i++) {
+ if (!fabreg->info[i].gateway) {
+ fabreg->info[i].priv_id = fabid + fabreg->info[i].id;
+ if (fabreg->info[i].id < SLAVE_ID_KEY) {
+ WARN(fabreg->info[i].id >= NMASTERS,
+ "id %d exceeds array size!\n",
+ fabreg->info[i].id);
+ master_iids[fabreg->info[i].id] =
+ fabreg->info[i].priv_id;
+ } else {
+ WARN((fabreg->info[i].id - SLAVE_ID_KEY) >=
+ NSLAVES, "id %d exceeds array size!\n",
+ fabreg->info[i].id);
+ slave_iids[fabreg->info[i].id - (SLAVE_ID_KEY)]
+ = fabreg->info[i].priv_id;
+ }
+ } else {
+ fabreg->info[i].priv_id = fabreg->info[i].id;
+ }
+ }
+}
+
+static int msm_bus_board_9625_get_iid(int id)
+{
+ if ((id < SLAVE_ID_KEY && id >= NMASTERS) ||
+ id >= (SLAVE_ID_KEY + NSLAVES)) {
+ MSM_BUS_ERR("Cannot get iid. Invalid id %d passed\n", id);
+ return -EINVAL;
+ }
+
+ return CHECK_ID(((id < SLAVE_ID_KEY) ? master_iids[id] :
+ slave_iids[id - SLAVE_ID_KEY]), id);
+}
+
+int msm_bus_board_rpm_get_il_ids(uint16_t *id)
+{
+ return -ENXIO;
+}
+
+static struct msm_bus_board_algorithm msm_bus_board_algo = {
+ .board_nfab = NFAB_9625,
+ .get_iid = msm_bus_board_9625_get_iid,
+ .assign_iids = msm_bus_board_assign_iids,
+};
+
+struct msm_bus_fabric_registration msm_bus_9625_sys_noc_pdata = {
+ .id = MSM_BUS_FAB_SYS_NOC,
+ .name = "msm_sys_noc",
+ .info = sys_noc_info,
+ .len = ARRAY_SIZE(sys_noc_info),
+ .ahb = 0,
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
+ .nmasters = 15,
+ .nslaves = 12,
+ .ntieredslaves = 0,
+ .board_algo = &msm_bus_board_algo,
+ .qos_freq = 4800,
+ .hw_sel = MSM_BUS_NOC,
+ .rpm_enabled = 1,
+};
+
+struct msm_bus_fabric_registration msm_bus_9625_bimc_pdata = {
+ .id = MSM_BUS_FAB_BIMC,
+ .name = "msm_bimc",
+ .info = bimc_info,
+ .len = ARRAY_SIZE(bimc_info),
+ .ahb = 0,
+ .fabclk[DUAL_CTX] = "mem_clk",
+ .fabclk[ACTIVE_CTX] = "mem_a_clk",
+ .nmasters = 7,
+ .nslaves = 4,
+ .ntieredslaves = 0,
+ .board_algo = &msm_bus_board_algo,
+ .qos_freq = 4800,
+ .hw_sel = MSM_BUS_BIMC,
+ .rpm_enabled = 1,
+};
+
+struct msm_bus_fabric_registration msm_bus_9625_periph_noc_pdata = {
+ .id = MSM_BUS_FAB_PERIPH_NOC,
+ .name = "msm_periph_noc",
+ .info = periph_noc_info,
+ .len = ARRAY_SIZE(periph_noc_info),
+ .ahb = 0,
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
+ .nmasters = 14,
+ .nslaves = 15,
+ .ntieredslaves = 0,
+ .board_algo = &msm_bus_board_algo,
+ .hw_sel = MSM_BUS_NOC,
+ .rpm_enabled = 1,
+};
+
+struct msm_bus_fabric_registration msm_bus_9625_config_noc_pdata = {
+ .id = MSM_BUS_FAB_CONFIG_NOC,
+ .name = "msm_config_noc",
+ .info = config_noc_info,
+ .len = ARRAY_SIZE(config_noc_info),
+ .ahb = 0,
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
+ .nmasters = 8,
+ .nslaves = 30,
+ .ntieredslaves = 0,
+ .board_algo = &msm_bus_board_algo,
+ .hw_sel = MSM_BUS_NOC,
+ .rpm_enabled = 1,
+};
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index b2160c5..c1c05af 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -384,9 +384,6 @@
static int msm_dcvs_do_freq(void *data)
{
struct dcvs_core *core = (struct dcvs_core *)data;
- static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
-
- sched_setscheduler(current, SCHED_FIFO, ¶m);
while (!kthread_should_stop()) {
wait_event(core->wait_q, !(core->pending_freq == 0 ||
diff --git a/arch/arm/mach-msm/msm_mpdecision.c b/arch/arm/mach-msm/msm_mpdecision.c
index 9f6cc11..a65b3ee 100644
--- a/arch/arm/mach-msm/msm_mpdecision.c
+++ b/arch/arm/mach-msm/msm_mpdecision.c
@@ -356,11 +356,8 @@
static int __cpuinit msm_mpd_do_hotplug(void *data)
{
int *event = (int *)data;
- static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
int cpu;
- sched_setscheduler(current, SCHED_FIFO, ¶m);
-
while (1) {
wait_event(msm_mpd.wait_hpq, *event || kthread_should_stop());
if (kthread_should_stop())
@@ -400,13 +397,10 @@
static int msm_mpd_do_update_scm(void *data)
{
struct msm_mpd_scm_data *scm_data = (struct msm_mpd_scm_data *)data;
- static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
unsigned long flags;
enum msm_dcvs_scm_event event;
int nr;
- sched_setscheduler(current, SCHED_FIFO, ¶m);
-
while (1) {
wait_event(msm_mpd.wait_q,
msm_mpd.data.event == MSM_DCVS_SCM_MPD_QOS_TIMER_EXPIRED
diff --git a/arch/arm/mach-msm/msm_qmi_interface.c b/arch/arm/mach-msm/msm_qmi_interface.c
new file mode 100644
index 0000000..4c4635a
--- /dev/null
+++ b/arch/arm/mach-msm/msm_qmi_interface.c
@@ -0,0 +1,694 @@
+/* Copyright (c) 2012, 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/slab.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+#include <linux/qmi_encdec.h>
+
+#include <mach/msm_qmi_interface.h>
+#include <mach/msm_ipc_router.h>
+
+#include "msm_qmi_interface_priv.h"
+
+static LIST_HEAD(svc_event_nb_list);
+static DEFINE_MUTEX(svc_event_nb_list_lock);
+
+struct elem_info qmi_response_type_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_2_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(struct qmi_response_type_v01,
+ result),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_SIGNED_2_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(struct qmi_response_type_v01,
+ error),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .elem_len = 0,
+ .elem_size = 0,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = 0,
+ .ei_array = NULL,
+ },
+};
+
+static void qmi_event_notify(unsigned event, void *priv)
+{
+ struct qmi_handle *handle = (struct qmi_handle *)priv;
+ unsigned long flags;
+
+ if (!handle)
+ return;
+
+ mutex_lock(&handle->handle_lock);
+ if (handle->handle_reset) {
+ mutex_unlock(&handle->handle_lock);
+ return;
+ }
+
+ switch (event) {
+ case MSM_IPC_ROUTER_READ_CB:
+ spin_lock_irqsave(&handle->notify_lock, flags);
+ handle->notify(handle, QMI_RECV_MSG, handle->notify_priv);
+ spin_unlock_irqrestore(&handle->notify_lock, flags);
+ break;
+
+ default:
+ break;
+ }
+ mutex_unlock(&handle->handle_lock);
+}
+
+struct qmi_handle *qmi_handle_create(
+ void (*notify)(struct qmi_handle *handle,
+ enum qmi_event_type event, void *notify_priv),
+ void *notify_priv)
+{
+ struct qmi_handle *temp_handle;
+ struct msm_ipc_port *port_ptr;
+
+ temp_handle = kzalloc(sizeof(struct qmi_handle), GFP_KERNEL);
+ if (!temp_handle) {
+ pr_err("%s: Failure allocating client handle\n", __func__);
+ return NULL;
+ }
+
+ port_ptr = msm_ipc_router_create_port(qmi_event_notify,
+ (void *)temp_handle);
+ if (!port_ptr) {
+ pr_err("%s: IPC router port creation failed\n", __func__);
+ kfree(temp_handle);
+ return NULL;
+ }
+
+ temp_handle->src_port = port_ptr;
+ temp_handle->next_txn_id = 1;
+ INIT_LIST_HEAD(&temp_handle->txn_list);
+ mutex_init(&temp_handle->handle_lock);
+ spin_lock_init(&temp_handle->notify_lock);
+ temp_handle->notify = notify;
+ temp_handle->notify_priv = notify_priv;
+ temp_handle->handle_reset = 0;
+ init_waitqueue_head(&temp_handle->reset_waitq);
+ return temp_handle;
+}
+EXPORT_SYMBOL(qmi_handle_create);
+
+static void clean_txn_info(struct qmi_handle *handle)
+{
+ struct qmi_txn *txn_handle, *temp_txn_handle;
+
+ list_for_each_entry_safe(txn_handle, temp_txn_handle,
+ &handle->txn_list, list) {
+ if (txn_handle->type == QMI_ASYNC_TXN) {
+ list_del(&txn_handle->list);
+ kfree(txn_handle);
+ } else if (txn_handle->type == QMI_SYNC_TXN) {
+ wake_up(&txn_handle->wait_q);
+ }
+ }
+}
+
+int qmi_handle_destroy(struct qmi_handle *handle)
+{
+ int rc;
+
+ if (!handle)
+ return -EINVAL;
+
+ mutex_lock(&handle->handle_lock);
+ handle->handle_reset = 1;
+ clean_txn_info(handle);
+ mutex_unlock(&handle->handle_lock);
+
+ rc = wait_event_interruptible(handle->reset_waitq,
+ list_empty(&handle->txn_list));
+
+ /* TODO: Destroy client owned transaction */
+ msm_ipc_router_close_port((struct msm_ipc_port *)(handle->src_port));
+ kfree(handle->dest_info);
+ kfree(handle);
+ return 0;
+}
+EXPORT_SYMBOL(qmi_handle_destroy);
+
+int qmi_register_ind_cb(struct qmi_handle *handle,
+ void (*ind_cb)(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ unsigned int msg_len, void *ind_cb_priv),
+ void *ind_cb_priv)
+{
+ if (!handle)
+ return -EINVAL;
+
+ mutex_lock(&handle->handle_lock);
+ if (handle->handle_reset) {
+ mutex_unlock(&handle->handle_lock);
+ return -ENETRESET;
+ }
+
+ handle->ind_cb = ind_cb;
+ handle->ind_cb_priv = ind_cb_priv;
+ mutex_unlock(&handle->handle_lock);
+ return 0;
+}
+EXPORT_SYMBOL(qmi_register_ind_cb);
+
+static int qmi_encode_and_send_req(struct qmi_txn **ret_txn_handle,
+ struct qmi_handle *handle, enum txn_type type,
+ struct msg_desc *req_desc, void *req, unsigned int req_len,
+ struct msg_desc *resp_desc, void *resp, unsigned int resp_len,
+ void (*resp_cb)(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ void *resp_cb_data),
+ void *resp_cb_data)
+{
+ struct qmi_txn *txn_handle;
+ int rc, encoded_req_len;
+ void *encoded_req;
+
+ if (!handle || !handle->dest_info ||
+ !req_desc || !req || !resp_desc || !resp)
+ return -EINVAL;
+
+ mutex_lock(&handle->handle_lock);
+ if (handle->handle_reset) {
+ mutex_unlock(&handle->handle_lock);
+ return -ENETRESET;
+ }
+
+ /* Allocate Transaction Info */
+ txn_handle = kzalloc(sizeof(struct qmi_txn), GFP_KERNEL);
+ if (!txn_handle) {
+ pr_err("%s: Failed to allocate txn handle\n", __func__);
+ mutex_unlock(&handle->handle_lock);
+ return -ENOMEM;
+ }
+ txn_handle->type = type;
+ INIT_LIST_HEAD(&txn_handle->list);
+ init_waitqueue_head(&txn_handle->wait_q);
+
+ /* Cache the parameters passed & mark it as sync*/
+ txn_handle->handle = handle;
+ txn_handle->resp_desc = resp_desc;
+ txn_handle->resp = resp;
+ txn_handle->resp_len = resp_len;
+ txn_handle->resp_received = 0;
+ txn_handle->resp_cb = resp_cb;
+ txn_handle->resp_cb_data = resp_cb_data;
+
+ /* Encode the request msg */
+ encoded_req_len = req_desc->max_msg_len + QMI_HEADER_SIZE;
+ encoded_req = kmalloc(encoded_req_len, GFP_KERNEL);
+ if (!encoded_req) {
+ pr_err("%s: Failed to allocate req_msg_buf\n", __func__);
+ rc = -ENOMEM;
+ goto encode_and_send_req_err1;
+ }
+ rc = qmi_kernel_encode(req_desc,
+ (void *)(encoded_req + QMI_HEADER_SIZE),
+ req_desc->max_msg_len, req);
+ if (rc < 0) {
+ pr_err("%s: Encode Failure %d\n", __func__, rc);
+ goto encode_and_send_req_err2;
+ }
+ encoded_req_len = rc;
+
+ /* Encode the header & Add to the txn_list */
+ if (!handle->next_txn_id)
+ handle->next_txn_id++;
+ txn_handle->txn_id = handle->next_txn_id++;
+ encode_qmi_header(encoded_req, QMI_REQUEST_CONTROL_FLAG,
+ txn_handle->txn_id, req_desc->msg_id,
+ encoded_req_len);
+ encoded_req_len += QMI_HEADER_SIZE;
+ list_add_tail(&txn_handle->list, &handle->txn_list);
+
+ /* Send the request */
+ rc = msm_ipc_router_send_msg((struct msm_ipc_port *)(handle->src_port),
+ (struct msm_ipc_addr *)handle->dest_info,
+ encoded_req, encoded_req_len);
+ if (rc < 0) {
+ pr_err("%s: send_msg failed %d\n", __func__, rc);
+ goto encode_and_send_req_err3;
+ }
+ mutex_unlock(&handle->handle_lock);
+
+ kfree(encoded_req);
+ if (ret_txn_handle)
+ *ret_txn_handle = txn_handle;
+ return 0;
+
+encode_and_send_req_err3:
+ list_del(&txn_handle->list);
+encode_and_send_req_err2:
+ kfree(encoded_req);
+encode_and_send_req_err1:
+ kfree(txn_handle);
+ mutex_unlock(&handle->handle_lock);
+ return rc;
+}
+
+int qmi_send_req_wait(struct qmi_handle *handle,
+ struct msg_desc *req_desc,
+ void *req, unsigned int req_len,
+ struct msg_desc *resp_desc,
+ void *resp, unsigned int resp_len,
+ unsigned long timeout_ms)
+{
+ struct qmi_txn *txn_handle = NULL;
+ int rc;
+
+ /* Encode and send the request */
+ rc = qmi_encode_and_send_req(&txn_handle, handle, QMI_SYNC_TXN,
+ req_desc, req, req_len,
+ resp_desc, resp, resp_len,
+ NULL, NULL);
+ if (rc < 0) {
+ pr_err("%s: Error encode & send req: %d\n", __func__, rc);
+ return rc;
+ }
+
+ /* Wait for the response */
+ if (!timeout_ms) {
+ rc = wait_event_interruptible(txn_handle->wait_q,
+ (txn_handle->resp_received ||
+ handle->handle_reset));
+ } else {
+ rc = wait_event_interruptible_timeout(txn_handle->wait_q,
+ (txn_handle->resp_received ||
+ handle->handle_reset),
+ msecs_to_jiffies(timeout_ms));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ }
+
+ mutex_lock(&handle->handle_lock);
+ if (!txn_handle->resp_received) {
+ pr_err("%s: Response Wait Error %d\n", __func__, rc);
+ if (handle->handle_reset)
+ rc = -ENETRESET;
+ if (rc >= 0)
+ rc = -EFAULT;
+ goto send_req_wait_err;
+ }
+ rc = 0;
+
+send_req_wait_err:
+ list_del(&txn_handle->list);
+ kfree(txn_handle);
+ mutex_unlock(&handle->handle_lock);
+ wake_up(&handle->reset_waitq);
+ return rc;
+}
+EXPORT_SYMBOL(qmi_send_req_wait);
+
+int qmi_send_req_nowait(struct qmi_handle *handle,
+ struct msg_desc *req_desc,
+ void *req, unsigned int req_len,
+ struct msg_desc *resp_desc,
+ void *resp, unsigned int resp_len,
+ void (*resp_cb)(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ void *resp_cb_data),
+ void *resp_cb_data)
+{
+ return qmi_encode_and_send_req(NULL, handle, QMI_ASYNC_TXN,
+ req_desc, req, req_len,
+ resp_desc, resp, resp_len,
+ resp_cb, resp_cb_data);
+}
+EXPORT_SYMBOL(qmi_send_req_nowait);
+
+static struct qmi_txn *find_txn_handle(struct qmi_handle *handle,
+ uint16_t txn_id)
+{
+ struct qmi_txn *txn_handle;
+
+ list_for_each_entry(txn_handle, &handle->txn_list, list) {
+ if (txn_handle->txn_id == txn_id)
+ return txn_handle;
+ }
+ return NULL;
+}
+
+static int handle_qmi_response(struct qmi_handle *handle,
+ unsigned char *resp_msg, uint16_t txn_id,
+ uint16_t msg_id, uint16_t msg_len)
+{
+ struct qmi_txn *txn_handle;
+ int rc;
+
+ /* Find the transaction handle */
+ txn_handle = find_txn_handle(handle, txn_id);
+ if (!txn_handle) {
+ pr_err("%s Response received for non-existent txn_id %d\n",
+ __func__, txn_id);
+ return -EINVAL;
+ }
+
+ /* Decode the message */
+ rc = qmi_kernel_decode(txn_handle->resp_desc, txn_handle->resp,
+ (void *)(resp_msg + QMI_HEADER_SIZE), msg_len);
+ if (rc < 0) {
+ pr_err("%s: Response Decode Failure <%d: %d: %d> rc: %d\n",
+ __func__, txn_id, msg_id, msg_len, rc);
+ wake_up(&txn_handle->wait_q);
+ if (txn_handle->type == QMI_ASYNC_TXN) {
+ list_del(&txn_handle->list);
+ kfree(txn_handle);
+ }
+ return rc;
+ }
+
+ /* Handle async or sync resp */
+ switch (txn_handle->type) {
+ case QMI_SYNC_TXN:
+ txn_handle->resp_received = 1;
+ wake_up(&txn_handle->wait_q);
+ rc = 0;
+ break;
+
+ case QMI_ASYNC_TXN:
+ if (txn_handle->resp_cb)
+ txn_handle->resp_cb(txn_handle->handle, msg_id,
+ txn_handle->resp,
+ txn_handle->resp_cb_data);
+ list_del(&txn_handle->list);
+ kfree(txn_handle);
+ rc = 0;
+ break;
+
+ default:
+ pr_err("%s: Unrecognized transaction type\n", __func__);
+ return -EFAULT;
+ }
+ return rc;
+}
+
+static int handle_qmi_indication(struct qmi_handle *handle, void *msg,
+ unsigned int msg_id, unsigned int msg_len)
+{
+ if (handle->ind_cb)
+ handle->ind_cb(handle, msg_id, msg,
+ msg_len, handle->ind_cb_priv);
+ return 0;
+}
+
+int qmi_recv_msg(struct qmi_handle *handle)
+{
+ unsigned int recv_msg_len;
+ unsigned char *recv_msg = NULL;
+ struct msm_ipc_addr src_addr;
+ unsigned char cntl_flag;
+ uint16_t txn_id, msg_id, msg_len;
+ int rc;
+
+ if (!handle)
+ return -EINVAL;
+
+ mutex_lock(&handle->handle_lock);
+ if (handle->handle_reset) {
+ mutex_unlock(&handle->handle_lock);
+ return -ENETRESET;
+ }
+
+ /* Read the messages */
+ rc = msm_ipc_router_read_msg((struct msm_ipc_port *)(handle->src_port),
+ &src_addr, &recv_msg, &recv_msg_len);
+ if (rc < 0) {
+ pr_err("%s: Read failed %d\n", __func__, rc);
+ mutex_unlock(&handle->handle_lock);
+ return rc;
+ }
+
+ /* Decode the header & Handle the req, resp, indication message */
+ decode_qmi_header(recv_msg, &cntl_flag, &txn_id, &msg_id, &msg_len);
+
+ switch (cntl_flag) {
+ case QMI_RESPONSE_CONTROL_FLAG:
+ rc = handle_qmi_response(handle, recv_msg,
+ txn_id, msg_id, msg_len);
+ break;
+
+ case QMI_INDICATION_CONTROL_FLAG:
+ rc = handle_qmi_indication(handle, recv_msg, msg_id, msg_len);
+ break;
+
+ default:
+ rc = -EFAULT;
+ pr_err("%s: Unsupported message type %d\n",
+ __func__, cntl_flag);
+ break;
+ }
+ kfree(recv_msg);
+ mutex_unlock(&handle->handle_lock);
+ return rc;
+}
+EXPORT_SYMBOL(qmi_recv_msg);
+
+int qmi_connect_to_service(struct qmi_handle *handle,
+ uint32_t service_id, uint32_t instance_id)
+{
+ struct msm_ipc_port_name svc_name;
+ struct msm_ipc_server_info svc_info;
+ struct msm_ipc_addr *svc_dest_addr;
+ int rc;
+
+ if (!handle)
+ return -EINVAL;
+
+ svc_dest_addr = kzalloc(sizeof(struct msm_ipc_addr),
+ GFP_KERNEL);
+ if (!svc_dest_addr) {
+ pr_err("%s: Failure allocating memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ svc_name.service = service_id;
+ svc_name.instance = instance_id;
+
+ rc = msm_ipc_router_lookup_server_name(&svc_name, &svc_info, 1, 0xFF);
+ if (rc <= 0) {
+ pr_err("%s: Server not found\n", __func__);
+ return -ENODEV;
+ }
+ svc_dest_addr->addrtype = MSM_IPC_ADDR_ID;
+ svc_dest_addr->addr.port_addr.node_id = svc_info.node_id;
+ svc_dest_addr->addr.port_addr.port_id = svc_info.port_id;
+ mutex_lock(&handle->handle_lock);
+ if (handle->handle_reset) {
+ mutex_unlock(&handle->handle_lock);
+ return -ENETRESET;
+ }
+ handle->dest_info = svc_dest_addr;
+ mutex_unlock(&handle->handle_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(qmi_connect_to_service);
+
+static struct svc_event_nb *find_svc_event_nb_by_name(const char *name)
+{
+ struct svc_event_nb *temp;
+
+ list_for_each_entry(temp, &svc_event_nb_list, list) {
+ if (!strncmp(name, temp->pdriver_name,
+ sizeof(temp->pdriver_name)))
+ return temp;
+ }
+ return NULL;
+}
+
+static int qmi_svc_event_probe(struct platform_device *pdev)
+{
+ struct svc_event_nb *temp;
+ unsigned long flags;
+
+ mutex_lock(&svc_event_nb_list_lock);
+ temp = find_svc_event_nb_by_name(pdev->name);
+ if (!temp) {
+ mutex_unlock(&svc_event_nb_list_lock);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&temp->nb_lock, flags);
+ temp->svc_avail = 1;
+ raw_notifier_call_chain(&temp->svc_event_rcvr_list,
+ QMI_SERVER_ARRIVE, NULL);
+ spin_unlock_irqrestore(&temp->nb_lock, flags);
+ mutex_unlock(&svc_event_nb_list_lock);
+ return 0;
+}
+
+static int qmi_svc_event_remove(struct platform_device *pdev)
+{
+ struct svc_event_nb *temp;
+ unsigned long flags;
+
+ mutex_lock(&svc_event_nb_list_lock);
+ temp = find_svc_event_nb_by_name(pdev->name);
+ if (!temp) {
+ mutex_unlock(&svc_event_nb_list_lock);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&temp->nb_lock, flags);
+ temp->svc_avail = 0;
+ raw_notifier_call_chain(&temp->svc_event_rcvr_list,
+ QMI_SERVER_EXIT, NULL);
+ spin_unlock_irqrestore(&temp->nb_lock, flags);
+ mutex_unlock(&svc_event_nb_list_lock);
+ return 0;
+}
+
+static struct svc_event_nb *find_svc_event_nb(uint32_t service_id,
+ uint32_t instance_id)
+{
+ struct svc_event_nb *temp;
+
+ list_for_each_entry(temp, &svc_event_nb_list, list) {
+ if (temp->service_id == service_id &&
+ temp->instance_id == instance_id)
+ return temp;
+ }
+ return NULL;
+}
+
+static struct svc_event_nb *find_and_add_svc_event_nb(uint32_t service_id,
+ uint32_t instance_id)
+{
+ struct svc_event_nb *temp;
+ int ret;
+
+ mutex_lock(&svc_event_nb_list_lock);
+ temp = find_svc_event_nb(service_id, instance_id);
+ if (temp) {
+ mutex_unlock(&svc_event_nb_list_lock);
+ return temp;
+ }
+
+ temp = kzalloc(sizeof(struct svc_event_nb), GFP_KERNEL);
+ if (!temp) {
+ mutex_unlock(&svc_event_nb_list_lock);
+ pr_err("%s: Failed to alloc notifier block\n", __func__);
+ return temp;
+ }
+
+ spin_lock_init(&temp->nb_lock);
+ temp->service_id = service_id;
+ temp->instance_id = instance_id;
+ INIT_LIST_HEAD(&temp->list);
+ temp->svc_driver.probe = qmi_svc_event_probe;
+ temp->svc_driver.remove = qmi_svc_event_remove;
+ scnprintf(temp->pdriver_name, sizeof(temp->pdriver_name),
+ "QMI%08x:%08x", service_id, instance_id);
+ temp->svc_driver.driver.name = temp->pdriver_name;
+ RAW_INIT_NOTIFIER_HEAD(&temp->svc_event_rcvr_list);
+
+ list_add_tail(&temp->list, &svc_event_nb_list);
+ mutex_unlock(&svc_event_nb_list_lock);
+
+ ret = platform_driver_register(&temp->svc_driver);
+ if (ret < 0) {
+ pr_err("%s: Failed pdriver register\n", __func__);
+ mutex_lock(&svc_event_nb_list_lock);
+ list_del(&temp->list);
+ mutex_unlock(&svc_event_nb_list_lock);
+ kfree(temp);
+ temp = NULL;
+ }
+
+ return temp;
+}
+
+int qmi_svc_event_notifier_register(uint32_t service_id,
+ uint32_t instance_id,
+ struct notifier_block *nb)
+{
+ struct svc_event_nb *temp;
+ unsigned long flags;
+ int ret;
+
+ temp = find_and_add_svc_event_nb(service_id, instance_id);
+ if (!temp)
+ return -EFAULT;
+
+ mutex_lock(&svc_event_nb_list_lock);
+ temp = find_svc_event_nb(service_id, instance_id);
+ if (!temp) {
+ mutex_unlock(&svc_event_nb_list_lock);
+ return -EFAULT;
+ }
+ spin_lock_irqsave(&temp->nb_lock, flags);
+ if (temp->svc_avail)
+ nb->notifier_call(nb, QMI_SERVER_ARRIVE, NULL);
+
+ ret = raw_notifier_chain_register(&temp->svc_event_rcvr_list, nb);
+ spin_unlock_irqrestore(&temp->nb_lock, flags);
+ mutex_unlock(&svc_event_nb_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(qmi_svc_event_notifier_register);
+
+int qmi_svc_event_notifier_unregister(uint32_t service_id,
+ uint32_t instance_id,
+ struct notifier_block *nb)
+{
+ int ret;
+ struct svc_event_nb *temp;
+ unsigned long flags;
+
+ mutex_lock(&svc_event_nb_list_lock);
+ temp = find_svc_event_nb(service_id, instance_id);
+ if (!temp) {
+ mutex_unlock(&svc_event_nb_list_lock);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&temp->nb_lock, flags);
+ ret = raw_notifier_chain_unregister(&temp->svc_event_rcvr_list, nb);
+ spin_unlock_irqrestore(&temp->nb_lock, flags);
+ mutex_unlock(&svc_event_nb_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(qmi_svc_event_notifier_unregister);
+
+MODULE_DESCRIPTION("MSM QMI Interface");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/msm_qmi_interface_priv.h b/arch/arm/mach-msm/msm_qmi_interface_priv.h
new file mode 100644
index 0000000..58f1ce3
--- /dev/null
+++ b/arch/arm/mach-msm/msm_qmi_interface_priv.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#ifndef _MSM_QMI_INTERFACE_PRIV_H_
+#define _MSM_QMI_INTERFACE_PRIV_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+#include <linux/platform_device.h>
+#include <linux/qmi_encdec.h>
+
+#include <mach/msm_qmi_interface.h>
+
+enum txn_type {
+ QMI_SYNC_TXN = 1,
+ QMI_ASYNC_TXN,
+};
+
+struct qmi_txn {
+ struct list_head list;
+ uint16_t txn_id;
+ enum txn_type type;
+ struct qmi_handle *handle;
+ struct msg_desc *resp_desc;
+ void *resp;
+ unsigned int resp_len;
+ int resp_received;
+ void (*resp_cb)(struct qmi_handle *handle, unsigned int msg_id,
+ void *msg, void *resp_cb_data);
+ void *resp_cb_data;
+ wait_queue_head_t wait_q;
+};
+
+struct svc_event_nb {
+ spinlock_t nb_lock;
+ uint32_t service_id;
+ uint32_t instance_id;
+ char pdriver_name[32];
+ int svc_avail;
+ struct platform_driver svc_driver;
+ struct raw_notifier_head svc_event_rcvr_list;
+ struct list_head list;
+};
+
+#endif
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 2eac6b7..550bb56 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, 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
@@ -51,7 +51,8 @@
#include "timer.h"
#include "pm-boot.h"
#include <mach/event_timer.h>
-
+#define CREATE_TRACE_POINTS
+#include "trace_msm_low_power.h"
/******************************************************************************
* Debug Definitions
*****************************************************************************/
@@ -724,6 +725,51 @@
return;
}
+static inline void msm_pm_ftrace_lpm_enter(unsigned int cpu,
+ uint32_t latency, uint32_t sleep_us,
+ uint32_t wake_up,
+ enum msm_pm_sleep_mode mode)
+{
+ switch (mode) {
+ case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
+ trace_msm_pm_enter_wfi(cpu, latency, sleep_us, wake_up);
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
+ trace_msm_pm_enter_spc(cpu, latency, sleep_us, wake_up);
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ trace_msm_pm_enter_pc(cpu, latency, sleep_us, wake_up);
+ break;
+ case MSM_PM_SLEEP_MODE_RETENTION:
+ trace_msm_pm_enter_ret(cpu, latency, sleep_us, wake_up);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void msm_pm_ftrace_lpm_exit(unsigned int cpu,
+ enum msm_pm_sleep_mode mode,
+ bool success)
+{
+ switch (mode) {
+ case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
+ trace_msm_pm_exit_wfi(cpu, success);
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
+ trace_msm_pm_exit_spc(cpu, success);
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ trace_msm_pm_exit_pc(cpu, success);
+ break;
+ case MSM_PM_SLEEP_MODE_RETENTION:
+ trace_msm_pm_exit_ret(cpu, success);
+ break;
+ default:
+ break;
+ }
+}
+
int msm_pm_idle_prepare(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
@@ -828,6 +874,11 @@
if (modified_time_us && !dev->cpu)
msm_pm_set_timer(modified_time_us);
+
+ msm_pm_ftrace_lpm_enter(dev->cpu, time_param.latency_us,
+ time_param.sleep_us, time_param.next_event_us,
+ ret);
+
return ret;
}
@@ -835,6 +886,7 @@
{
int64_t time;
int exit_stat;
+ bool collapsed = 1;
if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
pr_info("CPU%u: %s: mode %d\n",
@@ -854,7 +906,7 @@
break;
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
- msm_pm_power_collapse_standalone(true);
+ collapsed = msm_pm_power_collapse_standalone(true);
exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
break;
@@ -865,8 +917,6 @@
int ret = -ENODEV;
int notify_rpm =
(sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE);
- int collapsed;
-
timer_expiration = msm_pm_timer_enter_idle();
sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
@@ -901,6 +951,8 @@
time = ktime_to_ns(ktime_get()) - time;
msm_pm_add_stat(exit_stat, time);
+ msm_pm_ftrace_lpm_exit(smp_processor_id(), sleep_mode,
+ collapsed);
do_div(time, 1000);
return (int) time;
diff --git a/arch/arm/mach-msm/rpm-notifier.h b/arch/arm/mach-msm/rpm-notifier.h
index 33086c6..b9815a5 100644
--- a/arch/arm/mach-msm/rpm-notifier.h
+++ b/arch/arm/mach-msm/rpm-notifier.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, 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,4 +47,12 @@
* msm_rpm_exit_sleep - Notify RPM driver about resuming from power collapse
*/
void msm_rpm_exit_sleep(void);
+
+/**
+ * msm_rpm_waiting_for_ack - Indicate if there is RPM message
+ * pending acknowledgement.
+ * returns true for pending messages and false otherwise
+ */
+bool msm_rpm_waiting_for_ack(void);
+
#endif /*__ARCH_ARM_MACH_MSM_RPM_NOTIF_H */
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index f97eb9a..6d1d038 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, 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
@@ -354,6 +354,18 @@
}
}
+bool msm_rpm_waiting_for_ack(void)
+{
+ bool ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&msm_rpm_list_lock, flags);
+ ret = list_empty(&msm_rpm_wait_list);
+ spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
+
+ return !ret;
+}
+
static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id)
{
struct list_head *ptr;
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 3c0cbf7..b1dd1db 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -1016,6 +1016,7 @@
notify_dsp_smd();
notify_dsps_smd();
notify_wcnss_smd();
+ notify_rpm_smd();
/* change all remote states to CLOSED */
mutex_lock(&smd_probe_lock);
@@ -1031,6 +1032,7 @@
notify_dsp_smd();
notify_dsps_smd();
notify_wcnss_smd();
+ notify_rpm_smd();
SMD_DBG("%s: finished reset\n", __func__);
}
diff --git a/arch/arm/mach-msm/trace_msm_low_power.h b/arch/arm/mach-msm/trace_msm_low_power.h
new file mode 100644
index 0000000..4e9da85
--- /dev/null
+++ b/arch/arm/mach-msm/trace_msm_low_power.h
@@ -0,0 +1,154 @@
+/* Copyright (c) 2012, 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 msm_low_power
+
+#if !defined(_TRACE_MSM_LOW_POWER_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MSM_LOW_POWER_H_
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(msm_pm_enter,
+
+ TP_PROTO(unsigned int cpu, uint32_t latency,
+ uint32_t sleep_us, uint32_t wake_up),
+
+ TP_ARGS(cpu, latency, sleep_us, wake_up),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, cpu)
+ __field(uint32_t, latency)
+ __field(uint32_t, sleep_us)
+ __field(uint32_t, wake_up)
+ ),
+
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->latency = latency;
+ __entry->sleep_us = sleep_us;
+ __entry->wake_up = wake_up;
+ ),
+
+ TP_printk("cpu: %u latency: %uus sleep: %uus wake_up: %u",
+ __entry->cpu,
+ __entry->latency,
+ __entry->sleep_us,
+ __entry->wake_up)
+);
+
+DEFINE_EVENT(msm_pm_enter, msm_pm_enter_pc,
+
+ TP_PROTO(unsigned int cpu, uint32_t latency,
+ uint32_t sleep_us, uint32_t wake_up),
+
+ TP_ARGS(cpu, latency, sleep_us, wake_up)
+);
+
+DEFINE_EVENT(msm_pm_enter, msm_pm_enter_ret,
+
+ TP_PROTO(unsigned int cpu, uint32_t latency,
+ uint32_t sleep_us, uint32_t wake_up),
+
+ TP_ARGS(cpu, latency, sleep_us, wake_up)
+);
+
+DEFINE_EVENT(msm_pm_enter, msm_pm_enter_spc,
+
+ TP_PROTO(unsigned int cpu, uint32_t latency,
+ uint32_t sleep_us, uint32_t wake_up),
+
+ TP_ARGS(cpu, latency, sleep_us, wake_up)
+);
+
+DEFINE_EVENT(msm_pm_enter, msm_pm_enter_wfi,
+
+ TP_PROTO(unsigned int cpu, uint32_t latency,
+ uint32_t sleep_us, uint32_t wake_up),
+
+ TP_ARGS(cpu, latency, sleep_us, wake_up)
+);
+
+DECLARE_EVENT_CLASS(msm_pm_exit,
+
+ TP_PROTO(unsigned int cpu, bool success),
+
+ TP_ARGS(cpu, success),
+
+ TP_STRUCT__entry(
+ __field(unsigned int , cpu)
+ __field(int, success)
+ ),
+
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->success = success;
+ ),
+
+ TP_printk("cpu:%u success:%d",
+ __entry->cpu,
+ __entry->success)
+);
+
+DEFINE_EVENT(msm_pm_exit, msm_pm_exit_pc,
+
+ TP_PROTO(unsigned int cpu, bool success),
+
+ TP_ARGS(cpu, success)
+);
+
+DEFINE_EVENT(msm_pm_exit, msm_pm_exit_ret,
+
+ TP_PROTO(unsigned int cpu, bool success),
+
+ TP_ARGS(cpu, success)
+);
+
+DEFINE_EVENT(msm_pm_exit, msm_pm_exit_spc,
+
+ TP_PROTO(unsigned int cpu, bool success),
+
+ TP_ARGS(cpu, success)
+);
+
+DEFINE_EVENT(msm_pm_exit, msm_pm_exit_wfi,
+
+ TP_PROTO(unsigned int cpu, bool success),
+
+ TP_ARGS(cpu, success)
+);
+
+TRACE_EVENT(lpm_resources,
+
+ TP_PROTO(uint32_t sleep_value , char *name),
+
+ TP_ARGS(sleep_value, name),
+
+ TP_STRUCT__entry(
+ __field(uint32_t , sleep_value)
+ __string(name, name)
+ ),
+
+ TP_fast_assign(
+ __entry->sleep_value = sleep_value;
+ __assign_str(name, name);
+ ),
+
+ TP_printk("name:%s sleep_value:%d",
+ __get_str(name),
+ __entry->sleep_value)
+);
+#endif
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_msm_low_power
+#include <trace/define_trace.h>
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index ab1bd68..afaa39d 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -314,7 +314,8 @@
core_initcall(consistent_init);
static void *__alloc_from_contiguous(struct device *dev, size_t size,
- pgprot_t prot, struct page **ret_page);
+ pgprot_t prot, struct page **ret_page,
+ bool no_kernel_mapping);
static struct arm_vmregion_head coherent_head = {
.vm_lock = __SPIN_LOCK_UNLOCKED(&coherent_head.vm_lock),
@@ -343,7 +344,7 @@
if (!IS_ENABLED(CONFIG_CMA))
return 0;
- ptr = __alloc_from_contiguous(NULL, size, prot, &page);
+ ptr = __alloc_from_contiguous(NULL, size, prot, &page, false);
if (ptr) {
coherent_head.vm_start = (unsigned long) ptr;
coherent_head.vm_end = (unsigned long) ptr + size;
@@ -522,12 +523,27 @@
return 0;
}
-static void __dma_remap(struct page *page, size_t size, pgprot_t prot)
+static int __dma_clear_pte(pte_t *pte, pgtable_t token, unsigned long addr,
+ void *data)
+{
+ pte_clear(&init_mm, addr, pte);
+ return 0;
+}
+
+static void __dma_remap(struct page *page, size_t size, pgprot_t prot,
+ bool no_kernel_map)
{
unsigned long start = (unsigned long) page_address(page);
unsigned end = start + size;
+ int (*func)(pte_t *pte, pgtable_t token, unsigned long addr,
+ void *data);
- apply_to_page_range(&init_mm, start, size, __dma_update_pte, &prot);
+ if (no_kernel_map)
+ func = __dma_clear_pte;
+ else
+ func = __dma_update_pte;
+
+ apply_to_page_range(&init_mm, start, size, func, &prot);
dsb();
flush_tlb_kernel_range(start, end);
}
@@ -604,7 +620,8 @@
}
static void *__alloc_from_contiguous(struct device *dev, size_t size,
- pgprot_t prot, struct page **ret_page)
+ pgprot_t prot, struct page **ret_page,
+ bool no_kernel_mapping)
{
unsigned long order = get_order(size);
size_t count = size >> PAGE_SHIFT;
@@ -615,7 +632,7 @@
return NULL;
__dma_clear_buffer(page, size);
- __dma_remap(page, size, prot);
+ __dma_remap(page, size, prot, no_kernel_mapping);
*ret_page = page;
return page_address(page);
@@ -624,7 +641,7 @@
static void __free_from_contiguous(struct device *dev, struct page *page,
size_t size)
{
- __dma_remap(page, size, pgprot_kernel);
+ __dma_remap(page, size, pgprot_kernel, false);
dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT);
}
@@ -649,7 +666,7 @@
#define __alloc_remap_buffer(dev, size, gfp, prot, ret, c) NULL
#define __alloc_from_pool(dev, size, ret_page, c) NULL
-#define __alloc_from_contiguous(dev, size, prot, ret) NULL
+#define __alloc_from_contiguous(dev, size, prot, ret, w) NULL
#define __free_from_pool(cpu_addr, size) 0
#define __free_from_contiguous(dev, page, size) do { } while (0)
#define __dma_free_remap(cpu_addr, size) do { } while (0)
@@ -672,7 +689,8 @@
static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
- gfp_t gfp, pgprot_t prot, const void *caller)
+ gfp_t gfp, pgprot_t prot, const void *caller,
+ bool no_kernel_mapping)
{
u64 mask = get_coherent_dma_mask(dev);
struct page *page;
@@ -712,7 +730,8 @@
else if (gfp & GFP_ATOMIC)
addr = __alloc_from_pool(dev, size, &page, caller);
else
- addr = __alloc_from_contiguous(dev, size, prot, &page);
+ addr = __alloc_from_contiguous(dev, size, prot, &page,
+ no_kernel_mapping);
if (addr)
*handle = pfn_to_dma(dev, page_to_pfn(page));
@@ -729,12 +748,14 @@
{
pgprot_t prot = __get_dma_pgprot(attrs, pgprot_kernel);
void *memory;
+ bool no_kernel_mapping = dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING,
+ attrs);
if (dma_alloc_from_coherent(dev, size, handle, &memory))
return memory;
return __dma_alloc(dev, size, handle, gfp, prot,
- __builtin_return_address(0));
+ __builtin_return_address(0), no_kernel_mapping);
}
/*
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 92efd94..fe61d2d 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1037,8 +1037,8 @@
static int diagchar_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- int err, ret = 0, pkt_type;
- bool mdm_mask = false;
+ int err, ret = 0, pkt_type, token_offset = 0;
+ bool remote_data = false;
#ifdef DIAG_DEBUG
int length = 0, i;
#endif
@@ -1072,14 +1072,16 @@
if (pkt_type == USER_SPACE_DATA_TYPE) {
err = copy_from_user(driver->user_space_data, buf + 4,
payload_size);
+ /* Check for proc_type */
+ if (*(int *)driver->user_space_data == MDM_TOKEN) {
+ remote_data = true;
+ token_offset = 4;
+ payload_size -= 4;
+ buf += 4;
+ }
+
/* Check masks for On-Device logging */
if (driver->mask_check) {
- /* Check if mask is for MDM or MSM */
- if (*(int *)driver->user_space_data == MDM_TOKEN) {
- mdm_mask = true;
- driver->user_space_data += 4;
- buf += 4;
- }
if (!mask_request_validate(driver->user_space_data)) {
pr_alert("diag: mask request Invalid\n");
return -EFAULT;
@@ -1089,31 +1091,34 @@
#ifdef DIAG_DEBUG
pr_debug("diag: user space data %d\n", payload_size);
for (i = 0; i < payload_size; i++)
- pr_debug("\t %x", *((driver->user_space_data)+i));
+ pr_debug("\t %x", *((driver->user_space_data
+ + token_offset)+i));
#endif
#ifdef CONFIG_DIAG_SDIO_PIPE
/* send masks to 9k too */
- if (driver->sdio_ch && mdm_mask) {
+ if (driver->sdio_ch && remote_data) {
wait_event_interruptible(driver->wait_q,
(sdio_write_avail(driver->sdio_ch) >=
payload_size));
if (driver->sdio_ch && (payload_size > 0)) {
sdio_write(driver->sdio_ch, (void *)
- (driver->user_space_data), payload_size);
+ (driver->user_space_data + token_offset),
+ payload_size);
}
}
#endif
#ifdef CONFIG_DIAG_BRIDGE_CODE
/* send masks to 9k too */
- if (driver->hsic_ch && (payload_size > 0) && mdm_mask) {
+ if (driver->hsic_ch && (payload_size > 0) && remote_data) {
/* wait sending mask updates if HSIC ch not ready */
if (driver->in_busy_hsic_write)
wait_event_interruptible(driver->wait_q,
(driver->in_busy_hsic_write != 1));
driver->in_busy_hsic_write = 1;
driver->in_busy_hsic_read_on_device = 0;
- err = diag_bridge_write(driver->user_space_data,
- payload_size);
+ err = diag_bridge_write(
+ driver->user_space_data + token_offset,
+ payload_size);
if (err) {
pr_err("diag: err sending mask to MDM: %d\n",
err);
@@ -1127,11 +1132,12 @@
driver->in_busy_hsic_write = 0;
}
}
- if (driver->diag_smux_enabled && mdm_mask && driver->lcid) {
+ if (driver->diag_smux_enabled && remote_data
+ && driver->lcid) {
if (payload_size > 0) {
err = msm_smux_write(driver->lcid, NULL,
- driver->user_space_data,
- payload_size);
+ driver->user_space_data + token_offset,
+ payload_size);
if (err) {
pr_err("diag:send mask to MDM err %d",
err);
@@ -1141,9 +1147,10 @@
}
#endif
/* send masks to 8k now */
- if (!mdm_mask)
- diag_process_hdlc((void *)(driver->user_space_data),
- payload_size);
+ if (!remote_data)
+ diag_process_hdlc((void *)
+ (driver->user_space_data + token_offset),
+ payload_size);
return 0;
}
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index 8de2c70..4e4843b 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1718,9 +1718,15 @@
eoptimestamp));
if (context_id < KGSL_MEMSTORE_MAX) {
- kgsl_sharedmem_writel(&rb->device->memstore,
+ /* reset per context ts_cmp_enable */
+ kgsl_sharedmem_writel(&device->memstore,
KGSL_MEMSTORE_OFFSET(context_id,
ts_cmp_enable), 0);
+ /* Always reset global timestamp ts_cmp_enable */
+ kgsl_sharedmem_writel(&device->memstore,
+ KGSL_MEMSTORE_OFFSET(
+ KGSL_MEMSTORE_GLOBAL,
+ ts_cmp_enable), 0);
wmb();
}
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 4c7534c..9e71199 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2586,9 +2586,15 @@
eoptimestamp));
if (context_id < KGSL_MEMSTORE_MAX) {
+ /* reset per context ts_cmp_enable */
kgsl_sharedmem_writel(&device->memstore,
KGSL_MEMSTORE_OFFSET(context_id,
ts_cmp_enable), 0);
+ /* Always reset global timestamp ts_cmp_enable */
+ kgsl_sharedmem_writel(&device->memstore,
+ KGSL_MEMSTORE_OFFSET(
+ KGSL_MEMSTORE_GLOBAL,
+ ts_cmp_enable), 0);
wmb();
}
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 0d5e409..9648f27 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -535,12 +535,8 @@
/* internal ib command identifier for the ringbuffer */
total_sizedwords += (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) ? 2 : 0;
- /*
- * Add CP_COND_EXEC commands to generate CP_INTERRUPT only
- * for submissions from userspace.
- */
- total_sizedwords += (context &&
- !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) ? 7 : 0;
+ /* Add CP_COND_EXEC commands to generate CP_INTERRUPT */
+ total_sizedwords += context ? 7 : 0;
if (adreno_is_a3xx(adreno_dev))
total_sizedwords += 7;
@@ -676,7 +672,7 @@
rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
}
- if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
+ if (context) {
/* Conditional execution based on memory values */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_COND_EXEC, 4));
diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c
index 119e25d..98c7434 100644
--- a/drivers/gpu/msm/kgsl_drm.c
+++ b/drivers/gpu/msm/kgsl_drm.c
@@ -28,7 +28,7 @@
#define DRIVER_AUTHOR "Qualcomm"
#define DRIVER_NAME "kgsl"
#define DRIVER_DESC "KGSL DRM"
-#define DRIVER_DATE "20100127"
+#define DRIVER_DATE "20121107"
#define DRIVER_MAJOR 2
#define DRIVER_MINOR 1
@@ -635,6 +635,43 @@
}
int
+kgsl_gem_get_ion_fd_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_kgsl_gem_get_ion_fd *args = data;
+ struct drm_gem_object *obj;
+ struct drm_kgsl_gem_object *priv;
+ int ret = 0;
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+ if (obj == NULL) {
+ DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+ return -EBADF;
+ }
+
+ mutex_lock(&dev->struct_mutex);
+ priv = obj->driver_private;
+
+ if (TYPE_IS_FD(priv->type))
+ ret = -EINVAL;
+ else {
+ if (priv->ion_handle) {
+ args->ion_fd = ion_share_dma_buf(
+ kgsl_drm_ion_phys_client, priv->ion_handle);
+ } else {
+ DRM_ERROR("GEM object has no ion memory allocated.\n");
+ ret = -EINVAL;
+ }
+ }
+
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+
+int
kgsl_gem_setmemtype_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -1482,6 +1519,7 @@
DRM_IOCTL_DEF_DRV(KGSL_GEM_ALLOC, kgsl_gem_alloc_ioctl, 0),
DRM_IOCTL_DEF_DRV(KGSL_GEM_MMAP, kgsl_gem_mmap_ioctl, 0),
DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_BUFINFO, kgsl_gem_get_bufinfo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_ION_FD, kgsl_gem_get_ion_fd_ioctl, 0),
DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_BUFCOUNT,
kgsl_gem_set_bufcount_ioctl, 0),
DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_ACTIVE, kgsl_gem_set_active_ioctl, 0),
@@ -1547,7 +1585,7 @@
/* Create ION Client */
kgsl_drm_ion_phys_client = msm_ion_client_create(
- ION_HEAP_CARVEOUT_MASK, ION_SF_HEAP_NAME);
+ ION_HEAP_CARVEOUT_MASK, "kgsl_drm");
if (!kgsl_drm_ion_phys_client) {
DRM_ERROR("Unable to create ION client\n");
return -ENOMEM;
@@ -1559,5 +1597,10 @@
void kgsl_drm_exit(void)
{
kgsl_drm_inited = DRM_KGSL_NOT_INITED;
+
+ if (kgsl_drm_ion_phys_client)
+ ion_client_destroy(kgsl_drm_ion_phys_client);
+ kgsl_drm_ion_phys_client = NULL;
+
drm_platform_exit(&driver, driver.kdriver.platform_device);
}
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index 10c5a17..0e82cf7 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -54,28 +54,6 @@
#define QPNP_STATUS2_CONV_SEQ_TIMEOUT_STS BIT(0)
#define QPNP_CONV_TIMEOUT_ERR 2
-#define QPNP_INT_RT_ST 0x10
-#define QPNP_INT_SET_TYPE 0x11
-#define QPNP_INT_SET_TYPE_LOW_THR_INT_SET BIT(4)
-#define QPNP_INT_SET_TYPE_HIGH_THR_INT_SET BIT(3)
-#define QPNP_INT_SET_TYPE_CONV_SEQ_TIMEOUT_INT_SET BIT(2)
-#define QPNP_INT_SET_TYPE_FIFO_NOT_EMPTY_INT_SET BIT(1)
-#define QPNP_INT_SET_TYPE_EOC_SET_INT_TYPE BIT(0)
-#define QPNP_INT_POLARITY_HIGH 0x12
-#define QPNP_INT_POLARITY_LOW 0x13
-#define QPNP_INT_EN_SET 0x15
-#define QPNP_INT_EN_SET_LOW_THR_INT_EN_SET BIT(4)
-#define QPNP_INT_EN_SET_HIGH_THR_INT_EN_SET BIT(3)
-#define QPNP_INT_EN_SET_CONV_SEQ_TIMEOUT_INT_EN BIT(2)
-#define QPNP_INT_EN_SET_FIFO_NOT_EMPTY_INT_EN BIT(1)
-#define QPNP_INT_EN_SET_EOC_INT_EN_SET BIT(0)
-#define QPNP_INT_CLR 0x16
-#define QPNP_INT_CLR_LOW_THR_INT_EN_CLR BIT(4)
-#define QPNP_INT_CLR_HIGH_THR_INT_EN_CLKR BIT(3)
-#define QPNP_INT_CLR_CONV_SEQ_TIMEOUT_INT_EN BIT(2)
-#define QPNP_INT_CLR_FIFO_NOT_EMPTY_INT_EN BIT(1)
-#define QPNP_INT_CLR_EOC_INT_EN_CLR BIT(0)
-#define QPNP_INT_CLR_MASK 0x1f
#define QPNP_IADC_MODE_CTL 0x40
#define QPNP_OP_MODE_SHIFT 4
#define QPNP_USE_BMS_DATA BIT(4)
@@ -146,6 +124,7 @@
#define QPNP_RAW_CODE_16_BIT_LSB_MASK 0xff
#define QPNP_BIT_SHIFT_8 8
#define QPNP_RSENSE_MSB_SIGN_CHECK 0x80
+#define QPNP_ADC_COMPLETION_TIMEOUT HZ
struct qpnp_iadc_drv {
struct qpnp_adc_drv *adc;
@@ -192,47 +171,9 @@
return 0;
}
-static int32_t qpnp_iadc_configure_interrupt(void)
-{
- int rc = 0;
- u8 data = 0;
-
- /* Configure interrupt as an Edge trigger */
- rc = qpnp_iadc_write_reg(QPNP_INT_SET_TYPE,
- QPNP_INT_CLR_MASK);
- if (rc < 0) {
- pr_err("%s Interrupt configure failed\n", __func__);
- return rc;
- }
-
- /* Configure interrupt for rising edge trigger */
- rc = qpnp_iadc_write_reg(QPNP_INT_POLARITY_HIGH,
- QPNP_INT_CLR_MASK);
- if (rc < 0) {
- pr_err("%s Rising edge trigger configure failed\n", __func__);
- return rc;
- }
-
- /* Disable low level interrupt triggering */
- data = QPNP_INT_CLR_MASK;
- rc = qpnp_iadc_write_reg(QPNP_INT_POLARITY_LOW,
- (~data & QPNP_INT_CLR_MASK));
- if (rc < 0) {
- pr_err("%s Setting level low to disable failed\n", __func__);
- return rc;
- }
-
- return 0;
-}
-
static void trigger_iadc_completion(struct work_struct *work)
{
struct qpnp_iadc_drv *iadc = qpnp_iadc;
- int rc;
-
- rc = qpnp_iadc_write_reg(QPNP_INT_CLR, QPNP_INT_CLR_MASK);
- if (rc < 0)
- pr_err("qpnp iadc interrupt mask failed with %d\n", rc);
complete(&iadc->adc->adc_rslt_completion);
@@ -315,13 +256,6 @@
qpnp_iadc_conv_req = QPNP_IADC_CONV_REQ;
- rc = qpnp_iadc_write_reg(QPNP_INT_EN_SET,
- QPNP_INT_EN_SET_EOC_INT_EN_SET);
- if (rc < 0) {
- pr_err("qpnp adc configure error for interrupt setup\n");
- return rc;
- }
-
rc = qpnp_iadc_write_reg(QPNP_IADC_MODE_CTL, qpnp_iadc_mode_reg);
if (rc) {
pr_err("qpnp adc read adc failed with %d\n", rc);
@@ -366,7 +300,22 @@
return rc;
}
- wait_for_completion(&iadc->adc->adc_rslt_completion);
+ rc = wait_for_completion_timeout(&iadc->adc->adc_rslt_completion,
+ QPNP_ADC_COMPLETION_TIMEOUT);
+ if (!rc) {
+ u8 status1 = 0;
+ rc = qpnp_iadc_read_reg(QPNP_STATUS1, &status1);
+ if (rc < 0)
+ return rc;
+ status1 &= (QPNP_STATUS1_REQ_STS | QPNP_STATUS1_EOC);
+ if (status1 == QPNP_STATUS1_EOC)
+ pr_debug("End of conversion status set\n");
+ else {
+ pr_err("EOC interrupt not received\n");
+ return -EINVAL;
+ }
+ }
+
rc = qpnp_iadc_read_conversion_result(raw_code);
if (rc) {
@@ -766,12 +715,6 @@
}
iadc->iadc_hwmon = hwmon_device_register(&iadc->adc->spmi->dev);
- rc = qpnp_iadc_configure_interrupt();
- if (rc) {
- dev_err(&spmi->dev, "failed to configure interrupt\n");
- return rc;
- }
-
rc = qpnp_iadc_version_check();
if (rc) {
dev_err(&spmi->dev, "IADC version not supported\n");
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 5eef34f..c59aa5b 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -54,18 +54,6 @@
#define QPNP_VADC_STATUS2_CONV_SEQ_STATE_SHIFT 4
#define QPNP_VADC_CONV_TIMEOUT_ERR 2
-#define QPNP_VADC_INT_SET_TYPE 0x11
-#define QPNP_VADC_INT_POLARITY_HIGH 0x12
-#define QPNP_VADC_INT_POLARITY_LOW 0x13
-#define QPNP_VADC_INT_LATCHED_CLR 0x14
-#define QPNP_VADC_INT_EN_SET 0x15
-#define QPNP_VADC_INT_CLR 0x16
-#define QPNP_VADC_INT_LOW_THR_BIT BIT(4)
-#define QPNP_VADC_INT_HIGH_THR_BIT BIT(3)
-#define QPNP_VADC_INT_CONV_SEQ_TIMEOUT_BIT BIT(2)
-#define QPNP_VADC_INT_FIFO_NOT_EMPTY_BIT BIT(1)
-#define QPNP_VADC_INT_EOC_BIT BIT(0)
-#define QPNP_VADC_INT_CLR_MASK 0x1f
#define QPNP_VADC_MODE_CTL 0x40
#define QPNP_VADC_OP_MODE_SHIFT 4
#define QPNP_VADC_VREF_XO_THM_FORCE BIT(2)
@@ -101,6 +89,7 @@
#define QPNP_VADC_CONV_TIMEOUT_ERR 2
#define QPNP_VADC_CONV_TIME_MIN 2000
#define QPNP_VADC_CONV_TIME_MAX 2100
+#define QPNP_ADC_COMPLETION_TIMEOUT HZ
struct qpnp_vadc_drv {
struct qpnp_adc_drv *adc;
@@ -156,39 +145,6 @@
return 0;
}
-static int32_t qpnp_vadc_configure_interrupt(void)
-{
- int rc = 0;
- u8 data = 0;
-
- /* Configure interrupt as an Edge trigger */
- rc = qpnp_vadc_write_reg(QPNP_VADC_INT_SET_TYPE,
- QPNP_VADC_INT_CLR_MASK);
- if (rc < 0) {
- pr_err("%s Interrupt configure failed\n", __func__);
- return rc;
- }
-
- /* Configure interrupt for rising edge trigger */
- rc = qpnp_vadc_write_reg(QPNP_VADC_INT_POLARITY_HIGH,
- QPNP_VADC_INT_CLR_MASK);
- if (rc < 0) {
- pr_err("%s Rising edge trigger configure failed\n", __func__);
- return rc;
- }
-
- /* Disable low level interrupt triggering */
- data = QPNP_VADC_INT_CLR_MASK;
- rc = qpnp_vadc_write_reg(QPNP_VADC_INT_POLARITY_LOW,
- (~data & QPNP_VADC_INT_CLR_MASK));
- if (rc < 0) {
- pr_err("%s Setting level low to disable failed\n", __func__);
- return rc;
- }
-
- return 0;
-}
-
static int32_t qpnp_vadc_enable(bool state)
{
int rc = 0;
@@ -222,13 +178,6 @@
u8 mode_ctrl = 0;
int rc = 0;
- rc = qpnp_vadc_write_reg(QPNP_VADC_INT_EN_SET,
- QPNP_VADC_INT_EOC_BIT);
- if (rc < 0) {
- pr_err("Configure error for interrupt setup\n");
- return rc;
- }
-
/* Mode selection */
mode_ctrl = chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT;
rc = qpnp_vadc_write_reg(QPNP_VADC_MODE_CTL, mode_ctrl);
@@ -389,11 +338,6 @@
static void qpnp_vadc_work(struct work_struct *work)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
- int rc;
-
- rc = qpnp_vadc_write_reg(QPNP_VADC_INT_CLR, QPNP_VADC_INT_EOC_BIT);
- if (rc)
- pr_err("qpnp_vadc clear mask interrupt failed with %d\n", rc);
complete(&vadc->adc->adc_rslt_completion);
@@ -606,8 +550,11 @@
!= channel || dt_index > vadc->max_channels_available)
dt_index++;
- if (dt_index > vadc->max_channels_available)
+ if (dt_index > vadc->max_channels_available) {
+ pr_err("not a valid VADC channel\n");
+ rc = -EINVAL;
goto fail_unlock;
+ }
vadc->adc->amux_prop->decimation =
vadc->adc->adc_channels[dt_index].adc_decimation;
@@ -635,7 +582,22 @@
goto fail_unlock;
}
- wait_for_completion(&vadc->adc->adc_rslt_completion);
+ rc = wait_for_completion_timeout(&vadc->adc->adc_rslt_completion,
+ QPNP_ADC_COMPLETION_TIMEOUT);
+ if (!rc) {
+ u8 status1 = 0;
+ rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
+ if (rc < 0)
+ goto fail_unlock;
+ status1 &= (QPNP_VADC_STATUS1_REQ_STS | QPNP_VADC_STATUS1_EOC);
+ if (status1 == QPNP_VADC_STATUS1_EOC)
+ pr_debug("End of conversion status set\n");
+ else {
+ pr_err("EOC interrupt not received\n");
+ rc = -EINVAL;
+ goto fail_unlock;
+ }
+ }
if (trigger_channel < ADC_SEQ_NONE) {
rc = qpnp_vadc_read_status(vadc->adc->amux_prop->mode_sel);
@@ -797,25 +759,14 @@
rc = qpnp_vadc_init_hwmon(spmi);
if (rc) {
dev_err(&spmi->dev, "failed to initialize qpnp hwmon adc\n");
- goto fail_free_irq;
+ return rc;
}
vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
vadc->vadc_init_calib = false;
- vadc->vadc_initialized = true;
vadc->max_channels_available = count_adc_channel_list;
-
- rc = qpnp_vadc_configure_interrupt();
- if (rc) {
- dev_err(&spmi->dev, "failed to configure interrupt");
- goto fail_free_irq;
- }
+ vadc->vadc_initialized = true;
return 0;
-
-fail_free_irq:
- free_irq(vadc->adc->adc_irq, vadc);
-
- return rc;
}
static int __devexit qpnp_vadc_remove(struct spmi_device *spmi)
@@ -830,7 +781,6 @@
&vadc->sens_attr[i].dev_attr);
i++;
}
- free_irq(vadc->adc->adc_irq, vadc);
vadc->vadc_initialized = false;
dev_set_drvdata(&spmi->dev, NULL);
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index 80fdac5..912fad4 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -29,7 +29,6 @@
#include "msm_vidc_debug.h"
#include "vidc_hal_api.h"
#include "msm_smem.h"
-#include "msm_vidc_ssr.h"
#define BASE_DEVICE_NUMBER 32
#define SHARED_QSIZE 0x1000000
@@ -1383,9 +1382,6 @@
core->debugfs_root = msm_vidc_debugfs_init_core(
core, vidc_driver->debugfs_root);
pdev->dev.platform_data = core;
- rc = msm_vidc_ssr_init(core);
- if (rc < 0)
- dprintk(VIDC_ERR, "msm_vidc : Sub Systrem Restart failed\n");
return rc;
err_cores_exceeded:
@@ -1414,7 +1410,6 @@
vidc_hal_delete_device(core->device);
video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
- rc = msm_vidc_ssr_uninit(core);
v4l2_device_unregister(&core->v4l2_dev);
if (core->resources.ocmem.handle)
ocmem_notifier_unregister(core->resources.ocmem.handle,
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index fdadb36..d53da9e 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -23,6 +23,7 @@
#define DEFAULT_WIDTH 1280
#define MIN_NUM_OUTPUT_BUFFERS 4
#define MAX_NUM_OUTPUT_BUFFERS 8
+#define MAX_INPUT_BUFFERS 32
#define MIN_BIT_RATE 64000
#define MAX_BIT_RATE 160000000
#define DEFAULT_BIT_RATE 64000
@@ -136,11 +137,11 @@
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME,
.name = "Request I Frame",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .type = V4L2_CTRL_TYPE_BUTTON,
.minimum = 0,
- .maximum = 1,
+ .maximum = 0,
.default_value = 0,
- .step = 1,
+ .step = 0,
.menu_skip_mask = 0,
.qmenu = NULL,
},
@@ -558,6 +559,8 @@
{
int i, rc = 0;
struct msm_vidc_inst *inst;
+ struct hal_buffer_count_actual new_buf_count;
+ enum hal_property property_id;
unsigned long flags;
if (!q || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
@@ -593,6 +596,11 @@
max(*num_buffers, inst->buff_req.buffer[0].
buffer_count_actual);
spin_unlock_irqrestore(&inst->lock, flags);
+ property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
+ new_buf_count.buffer_type = HAL_BUFFER_INPUT;
+ new_buf_count.buffer_count_actual = MAX_INPUT_BUFFERS;
+ rc = vidc_hal_session_set_property(inst->session,
+ property_id, &new_buf_count);
dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
inst->buff_req.buffer[0].buffer_size,
inst->buff_req.buffer[0].buffer_alignment,
@@ -808,7 +816,7 @@
case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME:
property_id =
HAL_CONFIG_VENC_REQUEST_IFRAME;
- request_iframe.enable = control.value;
+ request_iframe.enable = true;
pdata = &request_iframe;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 772f0de..02773ff 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -587,7 +587,6 @@
static void handle_sys_watchdog_timeout(enum command_response cmd, void *data)
{
- subsystem_restart("msm_vidc");
dprintk(VIDC_ERR,
"msm_vidc: Sub System Restart initiated\n");
}
diff --git a/drivers/media/video/msm_wfd/enc-venus-subdev.c b/drivers/media/video/msm_wfd/enc-venus-subdev.c
index 89ad6c7..150c667 100644
--- a/drivers/media/video/msm_wfd/enc-venus-subdev.c
+++ b/drivers/media/video/msm_wfd/enc-venus-subdev.c
@@ -178,7 +178,7 @@
rc = msm_vidc_dqbuf(inst->vidc_context, &buffer);
if (rc) {
- WFD_MSG_ERR("Error dequeuing buffer" \
+ WFD_MSG_ERR("Error dequeuing buffer " \
"from vidc: %d", rc);
goto abort_dequeue;
}
@@ -1010,7 +1010,8 @@
inst = (struct venc_inst *)sd->dev_priv;
enc_cmd.cmd = V4L2_ENC_QCOM_CMD_FLUSH;
- enc_cmd.flags = BUF_TYPE_INPUT | BUF_TYPE_OUTPUT;
+ enc_cmd.flags = V4L2_QCOM_CMD_FLUSH_OUTPUT |
+ V4L2_QCOM_CMD_FLUSH_CAPTURE;
msm_vidc_encoder_cmd(inst->vidc_context, &enc_cmd);
wait_for_completion(&inst->cmd_complete);
diff --git a/drivers/media/video/msm_wfd/wfd-ioctl.c b/drivers/media/video/msm_wfd/wfd-ioctl.c
index 371ae3c..5f67a96 100644
--- a/drivers/media/video/msm_wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm_wfd/wfd-ioctl.c
@@ -271,8 +271,9 @@
goto alloc_fail;
}
- WFD_MSG_ERR("NOTE: enc paddr = %p, kvaddr = %p\n",
- enc_mregion->paddr,
+ WFD_MSG_DBG("NOTE: enc paddr = [%p->%p], kvaddr = %p\n",
+ enc_mregion->paddr, (int8_t *)
+ enc_mregion->paddr + enc_mregion->size,
enc_mregion->kvaddr);
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index 9afc3df..f009e06 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -922,10 +922,15 @@
case VC_TYPE:
vc_format = (struct v4l2_format_vc_ext *) &priv_fmt->u.timing;
c_data->vc_format = *vc_format;
+ c_data->stride = priv_fmt->stride;
size = (c_data->vc_format.hactive_end -
c_data->vc_format.hactive_start);
- size = VCAP_STRIDE_CALC(size);
+ if (c_data->stride == VC_STRIDE_32)
+ size = VCAP_STRIDE_CALC(size, VCAP_STRIDE_ALIGN_32);
+ else
+ size = VCAP_STRIDE_CALC(size, VCAP_STRIDE_ALIGN_16);
+
if (c_data->vc_format.color_space)
size *= 3;
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
index 08dcdf3..642074f 100644
--- a/drivers/media/video/vcap_vc.c
+++ b/drivers/media/video/vcap_vc.c
@@ -37,7 +37,10 @@
} else {
int size = (c_data->vc_format.hactive_end -
c_data->vc_format.hactive_start);
- size = VCAP_STRIDE_CALC(size);
+ if (c_data->stride == VC_STRIDE_32)
+ size = VCAP_STRIDE_CALC(size, VCAP_STRIDE_ALIGN_32);
+ else
+ size = VCAP_STRIDE_CALC(size, VCAP_STRIDE_ALIGN_16);
size *= (c_data->vc_format.vactive_end -
c_data->vc_format.vactive_start);
writel_relaxed(buf->paddr, y_addr);
@@ -527,7 +530,10 @@
writel_iowmb(0x000033FF, VCAP_VC_BUF_CTRL);
rc = vc_format->hactive_end - vc_format->hactive_start;
- rc = VCAP_STRIDE_CALC(rc);
+ if (c_data->stride == VC_STRIDE_32)
+ rc = VCAP_STRIDE_CALC(rc, VCAP_STRIDE_ALIGN_32);
+ else
+ rc = VCAP_STRIDE_CALC(rc, VCAP_STRIDE_ALIGN_16);
if (vc_format->color_space)
rc *= 3;
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
index 48bc92d..4996279 100644
--- a/drivers/mfd/pm8038-core.c
+++ b/drivers/mfd/pm8038-core.c
@@ -373,6 +373,11 @@
.num_resources = ARRAY_SIZE(ccadc_cell_resources),
};
+static struct mfd_cell vibrator_cell __devinitdata = {
+ .name = PM8XXX_VIBRATOR_DEV_NAME,
+ .id = -1,
+};
+
static struct pm8xxx_vreg regulator_data[] = {
/* name pc_name ctrl test hpm_min */
NLDO1200("8038_l1", 0x0AE, 0x0AF, LDO_1200),
@@ -609,6 +614,17 @@
}
}
+ if (pdata->vibrator_pdata) {
+ vibrator_cell.platform_data = pdata->vibrator_pdata;
+ vibrator_cell.pdata_size =
+ sizeof(struct pm8xxx_vibrator_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell, 1, NULL, 0);
+ if (ret) {
+ pr_err("Failed to add vibrator ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
if (pdata->spk_pdata) {
spk_cell.platform_data = pdata->spk_pdata;
spk_cell.pdata_size = sizeof(struct pm8xxx_spk_platform_data);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 27123bc..1972845 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1339,6 +1339,15 @@
/* Populate the remaining parameters */
load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
memcpy(load_req.app_name, appname, MAX_APP_NAME_SIZE);
+ mutex_lock(&app_access_lock);
+ ret = qsee_vote_for_clock(CLK_SFPB);
+ if (ret) {
+ kzfree(img_data);
+ pr_warning("Unable to vote for SFPB clock");
+ mutex_unlock(&app_access_lock);
+ return -EIO;
+ }
+
/* SCM_CALL to load the image */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
sizeof(struct qseecom_load_app_ireq),
@@ -1346,6 +1355,8 @@
kzfree(img_data);
if (ret) {
pr_err("scm_call to load failed : ret %d\n", ret);
+ qsee_disable_clock_vote(CLK_SFPB);
+ mutex_unlock(&app_access_lock);
return -EIO;
}
@@ -1368,6 +1379,9 @@
ret = -EINVAL;
break;
}
+ qsee_disable_clock_vote(CLK_SFPB);
+ mutex_unlock(&app_access_lock);
+
return ret;
}
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 85d2737..de87e82 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -25,6 +25,7 @@
int (*power_save)(struct mmc_host *);
int (*power_restore)(struct mmc_host *);
int (*alive)(struct mmc_host *);
+ int (*change_bus_speed)(struct mmc_host *, unsigned long *);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 5183f2a..a98ed3d 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -821,6 +821,69 @@
return err;
}
+/**
+ * mmc_change_bus_speed() - Change MMC card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the MMC card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card and if it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ */
+static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+ int err = 0;
+ struct mmc_card *card;
+
+ mmc_claim_host(host);
+ /*
+ * Assign card pointer after claiming host to avoid race
+ * conditions that may arise during removal of the card.
+ */
+ card = host->card;
+
+ if (!card || !freq) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (mmc_card_highspeed(card) || mmc_card_hs200(card)
+ || mmc_card_ddr_mode(card)) {
+ if (*freq > card->ext_csd.hs_max_dtr)
+ *freq = card->ext_csd.hs_max_dtr;
+ } else if (*freq > card->csd.max_dtr) {
+ *freq = card->csd.max_dtr;
+ }
+
+ if (*freq < host->f_min)
+ *freq = host->f_min;
+
+ mmc_set_clock(host, (unsigned int) (*freq));
+
+ if (mmc_card_hs200(card) && card->host->ops->execute_tuning) {
+ /*
+ * We try to probe host driver for tuning for any
+ * frequency, it is host driver responsibility to
+ * perform actual tuning only when required.
+ */
+ mmc_host_clk_hold(card->host);
+ err = card->host->ops->execute_tuning(card->host,
+ MMC_SEND_TUNING_BLOCK_HS200);
+ mmc_host_clk_release(card->host);
+
+ if (err)
+ pr_warn("%s: %s: tuning execution failed %d\n",
+ mmc_hostname(card->host), __func__, err);
+ }
+out:
+ mmc_release_host(host);
+ return err;
+}
+
/*
* Handle the detection and initialisation of a card.
*
@@ -1531,6 +1594,7 @@
.resume = NULL,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
+ .change_bus_speed = mmc_change_bus_speed,
};
static const struct mmc_bus_ops mmc_ops_unsafe = {
@@ -1542,6 +1606,7 @@
.resume = mmc_resume,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
+ .change_bus_speed = mmc_change_bus_speed,
};
static void mmc_attach_bus_ops(struct mmc_host *host)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ff5821b..8661929 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -609,6 +609,72 @@
return 0;
}
+/**
+ * mmc_sd_change_bus_speed() - Change SD card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the SD card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card and if it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ */
+static int mmc_sd_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+ int err = 0;
+ struct mmc_card *card;
+
+ mmc_claim_host(host);
+ /*
+ * Assign card pointer after claiming host to avoid race
+ * conditions that may arise during removal of the card.
+ */
+ card = host->card;
+
+ /* sanity checks */
+ if (!card || !freq) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (mmc_card_uhs(card)) {
+ if (*freq > card->sw_caps.uhs_max_dtr)
+ *freq = card->sw_caps.uhs_max_dtr;
+ } else {
+ if (*freq > mmc_sd_get_max_clock(card))
+ *freq = mmc_sd_get_max_clock(card);
+ }
+
+ if (*freq < host->f_min)
+ *freq = host->f_min;
+
+ mmc_set_clock(host, (unsigned int) (*freq));
+
+ if (!mmc_host_is_spi(card->host) && mmc_sd_card_uhs(card)
+ && card->host->ops->execute_tuning) {
+ /*
+ * We try to probe host driver for tuning for any
+ * frequency, it is host driver responsibility to
+ * perform actual tuning only when required.
+ */
+ mmc_host_clk_hold(card->host);
+ err = card->host->ops->execute_tuning(card->host,
+ MMC_SEND_TUNING_BLOCK);
+ mmc_host_clk_release(card->host);
+
+ if (err)
+ pr_warn("%s: %s: tuning execution failed %d\n",
+ mmc_hostname(card->host), __func__, err);
+ }
+
+out:
+ mmc_release_host(host);
+ return err;
+}
+
/*
* UHS-I specific initialization procedure
*/
@@ -1191,6 +1257,7 @@
.resume = NULL,
.power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
+ .change_bus_speed = mmc_sd_change_bus_speed,
};
static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
@@ -1200,6 +1267,7 @@
.resume = mmc_sd_resume,
.power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
+ .change_bus_speed = mmc_sd_change_bus_speed,
};
static void mmc_sd_attach_bus_ops(struct mmc_host *host)
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index a752357..de7e5bc 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -5271,7 +5271,7 @@
pull_data->on = pull;
pull_data->off = pull + pull_data->size;
- ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-pad-pull-on",
+ ret = msmsdcc_dt_get_array(dev, "qcom,pad-pull-on",
&tmp, &len, pull_data->size);
if (!ret) {
for (i = 0; i < len; i++) {
@@ -5284,7 +5284,7 @@
goto err;
}
- ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-pad-pull-off",
+ ret = msmsdcc_dt_get_array(dev, "qcom,pad-pull-off",
&tmp, &len, pull_data->size);
if (!ret) {
for (i = 0; i < len; i++) {
@@ -5349,7 +5349,7 @@
drv_data->on = drv;
drv_data->off = drv + drv_data->size;
- ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-pad-drv-on",
+ ret = msmsdcc_dt_get_array(dev, "qcom,pad-drv-on",
&tmp, &len, drv_data->size);
if (!ret) {
for (i = 0; i < len; i++) {
@@ -5362,7 +5362,7 @@
goto err;
}
- ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-pad-drv-off",
+ ret = msmsdcc_dt_get_array(dev, "qcom,pad-drv-off",
&tmp, &len, drv_data->size);
if (!ret) {
for (i = 0; i < len; i++) {
@@ -5442,7 +5442,7 @@
char result[32];
pin_data->gpio_data->gpio[i].no = of_get_gpio(np, i);
of_property_read_string_index(np,
- "qcom,sdcc-gpio-names", i, &name);
+ "qcom,gpio-names", i, &name);
snprintf(result, 32, "%s-%s",
dev_name(dev), name ? name : "?");
@@ -5501,17 +5501,17 @@
vreg->name = vreg_name;
snprintf(prop_name, MAX_PROP_SIZE,
- "qcom,sdcc-%s-always_on", vreg_name);
+ "qcom,%s-always-on", vreg_name);
if (of_get_property(np, prop_name, NULL))
vreg->always_on = true;
snprintf(prop_name, MAX_PROP_SIZE,
- "qcom,sdcc-%s-lpm_sup", vreg_name);
+ "qcom,%s-lpm-sup", vreg_name);
if (of_get_property(np, prop_name, NULL))
vreg->lpm_sup = true;
snprintf(prop_name, MAX_PROP_SIZE,
- "qcom,sdcc-%s-voltage_level", vreg_name);
+ "qcom,%s-voltage-level", vreg_name);
prop = of_get_property(np, prop_name, &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
dev_warn(dev, "%s %s property\n",
@@ -5522,7 +5522,7 @@
}
snprintf(prop_name, MAX_PROP_SIZE,
- "qcom,sdcc-%s-current_level", vreg_name);
+ "qcom,%s-current-level", vreg_name);
prop = of_get_property(np, prop_name, &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
dev_warn(dev, "%s %s property\n",
@@ -5558,7 +5558,7 @@
goto err;
}
- of_property_read_u32(np, "qcom,sdcc-bus-width", &bus_width);
+ of_property_read_u32(np, "qcom,bus-width", &bus_width);
if (bus_width == 8) {
pdata->mmc_bus_width = MMC_CAP_8_BIT_DATA;
} else if (bus_width == 4) {
@@ -5568,7 +5568,7 @@
pdata->mmc_bus_width = 0;
}
- ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-sup-voltages",
+ ret = msmsdcc_dt_get_array(dev, "qcom,sup-voltages",
&sup_voltages, &sup_volt_len, 0);
if (!ret) {
for (i = 0; i < sup_volt_len; i += 2) {
@@ -5583,7 +5583,7 @@
dev_dbg(dev, "OCR mask=0x%x\n", pdata->ocr_mask);
}
- ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-clk-rates",
+ ret = msmsdcc_dt_get_array(dev, "qcom,clk-rates",
&clk_table, &clk_table_len, 0);
if (!ret) {
pdata->sup_clk_table = clk_table;
@@ -5608,13 +5608,13 @@
if (msmsdcc_dt_parse_gpio_info(dev, pdata))
goto err;
- len = of_property_count_strings(np, "qcom,sdcc-bus-speed-mode");
+ len = of_property_count_strings(np, "qcom,bus-speed-mode");
for (i = 0; i < len; i++) {
const char *name = NULL;
of_property_read_string_index(np,
- "qcom,sdcc-bus-speed-mode", i, &name);
+ "qcom,bus-speed-mode", i, &name);
if (!name)
continue;
@@ -5640,7 +5640,7 @@
| MMC_CAP_UHS_DDR50;
}
- of_property_read_u32(np, "qcom,sdcc-current-limit", ¤t_limit);
+ of_property_read_u32(np, "qcom,current-limit", ¤t_limit);
if (current_limit == 800)
pdata->uhs_caps |= MMC_CAP_MAX_CURRENT_800;
else if (current_limit == 600)
@@ -5650,11 +5650,11 @@
else if (current_limit == 200)
pdata->uhs_caps |= MMC_CAP_MAX_CURRENT_200;
- if (of_get_property(np, "qcom,sdcc-xpc", NULL))
+ if (of_get_property(np, "qcom,xpc", NULL))
pdata->xpc_cap = true;
- if (of_get_property(np, "qcom,sdcc-nonremovable", NULL))
+ if (of_get_property(np, "qcom,nonremovable", NULL))
pdata->nonremovable = true;
- if (of_get_property(np, "qcom,sdcc-disable_cmd23", NULL))
+ if (of_get_property(np, "qcom,disable-cmd23", NULL))
pdata->disable_cmd23 = true;
of_property_read_u32(np, "qcom,dat1-mpm-int",
&pdata->mpm_sdiowakeup_int);
@@ -6241,7 +6241,7 @@
vreg_deinit:
msmsdcc_vreg_init(host, false);
clk_disable:
- clk_disable(host->clk);
+ clk_disable_unprepare(host->clk);
msmsdcc_msm_bus_unregister(host);
pm_qos_remove:
if (host->cpu_dma_latency)
diff --git a/drivers/platform/msm/sps/sps_dma.c b/drivers/platform/msm/sps/sps_dma.c
index f8b4f51d..335de9a 100644
--- a/drivers/platform/msm/sps/sps_dma.c
+++ b/drivers/platform/msm/sps/sps_dma.c
@@ -274,7 +274,6 @@
{
struct bamdma_device *dev;
struct sps_bam_props *props;
- u32 chan;
int result = SPS_ERROR;
mutex_lock(&bam_dma_lock);
@@ -343,14 +342,6 @@
dev->num_pipes = dev->bam->props.num_pipes;
- /* Disable all channels */
- if (dev->local)
- for (chan = 0; chan < (dev->num_pipes / 2); chan++) {
- dma_write_reg_field(dev->virt_addr,
- DMA_CHNL_CONFIG(chan),
- DMA_CHNL_ENABLE, 0);
- }
-
result = 0;
exit_err:
if (result) {
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 7b7e05e..bc2e2ae 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -333,6 +333,16 @@
To compile this driver as a module, choose M here: the module will
be called smb137b.
+config SMB137C_CHARGER
+ tristate "Summit SMB137C Battery Charger"
+ depends on I2C
+ depends on OF
+ help
+ The SMB137C charger chip from Summit is a switching mode based
+ charging solution. This driver supports enabling and disabling
+ charging, setting the input current limit, and enabling USB OTG mode
+ in order to supply 5 V on the VBUS line.
+
config SMB349_CHARGER
tristate "smb349 charger"
depends on I2C
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 3e74f35..990bd03 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -55,6 +55,7 @@
obj-$(CONFIG_BATTERY_BQ27541) += bq27541_fuelgauger.o
obj-$(CONFIG_BATTERY_BQ28400) += bq28400_battery.o
obj-$(CONFIG_SMB137B_CHARGER) += smb137b.o
+obj-$(CONFIG_SMB137C_CHARGER) += smb137c-charger.o
obj-$(CONFIG_PM8XXX_CCADC) += pm8xxx-ccadc.o
obj-$(CONFIG_PM8921_BMS) += pm8921-bms.o
obj-$(CONFIG_QPNP_BMS) += qpnp-bms.o
diff --git a/drivers/power/bq28400_battery.c b/drivers/power/bq28400_battery.c
index 39eac60..61d5835 100644
--- a/drivers/power/bq28400_battery.c
+++ b/drivers/power/bq28400_battery.c
@@ -486,6 +486,8 @@
u16 battery_status;
battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+ rsoc = bq28400_read_rsoc(client);
+ current_ma = bq28400_read_current(client);
if (battery_status & BAT_STATUS_EMPTY)
pr_debug("Battery report Empty.\n");
@@ -493,16 +495,16 @@
/* Battery may report FULL before rsoc is 100%
* for protection and cell-balancing.
* The FULL report may remain when rsoc drops from 100%.
+ * If battery is full but DC-Jack is removed then report discahrging.
*/
if (battery_status & BAT_STATUS_FULL) {
pr_debug("Battery report Full.\n");
bq28400_enable_charging(bq28400_dev, false);
+ if (current_ma < 0)
+ return POWER_SUPPLY_STATUS_DISCHARGING;
return POWER_SUPPLY_STATUS_FULL;
}
- rsoc = bq28400_read_rsoc(client);
- current_ma = bq28400_read_current(client);
-
if (rsoc == 100) {
bq28400_enable_charging(bq28400_dev, false);
pr_debug("Full.\n");
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index bc012ca..b5559db 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -702,7 +702,7 @@
}
static int
-qpnp_chg_charge_dis(struct qpnp_chg_chip *chip, int disable)
+qpnp_chg_force_run_on_batt(struct qpnp_chg_chip *chip, int disable)
{
/* This bit forces the charger to run off of the battery rather
* than a connected charger */
@@ -784,11 +784,9 @@
switch (psp) {
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- if (val->intval)
- qpnp_chg_charge_en(chip, val->intval);
- else
- qpnp_chg_charge_dis(chip, val->intval);
chip->charging_disabled = !(val->intval);
+ qpnp_chg_charge_en(chip, !chip->charging_disabled);
+ qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
break;
default:
return -EINVAL;
@@ -1275,7 +1273,7 @@
qpnp_chg_is_usb_chg_plugged_in(chip));
qpnp_chg_charge_en(chip, !chip->charging_disabled);
- qpnp_chg_charge_dis(chip, chip->charging_disabled);
+ qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
pr_info("Probe success !\n");
return 0;
diff --git a/drivers/power/smb137c-charger.c b/drivers/power/smb137c-charger.c
new file mode 100644
index 0000000..b865bd7
--- /dev/null
+++ b/drivers/power/smb137c-charger.c
@@ -0,0 +1,1352 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+struct smb137c_chip {
+ struct i2c_client *client;
+ struct power_supply psy;
+ struct power_supply *usb_psy;
+ struct mutex lock;
+ int charge_current_limit_ua;
+ int input_current_limit_ua;
+ int term_current_ua;
+ bool charging_enabled;
+ bool otg_mode_enabled;
+ bool charging_allowed;
+};
+
+struct input_current_config {
+ int current_limit_ua;
+ u8 cmd_b_reg;
+ u8 var_func_reg;
+ u8 input_cur_reg;
+};
+
+struct term_current_config {
+ int term_current_ua;
+ u8 charge_cur_reg;
+};
+
+#define INPUT_CURRENT(_current_limit_ua, _cmd_b_reg, _var_func_reg, \
+ _input_cur_reg) \
+ { \
+ .current_limit_ua = _current_limit_ua, \
+ .cmd_b_reg = _cmd_b_reg, \
+ .var_func_reg = _var_func_reg, \
+ .input_cur_reg = _input_cur_reg, \
+ }
+
+#define CHARGE_CURRENT_REG 0x00
+#define CHARGE_CURRENT_FAST_CHG_MASK 0xE0
+#define CHARGE_CURRENT_FAST_CHG_SHIFT 5
+#define CHARGE_CURRENT_PRE_CHG_MASK 0x18
+#define CHARGE_CURRENT_PRE_CHG_SHIFT 3
+#define CHARGE_CURRENT_TERM_CUR_MASK 0x06
+
+#define INPUT_CURRENT_REG 0x01
+#define INPUT_CURRENT_LIMIT_MASK 0xE0
+
+#define FLOAT_VOLTAGE_REG 0x02
+#define FLOAT_VOLTAGE_MASK 0x7F
+#define FLOAT_VOLTAGE_SHIFT 0
+
+#define CTRL_A_REG 0x03
+#define CTRL_A_AUTO_RECHARGE_MASK 0x80
+#define CTRL_A_AUTO_RECHARGE_ENABLED 0x00
+#define CTRL_A_AUTO_RECHARGE_DISABLED 0x80
+#define CTRL_A_TERM_CUR_MASK 0x40
+#define CTRL_A_TERM_CUR_ENABLED 0x00
+#define CTRL_A_TERM_CUR_DISABLED 0x40
+#define CTRL_A_THRESH_VOLTAGE_MASK 0x38
+#define CTRL_A_THRESH_VOLTAGE_SHIFT 3
+#define CTRL_A_VOUTL_MASK 0x02
+#define CTRL_A_VOUTL_4250MV 0x00
+#define CTRL_A_VOUTL_4460MV 0x02
+#define CTRL_A_THERM_MONITOR_MASK 0x01
+#define CTRL_A_THERM_MONITOR_ENABLED 0x01
+#define CTRL_A_THERM_MONITOR_DISABLED 0x00
+
+#define PIN_CTRL_REG 0x05
+#define PIN_CTRL_DEAD_BATT_CHG_MASK 0x80
+#define PIN_CTRL_DEAD_BATT_CHG_ENABLED 0x80
+#define PIN_CTRL_DEAD_BATT_CHG_DISABLED 0x00
+#define PIN_CTRL_OTG_LBR_MASK 0x20
+#define PIN_CTRL_OTG 0x00
+#define PIN_CTRL_LBR 0x20
+#define PIN_CTRL_USB_CUR_LIMIT_MASK 0x10
+#define PIN_CTRL_USB_CUR_LIMIT_REG 0x00
+#define PIN_CTRL_USB_CUR_LIMIT_PIN 0x10
+#define PIN_CTRL_CHG_EN_MASK 0x0C
+#define PIN_CTRL_CHG_EN_REG_LOW 0x00
+#define PIN_CTRL_CHG_EN_REG_HIGH 0x04
+#define PIN_CTRL_CHG_EN_PIN_LOW 0x08
+#define PIN_CTRL_CHG_EN_PIN_HIGH 0x0C
+#define PIN_CTRL_OTG_CTRL_MASK 0x02
+#define PIN_CTRL_OTG_CTRL_REG 0x00
+#define PIN_CTRL_OTG_CTRL_PIN 0x02
+
+#define OTG_CTRL_REG 0x06
+#define OTG_CTRL_BMD_MASK 0x80
+#define OTG_CTRL_BMD_ENABLED 0x80
+#define OTG_CTRL_BMD_DISABLED 0x00
+#define OTG_CTRL_AUTO_RECHARGE_MASK 0x40
+#define OTG_CTRL_AUTO_RECHARGE_75MV 0x00
+#define OTG_CTRL_AUTO_RECHARGE_120MV 0x40
+
+#define TEMP_MON_REG 0x08
+#define TEMP_MON_THERM_CURRENT_MASK 0xC0
+#define TEMP_MON_THERM_CURRENT_SHIFT 6
+#define TEMP_MON_TEMP_LOW_MASK 0x38
+#define TEMP_MON_TEMP_LOW_SHIFT 3
+#define TEMP_MON_TEMP_HIGH_MASK 0x07
+#define TEMP_MON_TEMP_HIGH_SHIFT 0
+
+#define SAFETY_TIMER_REG 0x09
+#define SAFETY_TIMER_RELOAD_MASK 0x40
+#define SAFETY_TIMER_RELOAD_ENABLED 0x40
+#define SAFETY_TIMER_RELOAD_DISABLED 0x00
+#define SAFETY_TIMER_CHG_TIMEOUT_MASK 0x0C
+#define SAFETY_TIMER_CHG_TIMEOUT_SHIFT 2
+#define SAFETY_TIMER_PRE_CHG_TIME_MASK 0x03
+#define SAFETY_TIMER_PRE_CHG_TIME_SHIFT 0
+
+#define VAR_FUNC_REG 0x0C
+#define VAR_FUNC_USB_MODE_MASK 0x80
+#define VAR_FUNC_BMD_MASK 0x0C
+#define VAR_FUNC_BMD_DISABLED 0x00
+#define VAR_FUNC_BMD_ALGO_PERIODIC 0x04
+#define VAR_FUNC_BMD_ALGO 0x08
+#define VAR_FUNC_BMD_THERM 0x0C
+
+#define CMD_A_REG 0x30
+#define CMD_A_VOLATILE_WRITE_MASK 0x80
+#define CMD_A_VOLATILE_WRITE_ALLOW 0x80
+#define CMD_A_VOLATILE_WRITE_DISALLOW 0x00
+#define CMD_A_FAST_CHG_MASK 0x40
+#define CMD_A_FAST_CHG_ALLOW 0x40
+#define CMD_A_FAST_CHG_DISALLOW 0x00
+#define CMD_A_OTG_MASK 0x10
+#define CMD_A_OTG_ENABLED 0x10
+#define CMD_A_OTG_DISABLED 0x00
+#define CMD_A_CHARGING_MASK 0x02
+#define CMD_A_CHARGING_ENABLED 0x00
+#define CMD_A_CHARGING_DISABLED 0x02
+
+#define CMD_B_REG 0x31
+#define CMD_B_USB_MODE_MASK 0x03
+
+#define DEV_ID_REG 0x33
+#define DEV_ID_PART_MASK 0x80
+#define DEV_ID_PART_SMB137C 0x00
+#define DEV_ID_GUI_REV_MASK 0x70
+#define DEV_ID_GUI_REV_SHIFT 4
+#define DEV_ID_SILICON_REV_MASK 0x0F
+#define DEV_ID_SILICON_REV_SHIFT 0
+
+#define IRQ_STAT_A_REG 0x35
+#define IRQ_STAT_A_BATT_HOT 0x40
+#define IRQ_STAT_A_BATT_COLD 0x10
+
+#define IRQ_STAT_B_REG 0x36
+#define IRQ_STAT_B_BATT_OVERVOLT 0x40
+#define IRQ_STAT_B_BATT_MISSING 0x10
+#define IRQ_STAT_B_BATT_UNDERVOLT 0x04
+
+#define STAT_C_REG 0x3D
+#define STAT_C_CHG_ERROR 0x40
+#define STAT_C_VBATT_LEVEL_BELOW_2P1V 0x10
+#define STAT_C_CHG_STAT_MASK 0x06
+#define STAT_C_CHG_STAT_SHIFT 1
+#define STAT_C_CHG_ENABLED 0x01
+
+/* Charge status register values */
+enum smb137c_charge_status {
+ CHARGE_STAT_NO_CHG = 0,
+ CHARGE_STAT_PRE_CHG = 1,
+ CHARGE_STAT_FAST_CHG = 2,
+ CHARGE_STAT_TAPER_CHG = 3,
+};
+
+#define PRE_CHARGE_CURRENT_MIN_UA 50000
+#define PRE_CHARGE_CURRENT_MAX_UA 200000
+#define PRE_CHARGE_CURRENT_STEP_UA 50000
+
+#define FLOAT_VOLTAGE_MIN_UV 3460000
+#define FLOAT_VOLTAGE_MAX_UV 4730000
+#define FLOAT_VOLTAGE_STEP_UV 10000
+
+#define PRE_CHG_THRESH_VOLTAGE_MIN_UV 2400000
+#define PRE_CHG_THRESH_VOLTAGE_MAX_UV 3100000
+#define PRE_CHG_THRESH_VOLTAGE_STEP_UV 100000
+
+#define USB_MIN_CURRENT_UA 100000
+
+static int smb137c_read_reg(struct smb137c_chip *chip, u8 reg, u8 *val)
+{
+ int rc;
+
+ rc = i2c_smbus_read_byte_data(chip->client, reg);
+ if (rc < 0) {
+ pr_err("i2c_smbus_read_byte_data failed. reg=0x%02X, rc=%d\n",
+ reg, rc);
+ } else {
+ *val = rc;
+ rc = 0;
+ pr_debug("read(0x%02X)=0x%02X\n", reg, *val);
+ }
+
+ return rc;
+}
+
+static int smb137c_write_reg(struct smb137c_chip *chip, u8 reg, u8 val)
+{
+ int rc;
+
+ rc = i2c_smbus_write_byte_data(chip->client, reg, val);
+ if (rc < 0)
+ pr_err("i2c_smbus_write_byte_data failed. reg=0x%02X, rc=%d\n",
+ reg, rc);
+ else
+ pr_debug("write(0x%02X)=0x%02X\n", reg, val);
+
+ return rc;
+}
+
+static int smb137c_masked_write_reg(struct smb137c_chip *chip, u8 reg, u8 mask,
+ u8 val)
+{
+ u8 reg_val;
+ int rc;
+
+ pr_debug("masked write(0x%02X), mask=0x%02X, value=0x%02X\n", reg, mask,
+ val);
+
+ rc = smb137c_read_reg(chip, reg, ®_val);
+ if (rc < 0)
+ return rc;
+
+ val = (reg_val & ~mask) | (val & mask);
+
+ if (val != reg_val)
+ rc = smb137c_write_reg(chip, reg, val);
+
+ return rc;
+}
+
+static int smb137c_enable_charging(struct smb137c_chip *chip)
+{
+ int rc = 0;
+
+ chip->charging_allowed = true;
+
+ if (chip->input_current_limit_ua > 0
+ && chip->charge_current_limit_ua > 0) {
+ if (!chip->charging_enabled)
+ rc = smb137c_masked_write_reg(chip, CMD_A_REG,
+ CMD_A_CHARGING_MASK, CMD_A_CHARGING_ENABLED);
+ chip->charging_enabled = true;
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s\n", __func__);
+ }
+
+ return rc;
+}
+
+static int smb137c_disable_charging(struct smb137c_chip *chip)
+{
+ int rc = 0;
+
+ chip->charging_allowed = false;
+
+ if (chip->charging_enabled) {
+ rc = smb137c_masked_write_reg(chip, CMD_A_REG,
+ CMD_A_CHARGING_MASK, CMD_A_CHARGING_DISABLED);
+ }
+
+ chip->charging_enabled = false;
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+ return rc;
+}
+
+static int smb137c_enable_otg_mode(struct smb137c_chip *chip)
+{
+ int rc = 0;
+
+ if (!chip->otg_mode_enabled) {
+ rc = smb137c_masked_write_reg(chip, CMD_A_REG, CMD_A_OTG_MASK,
+ CMD_A_OTG_ENABLED);
+ chip->otg_mode_enabled = true;
+ }
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+ return rc;
+}
+
+static int smb137c_disable_otg_mode(struct smb137c_chip *chip)
+{
+ int rc = 0;
+
+ if (chip->otg_mode_enabled) {
+ rc = smb137c_masked_write_reg(chip, CMD_A_REG, CMD_A_OTG_MASK,
+ CMD_A_OTG_DISABLED);
+ chip->otg_mode_enabled = false;
+ }
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+ return rc;
+}
+
+static struct input_current_config supported_input_current[] = {
+ INPUT_CURRENT(100000, 0x00, 0x00, 0x00),
+ INPUT_CURRENT(150000, 0x00, 0x80, 0x00),
+ INPUT_CURRENT(500000, 0x02, 0x00, 0x00),
+ INPUT_CURRENT(700000, 0x01, 0x00, 0x00),
+ INPUT_CURRENT(800000, 0x01, 0x00, 0x20),
+ INPUT_CURRENT(900000, 0x01, 0x00, 0x40),
+ INPUT_CURRENT(1000000, 0x01, 0x00, 0x60),
+ INPUT_CURRENT(1100000, 0x01, 0x00, 0x80),
+ INPUT_CURRENT(1200000, 0x01, 0x00, 0xA0),
+ INPUT_CURRENT(1300000, 0x01, 0x00, 0xC0),
+ INPUT_CURRENT(1500000, 0x01, 0x00, 0xE0),
+};
+
+static int smb137c_set_usb_input_current_limit(struct smb137c_chip *chip,
+ int current_limit_ua)
+{
+ struct input_current_config *config = NULL;
+ int rc = 0;
+ int i;
+
+ for (i = ARRAY_SIZE(supported_input_current) - 1; i >= 0; i--) {
+ if (current_limit_ua
+ >= supported_input_current[i].current_limit_ua) {
+ config = &supported_input_current[i];
+ break;
+ }
+ }
+
+ if (config) {
+ if (chip->input_current_limit_ua != config->current_limit_ua) {
+ rc = smb137c_masked_write_reg(chip, INPUT_CURRENT_REG,
+ INPUT_CURRENT_LIMIT_MASK, config->input_cur_reg);
+ if (rc)
+ return rc;
+
+ rc = smb137c_masked_write_reg(chip, VAR_FUNC_REG,
+ VAR_FUNC_USB_MODE_MASK, config->var_func_reg);
+ if (rc)
+ return rc;
+
+ rc = smb137c_masked_write_reg(chip, CMD_B_REG,
+ CMD_B_USB_MODE_MASK, config->cmd_b_reg);
+ if (rc)
+ return rc;
+
+ chip->input_current_limit_ua = config->current_limit_ua;
+ }
+
+ if (chip->charging_allowed)
+ rc = smb137c_enable_charging(chip);
+ } else {
+ /* Current limit is below all set points so disable charging. */
+ chip->input_current_limit_ua = 0;
+
+ rc = smb137c_disable_charging(chip);
+ }
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+ chip->input_current_limit_ua);
+
+ return rc;
+}
+
+static int fast_charge_current_ua[] = {
+ 500000,
+ 650000,
+ 750000,
+ 850000,
+ 950000,
+ 1100000,
+ 1300000,
+ 1500000,
+};
+
+static int smb137c_set_charge_current_limit(struct smb137c_chip *chip,
+ int current_limit_ua)
+{
+ int fast_charge_limit_ua = 0;
+ int rc = 0;
+ u8 val = 0;
+ int i;
+
+ for (i = ARRAY_SIZE(fast_charge_current_ua) - 1; i >= 0; i--) {
+ if (current_limit_ua >= fast_charge_current_ua[i]) {
+ val = i << CHARGE_CURRENT_FAST_CHG_SHIFT;
+ fast_charge_limit_ua = fast_charge_current_ua[i];
+ break;
+ }
+ }
+
+ if (fast_charge_limit_ua
+ && chip->charge_current_limit_ua != fast_charge_limit_ua)
+ rc = smb137c_masked_write_reg(chip, CHARGE_CURRENT_REG,
+ CHARGE_CURRENT_FAST_CHG_MASK, val);
+ else if (fast_charge_limit_ua == 0)
+ rc = smb137c_disable_charging(chip);
+
+ chip->charge_current_limit_ua = fast_charge_limit_ua;
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+ fast_charge_limit_ua);
+
+ return rc;
+}
+
+static int smb137c_get_charge_current_limit(struct smb137c_chip *chip)
+{
+ int fast_charge_limit_ua = 0;
+ u8 val = 0;
+ int rc, i;
+
+ rc = smb137c_read_reg(chip, CHARGE_CURRENT_REG, &val);
+ if (rc)
+ return rc;
+
+ i = (val & CHARGE_CURRENT_FAST_CHG_MASK)
+ >> CHARGE_CURRENT_FAST_CHG_SHIFT;
+
+ if (i >= 0 && i < ARRAY_SIZE(fast_charge_current_ua))
+ fast_charge_limit_ua = fast_charge_current_ua[i];
+
+ dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+ fast_charge_limit_ua);
+
+ return fast_charge_limit_ua;
+}
+
+static struct term_current_config term_current_ua[] = {
+ { 35000, 0x06},
+ { 50000, 0x00},
+ {100000, 0x02},
+ {150000, 0x04},
+};
+
+static int smb137c_set_term_current(struct smb137c_chip *chip,
+ int current_limit_ua)
+{
+ int term_current_limit_ua = 0;
+ int rc = 0;
+ u8 val = 0;
+ int i;
+
+ for (i = ARRAY_SIZE(term_current_ua) - 1; i >= 0; i--) {
+ if (current_limit_ua >= term_current_ua[i].term_current_ua) {
+ val = term_current_ua[i].charge_cur_reg;
+ term_current_limit_ua
+ = term_current_ua[i].term_current_ua;
+ break;
+ }
+ }
+
+ if (term_current_limit_ua) {
+ rc = smb137c_masked_write_reg(chip, CHARGE_CURRENT_REG,
+ CHARGE_CURRENT_TERM_CUR_MASK, val);
+ if (rc)
+ return rc;
+ rc = smb137c_masked_write_reg(chip, CTRL_A_REG,
+ CTRL_A_TERM_CUR_MASK, CTRL_A_TERM_CUR_ENABLED);
+ } else {
+ rc = smb137c_masked_write_reg(chip, CTRL_A_REG,
+ CTRL_A_TERM_CUR_MASK, CTRL_A_TERM_CUR_DISABLED);
+ }
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+ term_current_limit_ua);
+
+ return rc;
+}
+
+static int smb137c_set_pre_charge_current_limit(struct smb137c_chip *chip,
+ int current_limit_ua)
+{
+ int setpoint, rc;
+ u8 val;
+
+ if (current_limit_ua < PRE_CHARGE_CURRENT_MIN_UA ||
+ current_limit_ua > PRE_CHARGE_CURRENT_MAX_UA) {
+ dev_err(&chip->client->dev, "%s: current limit out of bounds: %d\n",
+ __func__, current_limit_ua);
+ return -EINVAL;
+ }
+
+ setpoint = (current_limit_ua - PRE_CHARGE_CURRENT_MIN_UA)
+ / PRE_CHARGE_CURRENT_STEP_UA;
+ val = setpoint << CHARGE_CURRENT_PRE_CHG_SHIFT;
+
+ rc = smb137c_masked_write_reg(chip, CHARGE_CURRENT_REG,
+ CHARGE_CURRENT_PRE_CHG_MASK, val);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+ setpoint * PRE_CHARGE_CURRENT_STEP_UA
+ + PRE_CHARGE_CURRENT_MIN_UA);
+
+ return rc;
+}
+
+static int smb137c_set_float_voltage(struct smb137c_chip *chip, int voltage_uv)
+{
+ int setpoint, rc;
+ u8 val;
+
+ if (voltage_uv < FLOAT_VOLTAGE_MIN_UV ||
+ voltage_uv > FLOAT_VOLTAGE_MAX_UV) {
+ dev_err(&chip->client->dev, "%s: voltage out of bounds: %d\n",
+ __func__, voltage_uv);
+ return -EINVAL;
+ }
+
+ setpoint = (voltage_uv - FLOAT_VOLTAGE_MIN_UV) / FLOAT_VOLTAGE_STEP_UV;
+ val = setpoint << FLOAT_VOLTAGE_SHIFT;
+
+ rc = smb137c_masked_write_reg(chip, FLOAT_VOLTAGE_REG,
+ FLOAT_VOLTAGE_MASK, val);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: voltage=%d uV\n", __func__,
+ setpoint * FLOAT_VOLTAGE_STEP_UV + FLOAT_VOLTAGE_MIN_UV);
+
+ return rc;
+}
+
+static int smb137c_set_pre_charge_threshold_voltage(struct smb137c_chip *chip,
+ int voltage_uv)
+{
+ int setpoint, rc;
+ u8 val;
+
+ if (voltage_uv < PRE_CHG_THRESH_VOLTAGE_MIN_UV ||
+ voltage_uv > PRE_CHG_THRESH_VOLTAGE_MAX_UV) {
+ dev_err(&chip->client->dev, "%s: voltage out of bounds: %d\n",
+ __func__, voltage_uv);
+ return -EINVAL;
+ }
+
+ setpoint = (voltage_uv - PRE_CHG_THRESH_VOLTAGE_MIN_UV)
+ / PRE_CHG_THRESH_VOLTAGE_STEP_UV;
+ val = setpoint << CTRL_A_THRESH_VOLTAGE_SHIFT;
+
+ rc = smb137c_masked_write_reg(chip, CTRL_A_REG,
+ CTRL_A_THRESH_VOLTAGE_MASK, val);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: voltage=%d uV\n", __func__,
+ setpoint * PRE_CHG_THRESH_VOLTAGE_STEP_UV
+ + PRE_CHG_THRESH_VOLTAGE_MIN_UV);
+
+ return rc;
+}
+
+static int smb137c_set_recharge_threshold_voltage(struct smb137c_chip *chip,
+ int voltage_uv)
+{
+ int rc;
+ u8 val;
+
+ if (voltage_uv == 75000) {
+ val = OTG_CTRL_AUTO_RECHARGE_75MV;
+ } else if (voltage_uv == 120000) {
+ val = OTG_CTRL_AUTO_RECHARGE_120MV;
+ } else {
+ dev_err(&chip->client->dev, "%s: voltage out of bounds: %d\n",
+ __func__, voltage_uv);
+ return -EINVAL;
+ }
+
+ rc = smb137c_masked_write_reg(chip, OTG_CTRL_REG,
+ OTG_CTRL_AUTO_RECHARGE_MASK, val);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: voltage=%d uV\n", __func__,
+ voltage_uv);
+
+ return rc;
+}
+
+static int smb137c_set_system_voltage(struct smb137c_chip *chip, int voltage_uv)
+{
+ int rc;
+ u8 val;
+
+ if (voltage_uv == 4250000) {
+ val = CTRL_A_VOUTL_4250MV;
+ } else if (voltage_uv == 4460000) {
+ val = CTRL_A_VOUTL_4460MV;
+ } else {
+ dev_err(&chip->client->dev, "%s: voltage out of bounds: %d\n",
+ __func__, voltage_uv);
+ return -EINVAL;
+ }
+
+ rc = smb137c_masked_write_reg(chip, CTRL_A_REG, CTRL_A_VOUTL_MASK, val);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: voltage=%d uV\n", __func__,
+ voltage_uv);
+
+ return rc;
+}
+
+static int charging_timeout[] = {
+ 382,
+ 764,
+ 1527,
+};
+
+static int smb137c_set_charging_timeout(struct smb137c_chip *chip, int timeout)
+{
+ int timeout_chosen = 0;
+ u8 val = 3 << SAFETY_TIMER_CHG_TIMEOUT_SHIFT;
+ int rc, i;
+
+ for (i = ARRAY_SIZE(charging_timeout) - 1; i >= 0; i--) {
+ if (timeout >= charging_timeout[i]) {
+ val = i << SAFETY_TIMER_CHG_TIMEOUT_SHIFT;
+ timeout_chosen = charging_timeout[i];
+ break;
+ }
+ }
+
+ rc = smb137c_masked_write_reg(chip, SAFETY_TIMER_REG,
+ SAFETY_TIMER_CHG_TIMEOUT_MASK, val);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: timeout=%d min\n", __func__,
+ timeout_chosen);
+
+ return rc;
+}
+
+static int pre_charge_timeout[] = {
+ 48,
+ 95,
+ 191,
+};
+
+static int smb137c_set_pre_charge_timeout(struct smb137c_chip *chip,
+ int timeout)
+{
+ int timeout_chosen = 0;
+ u8 val = 3 << SAFETY_TIMER_PRE_CHG_TIME_SHIFT;
+ int rc, i;
+
+ for (i = ARRAY_SIZE(pre_charge_timeout) - 1; i >= 0; i--) {
+ if (timeout >= pre_charge_timeout[i]) {
+ val = i << SAFETY_TIMER_PRE_CHG_TIME_SHIFT;
+ timeout_chosen = pre_charge_timeout[i];
+ break;
+ }
+ }
+
+ rc = smb137c_masked_write_reg(chip, SAFETY_TIMER_REG,
+ SAFETY_TIMER_PRE_CHG_TIME_MASK, val);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: timeout=%d min\n", __func__,
+ timeout_chosen);
+
+ return rc;
+}
+
+static int thermistor_current[] = {
+ 100,
+ 40,
+ 20,
+ 10,
+};
+
+static int smb137c_set_thermistor_current(struct smb137c_chip *chip,
+ int current_ua)
+{
+ bool found = false;
+ u8 val = 0;
+ int rc, i;
+
+ for (i = 0; i < ARRAY_SIZE(thermistor_current); i++) {
+ if (current_ua == thermistor_current[i]) {
+ found = true;
+ val = i << TEMP_MON_THERM_CURRENT_SHIFT;
+ }
+ }
+
+ if (!found) {
+ dev_err(&chip->client->dev, "%s: current out of bounds: %d\n",
+ __func__, current_ua);
+ return -EINVAL;
+ }
+
+ rc = smb137c_masked_write_reg(chip, TEMP_MON_REG,
+ TEMP_MON_THERM_CURRENT_MASK, val);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+ current_ua);
+
+ return 0;
+}
+
+static int smb137c_set_temperature_low_limit(struct smb137c_chip *chip,
+ int value)
+{
+ int rc;
+
+ if (value < 0 || value > 7) {
+ dev_err(&chip->client->dev, "%s: temperature value out of bounds: %d\n",
+ __func__, value);
+ return -EINVAL;
+ }
+
+ rc = smb137c_masked_write_reg(chip, TEMP_MON_REG,
+ TEMP_MON_TEMP_LOW_MASK, value << TEMP_MON_TEMP_LOW_SHIFT);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: temperature value=%d\n",
+ __func__, value);
+
+ return rc;
+}
+
+static int smb137c_set_temperature_high_limit(struct smb137c_chip *chip,
+ int value)
+{
+ int rc;
+
+ if (value < 0 || value > 7) {
+ dev_err(&chip->client->dev, "%s: temperature value out of bounds: %d\n",
+ __func__, value);
+ return -EINVAL;
+ }
+
+ rc = smb137c_masked_write_reg(chip, TEMP_MON_REG,
+ TEMP_MON_TEMP_HIGH_MASK, value << TEMP_MON_TEMP_HIGH_SHIFT);
+
+ if (!rc)
+ dev_dbg(&chip->client->dev, "%s: temperature value=%d\n",
+ __func__, value);
+
+ return rc;
+}
+
+static int charge_status_type_map[] = {
+ [CHARGE_STAT_NO_CHG] = POWER_SUPPLY_CHARGE_TYPE_NONE,
+ [CHARGE_STAT_PRE_CHG] = POWER_SUPPLY_CHARGE_TYPE_TRICKLE,
+ [CHARGE_STAT_FAST_CHG] = POWER_SUPPLY_CHARGE_TYPE_FAST,
+ [CHARGE_STAT_TAPER_CHG] = POWER_SUPPLY_CHARGE_TYPE_FAST,
+};
+
+static const char * const charge_status_name[] = {
+ [CHARGE_STAT_NO_CHG] = "none",
+ [CHARGE_STAT_PRE_CHG] = "pre-charge",
+ [CHARGE_STAT_FAST_CHG] = "fast-charge",
+ [CHARGE_STAT_TAPER_CHG] = "taper-charge",
+};
+
+static int smb137c_get_property_status(struct smb137c_chip *chip)
+{
+ int status = POWER_SUPPLY_STATUS_DISCHARGING;
+ enum smb137c_charge_status charging_status;
+ bool charging_enabled;
+ bool charging_error;
+ int rc;
+ u8 val;
+
+ rc = smb137c_read_reg(chip, STAT_C_REG, &val);
+ if (rc)
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+
+ charging_enabled = val & STAT_C_CHG_ENABLED;
+ charging_error = val & STAT_C_CHG_ERROR;
+ charging_status = (val & STAT_C_CHG_STAT_MASK) >> STAT_C_CHG_STAT_SHIFT;
+
+ if (charging_enabled && !charging_error
+ && charging_status != CHARGE_STAT_NO_CHG)
+ status = POWER_SUPPLY_STATUS_CHARGING;
+
+ dev_dbg(&chip->client->dev, "%s: status=%s\n", __func__,
+ (status == POWER_SUPPLY_STATUS_CHARGING ? "charging"
+ : "discharging"));
+
+ return status;
+}
+
+static int smb137c_get_property_battery_present(struct smb137c_chip *chip)
+{
+ int rc;
+ u8 val;
+
+ rc = smb137c_read_reg(chip, IRQ_STAT_B_REG, &val);
+ if (rc || (val & IRQ_STAT_B_BATT_MISSING))
+ return 0;
+
+ /* Treat battery voltage less than 2.1 V as battery not present. */
+ rc = smb137c_read_reg(chip, STAT_C_REG, &val);
+ if (rc || (val & STAT_C_VBATT_LEVEL_BELOW_2P1V))
+ return 0;
+
+ return 1;
+}
+
+static int smb137c_get_property_battery_health(struct smb137c_chip *chip)
+{
+ int rc;
+ u8 val;
+
+ /* The health of a disconnected battery is unknown. */
+ if (!smb137c_get_property_battery_present(chip))
+ return POWER_SUPPLY_HEALTH_UNKNOWN;
+
+ rc = smb137c_read_reg(chip, IRQ_STAT_B_REG, &val);
+ if (rc)
+ return POWER_SUPPLY_HEALTH_UNKNOWN;
+
+ if (val & IRQ_STAT_B_BATT_OVERVOLT)
+ return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else if (val & IRQ_STAT_B_BATT_UNDERVOLT)
+ return POWER_SUPPLY_HEALTH_DEAD;
+
+ rc = smb137c_read_reg(chip, IRQ_STAT_A_REG, &val);
+ if (rc)
+ return POWER_SUPPLY_HEALTH_UNKNOWN;
+
+ if (val & IRQ_STAT_A_BATT_HOT)
+ return POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (val & IRQ_STAT_A_BATT_COLD)
+ return POWER_SUPPLY_HEALTH_COLD;
+
+ return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int smb137c_get_property_charge_type(struct smb137c_chip *chip)
+{
+ enum smb137c_charge_status status;
+ int charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ bool charging_enabled;
+ bool charging_error;
+ int rc;
+ u8 val;
+
+ rc = smb137c_read_reg(chip, STAT_C_REG, &val);
+ if (rc)
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+
+ charging_enabled = val & STAT_C_CHG_ENABLED;
+ charging_error = val & STAT_C_CHG_ERROR;
+ status = (val & STAT_C_CHG_STAT_MASK) >> STAT_C_CHG_STAT_SHIFT;
+
+ if (!charging_enabled) {
+ dev_dbg(&chip->client->dev, "%s: not charging\n", __func__);
+ } else if (charging_error) {
+ dev_warn(&chip->client->dev, "%s: charger error detected\n",
+ __func__);
+ } else {
+ charge_type = charge_status_type_map[status];
+ }
+
+ dev_dbg(&chip->client->dev, "%s: charging status=%s\n", __func__,
+ charge_status_name[status]);
+
+ return charge_type;
+}
+
+static enum power_supply_property smb137c_power_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static int smb137c_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int smb137c_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct smb137c_chip *chip = container_of(psy, struct smb137c_chip, psy);
+
+ mutex_lock(&chip->lock);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ if (val->intval)
+ smb137c_enable_charging(chip);
+ else
+ smb137c_disable_charging(chip);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ smb137c_set_charge_current_limit(chip, val->intval);
+ break;
+ default:
+ mutex_unlock(&chip->lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&chip->lock);
+
+ power_supply_changed(&chip->psy);
+ return 0;
+}
+
+static int smb137c_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct smb137c_chip *chip = container_of(psy, struct smb137c_chip, psy);
+
+ mutex_lock(&chip->lock);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = smb137c_get_property_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = smb137c_get_property_battery_health(chip);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = smb137c_get_property_battery_present(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = chip->charging_enabled;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = smb137c_get_property_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = chip->charge_current_limit_ua;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = "SMB137C";
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = "Summit Microelectronics";
+ break;
+ default:
+ mutex_unlock(&chip->lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static void smb137c_external_power_changed(struct power_supply *psy)
+{
+ struct smb137c_chip *chip = container_of(psy, struct smb137c_chip, psy);
+ union power_supply_propval prop = {0,};
+
+ mutex_lock(&chip->lock);
+ dev_dbg(&chip->client->dev, "%s: start\n", __func__);
+
+ chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_ONLINE,
+ &prop);
+
+ if (prop.intval) {
+ /* USB online */
+ chip->usb_psy->get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_SCOPE, &prop);
+ if (prop.intval == POWER_SUPPLY_SCOPE_SYSTEM) {
+ /* USB host mode */
+ smb137c_enable_otg_mode(chip);
+ smb137c_disable_charging(chip);
+ } else {
+ /* USB device mode */
+ chip->usb_psy->get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
+ smb137c_set_usb_input_current_limit(chip, prop.intval);
+ smb137c_enable_charging(chip);
+ smb137c_disable_otg_mode(chip);
+ }
+ } else {
+ /* USB offline */
+ smb137c_disable_charging(chip);
+ smb137c_disable_otg_mode(chip);
+ smb137c_set_usb_input_current_limit(chip, USB_MIN_CURRENT_UA);
+ }
+
+ dev_dbg(&chip->client->dev, "%s: end\n", __func__);
+ mutex_unlock(&chip->lock);
+
+ power_supply_changed(&chip->psy);
+}
+
+static int __devinit smb137c_set_register_defaults(struct smb137c_chip *chip)
+{
+ int rc;
+ u8 val, mask;
+
+ /* Allow volatile register writes. */
+ rc = smb137c_masked_write_reg(chip, CMD_A_REG,
+ CMD_A_VOLATILE_WRITE_MASK, CMD_A_VOLATILE_WRITE_ALLOW);
+ if (rc)
+ return rc;
+
+ /* Do not reset register values on USB reinsertion. */
+ rc = smb137c_masked_write_reg(chip, SAFETY_TIMER_REG,
+ SAFETY_TIMER_RELOAD_MASK, SAFETY_TIMER_RELOAD_DISABLED);
+ if (rc)
+ return rc;
+
+ /* Set various default control parameters. */
+ val = PIN_CTRL_DEAD_BATT_CHG_ENABLED | PIN_CTRL_OTG
+ | PIN_CTRL_USB_CUR_LIMIT_REG | PIN_CTRL_CHG_EN_REG_LOW
+ | PIN_CTRL_OTG_CTRL_REG;
+ mask = PIN_CTRL_DEAD_BATT_CHG_MASK | PIN_CTRL_OTG_LBR_MASK
+ | PIN_CTRL_USB_CUR_LIMIT_MASK | PIN_CTRL_CHG_EN_MASK
+ | PIN_CTRL_OTG_CTRL_MASK;
+ rc = smb137c_masked_write_reg(chip, PIN_CTRL_REG, mask, val);
+ if (rc)
+ return rc;
+
+ /* Disable charging, disable OTG mode, and allow fast-charge current. */
+ val = CMD_A_CHARGING_DISABLED | CMD_A_OTG_DISABLED
+ | CMD_A_FAST_CHG_ALLOW;
+ mask = CMD_A_CHARGING_MASK | CMD_A_OTG_MASK | CMD_A_FAST_CHG_MASK;
+ rc = smb137c_masked_write_reg(chip, CMD_A_REG, mask, val);
+ if (rc)
+ return rc;
+
+ /* Enable auto recharging and full-time THERM monitor. */
+ val = CTRL_A_AUTO_RECHARGE_ENABLED | CTRL_A_THERM_MONITOR_ENABLED;
+ mask = CTRL_A_AUTO_RECHARGE_MASK | CTRL_A_THERM_MONITOR_MASK;
+ rc = smb137c_masked_write_reg(chip, CTRL_A_REG, mask, val);
+
+ return rc;
+}
+
+static int __devinit smb137c_apply_dt_configs(struct smb137c_chip *chip)
+{
+ struct device *dev = &chip->client->dev;
+ struct device_node *node = chip->client->dev.of_node;
+ int ret, current_ma, voltage_mv, timeout, value;
+ int rc = 0;
+
+ /*
+ * All device tree parameters are optional so it is ok if read calls
+ * fail.
+ */
+ ret = of_property_read_u32(node, "summit,chg-current-ma", ¤t_ma);
+ if (ret == 0) {
+ rc = smb137c_set_charge_current_limit(chip, current_ma * 1000);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set charge current, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ } else {
+ chip->charge_current_limit_ua
+ = smb137c_get_charge_current_limit(chip);
+ rc = chip->charge_current_limit_ua;
+ if (rc < 0) {
+ dev_err(dev, "%s: Failed to get charge current, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,term-current-ma", ¤t_ma);
+ if (ret == 0) {
+ rc = smb137c_set_term_current(chip, current_ma * 1000);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set termination current, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,pre-chg-current-ma",
+ ¤t_ma);
+ if (ret == 0) {
+ rc = smb137c_set_pre_charge_current_limit(chip,
+ current_ma * 1000);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set pre-charge current limit, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,float-voltage-mv",
+ &voltage_mv);
+ if (ret == 0) {
+ rc = smb137c_set_float_voltage(chip, voltage_mv * 1000);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set float voltage, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,thresh-voltage-mv",
+ &voltage_mv);
+ if (ret == 0) {
+ rc = smb137c_set_pre_charge_threshold_voltage(chip,
+ voltage_mv * 1000);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set fast-charge threshold voltage, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,recharge-thresh-mv",
+ &voltage_mv);
+ if (ret == 0) {
+ rc = smb137c_set_recharge_threshold_voltage(chip,
+ voltage_mv * 1000);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set recharge threshold voltage, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,system-voltage-mv",
+ &voltage_mv);
+ if (ret == 0) {
+ rc = smb137c_set_system_voltage(chip, voltage_mv * 1000);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set system voltage, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,charging-timeout", &timeout);
+ if (ret == 0) {
+ rc = smb137c_set_charging_timeout(chip, timeout);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set charging timeout, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,pre-charge-timeout", &timeout);
+ if (ret == 0) {
+ rc = smb137c_set_pre_charge_timeout(chip, timeout);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set pre-charge timeout, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,therm-current-ua", &value);
+ if (ret == 0) {
+ rc = smb137c_set_thermistor_current(chip, value);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set thermistor current, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,temperature-min", &value);
+ if (ret == 0) {
+ rc = smb137c_set_temperature_low_limit(chip, value);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set low temperature limit, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ ret = of_property_read_u32(node, "summit,temperature-max", &value);
+ if (ret == 0) {
+ rc = smb137c_set_temperature_high_limit(chip, value);
+ if (rc) {
+ dev_err(dev, "%s: Failed to set high temperature limit, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int __devinit smb137c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct smb137c_chip *chip;
+ struct device *dev = &client->dev;
+ struct device_node *node = client->dev.of_node;
+ int rc = 0;
+ int gui_rev, silicon_rev;
+ u8 dev_id;
+
+ if (!node) {
+ dev_err(dev, "%s: device tree information missing\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(dev, "%s: SMBUS_BYTE_DATA unsupported\n", __func__);
+ return -EIO;
+ }
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(dev, "%s: devm_kzalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&chip->lock);
+ chip->client = client;
+ i2c_set_clientdata(client, chip);
+
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (!chip->usb_psy) {
+ dev_dbg(dev, "%s: USB supply not found; deferring charger probe\n",
+ __func__);
+ return -EPROBE_DEFER;
+ }
+
+ rc = smb137c_read_reg(chip, DEV_ID_REG, &dev_id);
+ if (rc)
+ return rc;
+
+ if ((dev_id & DEV_ID_PART_MASK) != DEV_ID_PART_SMB137C) {
+ dev_err(dev, "%s: invalid device ID=0x%02X\n", __func__,
+ dev_id);
+ return -ENODEV;
+ }
+
+ gui_rev = (dev_id & DEV_ID_GUI_REV_MASK) >> DEV_ID_GUI_REV_SHIFT;
+ silicon_rev = (dev_id & DEV_ID_SILICON_REV_MASK)
+ >> DEV_ID_SILICON_REV_SHIFT;
+
+ rc = smb137c_set_register_defaults(chip);
+ if (rc)
+ return rc;
+
+ rc = smb137c_apply_dt_configs(chip);
+ if (rc)
+ return rc;
+
+ chip->psy.name = "battery";
+ chip->psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ chip->psy.properties = smb137c_power_properties;
+ chip->psy.num_properties = ARRAY_SIZE(smb137c_power_properties);
+ chip->psy.get_property = smb137c_power_get_property;
+ chip->psy.set_property = smb137c_power_set_property;
+ chip->psy.property_is_writeable = smb137c_property_is_writeable;
+ chip->psy.external_power_changed = smb137c_external_power_changed;
+
+ rc = power_supply_register(dev, &chip->psy);
+ if (rc < 0) {
+ dev_err(dev, "%s: power_supply_register failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ smb137c_external_power_changed(&chip->psy);
+
+ dev_info(dev, "%s: SMB137C charger probed successfully, gui_rev=%d, silicon_rev=%d\n",
+ __func__, gui_rev, silicon_rev);
+
+ return rc;
+}
+
+static int __devexit smb137c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id smb137c_id[] = {
+ { .name = "smb137c", },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, smb137c_id);
+
+/* TODO: should this be "summit,smb137c-charger"? */
+static const struct of_device_id smb137c_match[] = {
+ { .compatible = "summit,smb137c", },
+ { },
+};
+
+static struct i2c_driver smb137c_driver = {
+ .driver = {
+ .name = "smb137c",
+ .owner = THIS_MODULE,
+ .of_match_table = smb137c_match,
+ },
+ .probe = smb137c_probe,
+ .remove = __devexit_p(smb137c_remove),
+ .id_table = smb137c_id,
+};
+
+static int __init smb137c_init(void)
+{
+ return i2c_add_driver(&smb137c_driver);
+}
+module_init(smb137c_init);
+
+static void __exit smb137c_exit(void)
+{
+ return i2c_del_driver(&smb137c_driver);
+}
+module_exit(smb137c_exit);
+
+MODULE_DESCRIPTION("SMB137C Charger");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:smb137c");
diff --git a/drivers/power/smb350_charger.c b/drivers/power/smb350_charger.c
index 319caba..d07c100 100644
--- a/drivers/power/smb350_charger.c
+++ b/drivers/power/smb350_charger.c
@@ -212,21 +212,6 @@
return ret;
}
-#define SMB350_FLOAT_VOLT_BASE_MV 6920
-#define SMB350_FLOAT_VOLT_STEP_MV 40
-#define SMB350_FLOAT_VOLT_MAX_MV (6920 + 0x2F * 40)
-
-/* Fast-to-Taper charging volatge */
-static int smb350_get_float_voltage(struct i2c_client *client)
-{
- u16 val = smb350_read_reg(client, STATUS_A_REG);
-
- val = SMB350_FLOAT_VOLT_BASE_MV +
- ((val & 0x2F) * SMB350_FLOAT_VOLT_STEP_MV);
-
- return val;
-}
-
static bool smb350_is_dc_present(struct i2c_client *client)
{
u16 irq_status_f = smb350_read_reg(client, IRQ_STATUS_F_REG);
@@ -398,7 +383,6 @@
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CHARGE_TYPE,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
/* fixed */
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_MODEL_NAME,
@@ -429,10 +413,6 @@
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = smb350_get_prop_charge_type(dev);
break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = smb350_get_float_voltage(client);
- val->intval *= 1000; /* mV to uV */
- break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = SMB350_NAME;
break;
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 1f2a95e..02e1952 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -144,6 +144,30 @@
return IRQ_HANDLED;
}
+static int ngd_clk_pause_wakeup(struct slim_controller *ctrl)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ return msm_slim_qmi_power_request(dev, true);
+}
+
+static int ngd_qmi_available(struct notifier_block *n, unsigned long code,
+ void *_cmd)
+{
+ struct msm_slim_qmi *qmi = container_of(n, struct msm_slim_qmi, nb);
+ pr_info("Slimbus QMI NGD CB received event:%ld", code);
+ switch (code) {
+ case QMI_SERVER_ARRIVE:
+ complete(&qmi->qmi_comp);
+ break;
+ case QMI_SERVER_EXIT:
+ /* SSR implementation */
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
static int ngd_get_tid(struct slim_controller *ctrl, struct slim_msg_txn *txn,
u8 *tid, struct completion *done)
{
@@ -184,16 +208,21 @@
u32 *pbuf;
u8 *puc;
int ret = 0;
- int msgv = -1;
u8 la = txn->la;
u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+ if (txn->mc == (SLIM_MSG_CLK_PAUSE_SEQ_FLG |
+ SLIM_MSG_MC_RECONFIGURE_NOW))
+ return msm_slim_qmi_power_request(dev, false);
+ else if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
+ return 0;
+
if (txn->mt == SLIM_MSG_MT_CORE &&
(txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) {
return 0;
}
- msgv = msm_slim_get_ctrl(dev);
+ msm_slim_get_ctrl(dev);
mutex_lock(&dev->tx_lock);
if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE &&
(dev->state == MSM_CTRL_ASLEEP ||
@@ -206,8 +235,7 @@
if (timeout) {
mutex_lock(&dev->tx_lock);
} else {
- if (msgv >= 0)
- msm_slim_put_ctrl(dev);
+ msm_slim_put_ctrl(dev);
return -EBUSY;
}
}
@@ -293,8 +321,7 @@
*/
dev->pipes[wbuf[1]].connected = false;
mutex_unlock(&dev->tx_lock);
- if (msgv >= 0)
- msm_slim_put_ctrl(dev);
+ msm_slim_put_ctrl(dev);
return 0;
}
if (dev->err) {
@@ -336,8 +363,7 @@
txn->mc == SLIM_USR_MC_DISCONNECT_PORT)) {
int timeout;
mutex_unlock(&dev->tx_lock);
- if (msgv >= 0)
- msm_slim_put_ctrl(dev);
+ msm_slim_put_ctrl(dev);
timeout = wait_for_completion_timeout(txn->comp, HZ);
if (!timeout) {
pr_err("connect/disc :0x%x, tid:%d timed out", txn->mc,
@@ -353,8 +379,7 @@
}
ngd_xfer_err:
mutex_unlock(&dev->tx_lock);
- if (msgv >= 0)
- msm_slim_put_ctrl(dev);
+ msm_slim_put_ctrl(dev);
return ret ? ret : dev->err;
}
@@ -405,8 +430,9 @@
return -ENXIO;
}
if (txn.len == 0) {
+ /* Per protocol, only last 5 bits for client no. */
wbuf[txn.len++] = (u8) (slc->prop.dataf << 5) |
- sb->laddr;
+ (sb->laddr & 0x1f);
wbuf[txn.len] = slc->seglen;
if (slc->coeff == SLIM_COEFF_3)
wbuf[txn.len] |= 1 << 5;
@@ -420,6 +446,7 @@
}
}
wbuf[txn.len++] = slc->chan;
+ pr_debug("slim define chan:%d, tid:0x%x", slc->chan, txn.tid);
}
if (txn.len) {
txn.mc = SLIM_USR_MC_DEF_ACT_CHAN;
@@ -448,8 +475,9 @@
return -ENXIO;
}
if (txn.len == 0) {
+ /* Per protocol, only last 5 bits for client no. */
wbuf[txn.len++] = (u8) (SLIM_CH_REMOVE << 6) |
- sb->laddr;
+ (sb->laddr & 0x1f);
ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
if (ret) {
pr_err("no tid for channel define?");
@@ -457,6 +485,7 @@
}
}
wbuf[txn.len++] = slc->chan;
+ pr_debug("slim remove chan:%d, tid:0x%x", slc->chan, txn.tid);
}
if (txn.len) {
txn.mc = SLIM_USR_MC_CHAN_CTRL;
@@ -544,6 +573,7 @@
wbuf[3] = SAT_MSG_PROT;
txn.wbuf = wbuf;
txn.len = 4;
+ pr_info("SLIM SAT: Received master capability");
dev->use_rx_msgqs = 1;
msm_slim_sps_init(dev, dev->bam_mem,
NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS, true);
@@ -557,6 +587,12 @@
ret = ngd_xfer_msg(&dev->ctrl, &txn);
if (!ret) {
dev->state = MSM_CTRL_AWAKE;
+
+ pm_runtime_use_autosuspend(dev->dev);
+ pm_runtime_set_autosuspend_delay(dev->dev,
+ MSM_SLIM_AUTOSUSPEND);
+ pm_runtime_set_active(dev->dev);
+ pm_runtime_enable(dev->dev);
complete(&dev->reconf);
}
}
@@ -595,6 +631,41 @@
complete(txn->comp);
}
}
+
+static int ngd_slim_enable(struct msm_slim_ctrl *dev, bool enable)
+{
+ u32 ngd_int = (NGD_INT_RECFG_DONE | NGD_INT_TX_NACKED_2 |
+ NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
+ NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
+ NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
+ if (enable) {
+ int ret = msm_slim_qmi_init(dev, false);
+ if (ret)
+ return ret;
+ ret = msm_slim_qmi_power_request(dev, true);
+ if (ret)
+ return ret;
+ writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /*
+ * Enable NGD. Configure NGD in register acc. mode until master
+ * announcement is received
+ */
+ writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD enabling goes through */
+ mb();
+ } else {
+ writel_relaxed(0, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+ writel_relaxed(0, dev->base + NGD_INT_EN +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD disabling goes through */
+ mb();
+ msm_slim_qmi_exit(dev);
+ }
+
+ return 0;
+}
+
static int ngd_slim_rx_msgq_thread(void *data)
{
struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
@@ -605,6 +676,14 @@
u32 buffer[10];
u8 msg_len = 0;
+ wait_for_completion_interruptible(&dev->qmi.qmi_comp);
+ ret = ngd_slim_enable(dev, true);
+ /* Exit the thread if component can't be enabled */
+ if (ret) {
+ pr_err("Enabling NGD failed:%d", ret);
+ return 0;
+ }
+
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
ret = wait_for_completion_interruptible(notify);
@@ -648,7 +727,6 @@
struct resource *slim_mem;
struct resource *irq, *bam_irq;
enum apr_subsys_state q6_state;
- u32 ngd_int;
q6_state = apr_get_q6_state();
if (q6_state == APR_SUBSYS_DOWN) {
@@ -725,7 +803,7 @@
dev->ctrl.get_laddr = ngd_get_laddr;
dev->ctrl.allocbw = ngd_allocbw;
dev->ctrl.xfer_msg = ngd_xfer_msg;
- dev->ctrl.wakeup = NULL;
+ dev->ctrl.wakeup = ngd_clk_pause_wakeup;
dev->ctrl.config_port = msm_config_port;
dev->ctrl.port_xfer = msm_slim_port_xfer;
dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
@@ -743,10 +821,6 @@
dev->ver = readl_relaxed(dev->base);
/* Version info in 16 MSbits */
dev->ver >>= 16;
- ngd_int = (NGD_INT_RECFG_DONE | NGD_INT_TX_NACKED_2 |
- NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
- NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
- NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
init_completion(&dev->rx_msgq_notify);
/* Register with framework */
@@ -768,6 +842,15 @@
goto err_request_irq_failed;
}
+ init_completion(&dev->qmi.qmi_comp);
+ dev->qmi.nb.notifier_call = ngd_qmi_available;
+ ret = qmi_svc_event_notifier_register(SLIMBUS_QMI_SVC_ID,
+ SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
+ if (ret) {
+ pr_err("Slimbus QMI service registration failed:%d", ret);
+ goto qmi_register_failed;
+ }
+
/* 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");
@@ -777,30 +860,19 @@
goto err_thread_create_failed;
}
- writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
- NGD_BASE(dev->ctrl.nr, dev->ver));
- /*
- * Enable NGD. Configure NGD in register access mode until master
- * announcement is received
- */
- writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD enabling goes through */
- mb();
-
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);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_SLIM_AUTOSUSPEND);
- pm_runtime_set_active(&pdev->dev);
-
dev_dbg(dev->dev, "NGD SB controller is up!\n");
return 0;
err_thread_create_failed:
+ qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
+ SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
+qmi_register_failed:
free_irq(dev->irq, dev);
err_request_irq_failed:
slim_del_controller(&dev->ctrl);
@@ -816,6 +888,9 @@
static int __devexit ngd_slim_remove(struct platform_device *pdev)
{
struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ ngd_slim_enable(dev, false);
+ qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
+ SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
free_irq(dev->irq, dev);
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 7cd34d3..c62ac27 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -582,3 +582,351 @@
sps_deregister_bam_device(dev->bam.hdl);
}
}
+
+/* Slimbus QMI Messaging */
+#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01 0x0020
+#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01 0x0020
+#define SLIMBUS_QMI_POWER_REQ_V01 0x0021
+#define SLIMBUS_QMI_POWER_RESP_V01 0x0021
+
+enum slimbus_mode_enum_type_v01 {
+ /* To force a 32 bit signed enum. Do not change or use*/
+ SLIMBUS_MODE_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+ SLIMBUS_MODE_SATELLITE_V01 = 1,
+ SLIMBUS_MODE_MASTER_V01 = 2,
+ SLIMBUS_MODE_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+enum slimbus_pm_enum_type_v01 {
+ /* To force a 32 bit signed enum. Do not change or use*/
+ SLIMBUS_PM_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+ SLIMBUS_PM_INACTIVE_V01 = 1,
+ SLIMBUS_PM_ACTIVE_V01 = 2,
+ SLIMBUS_PM_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+struct slimbus_select_inst_req_msg_v01 {
+ /* Mandatory */
+ /* Hardware Instance Selection */
+ uint32_t instance;
+
+ /* Optional */
+ /* Optional Mode Request Operation */
+ /* Must be set to true if mode is being passed */
+ uint8_t mode_valid;
+ enum slimbus_mode_enum_type_v01 mode;
+};
+
+struct slimbus_select_inst_resp_msg_v01 {
+ /* Mandatory */
+ /* Result Code */
+ struct qmi_response_type_v01 resp;
+};
+
+struct slimbus_power_req_msg_v01 {
+ /* Mandatory */
+ /* Power Request Operation */
+ enum slimbus_pm_enum_type_v01 pm_req;
+};
+
+struct slimbus_power_resp_msg_v01 {
+ /* Mandatory */
+ /* Result Code */
+ struct qmi_response_type_v01 resp;
+};
+
+static struct elem_info slimbus_select_inst_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct slimbus_select_inst_req_msg_v01,
+ instance),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct slimbus_select_inst_req_msg_v01,
+ mode_valid),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(enum slimbus_mode_enum_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct slimbus_select_inst_req_msg_v01,
+ mode),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .elem_len = 0,
+ .elem_size = 0,
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x00,
+ .offset = 0,
+ .ei_array = NULL,
+ },
+};
+
+static struct elem_info slimbus_select_inst_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct slimbus_select_inst_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .elem_len = 0,
+ .elem_size = 0,
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x00,
+ .offset = 0,
+ .ei_array = NULL,
+ },
+};
+
+static struct elem_info slimbus_power_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(enum slimbus_pm_enum_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct slimbus_power_req_msg_v01, pm_req),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .elem_len = 0,
+ .elem_size = 0,
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x00,
+ .offset = 0,
+ .ei_array = NULL,
+ },
+};
+
+static struct elem_info slimbus_power_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct slimbus_power_resp_msg_v01, resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .elem_len = 0,
+ .elem_size = 0,
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x00,
+ .offset = 0,
+ .ei_array = NULL,
+ },
+};
+
+static void msm_slim_qmi_recv_msg(struct kthread_work *work)
+{
+ int rc;
+ struct msm_slim_qmi *qmi =
+ container_of(work, struct msm_slim_qmi, kwork);
+
+ rc = qmi_recv_msg(qmi->handle);
+ if (rc < 0)
+ pr_err("%s: Error receiving QMI message\n", __func__);
+}
+
+static void msm_slim_qmi_notify(struct qmi_handle *handle,
+ enum qmi_event_type event, void *notify_priv)
+{
+ struct msm_slim_ctrl *dev = notify_priv;
+ struct msm_slim_qmi *qmi = &dev->qmi;
+
+ switch (event) {
+ case QMI_RECV_MSG:
+ queue_kthread_work(&qmi->kworker, &qmi->kwork);
+ break;
+ default:
+ break;
+ }
+}
+
+static const char *get_qmi_error(struct qmi_response_type_v01 *r)
+{
+ if (r->result == QMI_RESULT_SUCCESS_V01 || r->error == QMI_ERR_NONE_V01)
+ return "No Error";
+ else if (r->error == QMI_ERR_NO_MEMORY_V01)
+ return "Out of Memory";
+ else if (r->error == QMI_ERR_INTERNAL_V01)
+ return "Unexpected error occurred";
+ else if (r->error == QMI_ERR_INCOMPATIBLE_STATE_V01)
+ return "Slimbus s/w already configured to a different mode";
+ else if (r->error == QMI_ERR_INVALID_ID_V01)
+ return "Slimbus hardware instance is not valid";
+ else
+ return "Unknown error";
+}
+
+static int msm_slim_qmi_send_select_inst_req(struct msm_slim_ctrl *dev,
+ struct slimbus_select_inst_req_msg_v01 *req)
+{
+ struct slimbus_select_inst_resp_msg_v01 resp = { { 0, 0 } };
+ struct msg_desc req_desc, resp_desc;
+ int rc;
+
+ req_desc.msg_id = SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01;
+ req_desc.max_msg_len = sizeof(*req);
+ req_desc.ei_array = slimbus_select_inst_req_msg_v01_ei;
+
+ resp_desc.msg_id = SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01;
+ resp_desc.max_msg_len = sizeof(resp);
+ resp_desc.ei_array = slimbus_select_inst_resp_msg_v01_ei;
+
+ rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
+ &resp_desc, &resp, sizeof(resp), 5000);
+ if (rc < 0) {
+ pr_err("%s: QMI send req failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ /* Check the response */
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ pr_err("%s: QMI request failed 0x%x (%s)\n", __func__,
+ resp.resp.result, get_qmi_error(&resp.resp));
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int msm_slim_qmi_send_power_request(struct msm_slim_ctrl *dev,
+ struct slimbus_power_req_msg_v01 *req)
+{
+ struct slimbus_power_resp_msg_v01 resp = { { 0, 0 } };
+ struct msg_desc req_desc, resp_desc;
+ int rc;
+
+ req_desc.msg_id = SLIMBUS_QMI_POWER_REQ_V01;
+ req_desc.max_msg_len = sizeof(*req);
+ req_desc.ei_array = slimbus_power_req_msg_v01_ei;
+
+ resp_desc.msg_id = SLIMBUS_QMI_POWER_RESP_V01;
+ resp_desc.max_msg_len = sizeof(resp);
+ resp_desc.ei_array = slimbus_power_resp_msg_v01_ei;
+
+ rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
+ &resp_desc, &resp, sizeof(resp), 5000);
+ if (rc < 0) {
+ pr_err("%s: QMI send req failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ /* Check the response */
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ pr_err("%s: QMI request failed 0x%x (%s)\n", __func__,
+ resp.resp.result, get_qmi_error(&resp.resp));
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master)
+{
+ int rc = 0;
+ struct qmi_handle *handle;
+ struct slimbus_select_inst_req_msg_v01 req;
+
+ init_kthread_worker(&dev->qmi.kworker);
+
+ dev->qmi.task = kthread_run(kthread_worker_fn,
+ &dev->qmi.kworker, "msm_slim_qmi_clnt%d", dev->ctrl.nr);
+
+ if (IS_ERR(dev->qmi.task)) {
+ pr_err("%s: Failed to create QMI client kthread\n", __func__);
+ return -ENOMEM;
+ }
+
+ init_kthread_work(&dev->qmi.kwork, msm_slim_qmi_recv_msg);
+
+ handle = qmi_handle_create(msm_slim_qmi_notify, dev);
+ if (!handle) {
+ rc = -ENOMEM;
+ pr_err("%s: QMI client handle alloc failed\n", __func__);
+ goto qmi_handle_create_failed;
+ }
+
+ rc = qmi_connect_to_service(handle, SLIMBUS_QMI_SVC_ID,
+ SLIMBUS_QMI_INS_ID);
+ if (rc < 0) {
+ pr_err("%s: QMI server not found\n", __func__);
+ goto qmi_connect_to_service_failed;
+ }
+
+ /* Instance is 0 based */
+ req.instance = dev->ctrl.nr - 1;
+ req.mode_valid = 1;
+
+ /* Mode indicates the role of the ADSP */
+ if (apps_is_master)
+ req.mode = SLIMBUS_MODE_SATELLITE_V01;
+ else
+ req.mode = SLIMBUS_MODE_MASTER_V01;
+
+ dev->qmi.handle = handle;
+
+ rc = msm_slim_qmi_send_select_inst_req(dev, &req);
+ if (rc) {
+ pr_err("%s: failed to select h/w instance\n", __func__);
+ goto qmi_select_instance_failed;
+ }
+
+ return 0;
+
+qmi_select_instance_failed:
+ dev->qmi.handle = NULL;
+qmi_connect_to_service_failed:
+ qmi_handle_destroy(handle);
+qmi_handle_create_failed:
+ flush_kthread_worker(&dev->qmi.kworker);
+ kthread_stop(dev->qmi.task);
+ dev->qmi.task = NULL;
+ return rc;
+}
+
+void msm_slim_qmi_exit(struct msm_slim_ctrl *dev)
+{
+ qmi_handle_destroy(dev->qmi.handle);
+ flush_kthread_worker(&dev->qmi.kworker);
+ kthread_stop(dev->qmi.task);
+ dev->qmi.task = NULL;
+ dev->qmi.handle = NULL;
+}
+
+int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active)
+{
+ struct slimbus_power_req_msg_v01 req;
+
+ if (active)
+ req.pm_req = SLIMBUS_PM_ACTIVE_V01;
+ else
+ req.pm_req = SLIMBUS_PM_INACTIVE_V01;
+
+ return msm_slim_qmi_send_power_request(dev, &req);
+}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 7d50620..3daf7ee 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -12,6 +12,10 @@
#ifndef _SLIM_MSM_H
#define _SLIM_MSM_H
+
+#include <linux/kthread.h>
+#include <mach/msm_qmi_interface.h>
+
/* Per spec.max 40 bytes per received message */
#define SLIM_RX_MSGQ_BUF_LEN 40
@@ -64,6 +68,10 @@
#define MSM_MAX_NSATS 2
#define MSM_MAX_SATCH 32
+/* Slimbus QMI service */
+#define SLIMBUS_QMI_SVC_ID 0x0301
+#define SLIMBUS_QMI_INS_ID 1
+
#define PGD_THIS_EE(r, v) ((v) ? PGD_THIS_EE_V2(r) : PGD_THIS_EE_V1(r))
#define PGD_PORT(r, p, v) ((v) ? PGD_PORT_V2(r, p) : PGD_PORT_V1(r, p))
#define CFG_PORT(r, v) ((v) ? CFG_PORT_V2(r) : CFG_PORT_V1(r))
@@ -161,6 +169,15 @@
bool connected;
};
+struct msm_slim_qmi {
+ struct qmi_handle *handle;
+ struct task_struct *task;
+ struct kthread_work kwork;
+ struct kthread_worker kworker;
+ struct completion qmi_comp;
+ struct notifier_block nb;
+};
+
struct msm_slim_ctrl {
struct slim_controller ctrl;
struct slim_framer framer;
@@ -197,6 +214,7 @@
enum msm_ctrl_state state;
int nsats;
u32 ver;
+ struct msm_slim_qmi qmi;
};
struct msm_sat_chan {
@@ -249,4 +267,8 @@
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
u32 pipe_reg, bool remote);
void msm_slim_sps_exit(struct msm_slim_ctrl *dev);
+
+void msm_slim_qmi_exit(struct msm_slim_ctrl *dev);
+int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
+int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active);
#endif
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 9a99238..43d17c2 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -67,6 +67,15 @@
---help---
Register processes to be killed when memory is low
+config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+ bool "Android Low Memory Killer: detect oom_adj values"
+ depends on ANDROID_LOW_MEMORY_KILLER
+ default y
+ ---help---
+ Detect oom_adj values written to
+ /sys/module/lowmemorykiller/parameters/adj and convert them
+ to oom_score_adj values.
+
source "drivers/staging/android/switch/Kconfig"
config ANDROID_INTF_ALARM_DEV
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 432abe5..c67b75b 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -259,9 +259,94 @@
unregister_shrinker(&lowmem_shrinker);
}
+#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
+{
+ if (oom_adj == OOM_ADJUST_MAX)
+ return OOM_SCORE_ADJ_MAX;
+ else
+ return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
+}
+
+static void lowmem_autodetect_oom_adj_values(void)
+{
+ int i;
+ int oom_adj;
+ int oom_score_adj;
+ int array_size = ARRAY_SIZE(lowmem_adj);
+
+ if (lowmem_adj_size < array_size)
+ array_size = lowmem_adj_size;
+
+ if (array_size <= 0)
+ return;
+
+ oom_adj = lowmem_adj[array_size - 1];
+ if (oom_adj > OOM_ADJUST_MAX)
+ return;
+
+ oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);
+ if (oom_score_adj <= OOM_ADJUST_MAX)
+ return;
+
+ lowmem_print(1, "lowmem_shrink: convert oom_adj to oom_score_adj:\n");
+ for (i = 0; i < array_size; i++) {
+ oom_adj = lowmem_adj[i];
+ oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);
+ lowmem_adj[i] = oom_score_adj;
+ lowmem_print(1, "oom_adj %d => oom_score_adj %d\n",
+ oom_adj, oom_score_adj);
+ }
+}
+
+static int lowmem_adj_array_set(const char *val, const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_array_ops.set(val, kp);
+
+ /* HACK: Autodetect oom_adj values in lowmem_adj array */
+ lowmem_autodetect_oom_adj_values();
+
+ return ret;
+}
+
+static int lowmem_adj_array_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_array_ops.get(buffer, kp);
+}
+
+static void lowmem_adj_array_free(void *arg)
+{
+ param_array_ops.free(arg);
+}
+
+static struct kernel_param_ops lowmem_adj_array_ops = {
+ .set = lowmem_adj_array_set,
+ .get = lowmem_adj_array_get,
+ .free = lowmem_adj_array_free,
+};
+
+static const struct kparam_array __param_arr_adj = {
+ .max = ARRAY_SIZE(lowmem_adj),
+ .num = &lowmem_adj_size,
+ .ops = ¶m_ops_int,
+ .elemsize = sizeof(lowmem_adj[0]),
+ .elem = lowmem_adj,
+};
+#endif
+
module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
+#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+__module_param_call(MODULE_PARAM_PREFIX, adj,
+ &lowmem_adj_array_ops,
+ .arr = &__param_arr_adj,
+ S_IRUGO | S_IWUSR, -1);
+__MODULE_PARM_TYPE(adj, "array of int");
+#else
module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size,
S_IRUGO | S_IWUSR);
+#endif
module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,
S_IRUGO | S_IWUSR);
module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 4a9c9a3..84cd3e7 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -168,6 +168,7 @@
struct work_struct clock_off_w; /* work for actual clock off */
struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
+ bool tty_flush_receive;
};
#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */
@@ -1250,6 +1251,13 @@
}
+static void msm_hs_flush_buffer(struct uart_port *uport)
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ msm_uport->tty_flush_receive = true;
+}
+
/*
* Standard API, Break Signal
*
@@ -1465,7 +1473,14 @@
*/
mb();
/* Complete DMA TX transactions and submit new transactions */
- tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE;
+
+ /* Do not update tx_buf.tail if uart_flush_buffer already
+ called in serial core */
+ if (!msm_uport->tty_flush_receive)
+ tx_buf->tail = (tx_buf->tail +
+ tx->tx_count) & ~UART_XMIT_SIZE;
+ else
+ msm_uport->tty_flush_receive = false;
tx->dma_in_flight = 0;
@@ -2209,6 +2224,7 @@
.config_port = msm_hs_config_port,
.release_port = msm_hs_release_port,
.request_port = msm_hs_request_port,
+ .flush_buffer = msm_hs_flush_buffer,
};
module_init(msm_serial_hs_init);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index a292416..494ec49 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1405,7 +1405,6 @@
* (This can't be done in usb_resume_interface()
* above because it doesn't own the right set of locks.)
*/
- pm_runtime_get_sync(dev->parent);
status = usb_resume_both(udev, msg);
if (status == 0) {
pm_runtime_disable(dev);
@@ -1413,7 +1412,6 @@
pm_runtime_enable(dev);
unbind_no_reset_resume_drivers_interfaces(udev);
}
- pm_runtime_put_sync(dev->parent);
/* Avoid PM error messages for devices disconnected while suspended
* as we'll display regular disconnect messages just a bit later.
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index cd02489..7b616e4 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -81,7 +81,6 @@
struct wake_lock wlock;
int peripheral_status_irq;
int wakeup_irq;
- int wakeup_gpio;
bool wakeup_irq_enabled;
atomic_t pm_usage_cnt;
uint32_t bus_perf_client;
@@ -525,22 +524,11 @@
if (rc < 0) {
dev_err(mehci->dev, "gpio request failed for HSIC DATA\n");
goto free_strobe;
- }
-
- if (mehci->wakeup_gpio) {
- rc = gpio_request(mehci->wakeup_gpio, "HSIC_WAKEUP_GPIO");
- if (rc < 0) {
- dev_err(mehci->dev, "gpio request failed for HSIC WAKEUP\n");
- goto free_data;
- }
}
return 0;
free_gpio:
- if (mehci->wakeup_gpio)
- gpio_free(mehci->wakeup_gpio);
-free_data:
gpio_free(pdata->data);
free_strobe:
gpio_free(pdata->strobe);
@@ -1596,10 +1584,9 @@
if (res)
mehci->peripheral_status_irq = res->start;
- res = platform_get_resource_byname(pdev, IORESOURCE_IO, "wakeup");
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "wakeup");
if (res) {
- mehci->wakeup_gpio = res->start;
- mehci->wakeup_irq = MSM_GPIO_TO_INT(res->start);
+ mehci->wakeup_irq = res->start;
dev_dbg(mehci->dev, "wakeup_irq: %d\n", mehci->wakeup_irq);
}
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index d10c692..d04c234 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -82,6 +82,11 @@
MODULE_PARM_DESC(override_phy_init,
"Override HSUSB PHY Init Settings");
+unsigned int lpm_disconnect_thresh = 1000;
+module_param(lpm_disconnect_thresh , uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(lpm_disconnect_thresh,
+ "Delay before entering LPM on USB disconnect");
+
static DECLARE_COMPLETION(pmic_vbus_init);
static struct msm_otg *the_msm_otg;
static bool debug_aca_enabled;
@@ -2440,7 +2445,12 @@
msm_otg_notify_charger(motg, 0);
msm_otg_reset(otg->phy);
pm_runtime_put_noidle(otg->phy->dev);
- pm_runtime_suspend(otg->phy->dev);
+ /*
+ * Only if autosuspend was enabled in probe, it will be
+ * used here. Otherwise, no delay will be used.
+ */
+ pm_runtime_mark_last_busy(otg->phy->dev);
+ pm_runtime_autosuspend(otg->phy->dev);
}
break;
case OTG_STATE_B_SRP_INIT:
@@ -3989,6 +3999,12 @@
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ if (motg->pdata->delay_lpm_on_disconnect) {
+ pm_runtime_set_autosuspend_delay(&pdev->dev,
+ lpm_disconnect_thresh);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ }
+
if (motg->pdata->bus_scale_table) {
motg->bus_perf_client =
msm_bus_scale_register_client(motg->pdata->bus_scale_table);
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 9f30041..7a92645 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -3022,8 +3022,6 @@
hdmi_msm_state->hdcp_activating = TRUE;
mutex_unlock(&hdmi_msm_state_mutex);
- fill_black_screen();
-
mutex_lock(&hdcp_auth_state_mutex);
/*
* Initialize this to zero here to make
@@ -3065,8 +3063,6 @@
if (ret)
goto error;
- unfill_black_screen();
-
mutex_lock(&hdmi_msm_state_mutex);
hdmi_msm_state->hdcp_activating = FALSE;
mutex_unlock(&hdmi_msm_state_mutex);
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 985d0b0..0c526fd 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -1452,6 +1452,11 @@
vsync_period);
if (diff_to_next > vsync_period)
return;
+ pr_debug("%s cur_time %d, pre_vsync %d, to_next %d\n",
+ __func__,
+ (int)ktime_to_ms(cur_time),
+ (int)ktime_to_ms(pre_vsync),
+ diff_to_next);
wakeup_time = ktime_add_ns(cur_time, diff_to_next * NSEC_PER_MSEC);
activate_event_timer(mfd->cpu_pm_hdl, wakeup_time);
}
@@ -3114,7 +3119,7 @@
{
mdp_suspend_sub();
#ifdef CONFIG_FB_MSM_DTV
- mdp4_dtv_set_black_screen();
+ mdp4_dtv_set_black_screen(FALSE);
#endif
mdp_footswitch_ctrl(FALSE);
}
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 3ea196a..d9da6f9 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -531,7 +531,7 @@
}
#endif /* CONFIG_FB_MSM_DTV */
-void mdp4_dtv_set_black_screen(void);
+void mdp4_dtv_set_black_screen(bool commit);
int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd,
struct mdp4_overlay_pipe *pipe);
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index c76e32f..11952f3 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -416,53 +416,34 @@
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
}
-#ifdef CONFIG_FB_MSM_HDMI_3D
-void unfill_black_screen(void) { return; }
-#else
-void unfill_black_screen(void)
+void fill_black_screen(bool on, uint8 pipe_num, uint8 mixer_num)
{
- uint32 temp_src_format;
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- /*
- * VG2 Constant Color
- */
- temp_src_format = inpdw(MDP_BASE + 0x30050);
- MDP_OUTP(MDP_BASE + 0x30050, temp_src_format&(~BIT(22)));
- /*
- * MDP_OVERLAY_REG_FLUSH
- */
- MDP_OUTP(MDP_BASE + 0x18000, BIT(3));
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
- return;
-}
-#endif
+ uint32 reg_base = 0x010000;
+ uint32 const_color_reg = reg_base * (pipe_num + 2) + 0x1008;
+ uint32 src_fmt_reg = reg_base * (pipe_num + 2) + 0x50;
+ uint32 color = 0x00000000;
+ uint32 temp_src_format = 0x00000000;
+ uint8 bit = pipe_num + 2;
-#ifdef CONFIG_FB_MSM_HDMI_3D
-void fill_black_screen(void) { return; }
-#else
-void fill_black_screen(void)
-{
- /*Black color*/
- uint32 color = 0x00000000;
- uint32 temp_src_format;
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- /*
- * VG2 Constant Color
- */
- MDP_OUTP(MDP_BASE + 0x31008, color);
- /*
- * MDP_VG2_SRC_FORMAT
- */
- temp_src_format = inpdw(MDP_BASE + 0x30050);
- MDP_OUTP(MDP_BASE + 0x30050, temp_src_format | BIT(22));
- /*
- * MDP_OVERLAY_REG_FLUSH
- */
- MDP_OUTP(MDP_BASE + 0x18000, BIT(3));
+
+ /* Fill constant color */
+ MDP_OUTP(MDP_BASE + const_color_reg, color);
+
+ /* Update source format for pipe */
+ temp_src_format = inpdw(MDP_BASE + src_fmt_reg);
+
+ if (on)
+ MDP_OUTP(MDP_BASE + src_fmt_reg, temp_src_format | BIT(22));
+ else
+ MDP_OUTP(MDP_BASE + src_fmt_reg, temp_src_format | (~BIT(22)));
+
+ /* MDP_OVERLAY_REG_FLUSH for pipe*/
+ MDP_OUTP(MDP_BASE + 0x18000, BIT(bit) | BIT(mixer_num));
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
return;
}
-#endif
void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe)
{
@@ -3132,6 +3113,9 @@
__func__);
}
+ if (hdmi_prim_display)
+ fill_black_screen(FALSE, pipe->pipe_num, pipe->mixer_num);
+
mdp4_overlay_mdp_pipe_req(pipe, mfd);
mutex_unlock(&mfd->dma->ov_mutex);
@@ -3204,8 +3188,12 @@
if (pipe->mixer_num == MDP4_MIXER0) {
} else { /* mixer1, DTV, ATV */
- if (ctrl->panel_mode & MDP4_PANEL_DTV)
+ if (ctrl->panel_mode & MDP4_PANEL_DTV) {
+ if (hdmi_prim_display)
+ fill_black_screen(TRUE, pipe->pipe_num,
+ pipe->mixer_num);
mdp4_overlay_dtv_unset(mfd, pipe);
+ }
}
mdp4_stat.overlay_unset[pipe->mixer_num]++;
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index 3ae05c1..0015403 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -70,6 +70,7 @@
struct vsync_update vlist[2];
int vsync_enabled;
int clk_enabled;
+ int new_update;
int clk_control;
ktime_t vsync_time;
struct work_struct clk_work;
@@ -272,6 +273,8 @@
pipe = vctrl->base_pipe;
mixer = pipe->mixer_num;
+ mdp_update_pm(vctrl->mfd, vctrl->vsync_time);
+
if (vp->update_cnt == 0) {
mutex_unlock(&vctrl->update_lock);
return cnt;
@@ -421,6 +424,7 @@
mipi_dsi_clk_cfg(1);
mdp_clk_ctrl(1);
vctrl->clk_enabled = 1;
+ vctrl->new_update = 1;
clk_set_on = 1;
}
if (clk_set_on) {
@@ -517,6 +521,12 @@
spin_lock(&vctrl->spin_lock);
vctrl->vsync_time = ktime_get();
+ if (vctrl->new_update) {
+ vctrl->new_update = 0;
+ spin_unlock(&vctrl->spin_lock);
+ return;
+ }
+
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 5551c9d..501c4e6 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -56,6 +56,7 @@
int wait_vsync_cnt;
int blt_change;
int blt_free;
+ int blt_ctrl;
int sysfs_created;
struct mutex update_lock;
struct completion ov_comp;
@@ -498,9 +499,11 @@
int ret = 0;
int cndx = 0;
struct vsycn_ctrl *vctrl;
+ struct msm_panel_info *pinfo;
vctrl = &vsync_ctrl_db[cndx];
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+ pinfo = &mfd->panel_info;
if (!mfd)
return -ENODEV;
@@ -510,6 +513,7 @@
vctrl->mfd = mfd;
vctrl->dev = mfd->fbi->dev;
+ vctrl->blt_ctrl = pinfo->lcd.blt_ctrl;
/* mdp clock on */
mdp_clk_ctrl(1);
@@ -1011,6 +1015,7 @@
int cndx = 0;
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ long long vtime;
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
@@ -1047,8 +1052,32 @@
spin_unlock_irqrestore(&vctrl->spin_lock, flag);
return;
}
-
spin_unlock_irqrestore(&vctrl->spin_lock, flag);
+
+ if (vctrl->blt_ctrl == BLT_SWITCH_TG_OFF) {
+ int tg_enabled;
+
+ vctrl->blt_change = 0;
+ tg_enabled = inpdw(MDP_BASE + DSI_VIDEO_BASE) & 0x01;
+ if (tg_enabled) {
+ mdp4_dsi_video_wait4vsync(0, &vtime);
+ MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
+ mdp4_dsi_video_wait4dmap_done(0);
+ }
+ mdp4_overlayproc_cfg(pipe);
+ mdp4_overlay_dmap_xy(pipe);
+ if (tg_enabled) {
+ /*
+ * need wait for more than 1 ms to
+ * make sure dsi lanes' fifo is empty and
+ * lanes in stop state befroe reset
+ * controller
+ */
+ usleep(2000);
+ mipi_dsi_sw_reset();
+ MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1);
+ }
+ }
}
void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd,
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index e71f49f..2d48781 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -1004,7 +1004,7 @@
spin_unlock(&vctrl->spin_lock);
}
-void mdp4_dtv_set_black_screen(void)
+void mdp4_dtv_set_black_screen(bool commit)
{
char *rgb_base;
/*Black color*/
@@ -1015,26 +1015,34 @@
vctrl = &vsync_ctrl_db[cndx];
if (vctrl->base_pipe == NULL || !hdmi_prim_display) {
- pr_err("dtv_pipe is not configured yet\n");
+ pr_debug("dtv_pipe is not configured yet\n");
return;
}
- rgb_base = MDP_BASE + MDP4_RGB_BASE;
- rgb_base += (MDP4_RGB_OFF * vctrl->base_pipe->pipe_num);
+ rgb_base = MDP_BASE;
+ rgb_base += (MDP4_RGB_OFF * (vctrl->base_pipe->pipe_num + 2));
- /*
- * RGB Constant Color
- */
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ /* RGB Constant Color */
MDP_OUTP(rgb_base + 0x1008, color);
- /*
- * MDP_RGB_SRC_FORMAT
- */
+
+ /* MDP_RGB_SRC_FORMAT */
temp_src_format = inpdw(rgb_base + 0x0050);
MDP_OUTP(rgb_base + 0x0050, temp_src_format | BIT(22));
- mdp4_overlay_reg_flush(vctrl->base_pipe, 1);
- mdp4_mixer_stage_up(vctrl->base_pipe, 0);
- mdp4_mixer_stage_commit(vctrl->base_pipe->mixer_num);
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ if (commit) {
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+ mdp4_overlay_reg_flush(vctrl->base_pipe, 1);
+
+ mdp4_mixer_stage_up(vctrl->base_pipe, 0);
+ mdp4_mixer_stage_commit(vctrl->base_pipe->mixer_num);
+ } else {
+ /* MDP_OVERLAY_REG_FLUSH for pipe*/
+ MDP_OUTP(MDP_BASE + 0x18000,
+ BIT(vctrl->base_pipe->pipe_num + 2) | BIT(MDP4_MIXER1));
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ }
}
static void mdp4_dtv_do_blt(struct msm_fb_data_type *mfd, int enable)
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index a8f853d..d932bc9 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -58,6 +58,7 @@
static int hdmi_tx_sysfs_enable_hpd(struct hdmi_tx_ctrl *hdmi_ctrl, int on);
static irqreturn_t hdmi_tx_isr(int irq, void *data);
+static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl);
struct mdss_hw hdmi_tx_hw = {
.hw_ndx = MDSS_HW_HDMI,
@@ -1082,6 +1083,8 @@
} else {
msm_dss_enable_clk(power_data->clk_config,
power_data->num_clk, 0);
+ msm_dss_clk_set_rate(power_data->clk_config,
+ power_data->num_clk);
msm_dss_enable_gpio(power_data->gpio_config,
power_data->num_gpio, 0);
msm_dss_enable_vreg(power_data->vreg_config,
@@ -1616,6 +1619,7 @@
bool polarity)
{
struct dss_io_data *io = NULL;
+ u32 cable_sense;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -1632,7 +1636,12 @@
else
DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(2));
- if ((DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) == polarity) {
+ cable_sense = (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1;
+ DEV_DBG("%s: listen = %s, sense = %s\n", __func__,
+ polarity ? "connect" : "disconnect",
+ cable_sense ? "connect" : "disconnect");
+
+ if (cable_sense == polarity) {
u32 reg_val = DSS_REG_R(io, HDMI_HPD_CTRL);
/* Toggle HPD circuit to trigger HPD sense */
@@ -1667,13 +1676,25 @@
}
hdmi_tx_powerdown_phy(hdmi_ctrl);
+
+ /*
+ * this is needed to avoid pll lock failure due to
+ * clk framework's rate caching.
+ */
+ hdmi_ctrl->pdata.power_data[HDMI_TX_CORE_PM].clk_config[0].rate = 0;
+
hdmi_tx_core_off(hdmi_ctrl);
mutex_lock(&hdmi_ctrl->mutex);
hdmi_ctrl->panel_power_on = false;
mutex_unlock(&hdmi_ctrl->mutex);
- hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_CONNECT_POLARITY);
+ if (hdmi_ctrl->hpd_off_pending) {
+ hdmi_tx_hpd_off(hdmi_ctrl);
+ hdmi_ctrl->hpd_off_pending = false;
+ } else {
+ hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_CONNECT_POLARITY);
+ }
DEV_INFO("%s: HDMI Core: OFF\n", __func__);
} /* hdmi_tx_power_off_work */
@@ -1860,10 +1881,20 @@
if (on) {
rc = hdmi_tx_hpd_on(hdmi_ctrl);
} else {
- hdmi_tx_hpd_off(hdmi_ctrl);
- switch_set_state(&hdmi_ctrl->sdev, 0);
- DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
- hdmi_ctrl->sdev.state);
+ /* If power down is already underway, wait for it to finish */
+ flush_work_sync(&hdmi_ctrl->power_off_work);
+
+ if (!hdmi_ctrl->panel_power_on) {
+ hdmi_tx_hpd_off(hdmi_ctrl);
+ } else {
+ hdmi_ctrl->hpd_off_pending = true;
+
+ switch_set_state(&hdmi_ctrl->sdev, 0);
+ DEV_DBG("%s: Hdmi state switch to %d\n", __func__,
+ hdmi_ctrl->sdev.state);
+ DEV_DBG("HDMI HPD: sent fake OFFLINE event\n");
+ kobject_uevent(hdmi_ctrl->kobj, KOBJ_OFFLINE);
+ }
}
return rc;
@@ -1954,6 +1985,8 @@
init_completion(&hdmi_ctrl->ddc_ctrl.ddc_sw_done);
hdmi_ctrl->hpd_state = false;
+ hdmi_ctrl->hpd_initialized = false;
+ hdmi_ctrl->hpd_off_pending = false;
INIT_WORK(&hdmi_ctrl->hpd_int_work, hdmi_tx_hpd_int_work);
INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index 3e9fd3c..ce19355 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -54,6 +54,7 @@
u32 hpd_initialized;
u32 hpd_state;
+ u32 hpd_off_pending;
u32 hpd_feature_on;
struct work_struct hpd_int_work;
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 3db13f5..b5f6ddf 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -474,15 +474,25 @@
if (ctl->intf_num == MDSS_MDP_NO_INTF) {
ctl->dst_format = mfd->panel_info.out_format;
} else {
+ struct mdp_dither_cfg_data dither = {
+ .block = mfd->index + MDP_LOGICAL_BLOCK_DISP_0,
+ .flags = MDP_PP_OPS_DISABLE,
+ };
+
switch (mfd->panel_info.bpp) {
case 18:
ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666;
+ dither.flags = MDP_PP_OPS_ENABLE | MDP_PP_OPS_WRITE;
+ dither.g_y_depth = 2;
+ dither.r_cr_depth = 2;
+ dither.b_cb_depth = 2;
break;
case 24:
default:
ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB888;
break;
}
+ mdss_mdp_dither_config(&dither, NULL);
}
if (ctl->mixer_right) {
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 3e5df2c..e4621d4 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -355,6 +355,9 @@
{
int ret;
+ if (!mfd->panel_power_on)
+ return -EPERM;
+
if (req->flags & MDSS_MDP_ROT_ONLY) {
ret = mdss_mdp_overlay_rotator_setup(mfd, req);
} else {
@@ -482,6 +485,9 @@
return ret;
}
+ if (!mfd->ctl->power_on)
+ return 0;
+
for (i = 0; unset_ndx != ndx && i < MDSS_MDP_MAX_SSPP; i++) {
pipe_ndx = BIT(i);
if (pipe_ndx & ndx) {
@@ -631,6 +637,9 @@
pr_debug("play req id=%x\n", req->id);
+ if (!mfd->panel_power_on)
+ return -EPERM;
+
if (req->id & MDSS_MDP_ROT_SESSION_MASK)
ret = mdss_mdp_overlay_rotate(mfd, req);
else
@@ -974,7 +983,7 @@
}
if (ret) {
- pr_err("OVERLAY_GET failed (%d)\n", ret);
+ pr_debug("OVERLAY_GET failed (%d)\n", ret);
ret = -EFAULT;
}
break;
@@ -988,7 +997,7 @@
ret = copy_to_user(argp, &req, sizeof(req));
}
if (ret) {
- pr_err("OVERLAY_SET failed (%d)\n", ret);
+ pr_debug("OVERLAY_SET failed (%d)\n", ret);
ret = -EFAULT;
}
break;
@@ -1020,7 +1029,7 @@
}
if (ret) {
- pr_err("OVERLAY_PLAY failed (%d)\n", ret);
+ pr_debug("OVERLAY_PLAY failed (%d)\n", ret);
ret = -EFAULT;
}
} else {
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 527b02f..5189b6d 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -653,12 +653,6 @@
struct msm_fb_panel_data *pdata = NULL;
int ret = 0;
- if (hdmi_prim_display) {
- MSM_FB_INFO("%s: hdmi primary handles early suspend only\n",
- __func__);
- return 0;
- }
-
if ((!mfd) || (mfd->key != MFD_KEY))
return 0;
@@ -684,12 +678,6 @@
struct msm_fb_panel_data *pdata = NULL;
int ret = 0;
- if (hdmi_prim_display) {
- MSM_FB_INFO("%s: hdmi primary handles early resume only\n",
- __func__);
- return 0;
- }
-
if ((!mfd) || (mfd->key != MFD_KEY))
return 0;
@@ -714,8 +702,7 @@
.runtime_suspend = msm_fb_runtime_suspend,
.runtime_resume = msm_fb_runtime_resume,
.runtime_idle = msm_fb_runtime_idle,
-#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL) && \
- !defined(CONFIG_FB_MSM_HDMI_AS_PRIMARY))
+#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL))
.suspend = msm_fb_ext_suspend,
.resume = msm_fb_ext_resume,
#endif
@@ -749,9 +736,7 @@
static void msmfb_early_suspend(struct early_suspend *h)
{
struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
- early_suspend);
- struct msm_fb_panel_data *pdata = NULL;
-
+ early_suspend);
#if defined(CONFIG_FB_MSM_MDP303)
/*
* For MDP with overlay, set framebuffer with black pixels
@@ -769,37 +754,12 @@
}
#endif
msm_fb_suspend_sub(mfd);
-
- pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
- if (hdmi_prim_display &&
- (mfd->panel_info.type == HDMI_PANEL ||
- mfd->panel_info.type == DTV_PANEL)) {
- /* Turn off the HPD circuitry */
- if (pdata->power_ctrl) {
- MSM_FB_INFO("%s: Turning off HPD circuitry\n",
- __func__);
- pdata->power_ctrl(FALSE);
- }
- }
}
static void msmfb_early_resume(struct early_suspend *h)
{
struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
- early_suspend);
- struct msm_fb_panel_data *pdata = NULL;
-
- pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
- if (hdmi_prim_display &&
- (mfd->panel_info.type == HDMI_PANEL ||
- mfd->panel_info.type == DTV_PANEL)) {
- /* Turn on the HPD circuitry */
- if (pdata->power_ctrl) {
- MSM_FB_INFO("%s: Turning on HPD circuitry\n", __func__);
- pdata->power_ctrl(TRUE);
- }
- }
-
+ early_suspend);
msm_fb_resume_sub(mfd);
}
#endif
@@ -1510,10 +1470,7 @@
ret = 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
-
- if (hdmi_prim_display ||
- (mfd->panel_info.type != DTV_PANEL &&
- mfd->panel_info.type != WRITEBACK_PANEL)) {
+ if (mfd->panel_info.type != DTV_PANEL) {
mfd->early_suspend.suspend = msmfb_early_suspend;
mfd->early_suspend.resume = msmfb_early_resume;
mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2;
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 1594825..7d8ad73 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -214,8 +214,7 @@
void msm_fb_config_backlight(struct msm_fb_data_type *mfd);
#endif
-void fill_black_screen(void);
-void unfill_black_screen(void);
+void fill_black_screen(bool on, uint8 pipe_num, uint8 mixer_num);
int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd,
struct fb_info *info);
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
index 7fc7a2f..bdd4f3bb 100644
--- a/drivers/video/msm/msm_fb_panel.h
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -56,6 +56,11 @@
MAX_PHYS_TARGET_NUM,
} DISP_TARGET_PHYS;
+enum {
+ BLT_SWITCH_TG_OFF,
+ BLT_SWITCH_TG_ON
+};
+
/* panel info type */
struct lcd_panel_info {
__u32 vsync_enable;
@@ -65,6 +70,7 @@
__u32 v_pulse_width;
__u32 hw_vsync_mode;
__u32 vsync_notifier_period;
+ __u32 blt_ctrl;
__u32 rev;
};
diff --git a/include/drm/kgsl_drm.h b/include/drm/kgsl_drm.h
index f1c7f4e..7ffae9d 100644
--- a/include/drm/kgsl_drm.h
+++ b/include/drm/kgsl_drm.h
@@ -19,6 +19,7 @@
#define DRM_KGSL_GEM_UNLOCK_HANDLE 0x0C
#define DRM_KGSL_GEM_UNLOCK_ON_TS 0x0D
#define DRM_KGSL_GEM_CREATE_FD 0x0E
+#define DRM_KGSL_GEM_GET_ION_FD 0x0F
#define DRM_IOCTL_KGSL_GEM_CREATE \
DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_CREATE, struct drm_kgsl_gem_create)
@@ -75,6 +76,10 @@
DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_CREATE_FD, \
struct drm_kgsl_gem_create_fd)
+#define DRM_IOCTL_KGSL_GEM_GET_ION_FD \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_GET_ION_FD, \
+struct drm_kgsl_gem_get_ion_fd)
+
/* Maximum number of sub buffers per GEM object */
#define DRM_KGSL_GEM_MAX_BUFFERS 2
@@ -189,4 +194,9 @@
uint32_t handle;
};
+struct drm_kgsl_gem_get_ion_fd {
+ uint32_t ion_fd;
+ uint32_t handle;
+};
+
#endif
diff --git a/include/linux/mfd/pm8xxx/pm8038.h b/include/linux/mfd/pm8xxx/pm8038.h
index 574dab6..9e25b5c 100644
--- a/include/linux/mfd/pm8xxx/pm8038.h
+++ b/include/linux/mfd/pm8xxx/pm8038.h
@@ -83,6 +83,7 @@
struct pm8921_bms_platform_data *bms_pdata;
struct pm8xxx_adc_platform_data *adc_pdata;
struct pm8xxx_led_platform_data *leds_pdata;
+ struct pm8xxx_vibrator_platform_data *vibrator_pdata;
struct pm8xxx_ccadc_platform_data *ccadc_pdata;
struct pm8xxx_spk_platform_data *spk_pdata;
};
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index d229ad3..1717cd9 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -11,7 +11,7 @@
#include <linux/of.h>
/*
- * irq_of_parse_and_map() is used ba all OF enabled platforms; but SPARC
+ * irq_of_parse_and_map() is used by all OF enabled platforms; but SPARC
* implements it differently. However, the prototype is the same for all,
* so declare it here regardless of the CONFIG_OF_IRQ setting.
*/
@@ -76,5 +76,13 @@
extern void of_irq_init(const struct of_device_id *matches);
#endif /* CONFIG_OF_IRQ */
-#endif /* CONFIG_OF */
+
+#else /* !CONFIG_OF */
+static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
+ int index)
+{
+ return 0;
+}
+#endif /* !CONFIG_OF */
+
#endif /* __OF_IRQ_H */
diff --git a/include/linux/qmi_encdec.h b/include/linux/qmi_encdec.h
new file mode 100644
index 0000000..4c5f6d3
--- /dev/null
+++ b/include/linux/qmi_encdec.h
@@ -0,0 +1,169 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#ifndef _QMI_ENCDEC_H_
+#define _QMI_ENCDEC_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+
+#define QMI_REQUEST_CONTROL_FLAG 0x00
+#define QMI_RESPONSE_CONTROL_FLAG 0x02
+#define QMI_INDICATION_CONTROL_FLAG 0x04
+#define QMI_HEADER_SIZE 7
+
+/**
+ * elem_type - Enum to identify the data type of elements in a data
+ * structure.
+ */
+enum elem_type {
+ QMI_OPT_FLAG = 1,
+ QMI_DATA_LEN,
+ QMI_UNSIGNED_1_BYTE,
+ QMI_UNSIGNED_2_BYTE,
+ QMI_UNSIGNED_4_BYTE,
+ QMI_UNSIGNED_8_BYTE,
+ QMI_SIGNED_2_BYTE_ENUM,
+ QMI_SIGNED_4_BYTE_ENUM,
+ QMI_STRUCT,
+ QMI_EOTI,
+};
+
+/**
+ * array_type - Enum to identify if an element in a data structure is
+ * an array. If so, then is it a static length array or a
+ * variable length array.
+ */
+enum array_type {
+ NO_ARRAY = 0,
+ STATIC_ARRAY = 1,
+ VAR_LEN_ARRAY = 2,
+};
+
+/**
+ * elem_info - Data structure to specify information about an element
+ * in a data structure. An array of this data structure
+ * can be used to specify info about a complex data
+ * structure to be encoded/decoded.
+ *
+ * @data_type: Data type of this element.
+ * @elem_len: Array length of this element, if an array.
+ * @elem_size: Size of a single instance of this data type.
+ * @is_array: Array type of this element.
+ * @tlv_type: QMI message specific type to identify which element
+ * is present in an incoming message.
+ * @offset: To identify the address of the first instance of this
+ * element in the data structure.
+ * @ei_array: Array to provide information about the nested structure
+ * within a data structure to be encoded/decoded.
+ */
+struct elem_info {
+ enum elem_type data_type;
+ uint32_t elem_len;
+ uint32_t elem_size;
+ enum array_type is_array;
+ uint8_t tlv_type;
+ uint32_t offset;
+ struct elem_info *ei_array;
+};
+
+/**
+ * @msg_desc - Describe about the main/outer structure to be
+ * encoded/decoded.
+ *
+ * @max_msg_len: Maximum possible length of the QMI message.
+ * @ei_array: Array to provide information about a data structure.
+ */
+struct msg_desc {
+ uint16_t msg_id;
+ int max_msg_len;
+ struct elem_info *ei_array;
+};
+
+struct qmi_header {
+ unsigned char cntl_flag;
+ uint16_t txn_id;
+ uint16_t msg_id;
+ uint16_t msg_len;
+} __attribute__((__packed__));
+
+static inline void encode_qmi_header(unsigned char *buf,
+ unsigned char cntl_flag, uint16_t txn_id,
+ uint16_t msg_id, uint16_t msg_len)
+{
+ struct qmi_header *hdr = (struct qmi_header *)buf;
+
+ hdr->cntl_flag = cntl_flag;
+ hdr->txn_id = txn_id;
+ hdr->msg_id = msg_id;
+ hdr->msg_len = msg_len;
+}
+
+static inline void decode_qmi_header(unsigned char *buf,
+ unsigned char *cntl_flag, uint16_t *txn_id,
+ uint16_t *msg_id, uint16_t *msg_len)
+{
+ struct qmi_header *hdr = (struct qmi_header *)buf;
+
+ *cntl_flag = hdr->cntl_flag;
+ *txn_id = hdr->txn_id;
+ *msg_id = hdr->msg_id;
+ *msg_len = hdr->msg_len;
+}
+
+#ifdef CONFIG_QMI_ENCDEC
+/**
+ * qmi_kernel_encode() - Encode to QMI message wire format
+ * @desc: Pointer to structure descriptor.
+ * @out_buf: Buffer to hold the encoded QMI message.
+ * @out_buf_len: Length of the out buffer.
+ * @in_c_struct: C Structure to be encoded.
+ *
+ * @return: size of encoded message on success, < 0 on error.
+ */
+int qmi_kernel_encode(struct msg_desc *desc,
+ void *out_buf, uint32_t out_buf_len,
+ void *in_c_struct);
+
+/**
+ * qmi_kernel_decode() - Decode to C Structure format
+ * @desc: Pointer to structure descriptor.
+ * @out_c_struct: Buffer to hold the decoded C structure.
+ * @in_buf: Buffer containg the QMI message to be decoded.
+ * @in_buf_len: Length of the incoming QMI message.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
+ void *in_buf, uint32_t in_buf_len);
+
+#else
+static inline int qmi_kernel_encode(struct msg_desc *desc,
+ void *out_buf, uint32_t out_buf_len,
+ void *in_c_struct)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int qmi_kernel_decode(struct msg_desc *desc,
+ void *out_c_struct,
+ void *in_buf, uint32_t in_buf_len)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+#endif
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index e5516ab..9d5de90 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -137,7 +137,7 @@
};
#define QPNP_ADC_625_UV 625000
-#define QPNP_ADC_HWMON_NAME_LENGTH 16
+#define QPNP_ADC_HWMON_NAME_LENGTH 64
/**
* enum qpnp_adc_decimation_type - Sampling rate supported.
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 1f13da3..0a1428e 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2041,6 +2041,7 @@
extern unsigned int sysctl_sched_min_granularity;
extern unsigned int sysctl_sched_wakeup_granularity;
extern unsigned int sysctl_sched_child_runs_first;
+extern unsigned int sysctl_sched_wake_to_idle;
enum sched_tunable_scaling {
SCHED_TUNABLESCALING_NONE,
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index a998ac2..578b9f9 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -199,6 +199,8 @@
* is connected.
* @core_clk_always_on_workaround: Don't disable core_clk when
* USB enters LPM.
+ * @delay_lpm_on_disconnect: Use a delay before entering LPM
+ * upon USB cable disconnection.
* @bus_scale_table: parameters for bus bandwidth requirements
* @mhl_dev_name: MHL device name used to register with MHL driver.
*/
@@ -218,6 +220,7 @@
bool pnoc_errata_fix;
bool enable_lpm_on_dev_suspend;
bool core_clk_always_on_workaround;
+ bool delay_lpm_on_disconnect;
struct msm_bus_scale_pdata *bus_scale_table;
const char *mhl_dev_name;
};
diff --git a/include/media/vcap_fmt.h b/include/media/vcap_fmt.h
index 13b3f05..84d9b42 100644
--- a/include/media/vcap_fmt.h
+++ b/include/media/vcap_fmt.h
@@ -120,8 +120,14 @@
VP_OUT_TYPE,
};
+enum vcap_stride {
+ VC_STRIDE_16,
+ VC_STRIDE_32,
+};
+
struct vcap_priv_fmt {
enum vcap_type type;
+ enum vcap_stride stride;
union {
struct v4l2_format_vc_ext timing;
struct v4l2_pix_format pix;
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index 2d3b5c5..39aa1b9 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -39,10 +39,10 @@
#define VCAP_USEC (1000000)
-#define VCAP_STRIDE_ALIGN 0x10
-#define VCAP_STRIDE_CALC(x) (((x / VCAP_STRIDE_ALIGN) + \
- (!(!(x % VCAP_STRIDE_ALIGN)))) * \
- VCAP_STRIDE_ALIGN)
+#define VCAP_STRIDE_ALIGN_16 0x10
+#define VCAP_STRIDE_ALIGN_32 0x20
+#define VCAP_STRIDE_CALC(x, align) (((x / align) + \
+ (!(!(x % align)))) * align)
#define VCAP_BASE (dev->vcapbase)
#define VCAP_OFFSET(off) (VCAP_BASE + off)
@@ -241,6 +241,7 @@
enum vcap_op_mode op_mode;
struct v4l2_format_vc_ext vc_format;
+ enum vcap_stride stride;
enum v4l2_buf_type vp_buf_type_field;
struct vp_format_data vp_in_fmt;
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 23c1d51..bfd7208 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -157,6 +157,55 @@
u16 reserved;
} __attribute__ ((packed));
+/*
+ * Opcode for AFE to start DTMF.
+ */
+#define AFE_PORTS_CMD_DTMF_CTL 0x00010102
+
+/** DTMF payload.*/
+struct afe_dtmf_generation_command {
+ struct apr_hdr hdr;
+
+ /*
+ * Duration of the DTMF tone in ms.
+ * -1 -> continuous,
+ * 0 -> disable
+ */
+ int64_t duration_in_ms;
+
+ /*
+ * The DTMF high tone frequency.
+ */
+ uint16_t high_freq;
+
+ /*
+ * The DTMF low tone frequency.
+ */
+ uint16_t low_freq;
+
+ /*
+ * The DTMF volume setting
+ */
+ uint16_t gain;
+
+ /*
+ * The number of ports to enable/disable on.
+ */
+ uint16_t num_ports;
+
+ /*
+ * The Destination ports - array .
+ * For DTMF on multiple ports, portIds needs to
+ * be populated numPorts times.
+ */
+ uint16_t port_ids;
+
+ /*
+ * variable for 32 bit alignment of APR packet.
+ */
+ uint16_t reserved;
+} __packed;
+
#define AFE_PCM_CFG_MODE_PCM 0x0
#define AFE_PCM_CFG_MODE_AUX 0x1
#define AFE_PCM_CFG_SYNC_EXT 0x0
diff --git a/include/sound/q6afe.h b/include/sound/q6afe.h
index 8451ac6..1e12d48 100644
--- a/include/sound/q6afe.h
+++ b/include/sound/q6afe.h
@@ -88,7 +88,10 @@
int afe_cmd_memory_map_nowait(u32 dma_addr_p, u32 dma_buf_sz);
int afe_cmd_memory_unmap(u32 dma_addr_p);
int afe_cmd_memory_unmap_nowait(u32 dma_addr_p);
-
+void afe_set_dtmf_gen_rx_portid(u16 rx_port_id, int set);
+int afe_dtmf_generate_rx(int64_t duration_in_ms,
+ uint16_t high_freq,
+ uint16_t low_freq, uint16_t gain);
int afe_register_get_events(u16 port_id,
void (*cb) (uint32_t opcode,
uint32_t token, uint32_t *payload, void *priv),
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 9885a9e..b175073 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -77,6 +77,14 @@
unsigned int sysctl_sched_child_runs_first __read_mostly;
/*
+ * Controls whether, when SD_SHARE_PKG_RESOURCES is on, if all
+ * tasks go to idle CPUs when woken. If this is off, note that the
+ * per-task flag PF_WAKE_ON_IDLE can still cause a task to go to an
+ * idle CPU upon being woken.
+ */
+unsigned int __read_mostly sysctl_sched_wake_to_idle;
+
+/*
* SCHED_OTHER wake-up granularity.
* (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds)
*
@@ -2654,7 +2662,8 @@
if (target == prev_cpu && idle_cpu(prev_cpu))
return prev_cpu;
- if (!(current->flags & PF_WAKE_UP_IDLE) &&
+ if (!sysctl_sched_wake_to_idle &&
+ !(current->flags & PF_WAKE_UP_IDLE) &&
!(p->flags & PF_WAKE_UP_IDLE))
return target;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index b693142..b390dad 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -264,6 +264,13 @@
.mode = 0644,
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "sched_wake_to_idle",
+ .data = &sysctl_sched_wake_to_idle,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
#ifdef CONFIG_SCHED_DEBUG
{
.procname = "sched_min_granularity_ns",
diff --git a/lib/Kconfig b/lib/Kconfig
index 4a8aba2..f1621d5 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -378,4 +378,13 @@
Digital signature verification. Currently only RSA is supported.
Implementation is done using GnuPG MPI library
+config QMI_ENCDEC
+ bool
+ help
+ Library to encode & decode QMI messages from within
+ the kernel. The kernel drivers encode the C structure into
+ QMI message wire format and then send it over a transport.
+ The kernel drivers receive the QMI message over a transport
+ and then decode it into a C structure.
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index acd6869..0fbcb04 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -123,6 +123,8 @@
obj-$(CONFIG_CLZ_TAB) += clz_tab.o
+obj-$(CONFIG_QMI_ENCDEC) += qmi_encdec.o
+
hostprogs-y := gen_crc32table
clean-files := crc32table.h
diff --git a/lib/qmi_encdec.c b/lib/qmi_encdec.c
new file mode 100644
index 0000000..d759885
--- /dev/null
+++ b/lib/qmi_encdec.c
@@ -0,0 +1,492 @@
+/* Copyright (c) 2012, 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/slab.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/qmi_encdec.h>
+
+#include "qmi_encdec_priv.h"
+
+#define TLV_LEN_SIZE sizeof(uint16_t)
+#define TLV_TYPE_SIZE sizeof(uint8_t)
+
+static int _qmi_kernel_encode(struct elem_info *ei_array,
+ void *out_buf, void *in_c_struct,
+ int enc_level);
+
+static int _qmi_kernel_decode(struct elem_info *ei_array,
+ void *out_c_struct,
+ void *in_buf, uint32_t in_buf_len,
+ int dec_level);
+
+/**
+ * qmi_kernel_encode() - Encode to QMI message wire format
+ * @desc: Pointer to structure descriptor.
+ * @out_buf: Buffer to hold the encoded QMI message.
+ * @out_buf_len: Length of the out buffer.
+ * @in_c_struct: C Structure to be encoded.
+ *
+ * @return: size of encoded message on success, < 0 for error.
+ */
+int qmi_kernel_encode(struct msg_desc *desc,
+ void *out_buf, uint32_t out_buf_len,
+ void *in_c_struct)
+{
+ int enc_level = 1;
+
+ if (!desc || !desc->ei_array)
+ return -EINVAL;
+
+ if (!out_buf || !in_c_struct)
+ return -EINVAL;
+
+ if (desc->max_msg_len < out_buf_len)
+ return -ETOOSMALL;
+
+ return _qmi_kernel_encode(desc->ei_array, out_buf,
+ in_c_struct, enc_level);
+}
+EXPORT_SYMBOL(qmi_kernel_encode);
+
+/**
+ * qmi_encode_basic_elem() - Encodes elements of basic/primary data type
+ * @buf_dst: Buffer to store the encoded information.
+ * @buf_src: Buffer containing the elements to be encoded.
+ * @elem_len: Number of elements, in the buf_src, to be encoded.
+ * @elem_size: Size of a single instance of the element to be encoded.
+ *
+ * @return: number of bytes of encoded information.
+ *
+ * This function encodes the "elem_len" number of data elements, each of
+ * size "elem_size" bytes from the source buffer "buf_src" and stores the
+ * encoded information in the destination buffer "buf_dst". The elements are
+ * of primary data type which include uint8_t - uint64_t or similar. This
+ * function returns the number of bytes of encoded information.
+ */
+static int qmi_encode_basic_elem(void *buf_dst, void *buf_src,
+ uint32_t elem_len, uint32_t elem_size)
+{
+ uint32_t i, rc = 0;
+
+ for (i = 0; i < elem_len; i++) {
+ QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size);
+ rc += elem_size;
+ }
+
+ return rc;
+}
+
+/**
+ * qmi_encode_struct_elem() - Encodes elements of struct data type
+ * @ei_array: Struct info array descibing the struct element.
+ * @buf_dst: Buffer to store the encoded information.
+ * @buf_src: Buffer containing the elements to be encoded.
+ * @elem_len: Number of elements, in the buf_src, to be encoded.
+ * @enc_level: Depth of the nested structure from the main structure.
+ *
+ * @return: Mumber of bytes of encoded information, on success.
+ * < 0 on error.
+ *
+ * This function encodes the "elem_len" number of struct elements, each of
+ * size "ei_array->elem_size" bytes from the source buffer "buf_src" and
+ * stores the encoded information in the destination buffer "buf_dst". The
+ * elements are of struct data type which includes any C structure. This
+ * function returns the number of bytes of encoded information.
+ */
+static int qmi_encode_struct_elem(struct elem_info *ei_array,
+ void *buf_dst, void *buf_src,
+ uint32_t elem_len, int enc_level)
+{
+ int i, rc, encoded_bytes = 0;
+ struct elem_info *temp_ei = ei_array;
+
+ for (i = 0; i < elem_len; i++) {
+ rc = _qmi_kernel_encode(temp_ei->ei_array,
+ buf_dst, buf_src, enc_level);
+ if (rc < 0) {
+ pr_err("%s: STRUCT Encode failure\n", __func__);
+ return rc;
+ }
+ buf_dst = buf_dst + rc;
+ buf_src = buf_src + temp_ei->elem_size;
+ encoded_bytes += rc;
+ }
+
+ return encoded_bytes;
+}
+
+/**
+ * skip_to_next_elem() - Skip to next element in the structure to be encoded
+ * @ei_array: Struct info describing the element to be skipped.
+ *
+ * @return: Struct info of the next element that can be encoded.
+ *
+ * This function is used while encoding optional elements. If the flag
+ * corresponding to an optional element is not set, then encoding the
+ * optional element can be skipped. This function can be used to perform
+ * that operation.
+ */
+static struct elem_info *skip_to_next_elem(struct elem_info *ei_array)
+{
+ struct elem_info *temp_ei = ei_array;
+ uint8_t tlv_type;
+
+ do {
+ tlv_type = temp_ei->tlv_type;
+ temp_ei = temp_ei + 1;
+ } while (tlv_type == temp_ei->tlv_type);
+
+ return temp_ei;
+}
+
+/**
+ * _qmi_kernel_encode() - Core Encode Function
+ * @ei_array: Struct info array describing the structure to be encoded.
+ * @out_buf: Buffer to hold the encoded QMI message.
+ * @in_c_struct: Pointer to the C structure to be encoded.
+ * @enc_level: Encode level to indicate the depth of the nested structure,
+ * within the main structure, being encoded.
+ *
+ * @return: Number of bytes of encoded information, on success.
+ * < 0 on error.
+ */
+static int _qmi_kernel_encode(struct elem_info *ei_array,
+ void *out_buf, void *in_c_struct,
+ int enc_level)
+{
+ struct elem_info *temp_ei = ei_array;
+ uint8_t opt_flag_value = 0;
+ uint32_t data_len_value = 0, data_len_sz;
+ uint8_t *buf_dst = (uint8_t *)out_buf;
+ uint8_t *tlv_pointer;
+ uint32_t tlv_len;
+ uint8_t tlv_type;
+ uint32_t encoded_bytes = 0;
+ void *buf_src;
+ int encode_tlv = 0;
+ int rc;
+
+ tlv_pointer = buf_dst;
+ tlv_len = 0;
+ buf_dst = buf_dst + (TLV_LEN_SIZE + TLV_TYPE_SIZE);
+
+ while (temp_ei->data_type != QMI_EOTI) {
+ buf_src = in_c_struct + temp_ei->offset;
+ tlv_type = temp_ei->tlv_type;
+
+ if (temp_ei->is_array == NO_ARRAY) {
+ data_len_value = 1;
+ } else if (temp_ei->is_array == STATIC_ARRAY) {
+ data_len_value = temp_ei->elem_len;
+ } else if (data_len_value <= 0 ||
+ temp_ei->elem_len < data_len_value) {
+ pr_err("%s: Invalid data length\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (temp_ei->data_type) {
+ case QMI_OPT_FLAG:
+ rc = qmi_encode_basic_elem(&opt_flag_value, buf_src,
+ 1, sizeof(uint8_t));
+ if (opt_flag_value)
+ temp_ei = temp_ei + 1;
+ else
+ temp_ei = skip_to_next_elem(temp_ei);
+ break;
+
+ case QMI_DATA_LEN:
+ memcpy(&data_len_value, buf_src, temp_ei->elem_size);
+ data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
+ sizeof(uint8_t) : sizeof(uint16_t);
+ rc = qmi_encode_basic_elem(buf_dst, &data_len_value,
+ 1, data_len_sz);
+ if (data_len_value) {
+ UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
+ encoded_bytes, tlv_len, encode_tlv, rc);
+ encode_tlv = 0;
+ } else {
+ temp_ei = skip_to_next_elem(temp_ei);
+ }
+ break;
+
+ case QMI_UNSIGNED_1_BYTE:
+ case QMI_UNSIGNED_2_BYTE:
+ case QMI_UNSIGNED_4_BYTE:
+ case QMI_UNSIGNED_8_BYTE:
+ case QMI_SIGNED_2_BYTE_ENUM:
+ case QMI_SIGNED_4_BYTE_ENUM:
+ rc = qmi_encode_basic_elem(buf_dst, buf_src,
+ data_len_value, temp_ei->elem_size);
+ UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
+ encoded_bytes, tlv_len, encode_tlv, rc);
+ break;
+
+ case QMI_STRUCT:
+ rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src,
+ data_len_value, (enc_level + 1));
+ if (rc < 0)
+ return rc;
+ UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
+ encoded_bytes, tlv_len, encode_tlv, rc);
+ break;
+
+ default:
+ pr_err("%s: Unrecognized data type\n", __func__);
+ return -EINVAL;
+
+ }
+
+ if (encode_tlv && enc_level == 1) {
+ QMI_ENCDEC_ENCODE_TLV(tlv_type, tlv_len, tlv_pointer);
+ encoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+ tlv_pointer = buf_dst;
+ tlv_len = 0;
+ buf_dst = buf_dst + TLV_LEN_SIZE + TLV_TYPE_SIZE;
+ encode_tlv = 0;
+ }
+ }
+ return encoded_bytes;
+}
+
+/**
+ * qmi_kernel_decode() - Decode to C Structure format
+ * @desc: Pointer to structure descriptor.
+ * @out_c_struct: Buffer to hold the decoded C structure.
+ * @in_buf: Buffer containg the QMI message to be decoded.
+ * @in_buf_len: Length of the incoming QMI message.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
+ void *in_buf, uint32_t in_buf_len)
+{
+ int dec_level = 1;
+ int rc = 0;
+
+ if (!desc || !desc->ei_array)
+ return -EINVAL;
+
+ if (!out_c_struct || !in_buf || !in_buf_len)
+ return -EINVAL;
+
+ if (desc->max_msg_len < in_buf_len)
+ return -EINVAL;
+
+ rc = _qmi_kernel_decode(desc->ei_array, out_c_struct,
+ in_buf, in_buf_len, dec_level);
+ if (rc < 0)
+ return rc;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(qmi_kernel_decode);
+
+/**
+ * qmi_decode_basic_elem() - Decodes elements of basic/primary data type
+ * @buf_dst: Buffer to store the decoded element.
+ * @buf_src: Buffer containing the elements in QMI wire format.
+ * @elem_len: Number of elements to be decoded.
+ * @elem_size: Size of a single instance of the element to be decoded.
+ *
+ * @return: Total size of the decoded data elements, in bytes.
+ *
+ * This function decodes the "elem_len" number of elements in QMI wire format,
+ * each of size "elem_size" bytes from the source buffer "buf_src" and stores
+ * the decoded elements in the destination buffer "buf_dst". The elements are
+ * of primary data type which include uint8_t - uint64_t or similar. This
+ * function returns the number of bytes of decoded information.
+ */
+static int qmi_decode_basic_elem(void *buf_dst, void *buf_src,
+ uint32_t elem_len, uint32_t elem_size)
+{
+ uint32_t i, rc = 0;
+
+ for (i = 0; i < elem_len; i++) {
+ QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size);
+ rc += elem_size;
+ }
+
+ return rc;
+}
+
+/**
+ * qmi_decode_struct_elem() - Decodes elements of struct data type
+ * @ei_array: Struct info array descibing the struct element.
+ * @buf_dst: Buffer to store the decoded element.
+ * @buf_src: Buffer containing the elements in QMI wire format.
+ * @elem_len: Number of elements to be decoded.
+ * @tlv_len: Total size of the encoded inforation corresponding to
+ * this struct element.
+ * @dec_level: Depth of the nested structure from the main structure.
+ *
+ * @return: Total size of the decoded data elements, on success.
+ * < 0 on error.
+ *
+ * This function decodes the "elem_len" number of elements in QMI wire format,
+ * each of size "(tlv_len/elem_len)" bytes from the source buffer "buf_src"
+ * and stores the decoded elements in the destination buffer "buf_dst". The
+ * elements are of struct data type which includes any C structure. This
+ * function returns the number of bytes of decoded information.
+ */
+static int qmi_decode_struct_elem(struct elem_info *ei_array, void *buf_dst,
+ void *buf_src, uint32_t elem_len,
+ uint32_t tlv_len, int dec_level)
+{
+ int i, rc, decoded_bytes = 0;
+ struct elem_info *temp_ei = ei_array;
+
+ for (i = 0; i < elem_len; i++) {
+ rc = _qmi_kernel_decode(temp_ei->ei_array, buf_dst, buf_src,
+ (tlv_len/elem_len), dec_level);
+ if (rc < 0)
+ return rc;
+ if (rc != (tlv_len/elem_len)) {
+ pr_err("%s: Fault in decoding\n", __func__);
+ return -EFAULT;
+ }
+ buf_src = buf_src + rc;
+ buf_dst = buf_dst + temp_ei->elem_size;
+ decoded_bytes += rc;
+ }
+
+ return decoded_bytes;
+}
+
+/**
+ * find_ei() - Find element info corresponding to TLV Type
+ * @ei_array: Struct info array of the message being decoded.
+ * @type: TLV Type of the element being searched.
+ *
+ * @return: Pointer to struct info, if found
+ *
+ * Every element that got encoded in the QMI message will have a type
+ * information associated with it. While decoding the QMI message,
+ * this function is used to find the struct info regarding the element
+ * that corresponds to the type being decoded.
+ */
+static struct elem_info *find_ei(struct elem_info *ei_array,
+ uint32_t type)
+{
+ struct elem_info *temp_ei = ei_array;
+ while (temp_ei->data_type != QMI_EOTI) {
+ if (temp_ei->tlv_type == (uint8_t)type)
+ return temp_ei;
+ temp_ei = temp_ei + 1;
+ }
+ return NULL;
+}
+
+/**
+ * _qmi_kernel_decode() - Core Decode Function
+ * @ei_array: Struct info array describing the structure to be decoded.
+ * @out_c_struct: Buffer to hold the decoded C struct
+ * @in_buf: Buffer containing the QMI message to be decoded
+ * @in_buf_len: Length of the QMI message to be decoded
+ * @dec_level: Decode level to indicate the depth of the nested structure,
+ * within the main structure, being decoded
+ *
+ * @return: Number of bytes of decoded information, on success
+ * < 0 on error.
+ */
+static int _qmi_kernel_decode(struct elem_info *ei_array,
+ void *out_c_struct,
+ void *in_buf, uint32_t in_buf_len,
+ int dec_level)
+{
+ struct elem_info *temp_ei = ei_array;
+ uint8_t opt_flag_value = 1;
+ uint32_t data_len_value = 0, data_len_sz = 0;
+ uint8_t *buf_dst = out_c_struct;
+ uint8_t *tlv_pointer;
+ uint32_t tlv_len = 0;
+ uint32_t tlv_type;
+ uint32_t decoded_bytes = 0;
+ void *buf_src = in_buf;
+ int rc;
+
+ while (decoded_bytes < in_buf_len) {
+ if (dec_level == 1) {
+ tlv_pointer = buf_src;
+ QMI_ENCDEC_DECODE_TLV(&tlv_type,
+ &tlv_len, tlv_pointer);
+ buf_src += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+ decoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+ temp_ei = find_ei(ei_array, tlv_type);
+ if (!temp_ei) {
+ pr_err("%s: Inval element info\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ buf_dst = out_c_struct + temp_ei->offset;
+ if (temp_ei->data_type == QMI_OPT_FLAG) {
+ memcpy(buf_dst, &opt_flag_value, sizeof(uint8_t));
+ temp_ei = temp_ei + 1;
+ buf_dst = out_c_struct + temp_ei->offset;
+ }
+
+ if (temp_ei->data_type == QMI_DATA_LEN) {
+ data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
+ sizeof(uint8_t) : sizeof(uint16_t);
+ rc = qmi_decode_basic_elem(&data_len_value, buf_src,
+ 1, data_len_sz);
+ memcpy(buf_dst, &data_len_value, sizeof(uint32_t));
+ temp_ei = temp_ei + 1;
+ buf_dst = out_c_struct + temp_ei->offset;
+ UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
+ }
+
+ if (temp_ei->is_array == NO_ARRAY) {
+ data_len_value = 1;
+ } else if (temp_ei->is_array == STATIC_ARRAY) {
+ data_len_value = temp_ei->elem_len;
+ } else if (data_len_value > temp_ei->elem_len) {
+ pr_err("%s: Data len %d > max spec %d\n",
+ __func__, data_len_value, temp_ei->elem_len);
+ return -ETOOSMALL;
+ }
+
+ switch (temp_ei->data_type) {
+ case QMI_UNSIGNED_1_BYTE:
+ case QMI_UNSIGNED_2_BYTE:
+ case QMI_UNSIGNED_4_BYTE:
+ case QMI_UNSIGNED_8_BYTE:
+ case QMI_SIGNED_2_BYTE_ENUM:
+ case QMI_SIGNED_4_BYTE_ENUM:
+ rc = qmi_decode_basic_elem(buf_dst, buf_src,
+ data_len_value, temp_ei->elem_size);
+ UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
+ break;
+
+ case QMI_STRUCT:
+ rc = qmi_decode_struct_elem(temp_ei, buf_dst, buf_src,
+ data_len_value, tlv_len, (dec_level + 1));
+ if (rc < 0)
+ return rc;
+ UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
+ break;
+ default:
+ pr_err("%s: Unrecognized data type\n", __func__);
+ return -EINVAL;
+ }
+ temp_ei = temp_ei + 1;
+ }
+ return decoded_bytes;
+}
+MODULE_DESCRIPTION("QMI kernel enc/dec");
+MODULE_LICENSE("GPL v2");
diff --git a/lib/qmi_encdec_priv.h b/lib/qmi_encdec_priv.h
new file mode 100644
index 0000000..97fe45b
--- /dev/null
+++ b/lib/qmi_encdec_priv.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#ifndef _QMI_ENCDEC_PRIV_H_
+#define _QMI_ENCDEC_PRIV_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+#include <linux/qmi_encdec.h>
+
+#define QMI_ENCDEC_ENCODE_TLV(type, length, p_dst) do { \
+ *p_dst++ = type; \
+ *p_dst++ = ((uint8_t)((length) & 0xFF)); \
+ *p_dst++ = ((uint8_t)(((length) >> 8) & 0xFF)); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_TLV(p_type, p_length, p_src) do { \
+ *p_type = (uint8_t)*p_src++; \
+ *p_length = (uint8_t)*p_src++; \
+ *p_length |= ((uint8_t)*p_src) << 8; \
+} while (0)
+
+#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \
+do { \
+ memcpy(p_dst, p_src, size); \
+ p_dst = (uint8_t *)p_dst + size; \
+ p_src = (uint8_t *)p_src + size; \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \
+do { \
+ memcpy(p_dst, p_src, size); \
+ p_dst = (uint8_t *)p_dst + size; \
+ p_src = (uint8_t *)p_src + size; \
+} while (0)
+
+#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \
+ encoded_bytes, tlv_len, encode_tlv, rc) \
+do { \
+ buf_dst = (uint8_t *)buf_dst + rc; \
+ encoded_bytes += rc; \
+ tlv_len += rc; \
+ temp_si = temp_si + 1; \
+ encode_tlv = 1; \
+} while (0)
+
+#define UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc) \
+do { \
+ buf_src = (uint8_t *)buf_src + rc; \
+ decoded_bytes += rc; \
+} while (0)
+
+#endif
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 6b3287e..f28fd774 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -1889,6 +1889,8 @@
pr_err:
pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
__func__, port_id + 1);
+ mutex_unlock(&codec->mutex);
+ return 0;
err:
mutex_unlock(&codec->mutex);
return -EINVAL;
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 6f2885f..9f4babb 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -4504,6 +4504,8 @@
/* Disable TX7 internal biasing path which can cause leakage */
TAIKO_REG_VAL(TAIKO_A_TX_SUP_SWITCH_CTRL_1, 0xBF),
+ /* Enable MICB 4 VDDIO switch to prevent leakage */
+ TAIKO_REG_VAL(TAIKO_A_MICB_4_MBHC, 0x81),
};
static void taiko_update_reg_defaults(struct snd_soc_codec *codec)
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index 99302eb..a261184 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -58,7 +58,7 @@
snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
obj-$(CONFIG_SND_SOC_MSM_QDSP6_HDMI_AUDIO) += msm-dai-q6-hdmi.o
-obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o
+obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o msm-pcm-dtmf.o
snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
obj-$(CONFIG_SND_SOC_QDSP6) += snd-soc-qdsp6.o
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 5a47efe..0125c1a 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -2014,6 +2014,21 @@
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = 1,
},
+ {
+ .name = "DTMF RX Hostless",
+ .stream_name = "DTMF RX Hostless",
+ .cpu_dai_name = "DTMF_RX_HOSTLESS",
+ .platform_name = "msm-pcm-dtmf",
+ .dynamic = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .be_id = MSM_FRONTEND_DAI_DTMF_RX,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ },
+
/* Backend BT DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index e91ed86..0926865 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -503,6 +503,20 @@
.ops = &msm_fe_Multimedia_dai_ops,
.name = "Pseudo",
},
+ {
+ .playback = {
+ .stream_name = "DTMF_RX Hostless Playback",
+ .aif_name = "DTMF_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "DTMF_RX_HOSTLESS",
+ },
};
static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm-pcm-dtmf.c b/sound/soc/msm/msm-pcm-dtmf.c
new file mode 100644
index 0000000..04ab7a9
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-dtmf.c
@@ -0,0 +1,100 @@
+/* Copyright (c) 2012, 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/platform_device.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/q6afe.h>
+
+static int msm_dtmf_rx_generate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint16_t low_freq = ucontrol->value.integer.value[0];
+ uint16_t high_freq = ucontrol->value.integer.value[1];
+ int64_t duration = ucontrol->value.integer.value[2];
+ uint16_t gain = ucontrol->value.integer.value[3];
+
+ pr_debug("%s: low_freq=%d high_freq=%d duration=%d gain=%d\n",
+ __func__, low_freq, high_freq, (int)duration, gain);
+ afe_dtmf_generate_rx(duration, high_freq, low_freq, gain);
+ return 0;
+}
+
+static int msm_dtmf_rx_generate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s:\n", __func__);
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static struct snd_kcontrol_new msm_dtmf_controls[] = {
+ SOC_SINGLE_MULTI_EXT("DTMF_Generate Rx Low High Duration Gain",
+ SND_SOC_NOPM, 0, 5000, 0, 4,
+ msm_dtmf_rx_generate_get,
+ msm_dtmf_rx_generate_put),
+};
+
+static int msm_pcm_dtmf_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform, msm_dtmf_controls,
+ ARRAY_SIZE(msm_dtmf_controls));
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {};
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .probe = msm_pcm_dtmf_probe,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-dtmf",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("DTMF platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 378baf1..1ddb6c9 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -655,6 +655,14 @@
else
clear_bit(val, &msm_bedais[reg].fe_sessions);
+ if (val == MSM_FRONTEND_DAI_DTMF_RX &&
+ afe_get_port_type(msm_bedais[reg].port_id) ==
+ MSM_AFE_PORT_TYPE_RX) {
+ pr_debug("%s(): set=%d port id=0x%x for dtmf generation\n",
+ __func__, set, msm_bedais[reg].port_id);
+ afe_set_dtmf_gen_rx_portid(msm_bedais[reg].port_id, set);
+ }
+
mutex_unlock(&routing_lock);
if (afe_get_port_type(msm_bedais[reg].port_id) ==
@@ -1588,6 +1596,9 @@
SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
};
static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
@@ -1603,6 +1614,9 @@
SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
@@ -1618,6 +1632,9 @@
SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
@@ -1636,6 +1653,9 @@
SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = {
@@ -1651,6 +1671,9 @@
SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_MI2S_RX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
@@ -1669,6 +1692,9 @@
SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
@@ -1687,6 +1713,9 @@
SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = {
@@ -1702,6 +1731,9 @@
SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
@@ -1720,6 +1752,9 @@
SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
@@ -2288,6 +2323,8 @@
0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DTMF_DL_HL", "DTMF_RX_HOSTLESS Playback",
+ 0, 0, 0, 0),
/* Backend AIF */
/* Stream name equals to backend dai link stream name
@@ -2606,48 +2643,56 @@
{"PRI_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"PRI_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"PRI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
{"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"SEC_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SEC_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
{"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"SLIM_0_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIM_0_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
{"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"AFE_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"AFE_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
{"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"AUX_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
{"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"HDMI_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"HDMI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"HDMI", NULL, "HDMI_RX_Voice Mixer"},
{"HDMI", NULL, "HDMI_DL_HL"},
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 32ab063..0c0d3b4 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -70,6 +70,7 @@
MSM_FRONTEND_DAI_VOICE_STUB,
MSM_FRONTEND_DAI_VOLTE,
MSM_FRONTEND_DAI_SGLTE,
+ MSM_FRONTEND_DAI_DTMF_RX,
MSM_FRONTEND_DAI_MAX,
};
diff --git a/sound/soc/msm/msm-pcm-voip.c b/sound/soc/msm/msm-pcm-voip.c
index 359414b..4d2b253 100644
--- a/sound/soc/msm/msm-pcm-voip.c
+++ b/sound/soc/msm/msm-pcm-voip.c
@@ -43,6 +43,25 @@
#define MODE_AMR 0x5
#define MODE_AMR_WB 0xD
#define MODE_PCM 0xC
+#define MODE_G711 0xA
+#define MODE_G711A 0xF
+
+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
+};
enum format {
FORMAT_S16_LE = 2,
@@ -135,7 +154,7 @@
unsigned int pcm_capture_buf_pos; /* position in buffer */
};
-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);
static int voip_get_rate_type(uint32_t mode,
uint32_t rate,
@@ -309,6 +328,79 @@
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.header.frame_type =
+ (*voc_pkt) & 0x03;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ /* There are two frames in the buffer. Length of the
+ * first frame:
+ */
+ buf_node->frame.len = (pkt_len -
+ 2 * DSP_FRAME_HDR_LEN) / 2;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.len);
+ voc_pkt = voc_pkt + buf_node->frame.len;
+
+ 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.header.frame_type =
+ (*voc_pkt) & 0x03;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ /* There are two frames in the buffer. Length
+ * of the second frame:
+ */
+ buf_node->frame.len = (pkt_len -
+ 2 * DSP_FRAME_HDR_LEN) / 2;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.len);
+
+ 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.len = pkt_len;
memcpy(&buf_node->frame.voc_pkt[0],
@@ -383,6 +475,67 @@
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_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (buf_node->frame.header.frame_type & 0x03);
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.len);
+ voc_pkt = voc_pkt + buf_node->frame.len;
+
+ 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.header.frame_type & 0x03);
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ *pkt_len = *pkt_len +
+ buf_node->frame.len + DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.len);
+
+ 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__);
+ }
+ break;
+ }
default: {
*pkt_len = buf_node->frame.len;
@@ -753,7 +906,8 @@
if ((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_WB) || (prtd->mode == MODE_G711) ||
+ (prtd->mode == MODE_G711A))) {
pr_err("mode:%d and format:%u are not mached\n",
prtd->mode, (uint32_t)runtime->format);
ret = -EINVAL;
@@ -781,6 +935,7 @@
}
prtd->rate_type = rate_type;
media_type = voip_get_media_type(prtd->mode,
+ prtd->rate_type,
prtd->play_samp_rate);
if (media_type < 0) {
pr_err("fail at getting media_type\n");
@@ -1041,6 +1196,10 @@
}
break;
}
+ case MODE_G711:
+ case MODE_G711A:
+ *rate_type = rate;
+ break;
default:
pr_err("wrong mode type.\n");
ret = -EINVAL;
@@ -1050,7 +1209,7 @@
return ret;
}
-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)
{
uint32_t media_type;
@@ -1079,6 +1238,13 @@
case MODE_4GV_WB: /* EVRC-WB */
media_type = VSS_MEDIA_ID_4GV_WB_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");
media_type = -EINVAL;
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 9c62a2e..6cabc97 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -33,6 +33,7 @@
uint32_t token, uint32_t *payload, void *priv);
void *tx_private_data;
void *rx_private_data;
+ u16 dtmf_gen_rx_portid;
};
static struct afe_ctl this_afe;
@@ -91,6 +92,7 @@
case AFE_SERVICE_CMD_MEMORY_MAP:
case AFE_SERVICE_CMD_MEMORY_UNMAP:
case AFE_SERVICE_CMD_UNREG_RTPORT:
+ case AFE_PORTS_CMD_DTMF_CTL:
atomic_set(&this_afe.state, 0);
wake_up(&this_afe.wait);
break;
@@ -1621,6 +1623,83 @@
.write = afe_debug_write
};
#endif
+
+void afe_set_dtmf_gen_rx_portid(u16 port_id, int set)
+{
+ if (set)
+ this_afe.dtmf_gen_rx_portid = port_id;
+ else if (this_afe.dtmf_gen_rx_portid == port_id)
+ this_afe.dtmf_gen_rx_portid = -1;
+}
+
+int afe_dtmf_generate_rx(int64_t duration_in_ms,
+ uint16_t high_freq,
+ uint16_t low_freq, uint16_t gain)
+{
+ int ret = 0;
+ struct afe_dtmf_generation_command cmd_dtmf;
+
+ pr_debug("%s: DTMF AFE Gen\n", __func__);
+
+ if (afe_validate_port(this_afe.dtmf_gen_rx_portid) < 0) {
+ pr_err("%s: Failed : Invalid Port id = %d\n",
+ __func__, this_afe.dtmf_gen_rx_portid);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ pr_debug("dur=%lld: hfreq=%d lfreq=%d gain=%d portid=%x\n",
+ duration_in_ms, high_freq, low_freq, gain,
+ this_afe.dtmf_gen_rx_portid);
+
+ cmd_dtmf.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd_dtmf.hdr.pkt_size = sizeof(cmd_dtmf);
+ cmd_dtmf.hdr.src_port = 0;
+ cmd_dtmf.hdr.dest_port = 0;
+ cmd_dtmf.hdr.token = 0;
+ cmd_dtmf.hdr.opcode = AFE_PORTS_CMD_DTMF_CTL;
+ cmd_dtmf.duration_in_ms = duration_in_ms;
+ cmd_dtmf.high_freq = high_freq;
+ cmd_dtmf.low_freq = low_freq;
+ cmd_dtmf.gain = gain;
+ cmd_dtmf.num_ports = 1;
+ cmd_dtmf.port_ids = this_afe.dtmf_gen_rx_portid;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_dtmf);
+ if (ret < 0) {
+ pr_err("%s: AFE DTMF failed for num_ports:%d ids:%x\n",
+ __func__, cmd_dtmf.num_ports, cmd_dtmf.port_ids);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (ret < 0) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ return 0;
+
+fail_cmd:
+ return ret;
+}
+
int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain)
{
struct afe_port_sidetone_command cmd_sidetone;
@@ -1755,6 +1834,7 @@
atomic_set(&this_afe.state, 0);
atomic_set(&this_afe.status, 0);
this_afe.apr = NULL;
+ this_afe.dtmf_gen_rx_portid = -1;
#ifdef CONFIG_DEBUG_FS
debugfs_afelb = debugfs_create_file("afe_loopback",
S_IFREG | S_IWUGO, NULL, (void *) "afe_loopback",