Merge "HID: uhid: forward raw output reports to user-space"
diff --git a/Documentation/devicetree/bindings/arm/msm/ipc-spinlock.txt b/Documentation/devicetree/bindings/arm/msm/ipc-spinlock.txt
index c71b190..24dbb4b 100644
--- a/Documentation/devicetree/bindings/arm/msm/ipc-spinlock.txt
+++ b/Documentation/devicetree/bindings/arm/msm/ipc-spinlock.txt
@@ -1,14 +1,27 @@
Qualcomm Interprocessor Communication Spinlock
+--Dedicated Hardware Implementation--
Required properties:
-- compatible : should be "qcom,ipc-spinlock"
+- compatible : should be "qcom,ipc-spinlock-sfpb"
- reg : the location and size of the spinlock hardware
- qcom,num-locks : the number of locks supported
Example:
qcom,ipc-spinlock@fd484000 {
- compatible = "qcom,ipc-spinlock";
+ compatible = "qcom,ipc-spinlock-sfpb";
reg = <0xfd484000 0x1000>;
qcom,num-locks = <32>;
};
+
+--LDREX Implementation--
+Required properties:
+- compatible : should be "qcom,ipc-spinlock-ldrex"
+- reg : the location and size of the shared lock memory
+
+Example:
+
+ qcom,ipc-spinlock@fa00000 {
+ compatible = "qcom,ipc-spinlock-ldrex";
+ reg = <0xfa00000 0x200000>;
+ };
diff --git a/Documentation/devicetree/bindings/bif/bif.txt b/Documentation/devicetree/bindings/bif/bif.txt
new file mode 100644
index 0000000..c4ff08b
--- /dev/null
+++ b/Documentation/devicetree/bindings/bif/bif.txt
@@ -0,0 +1,22 @@
+BIF (Battery Interface) Controllers
+
+Optional properties:
+- qcom,known-device-addresses: Specifies a list of integers which correspond to
+ the 8-bit BIF bus device addresses of BIF slaves
+ found on the target.
+
+BIF Consumers
+
+Optional properties:
+- qcom,bif-ctrl: phandle of parent BIF controller device node
+
+Example:
+ foo_ctrl: foo-controller {
+ ...
+ qcom,known-device-addresses = <0x80, 0x81>;
+ };
+
+ bar-consumer {
+ ...
+ qcom,bif-ctrl = <&foo_ctrl>;
+ };
diff --git a/Documentation/devicetree/bindings/bif/qpnp-bsi.txt b/Documentation/devicetree/bindings/bif/qpnp-bsi.txt
new file mode 100644
index 0000000..29267dd
--- /dev/null
+++ b/Documentation/devicetree/bindings/bif/qpnp-bsi.txt
@@ -0,0 +1,91 @@
+Qualcomm QPNP BSI - battery serial interface devices
+
+qpnp-bsi is a BIF driver which supports the BSI peripheral inside of PMICs
+that utilize the MSM SPMI implementation.
+
+Required properties:
+- compatible: Must be "qcom,qpnp-bsi".
+- reg: Specifies the SPMI address and size for this BSI device as
+ well as the address of the BATT_ID status register.
+- reg-names: A list of strings identifying the reg property entries. The
+ list must contain both "bsi-base" and "batt-id-status".
+- label: A string used as a descriptive name for this BIF controller.
+- interrupts: Specifies a list of four interrupts corresponding to
+ IRQ ERR, IRQ RX, IRQ TX, and IRQ BATT_PRESENT in any order.
+- interrupt-names: Must be a list of strings containing all three of these
+ strings: "err", "rx", "tx", "batt-present". The ordering of
+ these strings must match the ordering of the interrupts in
+ the "interrupts" property.
+
+Required structure:
+- A qcom,qpnp-bsi node must be a child of an SPMI node that has specified the
+ spmi-slave-container property.
+
+Optional properties:
+- qcom,min-clock-period: This property specifies a minimum clock period for the
+ Tau BIF reference in nanoseconds. It can be used to
+ impose a minimum period which is higher (i.e. more
+ restrictive) than that supported by the hardware.
+ The BSI module supports 8 possible periods between
+ 2080 ns and 150420 ns.
+- qcom,max-clock-period: This property specifies a maximum clock period for the
+ Tau BIF reference in nanoseconds. It can be used to
+ impose a maximum period which is lower (i.e. more
+ restrictive) than that supported by the hardware.
+ The BSI module supports 8 possible periods between
+ 2080 ns and 150420 ns.
+- qcom,sample-rate: Specifies the rate at which the BIF BCL should be
+ sampled during communication with respect to the Tau
+ BIF reference rate. Supported values are 4 and 8
+ which represent 4x and 8x sampling rates
+ respectively. If this property is not specified,
+ then 4x sampling is assumed.
+- qcom,channel-num: VADC channel number associated PMIC BATT_ID pin. If
+ no channel is specified, then it will not be
+ possible to measure the slave Rid.
+- qcom,pullup-ohms: Host side pull-up resistance present on BCL in ohms.
+ If no value is specified, then 100000 ohms is
+ assumed.
+- qcom,vref-microvolts: Reference voltage used for BCL divider circuit in
+ microvolts. If no value is specified, then
+ 1800000 uV is assumed.
+
+All properties specified within for the BIF framework can also be used. These
+properties can be found in bif.txt.
+
+Example:
+ qcom,spmi@fc4c0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ qcom,pm8941@1 {
+ spmi-slave-container;
+ reg = <0x1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,bsi@1b00 {
+ compatible = "qcom,qpnp-bsi";
+ reg = <0x1b00 0x100>,
+ <0x1208 0x1>;
+ reg-names = "bsi-base", "batt-id-status";
+ label = "pm8941-bsi";
+ interrupts = <0x0 0x1b 0x0>,
+ <0x0 0x1b 0x1>,
+ <0x0 0x1b 0x2>,
+ <0x0 0x12 0x0>;
+ interrupt-names = "err",
+ "rx",
+ "tx",
+ "batt-present";
+ qcom,sample-rate = <8>;
+ qcom,min-clock-period = <15830>;
+ qcom,max-clock-period = <122080>;
+ qcom,channel-num = <0x31>;
+ qcom,pullup-ohms = <100000>;
+ qcom,vref-microvolts = <1800000>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt
index 48f25de..c830bc4 100644
--- a/Documentation/devicetree/bindings/coresight/coresight.txt
+++ b/Documentation/devicetree/bindings/coresight/coresight.txt
@@ -10,9 +10,68 @@
Required properties:
-- compatible : name of the component used for driver matching
+- compatible : name of the component used for driver matching, should be one of
+ the following:
+ "arm,coresight-tmc" for coresight tmc-etr or tmc-etf device,
+ "arm,coresight-tpiu" for coresight tpiu device,
+ "qcom,coresight-replicator" for coresight replicator device,
+ "arm,coresight-funnel" for coresight funnel devices,
+ "arm,coresight-stm" for coresight stm trace device,
+ "arm,coresight-etm" for coresight etm trace devices,
+ "qcom,coresight-csr" for coresight csr device,
+ "arm,coresight-cti" for coresight cti devices
- reg : physical base address and length of the register set(s) of the component
-- reg-names: names corresponding to each reg property value
+- reg-names : names corresponding to each reg property value. The reg-names that
+ need to be used with corresponding compatible string for a coresight device
+ are:
+ - for coresight tmc-etr device:
+ compatible : should be "arm,coresight-tmc"
+ reg-names : should be:
+ "tmc-etr-base" - physical base address of tmc-etr registers
+ "tmc-etr-bam-base" - physical base address of tmc-etr bam
+ registers
+ - for coresight tmc-etf device:
+ compatible : should be "arm,coresight-tmc"
+ reg-names : should be:
+ "tmc-etf-base" - physical base address of tmc-etf registers
+ - for coresight tpiu device:
+ compatible : should be "arm,coresight-tpiu"
+ reg-names : should be:
+ "tpiu-base" - physical base address of tpiu registers
+ - for coresight replicator device
+ compatible : should be "qcom,coresight-replicator"
+ reg-names : should be:
+ "replicator-base" - physical base address of replicator registers
+ - for coresight funnel devices
+ compatible : should be "arm,coresight-funnel"
+ reg-names : should be:
+ "funnel-<val>-base" - physical base address of funnel registers
+ where <val> can be "merg", "in0", "in1", "kpss", "a7ss" or
+ "mmss"
+ - for coresight stm trace device
+ compatible : should be "arm,coresight-stm"
+ reg-names : should be:
+ "stm-base" - physical base address of stm registers
+ "stm-data-base" - physical base address of stm data registers
+ - for coresight etm trace devices
+ compatible : should be "arm,coresight-etm"
+ reg-names : should be:
+ "etm<num>-base" - physical base address of etm registers in
+ general where <num> is the number of etm components or cores
+ present for more than one cpu core
+ - for coresight csr device:
+ compatible : should be "qcom,coresight-csr"
+ reg-names : should be:
+ "csr-base" - physical base address of csr registers
+ - for coresight cti devices:
+ compatible : should be "arm,coresight-cti"
+ reg-names : should be:
+ "cti<num>-base" - physical base address of cti registers in general
+ where <num> is the cti component number for more than one
+ cti components
+ "cti-cpu<num>-base" - physical base address of cti cpu registers
+ where <num> is the component number for more than one cpu core
+ "cti-l2" - physical base address of L2 cti registers
- coresight-id : unique integer identifier for the component
- coresight-name : unique descriptive name of the component
- coresight-nr-inports : number of input ports on the component
diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
index 0f31a38..17b878c 100644
--- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
@@ -43,6 +43,11 @@
previous property, the amount of fetch ids
defined should match the number of offsets
defined in property: qcom,mdss-pipe-dma-off
+- qcom,mdss-smp-data: Array of shared memory pool data. There should
+ be only two values in this property. The first
+ value corresponds to the number of smp blocks
+ and the second is the size of each block
+ present in the mdss hardware.
- qcom,mdss-ctl-off: Array of offset addresses for the available ctl
hw blocks within MDP, these offsets are
calculated from register "mdp_phys" defined in
@@ -123,6 +128,7 @@
qcom,mdss-pipe-vig-fetch-id = <1 4 7>;
qcom,mdss-pipe-rgb-fetch-id = <16 17 18>;
qcom,mdss-pipe-dma-fetch-id = <10 13>;
+ qcom,mdss-smp-data = <22 4096>;
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
0x00000900 0x0000A00>;
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index bb66e7b..8de8bdd 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -30,7 +30,16 @@
2 : 2K
3 : 4K
- qcom,pre-div-channel-scaling : Pre-div used for the channel before the signal
- is being measured.
+ is being measured. Some of the AMUX channels
+ support dividing the signal from a predetermined
+ ratio. The configuration for this node is to know
+ the pre-determined ratio and use it for post scaling.
+ Select from the following unsinged int.
+ 0 : {1, 1}
+ 1 : {1, 3}
+ 2 : {1, 4}
+ 3 : {1, 6}
+ 4 : {1, 20}
- qcom,calibration-type : Reference voltage to use for channel calibration.
Channel calibration is dependendent on the channel.
Certain channels like XO_THERM, BATT_THERM use ratiometric
@@ -96,10 +105,39 @@
label = "usb_in";
reg = <0>;
qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <20>;
+ qcom,pre-div-channel-scaling = <4>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
qcom,fast-avg-setup = <0>;
};
};
+
+/* Clients have an option of measuring an analog signal through an MPP.
+ MPP block is not part of the VADC block but is an individual PMIC
+ block that has an option to support clients to configure an MPP as
+ an analog input which can be routed through one of the VADC pre-mux
+ inputs. Here is an example of how to configure an MPP as an analog
+ input */
+
+/* Configure MPP4 as an Analog input to AMUX8 and read from channel 0x23 */
+/* MPP DT configuration in the platform DT file*/
+ mpp@a300 { /* MPP 4 */
+ qcom,mode = <4>; /* AIN input */
+ qcom,invert = <1>; /* Enable MPP */
+ qcom,ain-route = <3>; /* AMUX 8 */
+ qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* Function constant */
+ };
+
+/* VADC Channel configuration */
+ chan@23 {
+ label = "mpp4_div3";
+ reg = <0x23>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/i2c/i2c-qup.txt b/Documentation/devicetree/bindings/i2c/i2c-qup.txt
index 60de396..a7976e8 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-qup.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-qup.txt
@@ -24,6 +24,11 @@
desired I2C bus frequency. If this value is not
provided, the source clock is assumed to be running
at 19.2 MHz.
+ - qcom,scl-gpio : I2C clock GPIO number. Required for execution of bus
+ recovery procedure.
+ - qcom,sda-gpio : I2C data GPIO number. Required for execution of bus
+ recovery procedure.
+
Example:
i2c@f9966000 {
cell-index = <0>;
@@ -34,4 +39,6 @@
interrupt-names = "qup_err_intr";
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <24000000>;
+ qcom,scl-gpio = <&msmgpio 7 0>;
+ qcom,sda-gpio = <&msmgpio 6 0>;
};
diff --git a/Documentation/devicetree/bindings/media/video/msm-cpp.txt b/Documentation/devicetree/bindings/media/video/msm-cpp.txt
index 9d176d8..c345d38 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cpp.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cpp.txt
@@ -7,8 +7,9 @@
- reg : offset and length of the register set for the device
for the cpp operating in compatible mode.
- reg-names : should specify relevant names to each reg property defined.
- - cpp - has CPP hardware register set.
+ - cpp - has CPP MICRO register set.
- cpp_vbif - has VBIF core register set used by CPP.
+ - cpp_hw - has CPP hardware register set.
- interrupts : should contain the cpp interrupt.
- interrupt-names : should specify relevant names to each interrupts
property defined.
@@ -21,7 +22,8 @@
compatible = "qcom,cpp";
reg = <0xfda04000 0x100>;
<0xfda40000 0x200>;
- reg-names = "cpp", "cpp_vbif";
+ <0xfd180000 0x008>;
+ reg-names = "cpp", "cpp_vbif", "cpp_hw";
interrupts = <0 49 0>;
interrupt-names = "cpp";
vdd-supply = <&gdsc_vfe>;
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
index cd14056..f97e063 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -31,6 +31,14 @@
request by different video encoder usecases.
- qcom,dec-ddr-ab-ib : list of bus vectors(ab, ib pair) for ddr bandwidth
request by different video decoder usecases.
+- qcom,iommu-groups : list of IOMMU groups to be used. Groups are defined as
+ phandles in <target>-iommu-domains.dtsi (e.g msm8974-v1-iommu-domains.dtsi)
+- qcom,iommu-group-buffer-types : bitmap of buffer types that can be mapped into
+ the corresponding IOMMU group. Buffer types are defined within the vidc driver
+ by "enum hal_buffer" in msm_smem.h
+- qcom,buffer-type-tz-usage-table : a key-value pair, mapping a buffer type
+ (enum hal_buffer) to its corresponding TZ usage. The TZ usages are defined
+ as "enum cp_mem_usage" in include/linux/msm_ion.h
Example:
@@ -59,4 +67,8 @@
<60000 664950>;
qcom,dec-ddr-ab-ib = <0 0>,
<110000 909000>;
+ qcom,iommu-groups = <&venus_domain_ns &venus_domain_cp>;
+ qcom,iommu-group-buffer-types = <0xfff 0x1ff>;
+ qcom,buffer-type-tz-usage-table = <0x1 0x1>,
+ <0x1fe 0x2>;
};
diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt
new file mode 100644
index 0000000..e98ee05
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory.txt
@@ -0,0 +1,106 @@
+* Memory binding
+
+The /memory node provides basic information about the address and size
+of the physical memory. This node is usually filled or updated by the
+bootloader, depending on the actual memory configuration of the given
+hardware.
+
+The memory layout is described by the folllowing node:
+
+memory {
+ reg = <(baseaddr1) (size1)
+ (baseaddr2) (size2)
+ ...
+ (baseaddrN) (sizeN)>;
+};
+
+baseaddrX: the base address of the defined memory bank
+sizeX: the size of the defined memory bank
+
+More than one memory bank can be defined.
+
+
+* Memory regions
+
+In /memory node one can create additional nodes describing particular
+memory regions, usually for the special usage by various device drivers.
+A good example are contiguous memory allocations or memory sharing with
+other operating system on the same hardware board. Those special memory
+regions might depend on the board configuration and devices used on the
+target system.
+
+Parameters for each memory region can be encoded into the device tree
+wit the following convention:
+
+(name): region@(base-address) {
+ reg = <(baseaddr) (size)>;
+ (linux,contiguous-region);
+ (linux,default-contiguous-region);
+ label = (unique_name);
+};
+
+name: an name given to the defined region.
+base-address: the base address of the defined region.
+size: the size of the memory region.
+linux,contiguous-region: property indicating that the defined memory
+ region is used for contiguous memory allocations,
+ Linux specific (optional)
+linux,default-contiguous-region: property indicating that the region
+ is the default region for all contiguous memory
+ allocations, Linux specific (optional)
+label: an internal name used for automatically associating the
+ cma region with a given device. The label is optional;
+ if the label is not given the client is responsible for
+ calling the appropriate functions to associate the region
+ with a device.
+
+* Device nodes
+
+Once the regions in the /memory node are defined, they can be assigned
+to device some device nodes for their special use. The following
+properties are defined:
+
+linux,contiguous-region = <&phandle>;
+ This property indicates that the device driver should use the
+ memory region pointed by the given phandle.
+
+
+* Example:
+
+This example defines a memory consisting of 4 memory banks. 2 contiguous
+regions are defined for Linux kernel, one default of all device drivers
+(named contig_mem, placed at 0x72000000, 64MiB) and one dedicated to the
+framebuffer device (named display_mem, placed at 0x78000000, 16MiB). The
+display_mem region is then assigned to fb@12300000 device for contiguous
+memory allocation with Linux kernel drivers.
+
+The reason for creating a separate region for framebuffer device is to
+match the framebuffer address of from configuration done by bootloader,
+so once Linux kernel drivers starts, no glitches on the displayed boot
+logo appears.
+
+/ {
+ /* ... */
+ memory {
+ reg = <0x40000000 0x10000000
+ 0x50000000 0x10000000
+ 0x60000000 0x10000000
+ 0x70000000 0x10000000>;
+
+ contig_mem: region@72000000 {
+ linux,contiguous-region;
+ linux,default-contiguous-region;
+ reg = <0x72000000 0x4000000>;
+ };
+
+ display_mem: region@78000000 {
+ linux,contiguous-region;
+ reg = <0x78000000 0x1000000>;
+ };
+ };
+
+ fb@12300000 {
+ linux,contiguous-region = <&display_mem>;
+ status = "okay";
+ };
+};
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
new file mode 100644
index 0000000..24b6b36
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -0,0 +1,107 @@
+Qualcomm Standard Secure Digital Host Controller (SDHC)
+
+Secure Digital Host Controller provides standard host interface to SD/MMC/SDIO cards.
+
+Required properties:
+ - compatible : should be "qcom,sdhci-msm"
+ - reg : should contain SDHC, SD Core register map.
+ - reg-names : indicates various resources passed to driver (via reg proptery) by name.
+ Required "reg-names" are "hc_mem" and "core_mem"
+ - interrupts : should contain SDHC interrupts.
+ - interrupt-names : indicates interrupts passed to driver (via interrupts property) by name.
+ Required "interrupt-names" are "hc_irq" and "pwr_irq".
+ - <supply-name>-supply: phandle to the regulator device tree node
+ Required "supply-name" are "vdd" and "vdd-io".
+
+Required alias:
+- The slot number is specified via an alias with the following format
+ 'sdhc{n}' where n is the slot number.
+
+Optional Properties:
+ - interrupt-names - "status_irq". This status_irq will be used for card
+ detection.
+ - qcom,bus-width - defines the bus I/O width that controller supports.
+ Units - number of bits. The valid bus-width values are
+ 1, 4 and 8.
+ - qcom,nonremovable - specifies whether the card in slot is
+ hot pluggable or hard wired.
+ - qcom,bus-speed-mode - specifies supported bus speed modes by host.
+ The supported bus speed modes are :
+ "HS200_1p8v" - indicates that host can support HS200 at 1.8v.
+ "HS200_1p2v" - indicates that host can support HS200 at 1.2v.
+ "DDR_1p8v" - indicates that host can support DDR mode at 1.8v.
+ "DDR_1p2v" - indicates that host can support DDR mode at 1.2v.
+
+In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
+ - 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,<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 sdhc slot.
+ - 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,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>
+
+Example:
+
+ aliases {
+ sdhc1 = &sdhc_1;
+ sdhc2 = &sdhc_2;
+ };
+
+ sdhc_1: qcom,sdhc@f9824900 {
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+ interrupts = <0 123 0>, <0 138 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+
+ vdd-supply = <&pm8941_l21>;
+ vdd-io-supply = <&pm8941_l13>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ 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,bus-width = <4>;
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+
+ gpios = <&msmgpio 40 0>, /* CLK */
+ <&msmgpio 39 0>, /* CMD */
+ <&msmgpio 38 0>, /* DATA0 */
+ <&msmgpio 37 0>, /* DATA1 */
+ <&msmgpio 36 0>, /* DATA2 */
+ <&msmgpio 35 0>; /* DATA3 */
+ qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+ };
+
+ sdhc_2: qcom,sdhc@f98a4900 {
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+ interrupts = <0 123 0>, <0 138 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+
+ vdd-supply = <&pm8941_l21>;
+ vdd-io-supply = <&pm8941_l13>;
+
+ qcom,bus-width = <4>;
+
+ 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 */
+ };
diff --git a/Documentation/devicetree/bindings/power/qpnp-charger.txt b/Documentation/devicetree/bindings/power/qpnp-charger.txt
index a868b75..4590227 100644
--- a/Documentation/devicetree/bindings/power/qpnp-charger.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-charger.txt
@@ -54,7 +54,7 @@
each should be their own subnode.
Sub node required properties:
-- compatible: Must be "qcom,charger".
+- compatible: Must be "qcom,qpnp-charger".
- reg: Specifies the SPMI address and size for this peripheral.
- interrupts: Specifies the interrupt associated with the peripheral.
- interrupt-names: Specifies the interrupt names for the peripheral. Every
@@ -125,7 +125,7 @@
Example:
pm8941-chg {
spmi-dev-container;
- compatible = "qcom,charger";
+ compatible = "qcom,qpnp-charger";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/Documentation/devicetree/bindings/regulator/krait-regulator.txt b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
index f057834..aaa731e 100644
--- a/Documentation/devicetree/bindings/regulator/krait-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
@@ -1,5 +1,24 @@
Krait Voltage regulators
+The cpus are powered using a single supply powered by PMIC ganged regulators operating in
+different phases. Individual kraits further can draw power from the single supply via
+a LDO or a head switch (BHS). The first level node represents the PMIC ganged regulator
+and its properties and encompasses second level nodes that represent the individual
+krait LDO/BHS control regulator.
+
+[First Level Nodes]
+Required properties:
+- compatible: Must be "qcom,krait-pdn"
+- reg: Specifies the physical address of the APCS GCC
+ register base
+- reg-names: "apcs_gcc" -string to identify the area where
+ the APCS GCC registers reside.
+
+Optional properties:
+- qcom,use-phase-switching indicates whether the driver should add/shed phases on the PMIC
+ ganged regulator as cpus are hotplugged.
+
+[Second Level Nodes]
Required properties:
- compatible: Must be "qcom,krait-regulator"
- reg: Specifies the address and size for this regulator device,
@@ -27,19 +46,28 @@
binding, defined in regulator.txt, can also be used.
Example:
- krait0_vreg: regulator@f9088000 {
- compatible = "qcom,krait-regulator";
- regulator-name = "krait0";
- reg = <0xf9088000 0x1000>, /* APCS_ALIAS0_KPSS_ACS */
- <0xf908a800 0x1000>; /* APCS_ALIAS0_KPSS_MDD */
- reg-names = "acs", "mdd";
- regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <1100000>;
- qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <745000>;
- qcom,ldo-default-voltage = <745000>;
- qcom,ldo-threshold-voltage = <750000>;
- qcom,ldo-delta-voltage = <50000>;
- qcom,cpu-num = 0;
- };
+ krait_pdn: krait-pdn@f9011000 {
+ reg = <0xf9011000 0x1000>;
+ reg-names = "apcs_gcc";
+ compatible = "qcom,krait-pdn";
+ qcom,use-phase-switching;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ krait0_vreg: regulator@f9088000 {
+ compatible = "qcom,krait-regulator";
+ regulator-name = "krait0";
+ reg = <0xf9088000 0x1000>, /* APCS_ALIAS0_KPSS_ACS */
+ <0xf908a800 0x1000>; /* APCS_ALIAS0_KPSS_MDD */
+ reg-names = "acs", "mdd";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,headroom-voltage = <150000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
+ qcom,cpu-num = <0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index fed8cb4..4cd9f99 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -434,6 +434,7 @@
- prim-auxpcm-gpio-sync : GPIO on which AUXPCM SYNC signal is coming.
- prim-auxpcm-gpio-din : GPIO on which AUXPCM DIN signal is coming.
- prim-auxpcm-gpio-dout : GPIO on which AUXPCM DOUT signal is coming.
+- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
Optional properties:
- qcom,hdmi-audio-rx: specifies if HDMI audio support is enabled or not.
@@ -476,6 +477,7 @@
qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
taiko-mclk-clk = <&pm8941_clkdiv1>;
qcom,taiko-mclk-clk-freq = <9600000>;
+ qcom,us-euro-gpios = <&pm8941_gpios 20 0>;
qcom,hdmi-audio-rx;
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 9d0b0a5..6915827 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -52,6 +52,15 @@
- qcom,hsusb-otg-clk-always-on-workaround: If present then USB core clocks
remain active upon receiving bus suspend and USB cable is connected.
Used for allowing USB to respond for remote wakup.
+- qcom,hsusb-otg-delay-lpm: If present then USB core will wait one second
+ after disconnect before entering low power mode.
+- <supply-name>-supply: handle to the regulator device tree node
+ Required "supply-name" is "HSUSB_VDDCX" (when voting for VDDCX) or
+ "hsusb_vdd_dig" (when voting for VDDCX Corner voltage),
+ "HSUSB_1p8-supply" and "HSUSB_3p3-supply".
+- qcom,vdd-voltage-level: This property must be a list of three integer
+ values (no, min, max) where each value represents either a voltage
+ in microvolts or a value corresponding to voltage corner.
Example HSUSB OTG controller device node :
usb@f9690000 {
@@ -72,6 +81,10 @@
qcom,hsusb-otg-pmic-id-irq = <47>
qcom,hsusb-otg-lpm-on-dev-suspend;
qcom,hsusb-otg-clk-always-on-workaround;
+ hsusb_vdd_dig-supply = <&pm8226_s1_corner>;
+ HSUSB_1p8-supply = <&pm8226_l10>;
+ HSUSB_3p3-supply = <&pm8226_l20>;
+ qcom,vdd-voltage-level = <1 5 7>;
qcom,msm_bus,name = "usb2";
qcom,msm_bus,num_cases = <2>;
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 2f686fa..4678337 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1885,18 +1885,9 @@
config ENABLE_DMM
def_bool n
-config FIX_MOVABLE_ZONE
- def_bool n
-
config DONT_MAP_HOLE_AFTER_MEMBANK0
def_bool n
-config ARCH_ENABLE_MEMORY_HOTPLUG
- def_bool n
-
-config ARCH_ENABLE_MEMORY_HOTREMOVE
- def_bool n
-
config HOLES_IN_ZONE
def_bool n
depends on SPARSEMEM
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index ed18cae..3a9b770 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -63,27 +63,6 @@
8 - SIGSEGV faults
16 - SIGBUS faults
-config DEBUG_RODATA
- bool "Write protect kernel text section"
- default n
- depends on DEBUG_KERNEL && MMU
- ---help---
- Mark the kernel text section as write-protected in the pagetables,
- in order to catch accidental (and incorrect) writes to such const
- data. This will cause the size of the kernel, plus up to 4MB, to
- be mapped as pages instead of sections, which will increase TLB
- pressure.
- If in doubt, say "N".
-
-config DEBUG_RODATA_TEST
- bool "Testcase for the DEBUG_RODATA feature"
- depends on DEBUG_RODATA
- default n
- ---help---
- This option enables a testcase for the DEBUG_RODATA
- feature.
- If in doubt, say "N"
-
# These options are only for real kernel hackers who want to get their hands dirty.
config DEBUG_LL
bool "Kernel low-level debugging functions (read help!)"
diff --git a/arch/arm/boot/dts/dsi-panel-toshiba-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-toshiba-720p-video.dtsi
index 1c3cf29..42f6033 100644
--- a/arch/arm/boot/dts/dsi-panel-toshiba-720p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-toshiba-720p-video.dtsi
@@ -25,7 +25,7 @@
qcom,mdss-pan-porch-values = <32 12 144 3 4 9>;
qcom,mdss-pan-underflow-clr = <0xff>;
qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
- qcom,mdss-pan-bl-levels = <1 255>;
+ qcom,mdss-pan-bl-levels = <1 4095>;
qcom,mdss-pan-dsi-mode = <0>;
qcom,mdss-pan-dsi-h-pulse-mode = <0>;
qcom,mdss-pan-dsi-h-power-stop = <0 0 0>;
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index de23f4c..44ece9e 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -22,6 +22,136 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0>,
+ <0x0 0x8 0x1>,
+ <0x0 0x8 0x4>;
+ interrupt-names = "kpdpwr", "resin", "resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+
+ qcom,pon_2 {
+ qcom,pon-type = <1>;
+ qcom,support-reset = <1>;
+ qcom,pull-up = <1>;
+ qcom,s1-timer = <0>;
+ qcom,s2-timer = <2000>;
+ qcom,s2-type = <1>;
+ linux,code = <114>;
+ };
+ };
+
+ pm8226_chg: qcom,charger {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-charger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+
+ qcom,chg-vddmax-mv = <4200>;
+ qcom,chg-vddsafe-mv = <4200>;
+ qcom,chg-vinmin-mv = <4200>;
+ qcom,chg-vbatdet-mv = <4100>;
+ qcom,chg-ibatmax-ma = <1500>;
+ qcom,chg-ibatterm-ma = <200>;
+ qcom,chg-ibatsafe-ma = <1500>;
+ qcom,chg-thermal-mitigation = <1500 700 600 325>;
+
+ qcom,chg-chgr@1000 {
+ status = "disabled";
+ reg = <0x1000 0x100>;
+ interrupts = <0x0 0x10 0x0>,
+ <0x0 0x10 0x1>,
+ <0x0 0x10 0x2>,
+ <0x0 0x10 0x3>,
+ <0x0 0x10 0x4>,
+ <0x0 0x10 0x5>,
+ <0x0 0x10 0x6>,
+ <0x0 0x10 0x7>;
+
+ interrupt-names = "vbat-det-lo",
+ "vbat-det-hi",
+ "chgwdog",
+ "state-change",
+ "trkl-chg-on",
+ "fast-chg-on",
+ "chg-failed",
+ "chg-done";
+ };
+
+ qcom,chg-buck@1100 {
+ status = "disabled";
+ reg = <0x1100 0x100>;
+ interrupts = <0x0 0x11 0x0>,
+ <0x0 0x11 0x1>,
+ <0x0 0x11 0x2>,
+ <0x0 0x11 0x3>,
+ <0x0 0x11 0x4>,
+ <0x0 0x11 0x5>,
+ <0x0 0x11 0x6>;
+
+ interrupt-names = "vbat-ov",
+ "vreg-ov",
+ "overtemp",
+ "vchg-loop",
+ "ichg-loop",
+ "ibat-loop",
+ "vdd-loop";
+ };
+
+ qcom,chg-bat-if@1200 {
+ status = "disabled";
+ reg = <0x1200 0x100>;
+ interrupts = <0x0 0x12 0x0>,
+ <0x0 0x12 0x1>,
+ <0x0 0x12 0x2>,
+ <0x0 0x12 0x3>,
+ <0x0 0x12 0x4>;
+
+ interrupt-names = "batt-pres",
+ "bat-temp-ok",
+ "bat-fet-on",
+ "vcp-on",
+ "psi";
+
+ };
+
+ qcom,chg-usb-chgpth@1300 {
+ status = "disabled";
+ reg = <0x1300 0x100>;
+ interrupts = <0 0x13 0x0>,
+ <0 0x13 0x1>,
+ <0x0 0x13 0x2>;
+
+ interrupt-names = "coarse-det-usb",
+ "usbin-valid",
+ "chg-gone";
+ };
+
+ qcom,chg-boost@1500 {
+ status = "disabled";
+ reg = <0x1500 0x100>;
+ interrupts = <0x0 0x15 0x0>,
+ <0x0 0x15 0x1>;
+
+ interrupt-names = "boost-pwr-ok",
+ "limit-error";
+ };
+
+ qcom,chg-misc@1600 {
+ status = "disabled";
+ reg = <0x1600 0x100>;
+ };
+ };
+
pm8226_gpios: gpios {
spmi-dev-container;
compatible = "qcom,qpnp-pin";
@@ -471,6 +601,12 @@
status = "disabled";
};
+ qcom,leds@d800 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd800 0x100>;
+ label = "wled";
+ };
+
regulator@8000 {
regulator-name = "8226_lvs1";
reg = <0x8000 0x100>;
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 54f603d..892afbb 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -58,6 +58,28 @@
};
};
+ bif_ctrl: qcom,bsi@1b00 {
+ compatible = "qcom,qpnp-bsi";
+ reg = <0x1b00 0x100>,
+ <0x1208 0x1>;
+ reg-names = "bsi-base", "batt-id-status";
+ label = "pm8941-bsi";
+ interrupts = <0x0 0x1b 0x0>,
+ <0x0 0x1b 0x1>,
+ <0x0 0x1b 0x2>,
+ <0x0 0x12 0x0>;
+ interrupt-names = "err",
+ "rx",
+ "tx",
+ "batt-present";
+ qcom,channel-num = <0x31>;
+ qcom,pullup-ohms = <100000>;
+ qcom,vref-microvolts = <1800000>;
+ qcom,min-clock-period = <1000>;
+ qcom,max-clock-period = <160000>;
+ qcom,sample-rate = <4>;
+ };
+
pm8941_bms: qcom,bms {
spmi-dev-container;
compatible = "qcom,qpnp-bms";
diff --git a/arch/arm/boot/dts/msm8226-bus.dtsi b/arch/arm/boot/dts/msm8226-bus.dtsi
index 28d0840..750e591 100644
--- a/arch/arm/boot/dts/msm8226-bus.dtsi
+++ b/arch/arm/boot/dts/msm8226-bus.dtsi
@@ -1049,6 +1049,8 @@
qcom,ws = <10000>;
qcom,mas-hw-id = <3>;
qcom,slv-hw-id = <2>;
+ qcom,mode = "Bypass";
+ qcom,hw-sel = "RPM";
};
slv-ebi-ch0 {
diff --git a/arch/arm/boot/dts/msm8226-cdp.dts b/arch/arm/boot/dts/msm8226-cdp.dts
index 1c431e8..a2707ee 100644
--- a/arch/arm/boot/dts/msm8226-cdp.dts
+++ b/arch/arm/boot/dts/msm8226-cdp.dts
@@ -22,3 +22,142 @@
status = "ok";
};
};
+
+&sdcc1 {
+ vdd-supply = <&pm8226_l17>;
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ vdd-io-supply = <&pm8226_l6>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ 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 = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,nonremovable;
+
+ status = "ok";
+};
+
+&sdcc2 {
+ vdd-supply = <&pm8226_l18>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ vdd-io-supply = <&pm8226_l21>;
+ 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,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 = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <600>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdcc2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 220 0
+ 2 &msmgpio 38 0x3>;
+ interrupt-names = "core_irq", "bam_irq", "status_irq";
+ cd-gpios = <&msmgpio 38 0x1>;
+
+ status = "ok";
+};
+
+&spmi_bus {
+ qcom,pm8226@1 {
+ qcom,leds@d800 {
+ status = "okay";
+ qcom,wled_0 {
+ label = "wled";
+ linux,name = "wled:backlight";
+ linux,default-trigger = "bkl-trigger";
+ qcom,cs-out-en;
+ qcom,op-fdbck;
+ qcom,default-state = "on";
+ qcom,max-current = <25>;
+ qcom,ctrl-delay-us = <0>;
+ qcom,boost-curr-lim = <3>;
+ qcom,cp-sel = <0>;
+ qcom,switch-freq = <2>;
+ qcom,ovp-val = <2>;
+ qcom,num-strings = <1>;
+ qcom,id = <0>;
+ };
+ };
+ };
+};
+
+&pm8226_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ };
+};
+
+&pm8226_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+
+ mpp@a400 { /* MPP 5 */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ };
+
+ mpp@a600 { /* MPP 7 */
+ };
+
+ mpp@a700 { /* MPP 8 */
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-coresight.dtsi b/arch/arm/boot/dts/msm8226-coresight.dtsi
new file mode 100644
index 0000000..8d5d23c
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226-coresight.dtsi
@@ -0,0 +1,156 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/ {
+ tmc_etr: tmc@fc322000 {
+ compatible = "arm,coresight-tmc";
+ reg = <0xfc322000 0x1000>,
+ <0xfc37c000 0x3000>;
+ reg-names = "tmc-etr-base", "tmc-etr-bam-base";
+
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
+
+ coresight-id = <0>;
+ coresight-name = "coresight-tmc-etr";
+ coresight-nr-inports = <1>;
+ };
+
+ tpiu: tpiu@fc318000 {
+ compatible = "arm,coresight-tpiu";
+ reg = <0xfc318000 0x1000>;
+ reg-names = "tpiu-base";
+
+ coresight-id = <1>;
+ coresight-name = "coresight-tpiu";
+ coresight-nr-inports = <1>;
+ };
+
+ replicator: replicator@fc31c000 {
+ compatible = "qcom,coresight-replicator";
+ reg = <0xfc31c000 0x1000>;
+ reg-names = "replicator-base";
+
+ coresight-id = <2>;
+ coresight-name = "coresight-replicator";
+ coresight-nr-inports = <1>;
+ coresight-outports = <0 1>;
+ coresight-child-list = <&tmc_etr &tpiu>;
+ coresight-child-ports = <0 0>;
+ };
+
+ tmc_etf: tmc@fc307000 {
+ compatible = "arm,coresight-tmc";
+ reg = <0xfc307000 0x1000>;
+ reg-names = "tmc-etf-base";
+
+ coresight-id = <3>;
+ coresight-name = "coresight-tmc-etf";
+ coresight-nr-inports = <1>;
+ coresight-outports = <0>;
+ coresight-child-list = <&replicator>;
+ coresight-child-ports = <0>;
+ coresight-default-sink;
+ };
+
+ funnel_merg: funnel@fc31b000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc31b000 0x1000>;
+ reg-names = "funnel-merg-base";
+
+ coresight-id = <4>;
+ coresight-name = "coresight-funnel-merg";
+ coresight-nr-inports = <2>;
+ coresight-outports = <0>;
+ coresight-child-list = <&tmc_etf>;
+ coresight-child-ports = <0>;
+ };
+
+ funnel_in0: funnel@fc319000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc319000 0x1000>;
+ reg-names = "funnel-in0-base";
+
+ coresight-id = <5>;
+ coresight-name = "coresight-funnel-in0";
+ coresight-nr-inports = <8>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_merg>;
+ coresight-child-ports = <0>;
+ };
+
+ funnel_in1: funnel@fc31a000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc31a000 0x1000>;
+ reg-names = "funnel-in1-base";
+
+ coresight-id = <6>;
+ coresight-name = "coresight-funnel-in1";
+ coresight-nr-inports = <8>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_merg>;
+ coresight-child-ports = <1>;
+ };
+
+ funnel_a7ss: funnel@fc345000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc345000 0x1000>;
+ reg-names = "funnel-a7ss-base";
+
+ coresight-id = <7>;
+ coresight-name = "coresight-funnel-a7ss";
+ coresight-nr-inports = <4>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in1>;
+ coresight-child-ports = <5>;
+ };
+
+ funnel_mmss: funnel@fc364000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc364000 0x1000>;
+ reg-names = "funnel-mmss-base";
+
+
+ coresight-id = <8>;
+ coresight-name = "coresight-funnel-mmss";
+ coresight-nr-inports = <8>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in1>;
+ coresight-child-ports = <1>;
+ };
+
+ stm: stm@fc321000 {
+ compatible = "arm,coresight-stm";
+ reg = <0xfc321000 0x1000>,
+ <0xfa280000 0x180000>;
+ reg-names = "stm-base", "stm-data-base";
+
+ coresight-id = <9>;
+ coresight-name = "coresight-stm";
+ coresight-nr-inports = <0>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in1>;
+ coresight-child-ports = <7>;
+ };
+
+ csr: csr@fc302000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0xfc302000 0x1000>;
+ reg-names = "csr-base";
+
+ coresight-id = <10>;
+ coresight-name = "coresight-csr";
+ coresight-nr-inports = <0>;
+
+ qcom,blk-size = <3>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-iommu.dtsi b/arch/arm/boot/dts/msm8226-iommu.dtsi
index 51c2f38..20e1444 100644
--- a/arch/arm/boot/dts/msm8226-iommu.dtsi
+++ b/arch/arm/boot/dts/msm8226-iommu.dtsi
@@ -15,21 +15,21 @@
&jpeg_iommu {
status = "ok";
- qcom,iommu-bfb-regs = <0x604c
- 0x6050
- 0x6514
- 0x6540
- 0x656c
- 0x6314
- 0x6394
- 0x6414
- 0x60ac
- 0x615c
- 0x620c
- 0x6008
- 0x600c
- 0x6010
- 0x6014>;
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014>;
qcom,iommu-bfb-data = <0x0000ffff
0x00000000
@@ -53,24 +53,24 @@
/* HACK: set to -1 during pre-si due to lack of TZ */
qcom,iommu-secure-id = <0xFFFFFFFF>;
- qcom,iommu-bfb-regs = <0x604c
- 0x6050
- 0x6514
- 0x6540
- 0x656c
- 0x60ac
- 0x615c
- 0x620c
- 0x6314
- 0x6394
- 0x6414
- 0x6008
- 0x600c
- 0x6010
- 0x6014
- 0x6018
- 0x601c
- 0x6020>;
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020>;
qcom,iommu-bfb-data = <0xffffffff
0x00000000
@@ -97,30 +97,30 @@
/* HACK: set to -1 during pre-si due to lack of TZ */
qcom,iommu-secure-id = <0xFFFFFFFF>;
- qcom,iommu-bfb-regs = <0x604c
- 0x6050
- 0x6514
- 0x6540
- 0x656c
- 0x60ac
- 0x615c
- 0x620c
- 0x6314
- 0x6394
- 0x6414
- 0x6008
- 0x600c
- 0x6010
- 0x6014
- 0x6018
- 0x601c
- 0x6020
- 0x6024
- 0x6028
- 0x602c
- 0x6030
- 0x6034
- 0x6038>;
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020
+ 0x2024
+ 0x2028
+ 0x202c
+ 0x2030
+ 0x2034
+ 0x2038>;
qcom,iommu-bfb-data = <0xffffffff
0xffffffff
@@ -159,18 +159,18 @@
&kgsl_iommu {
status = "ok";
- qcom,iommu-bfb-regs = <0x604c
- 0x6050
- 0x6514
- 0x6540
- 0x656c
- 0x60ac
- 0x615c
- 0x620c
- 0x6314
- 0x6394
- 0x6414
- 0x6008>;
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008>;
qcom,iommu-bfb-data = <0x00000003
0x0
@@ -189,24 +189,24 @@
&vfe_iommu {
status = "ok";
- qcom,iommu-bfb-regs = <0x604c
- 0x6050
- 0x6514
- 0x6540
- 0x656c
- 0x6314
- 0x6394
- 0x6414
- 0x60ac
- 0x615c
- 0x620c
- 0x6008
- 0x600c
- 0x6010
- 0x6014
- 0x6018
- 0x601c
- 0x6020>;
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020>;
qcom,iommu-bfb-data = <0xffffffff
0x00000000
diff --git a/arch/arm/boot/dts/msm8226-mdss.dtsi b/arch/arm/boot/dts/msm8226-mdss.dtsi
new file mode 100644
index 0000000..1691743
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226-mdss.dtsi
@@ -0,0 +1,62 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/ {
+ qcom,mdss_mdp@fd900000 {
+ compatible = "qcom,mdss_mdp";
+ reg = <0xfd900000 0x22100>,
+ <0xfd924000 0x1000>;
+ reg-names = "mdp_phys", "vbif_phys";
+ interrupts = <0 72 0>;
+ vdd-supply = <&gdsc_mdss>;
+
+ qcom,mdss-pipe-vig-off = <0x00001200>;
+ qcom,mdss-pipe-rgb-off = <0x00001E00>;
+ qcom,mdss-pipe-dma-off = <0x00002A00>;
+ qcom,mdss-pipe-vig-fetch-id = <1>;
+ qcom,mdss-pipe-rgb-fetch-id = <7>;
+ qcom,mdss-pipe-dma-fetch-id = <4>;
+ qcom,mdss-smp-data = <7 4096>;
+
+ qcom,mdss-ctl-off = <0x00000600 0x00000700>;
+ qcom,mdss-mixer-intf-off = <0x00003200>;
+ qcom,mdss-mixer-wb-off = <0x00003E00>;
+ qcom,mdss-dspp-off = <0x00004600>;
+ qcom,mdss-wb-off = <0x00011100 0x00013100>;
+ qcom,mdss-intf-off = <0x00000000 0x00021300>;
+
+ qcom,vbif-settings = <0x004 0x00000001>,
+ <0x0D8 0x00000707>,
+ <0x124 0x00000003>;
+ qcom,mdp-settings = <0x02E0 0x000000A9>,
+ <0x02E4 0x00000055>;
+
+ mdss_fb0: qcom,mdss_fb_primary {
+ cell-index = <0>;
+ compatible = "qcom,mdss-fb";
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x800000>;
+ };
+
+ mdss_fb1: qcom,mdss_fb_wfd {
+ cell-index = <1>;
+ compatible = "qcom,mdss-fb";
+ };
+ };
+
+ qcom,mdss_wb_panel {
+ compatible = "qcom,mdss_wb";
+ qcom,mdss_pan_res = <1280 720>;
+ qcom,mdss_pan_bpp = <24>;
+ qcom,mdss-fb-map = <&mdss_fb1>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-mtp.dts b/arch/arm/boot/dts/msm8226-mtp.dts
index ef0fdc0..43f6685 100644
--- a/arch/arm/boot/dts/msm8226-mtp.dts
+++ b/arch/arm/boot/dts/msm8226-mtp.dts
@@ -22,3 +22,139 @@
status = "ok";
};
};
+
+&sdcc1 {
+ vdd-supply = <&pm8226_l17>;
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ vdd-io-supply = <&pm8226_l6>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ 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 = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,nonremovable;
+
+ status = "ok";
+};
+
+&sdcc2 {
+ vdd-supply = <&pm8226_l18>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ vdd-io-supply = <&pm8226_l21>;
+ 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,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 = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <600>; #address-cells = <0>; interrupt-parent = <&sdcc2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 220 0
+ 2 &msmgpio 38 0x3>;
+ interrupt-names = "core_irq", "bam_irq", "status_irq";
+ cd-gpios = <&msmgpio 38 0x1>;
+
+ status = "ok";
+};
+
+&spmi_bus {
+ qcom,pm8226@1 {
+ qcom,leds@d800 {
+ status = "okay";
+ qcom,wled_0 {
+ label = "wled";
+ linux,name = "wled:backlight";
+ linux,default-trigger = "bkl-trigger";
+ qcom,cs-out-en;
+ qcom,op-fdbck;
+ qcom,default-state = "on";
+ qcom,max-current = <25>;
+ qcom,ctrl-delay-us = <0>;
+ qcom,boost-curr-lim = <3>;
+ qcom,cp-sel = <0>;
+ qcom,switch-freq = <2>;
+ qcom,ovp-val = <2>;
+ qcom,num-strings = <1>;
+ qcom,id = <0>;
+ };
+ };
+ };
+};
+
+&pm8226_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ };
+};
+
+&pm8226_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+
+ mpp@a400 { /* MPP 5 */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ };
+
+ mpp@a600 { /* MPP 7 */
+ };
+
+ mpp@a700 { /* MPP 8 */
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-pm.dtsi b/arch/arm/boot/dts/msm8226-pm.dtsi
index 4937efe..6348f5a 100644
--- a/arch/arm/boot/dts/msm8226-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-pm.dtsi
@@ -309,10 +309,12 @@
qcom,gic-map = <47 172>, /* usb2_hsic_async_wakeup_irq */
<53 104>, /* mdss_irq */
<62 222>, /* ee0_krait_hlos_spmi_periph_irq */
+ <0xff 56>, /* q6_wdog_expired_irq */
<0xff 57>, /* mss_to_apps_irq(0) */
<0xff 58>, /* mss_to_apps_irq(1) */
<0xff 59>, /* mss_to_apps_irq(2) */
<0xff 60>, /* mss_to_apps_irq(3) */
+ <0xff 61>, /* mss_a2_bam_irq */
<0xff 173>, /* o_wcss_apss_smd_hi */
<0xff 174>, /* o_wcss_apss_smd_med */
<0xff 175>, /* o_wcss_apss_smd_low */
@@ -320,17 +322,15 @@
<0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
<0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
<0xff 179>, /* o_wcss_apss_asic_intr
-
+ <0xff 181>, /* o_wcss_apss_wdog_bite_and_reset_rdy */
<0xff 188>, /* lpass_irq_out_apcs(0) */
<0xff 189>, /* lpass_irq_out_apcs(1) */
<0xff 190>, /* lpass_irq_out_apcs(2) */
<0xff 191>, /* lpass_irq_out_apcs(3) */
<0xff 192>, /* lpass_irq_out_apcs(4) */
- <0xff 193>, /* lpass_irq_out_apcs(5) */
- <0xff 194>, /* lpass_irq_out_apcs(6) */
- <0xff 195>, /* lpass_irq_out_apcs(7) */
- <0xff 196>, /* lpass_irq_out_apcs(8) */
- <0xff 197>, /* lpass_irq_out_apcs(9) */
+ <0xff 194>, /* lpass_irq_out_apcs[6] */
+ <0xff 195>, /* lpass_irq_out_apcs[7] */
+ <0xff 196>, /* lpass_irq_out_apcs[8] */
<0xff 200>, /* rpm_ipc(4) */
<0xff 201>, /* rpm_ipc(5) */
<0xff 202>, /* rpm_ipc(6) */
@@ -339,47 +339,54 @@
<0xff 205>, /* rpm_ipc(25) */
<0xff 206>, /* rpm_ipc(26) */
<0xff 207>, /* rpm_ipc(27) */
+ <0xff 258>, /* rpm_ipc(28) */
+ <0xff 259>, /* rpm_ipc(29) */
+ <0xff 275>, /* rpm_ipc(30) */
+ <0xff 276>, /* rpm_ipc(31) */
+ <0xff 269>, /* rpm_wdog_expired_irq */
<0xff 240>; /* summary_irq_kpss */
qcom,gpio-parent = <&msmgpio>;
- qcom,gpio-map = <3 102>,
- <4 1 >,
+ qcom,gpio-map = <3 1>,
+ <4 4 >,
<5 5 >,
<6 9 >,
- <7 18>,
- <8 20>,
- <9 24>,
+ <7 13>,
+ <8 17>,
+ <9 21>,
<10 27>,
- <11 28>,
- <12 34>,
- <13 35>,
- <14 37>,
- <15 42>,
- <16 44>,
- <17 46>,
- <18 50>,
- <19 54>,
- <20 59>,
- <21 61>,
- <22 62>,
- <23 64>,
- <24 65>,
- <25 66>,
- <26 67>,
- <27 68>,
- <28 71>,
- <29 72>,
- <30 73>,
- <31 74>,
- <32 75>,
- <33 77>,
- <34 79>,
- <35 80>,
- <36 82>,
- <37 86>,
- <38 92>,
- <39 93>,
- <40 95>;
+ <11 29>,
+ <12 31>,
+ <13 33>,
+ <14 35>,
+ <15 37>,
+ <16 38>,
+ <17 39>,
+ <18 41>,
+ <19 46>,
+ <20 48>,
+ <21 49>,
+ <22 50>,
+ <23 51>,
+ <24 52>,
+ <25 54>,
+ <26 62>,
+ <27 63>,
+ <28 64>,
+ <29 65>,
+ <30 66>,
+ <31 67>,
+ <32 68>,
+ <33 69>,
+ <34 71>,
+ <35 72>,
+ <36 106>,
+ <37 107>,
+ <38 108>,
+ <39 109>,
+ <40 110>,
+ <54 111>,
+ <55 113>;
};
qcom,pm-8x60@fe805664 {
diff --git a/arch/arm/boot/dts/msm8226-qrd.dts b/arch/arm/boot/dts/msm8226-qrd.dts
index 7909435..482a5da 100644
--- a/arch/arm/boot/dts/msm8226-qrd.dts
+++ b/arch/arm/boot/dts/msm8226-qrd.dts
@@ -22,3 +22,142 @@
status = "ok";
};
};
+
+&sdcc1 {
+ vdd-supply = <&pm8226_l17>;
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ vdd-io-supply = <&pm8226_l6>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ 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 = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,nonremovable;
+
+ status = "ok";
+};
+
+&sdcc2 {
+ vdd-supply = <&pm8226_l18>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ vdd-io-supply = <&pm8226_l21>;
+ 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,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 = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <600>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdcc2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 220 0
+ 2 &msmgpio 38 0x3>;
+ interrupt-names = "core_irq", "bam_irq", "status_irq";
+ cd-gpios = <&msmgpio 38 0x1>;
+
+ status = "ok";
+};
+
+&spmi_bus {
+ qcom,pm8226@1 {
+ qcom,leds@d800 {
+ status = "okay";
+ qcom,wled_0 {
+ label = "wled";
+ linux,name = "wled:backlight";
+ linux,default-trigger = "bkl-trigger";
+ qcom,cs-out-en;
+ qcom,op-fdbck;
+ qcom,default-state = "on";
+ qcom,max-current = <25>;
+ qcom,ctrl-delay-us = <0>;
+ qcom,boost-curr-lim = <3>;
+ qcom,cp-sel = <0>;
+ qcom,switch-freq = <2>;
+ qcom,ovp-val = <2>;
+ qcom,num-strings = <1>;
+ qcom,id = <0>;
+ };
+ };
+ };
+};
+
+&pm8226_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ };
+};
+
+&pm8226_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+
+ mpp@a400 { /* MPP 5 */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ };
+
+ mpp@a600 { /* MPP 7 */
+ };
+
+ mpp@a700 { /* MPP 8 */
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 3533d19..8111b40 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -18,6 +18,8 @@
/include/ "msm8226-smp2p.dtsi"
/include/ "msm8226-gpu.dtsi"
/include/ "msm8226-bus.dtsi"
+/include/ "msm8226-mdss.dtsi"
+/include/ "msm8226-coresight.dtsi"
/ {
model = "Qualcomm MSM 8226";
@@ -85,14 +87,23 @@
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0>, <0 140 0>;
interrupt-names = "core_irq", "async_irq";
- HSUSB_VDDCX-supply = <&pm8226_s1>;
+ hsusb_vdd_dig-supply = <&pm8226_s1_corner>;
HSUSB_1p8-supply = <&pm8226_l10>;
HSUSB_3p3-supply = <&pm8226_l20>;
+ qcom,vdd-voltage-level = <1 5 7>;
qcom,hsusb-otg-phy-type = <2>;
qcom,hsusb-otg-mode = <1>;
- qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-otg-control = <2>;
qcom,hsusb-otg-disable-reset;
+
+ qcom,msm-bus,name = "usb2";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <87 512 0 0>,
+ <87 512 60000 960000>;
};
android_usb@fe8050c8 {
@@ -337,7 +348,7 @@
qcom,smem@fa00000 {
compatible = "qcom,smem";
reg = <0xfa00000 0x200000>,
- <0xfa006000 0x1000>,
+ <0xf9011000 0x1000>,
<0xfc428000 0x4000>;
reg-names = "smem", "irq-reg-base", "aux-mem1";
@@ -541,8 +552,8 @@
<0xfc4b8000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
- qcom,sensors = <6>;
- qcom,slope = <3200 3200 3200 3200 3200 3200>;
+ qcom,sensors = <4>;
+ qcom,slope = <2901 2846 3038 2955>;
qcom,calib-mode = "fuse_map2";
};
@@ -578,6 +589,17 @@
qcom,bam-producer-pipe-index = <13>;
};
+ qcom,bam_dmux@fc834000 {
+ compatible = "qcom,bam_dmux";
+ reg = <0xfc834000 0x7000>;
+ interrupts = <0 29 1>;
+ };
+
+ qcom,msm-rtb {
+ compatible = "qcom,msm-rtb";
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
+ };
};
&gdsc_venus {
@@ -695,4 +717,32 @@
qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
+
+};
+
+&pm8226_chg {
+ status = "ok";
+
+ qcom,chg-charging-disabled;
+ qcom,chg-use-default-batt-values;
+
+ qcom,chg-chgr@1000 {
+ status = "ok";
+ };
+
+ qcom,chg-buck@1100 {
+ status = "ok";
+ };
+
+ qcom,chg-usb-chgpth@1300 {
+ status = "ok";
+ };
+
+ qcom,chg-boost@1500 {
+ status = "ok";
+ };
+
+ qcom,chg-misc@1600 {
+ status = "ok";
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-ion.dtsi b/arch/arm/boot/dts/msm8610-ion.dtsi
index 0abaca5..107961d 100644
--- a/arch/arm/boot/dts/msm8610-ion.dtsi
+++ b/arch/arm/boot/dts/msm8610-ion.dtsi
@@ -20,14 +20,6 @@
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>;
};
@@ -37,7 +29,7 @@
reg = <27>;
qcom,heap-align = <0x1000>;
qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x780000>;
+ qcom,memory-reservation-size = <0x100000>;
};
qcom,ion-heap@28 { /* AUDIO HEAP */
@@ -47,16 +39,6 @@
qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
qcom,memory-reservation-size = <0x314000>;
};
-
- qcom,ion-heap@29 { /* FIRMWARE HEAP */
- compatible = "qcom,msm-ion-reserve";
- reg = <29>;
- qcom,heap-align = <0x20000>;
- qcom,heap-adjacent = <8>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0xA00000>;
- };
-
};
};
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index ce6011b..2dff4c7 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -166,7 +166,7 @@
qcom,smem@d600000 {
compatible = "qcom,smem";
reg = <0xd600000 0x200000>,
- <0xfa006000 0x1000>,
+ <0xf9011000 0x1000>,
<0xfc428000 0x4000>;
reg-names = "smem", "irq-reg-base", "aux-mem1";
diff --git a/arch/arm/boot/dts/msm8974-bus.dtsi b/arch/arm/boot/dts/msm8974-bus.dtsi
index ba4a14e8..8f58c3e 100644
--- a/arch/arm/boot/dts/msm8974-bus.dtsi
+++ b/arch/arm/boot/dts/msm8974-bus.dtsi
@@ -1217,6 +1217,8 @@
qcom,ws = <10000>;
qcom,mas-hw-id = <3>;
qcom,slv-hw-id = <2>;
+ qcom,mode = "Bypass";
+ qcom,hw-sel = "RPM";
};
slv-ebi-ch0 {
diff --git a/arch/arm/boot/dts/msm8974-camera.dtsi b/arch/arm/boot/dts/msm8974-camera.dtsi
index ea99aa3..ebb3912 100644
--- a/arch/arm/boot/dts/msm8974-camera.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera.dtsi
@@ -159,8 +159,9 @@
cell-index = <0>;
compatible = "qcom,cpp";
reg = <0xfda04000 0x100>,
- <0xfda40000 0x200>;
- reg-names = "cpp", "cpp_vbif";
+ <0xfda40000 0x200>,
+ <0xfda18000 0x008>;
+ reg-names = "cpp", "cpp_vbif", "cpp_hw";
interrupts = <0 49 0>;
interrupt-names = "cpp";
vdd-supply = <&gdsc_vfe>;
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 0acfaf6..ad26061 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -294,8 +294,49 @@
wp-gpios = <&pm8941_gpios 29 0x1>;
};
+&sdhc_1 {
+ vdd-supply = <&pm8941_l20>;
+ vdd-io-supply = <&pm8941_s3>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ 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,nonremovable;
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm8941_l21>;
+ vdd-io-supply = <&pm8941_l13>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ 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,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 */
+};
+
&uart7 {
status = "ok";
+ qcom,tx-gpio = <&msmgpio 41 0x00>;
+ qcom,rx-gpio = <&msmgpio 42 0x00>;
+ qcom,cts-gpio = <&msmgpio 43 0x00>;
+ qcom,rfr-gpio = <&msmgpio 44 0x00>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 92a6e01..dbb8958 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -291,6 +291,43 @@
cd-gpios = <&msmgpio 62 0x1>;
};
+&sdhc_1 {
+ vdd-supply = <&pm8941_l20>;
+ vdd-io-supply = <&pm8941_s3>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ 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,nonremovable;
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm8941_l21>;
+ vdd-io-supply = <&pm8941_l13>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ 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,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 */
+};
+
&usb3 {
qcom,otg-capability;
};
diff --git a/arch/arm/boot/dts/msm8974-ion.dtsi b/arch/arm/boot/dts/msm8974-ion.dtsi
index f55cff2..dfa22c1 100644
--- a/arch/arm/boot/dts/msm8974-ion.dtsi
+++ b/arch/arm/boot/dts/msm8974-ion.dtsi
@@ -24,8 +24,7 @@
compatible = "qcom,msm-ion-reserve";
reg = <8>;
qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x7800000>;
+ linux,contiguous-region = <&secure_mem>;
};
qcom,ion-heap@25 { /* IOMMU HEAP */
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 68fed68..4dd92b98 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -380,7 +380,7 @@
};
&usb3 {
- qcom,charging-disabled;
+ qcom,otg-capability;
};
&pm8941_mvs1 {
@@ -708,3 +708,70 @@
};
};
};
+
+&pm8941_chg {
+ status = "ok";
+
+ qcom,chg-charging-disabled;
+
+ qcom,chg-chgr@1000 {
+ status = "ok";
+ };
+
+ qcom,chg-buck@1100 {
+ status = "ok";
+ };
+
+ qcom,chg-usb-chgpth@1300 {
+ status = "ok";
+ };
+
+ qcom,chg-dc-chgpth@1400 {
+ status = "ok";
+ };
+
+ qcom,chg-boost@1500 {
+ status = "ok";
+ };
+
+ qcom,chg-misc@1600 {
+ status = "ok";
+ };
+};
+
+&sdhc_1 {
+ vdd-supply = <&pm8941_l20>;
+ vdd-io-supply = <&pm8941_s3>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ 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,nonremovable;
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm8941_l21>;
+ vdd-io-supply = <&pm8941_l13>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ 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,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 */
+};
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index 0b95419..f382c3e 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -27,6 +27,7 @@
qcom,mdss-pipe-vig-fetch-id = <1 4 7>;
qcom,mdss-pipe-rgb-fetch-id = <16 17 18>;
qcom,mdss-pipe-dma-fetch-id = <10 13>;
+ qcom,mdss-smp-data = <22 4096>;
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
0x00000900 0x0000A00>;
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index c6935f4..d3d53dd 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -262,6 +262,43 @@
cd-gpios = <&msmgpio 62 0x1>;
};
+&sdhc_1 {
+ vdd-supply = <&pm8941_l20>;
+ vdd-io-supply = <&pm8941_s3>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ 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,nonremovable;
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm8941_l21>;
+ vdd-io-supply = <&pm8941_l13>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ 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,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 */
+};
+
&usb_otg {
qcom,hsusb-otg-otg-control = <2>;
};
diff --git a/arch/arm/boot/dts/msm8974-regulator.dtsi b/arch/arm/boot/dts/msm8974-regulator.dtsi
index 1a6d9ba..5eff79c 100644
--- a/arch/arm/boot/dts/msm8974-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8974-regulator.dtsi
@@ -423,68 +423,77 @@
};
/ {
- krait0_vreg: regulator@f9088000 {
- compatible = "qcom,krait-regulator";
- regulator-name = "krait0";
- reg = <0xf9088000 0x1000>, /* APCS_ALIAS0_KPSS_ACS */
- <0xf908a800 0x1000>; /* APCS_ALIAS0_KPSS_MDD */
- reg-names = "acs", "mdd";
- regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <1100000>;
- qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <675000>;
- qcom,ldo-default-voltage = <750000>;
- qcom,ldo-threshold-voltage = <850000>;
- qcom,ldo-delta-voltage = <50000>;
- qcom,cpu-num = <0>;
- };
+ krait_pdn: krait-pdn@f9011000 {
+ reg = <0xf9011000 0x1000>;
+ reg-names = "apcs_gcc";
+ compatible = "qcom,krait-pdn";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
- krait1_vreg: regulator@f9098000 {
- compatible = "qcom,krait-regulator";
- regulator-name = "krait1";
- reg = <0xf9098000 0x1000>, /* APCS_ALIAS1_KPSS_ACS */
- <0xf909a800 0x1000>; /* APCS_ALIAS1_KPSS_MDD */
- reg-names = "acs", "mdd";
- regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <1100000>;
- qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <675000>;
- qcom,ldo-default-voltage = <750000>;
- qcom,ldo-threshold-voltage = <850000>;
- qcom,ldo-delta-voltage = <50000>;
- qcom,cpu-num = <1>;
- };
+ krait0_vreg: regulator@f9088000 {
+ compatible = "qcom,krait-regulator";
+ regulator-name = "krait0";
+ reg = <0xf9088000 0x1000>, /* APCS_ALIAS0_KPSS_ACS */
+ <0xf908a800 0x1000>; /* APCS_ALIAS0_KPSS_MDD */
+ reg-names = "acs", "mdd";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,headroom-voltage = <150000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
+ qcom,cpu-num = <0>;
+ };
- krait2_vreg: regulator@f90a8000 {
- compatible = "qcom,krait-regulator";
- regulator-name = "krait2";
- reg = <0xf90a8000 0x1000>, /* APCS_ALIAS2_KPSS_ACS */
- <0xf90aa800 0x1000>; /* APCS_ALIAS2_KPSS_MDD */
- reg-names = "acs", "mdd";
- regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <1100000>;
- qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <675000>;
- qcom,ldo-default-voltage = <750000>;
- qcom,ldo-threshold-voltage = <850000>;
- qcom,ldo-delta-voltage = <50000>;
- qcom,cpu-num = <2>;
- };
+ krait1_vreg: regulator@f9098000 {
+ compatible = "qcom,krait-regulator";
+ regulator-name = "krait1";
+ reg = <0xf9098000 0x1000>, /* APCS_ALIAS1_KPSS_ACS */
+ <0xf909a800 0x1000>; /* APCS_ALIAS1_KPSS_MDD */
+ reg-names = "acs", "mdd";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,headroom-voltage = <150000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
+ qcom,cpu-num = <1>;
+ };
- krait3_vreg: regulator@f90b8000 {
- compatible = "qcom,krait-regulator";
- regulator-name = "krait3";
- reg = <0xf90b8000 0x1000>, /* APCS_ALIAS3_KPSS_ACS */
- <0xf90ba800 0x1000>; /* APCS_ALIAS3_KPSS_MDD */
- reg-names = "acs", "mdd";
- regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <1100000>;
- qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <675000>;
- qcom,ldo-default-voltage = <750000>;
- qcom,ldo-threshold-voltage = <850000>;
- qcom,ldo-delta-voltage = <50000>;
- qcom,cpu-num = <3>;
+ krait2_vreg: regulator@f90a8000 {
+ compatible = "qcom,krait-regulator";
+ regulator-name = "krait2";
+ reg = <0xf90a8000 0x1000>, /* APCS_ALIAS2_KPSS_ACS */
+ <0xf90aa800 0x1000>; /* APCS_ALIAS2_KPSS_MDD */
+ reg-names = "acs", "mdd";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,headroom-voltage = <150000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
+ qcom,cpu-num = <2>;
+ };
+
+ krait3_vreg: regulator@f90b8000 {
+ compatible = "qcom,krait-regulator";
+ regulator-name = "krait3";
+ reg = <0xf90b8000 0x1000>, /* APCS_ALIAS3_KPSS_ACS */
+ <0xf90ba800 0x1000>; /* APCS_ALIAS3_KPSS_MDD */
+ reg-names = "acs", "mdd";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,headroom-voltage = <150000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
+ qcom,cpu-num = <3>;
+ };
};
spi_eth_vreg: spi_eth_phy_vreg {
diff --git a/arch/arm/boot/dts/msm8974-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
similarity index 100%
rename from arch/arm/boot/dts/msm8974-pm.dtsi
rename to arch/arm/boot/dts/msm8974-v1-pm.dtsi
diff --git a/arch/arm/boot/dts/msm8974-v1.dtsi b/arch/arm/boot/dts/msm8974-v1.dtsi
index aed4daf..bccf0fe 100644
--- a/arch/arm/boot/dts/msm8974-v1.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1.dtsi
@@ -19,6 +19,7 @@
/include/ "msm8974.dtsi"
/include/ "msm8974-v1-iommu.dtsi"
/include/ "msm8974-v1-iommu-domains.dtsi"
+/include/ "msm8974-v1-pm.dtsi"
/ {
android_usb@fc42b0c8 {
@@ -110,4 +111,16 @@
<1010000 1818000>,
<1616000 2908800>,
<2020000 6400000>;
+ qcom,iommu-groups = <&venus_domain_ns &venus_domain_cp>;
+ qcom,iommu-group-buffer-types = <0xfff 0x1ff>;
+ qcom,buffer-type-tz-usage-table = <0x1 0x1>,
+ <0x1fe 0x2>;
+};
+
+&sfpb_spinlock {
+ status = "disable";
+};
+
+&ldrex_spinlock {
+ status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
new file mode 100644
index 0000000..4d98a1d
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -0,0 +1,426 @@
+/* Copyright (c) 2013, 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/ "skeleton.dtsi"
+
+/ {
+ qcom,spm@f9089000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9089000 0x1000>;
+ qcom,core-id = <0>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-avs-ctl = <0>;
+ qcom,saw2-avs-hysteresis = <0>;
+ qcom,saw2-avs-limit = <0>;
+ qcom,saw2-avs-dly= <0>;
+ qcom,saw2-spm-dly= <0x3C102800>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 03 d4 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 E0 03 6E 70 3B
+ E4 5B 82 3F 50 10 0B 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 50 80 60 70 10 E0 07 6E 70 3B
+ E4 5B 82 3F 50 10 0B 30 06 26 30 0F];
+ };
+
+ qcom,spm@f9099000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9099000 0x1000>;
+ qcom,core-id = <1>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-avs-ctl = <0>;
+ qcom,saw2-avs-hysteresis = <0>;
+ qcom,saw2-avs-limit = <0>;
+ qcom,saw2-avs-dly= <0>;
+ qcom,saw2-spm-dly= <0x3C102800>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 03 d4 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 E0 03 6E 70 3B
+ E4 5B 82 3F 50 10 0B 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 50 80 60 70 10 E0 07 6E 70 3B
+ E4 5B 82 3F 50 10 0B 30 06 26 30 0F];
+ };
+
+ qcom,spm@f90a9000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf90a9000 0x1000>;
+ qcom,core-id = <2>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-avs-ctl = <0>;
+ qcom,saw2-avs-hysteresis = <0>;
+ qcom,saw2-avs-limit = <0>;
+ qcom,saw2-avs-dly= <0>;
+ qcom,saw2-spm-dly= <0x3C102800>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 03 d4 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 E0 03 6E 70 3B
+ E4 5B 82 3F 50 10 0B 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 50 80 60 70 10 E0 07 6E 70 3B
+ E4 5B 82 3F 50 10 0B 30 06 26 30 0F];
+ };
+
+ qcom,spm@f90b9000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf90b9000 0x1000>;
+ qcom,core-id = <3>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-avs-ctl = <0>;
+ qcom,saw2-avs-hysteresis = <0>;
+ qcom,saw2-avs-limit = <0>;
+ qcom,saw2-avs-dly= <0>;
+ qcom,saw2-spm-dly= <0x3C102800>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 03 d4 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 E0 03 6E 70 3B
+ E4 5B 82 3F 50 10 0B 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 50 80 60 70 10 E0 07 6E 70 3B
+ E4 5B 82 3F 50 10 0B 30 06 26 30 0F];
+ };
+
+ qcom,spm@f9012000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9012000 0x1000>;
+ qcom,core-id = <0xffff>; /* L2/APCS SAW */
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x14>;
+ qcom,saw2-avs-ctl = <0>;
+ qcom,saw2-avs-hysteresis = <0>;
+ qcom,saw2-avs-limit = <0>;
+ qcom,saw2-avs-dly= <0>;
+ qcom,saw2-spm-dly= <0x3C102800>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-pmic-data0 = <0x02030080>;
+ qcom,saw2-pmic-data1 = <0x00030000>;
+ qcom,vctl-timeout-us = <50>;
+ qcom,vctl-port = <0x0>;
+ qcom,phase-port = <0x1>;
+ qcom,pfm-port = <0x2>;
+ qcom,saw2-spm-cmd-ret = [1f 00 20 03 22 00 0f];
+ qcom,saw2-spm-cmd-gdhs = [00 20 32 60 70 80 42 07 78 80 44 22 50
+ 3b 60 02 32 50 0f];
+ qcom,saw2-spm-cmd-pc = [00 10 32 60 70 80 b0 11 42 07 01 b0 78
+ 80 12 44 50 3b 60 02 32 50 0f];
+ };
+
+ qcom,lpm-resources {
+ compatible = "qcom,lpm-resources";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,lpm-resources@0 {
+ reg = <0x0>;
+ qcom,name = "vdd-dig";
+ qcom,resource-type = <0>;
+ qcom,type = <0x62706d73>; /* "smpb" */
+ qcom,id = <0x02>;
+ qcom,key = <0x6e726f63>; /* "corn" */
+ qcom,init-value = <5>; /* Super Turbo */
+ };
+
+ qcom,lpm-resources@1 {
+ reg = <0x1>;
+ qcom,name = "vdd-mem";
+ qcom,resource-type = <0>;
+ qcom,type = <0x62706d73>; /* "smpb" */
+ qcom,id = <0x01>;
+ qcom,key = <0x7675>; /* "uv" */
+ qcom,init-value = <1050000>; /* Super Turbo */
+ };
+
+ qcom,lpm-resources@2 {
+ reg = <0x2>;
+ qcom,name = "pxo";
+ qcom,resource-type = <0>;
+ qcom,type = <0x306b6c63>; /* "clk0" */
+ qcom,id = <0x00>;
+ qcom,key = <0x62616e45>; /* "Enab" */
+ qcom,init-value = <1>; /* On */
+ };
+
+ qcom,lpm-resources@3 {
+ reg = <0x3>;
+ qcom,name = "l2";
+ qcom,resource-type = <1>;
+ qcom,init-value = <2>; /* Retention */
+ };
+ };
+
+ qcom,lpm-levels {
+ compatible = "qcom,lpm-levels";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,lpm-level@0 {
+ reg = <0x0>;
+ qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <2>; /* Retention */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
+ qcom,irqs-detectable;
+ qcom.gpios-detectable;
+ qcom,latency-us = <1>;
+ qcom,ss-power = <784>;
+ qcom,energy-overhead = <190000>;
+ qcom,time-overhead = <100>;
+ };
+
+ qcom,lpm-level@1 {
+ reg = <0x1>;
+ qcom,mode = <4>; /* MSM_PM_SLEEP_MODE_RETENTION*/
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <2>; /* Retention */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
+ qcom,irqs-detectable;
+ qcom.gpios-detectable;
+ qcom,latency-us = <75>;
+ qcom,ss-power = <735>;
+ qcom,energy-overhead = <77341>;
+ qcom,time-overhead = <105>;
+ };
+
+ qcom,lpm-level@2 {
+ reg = <0x2>;
+ qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <2>; /* Retention */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
+ qcom,irqs-detectable;
+ qcom.gpios-detectable;
+ qcom,latency-us = <95>;
+ qcom,ss-power = <725>;
+ qcom,energy-overhead = <99500>;
+ qcom,time-overhead = <130>;
+ };
+
+ qcom,lpm-level@3 {
+ reg = <0x3>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
+ qcom,irqs-detectable;
+ qcom.gpios-detectable;
+ qcom,latency-us = <2000>;
+ qcom,ss-power = <138>;
+ qcom,energy-overhead = <1208400>;
+ qcom,time-overhead = <3200>;
+ };
+
+ qcom,lpm-level@4 {
+ reg = <0x4>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
+ qcom,irqs-detectable;
+ qcom.gpios-detectable;
+ qcom,latency-us = <3000>;
+ qcom,ss-power = <110>;
+ qcom,energy-overhead = <1250300>;
+ qcom,time-overhead = <3500>;
+ };
+
+ qcom,lpm-level@5 {
+ reg = <0x5>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
+ qcom,latency-us = <3000>;
+ qcom,ss-power = <68>;
+ qcom,energy-overhead = <1350200>;
+ qcom,time-overhead = <4000>;
+ };
+
+ qcom,lpm-level@6 {
+ reg = <0x6>;
+ qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <950000>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
+ qcom,latency-us = <18000>;
+ qcom,ss-power = <10>;
+ qcom,energy-overhead = <3202600>;
+ qcom,time-overhead = <27000>;
+ };
+
+ qcom,lpm-level@7 {
+ reg = <0x7>;
+ qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <950000>; /* SVS SOC */
+ qcom,vdd-mem-lower-bound = <675000>; /* RETENTION */
+ qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
+ qcom,latency-us = <20000>;
+ qcom,ss-power = <2>;
+ qcom,energy-overhead = <4252000>;
+ qcom,time-overhead = <32000>;
+ };
+ };
+
+ qcom,pm-boot {
+ compatible = "qcom,pm-boot";
+ qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */
+ };
+
+ qcom,mpm@fc4281d0 {
+ compatible = "qcom,mpm-v2";
+ reg = <0xfc4281d0 0x1000>, /* MSM_RPM_MPM_BASE 4K */
+ <0xf9011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
+ reg-names = "vmpm", "ipc";
+ interrupts = <0 171 1>;
+
+ qcom,ipc-bit-offset = <1>;
+
+ qcom,gic-parent = <&intc>;
+ qcom,gic-map = <47 172>, /* usb2_hsic_async_wakeup_irq */
+ <53 104>, /* mdss_irq */
+ <62 222>, /* ee0_krait_hlos_spmi_periph_irq */
+ <0xff 57>, /* mss_to_apps_irq(0) */
+ <0xff 58>, /* mss_to_apps_irq(1) */
+ <0xff 59>, /* mss_to_apps_irq(2) */
+ <0xff 60>, /* mss_to_apps_irq(3) */
+ <0xff 173>, /* o_wcss_apss_smd_hi */
+ <0xff 174>, /* o_wcss_apss_smd_med */
+ <0xff 175>, /* o_wcss_apss_smd_low */
+ <0xff 176>, /* o_wcss_apss_smsm_irq */
+ <0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
+ <0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
+ <0xff 179>, /* o_wcss_apss_asic_intr
+
+ <0xff 188>, /* lpass_irq_out_apcs(0) */
+ <0xff 189>, /* lpass_irq_out_apcs(1) */
+ <0xff 190>, /* lpass_irq_out_apcs(2) */
+ <0xff 191>, /* lpass_irq_out_apcs(3) */
+ <0xff 192>, /* lpass_irq_out_apcs(4) */
+ <0xff 193>, /* lpass_irq_out_apcs(5) */
+ <0xff 194>, /* lpass_irq_out_apcs(6) */
+ <0xff 195>, /* lpass_irq_out_apcs(7) */
+ <0xff 196>, /* lpass_irq_out_apcs(8) */
+ <0xff 197>, /* lpass_irq_out_apcs(9) */
+ <0xff 200>, /* rpm_ipc(4) */
+ <0xff 201>, /* rpm_ipc(5) */
+ <0xff 202>, /* rpm_ipc(6) */
+ <0xff 203>, /* rpm_ipc(7) */
+ <0xff 204>, /* rpm_ipc(24) */
+ <0xff 205>, /* rpm_ipc(25) */
+ <0xff 206>, /* rpm_ipc(26) */
+ <0xff 207>, /* rpm_ipc(27) */
+ <0xff 240>; /* summary_irq_kpss */
+
+ qcom,gpio-parent = <&msmgpio>;
+ qcom,gpio-map = <3 102>,
+ <4 1 >,
+ <5 5 >,
+ <6 9 >,
+ <7 18>,
+ <8 20>,
+ <9 24>,
+ <10 27>,
+ <11 28>,
+ <12 34>,
+ <13 35>,
+ <14 37>,
+ <15 42>,
+ <16 44>,
+ <17 46>,
+ <18 50>,
+ <19 54>,
+ <20 59>,
+ <21 61>,
+ <22 62>,
+ <23 64>,
+ <24 65>,
+ <25 66>,
+ <26 67>,
+ <27 68>,
+ <28 71>,
+ <29 72>,
+ <30 73>,
+ <31 74>,
+ <32 75>,
+ <33 77>,
+ <34 79>,
+ <35 80>,
+ <36 82>,
+ <37 86>,
+ <38 92>,
+ <39 93>,
+ <40 95>;
+ };
+
+ qcom,pm-8x60@fe805664 {
+ compatible = "qcom,pm-8x60";
+ reg = <0xfe805664 0x40>;
+ qcom,pc-mode = <0>; /*MSM_PC_TZ_L2_INT */
+ qcom,use-sync-timer;
+ qcom,saw-turns-off-pll;
+ };
+
+ qcom,rpm-log@fc19dc00 {
+ compatible = "qcom,rpm-log";
+ reg = <0xfc19dc00 0x4000>;
+ qcom,rpm-addr-phys = <0xfc000000>;
+ qcom,offset-version = <4>;
+ qcom,offset-page-buffer-addr = <36>;
+ qcom,offset-log-len = <40>;
+ qcom,offset-log-len-mask = <44>;
+ qcom,offset-page-indices = <56>;
+ };
+
+ qcom,rpm-stats@0xfc19dbd0{
+ compatible = "qcom,rpm-stats";
+ reg = <0xfc19dbd0 0x1000>;
+ reg-names = "phys_addr_base";
+ qcom,sleep-stats-version = <2>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index 1a7c628..16cdeb1 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -19,6 +19,7 @@
/include/ "msm8974.dtsi"
/include/ "msm8974-v2-iommu.dtsi"
/include/ "msm8974-v2-iommu-domains.dtsi"
+/include/ "msm8974-v2-pm.dtsi"
/ {
android_usb@fe8050c8 {
@@ -110,4 +111,14 @@
<1620000 970000>,
<2024000 1212000>,
<2132000 1279000>;
+ qcom,iommu-groups = <&venus_domain_ns &venus_domain_sec_bitstream
+ &venus_domain_sec_pixel &venus_domain_sec_non_pixel>;
+ qcom,iommu-group-buffer-types = <0xfff 0x91 0x42 0x120>;
+ qcom,buffer-type-tz-usage-table = <0x91 0x1>,
+ <0x42 0x2>,
+ <0x120 0x3>;
+};
+
+&krait_pdn {
+ qcom,use-phase-switching;
};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index b0b7677..ab6b7c8 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -11,7 +11,6 @@
*/
/include/ "skeleton.dtsi"
-/include/ "msm8974-pm.dtsi"
/include/ "msm8974-camera.dtsi"
/include/ "msm8974-coresight.dtsi"
/include/ "msm-gdsc.dtsi"
@@ -29,6 +28,19 @@
aliases {
spi0 = &spi_0;
spi7 = &spi_7;
+ sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
+ sdhc2 = &sdhc_2; /* SDC2 SD card slot */
+ sdhc3 = &sdhc_3; /* SDC3 SDIO slot */
+ sdhc4 = &sdhc_4; /* SDC4 SDIO slot */
+ };
+
+ memory {
+
+ secure_mem: region@0 {
+ linux,contiguous-region;
+ reg = <0 0x7800000>;
+ label = "secure_mem";
+ };
};
intc: interrupt-controller@F9000000 {
@@ -324,6 +336,64 @@
status = "disable";
};
+ sdhc_1: sdhci@f9824900 {
+ qcom,bus-width = <8>;
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+ interrupts = <0 123 0>, <0 138 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ status = "disable";
+ };
+
+ sdhc_2: sdhci@f98a4900 {
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+ interrupts = <0 125 0>, <0 221 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+
+ qcom,bus-width = <4>;
+ status = "disable";
+ };
+
+ sdhc_3: sdhci@f9864900 {
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf9864900 0x11c>, <0xf9864000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+ interrupts = <0 127 0>, <0 224 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+ gpios = <&msmgpio 40 0>, /* CLK */
+ <&msmgpio 39 0>, /* CMD */
+ <&msmgpio 38 0>, /* DATA0 */
+ <&msmgpio 37 0>, /* DATA1 */
+ <&msmgpio 36 0>, /* DATA2 */
+ <&msmgpio 35 0>; /* DATA3 */
+ qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+ qcom,bus-width = <4>;
+ status = "disable";
+ };
+
+ sdhc_4: sdhci@f98e4900 {
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf98e4900 0x11c>, <0xf98e4000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+ interrupts = <0 129 0>, <0 227 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+ gpios = <&msmgpio 93 0>, /* CLK */
+ <&msmgpio 91 0>, /* CMD */
+ <&msmgpio 96 0>, /* DATA0 */
+ <&msmgpio 95 0>, /* DATA1 */
+ <&msmgpio 94 0>, /* DATA2 */
+ <&msmgpio 92 0>; /* DATA3 */
+ qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+ qcom,bus-width = <4>;
+ status = "disable";
+ };
+
qcom,sps@f9980000 {
compatible = "qcom,msm_sps";
reg = <0xf9984000 0x15000>,
@@ -1161,7 +1231,7 @@
qcom,smem@fa00000 {
compatible = "qcom,smem";
reg = <0xfa00000 0x200000>,
- <0xfa006000 0x1000>,
+ <0xf9011000 0x1000>,
<0xfc428000 0x4000>;
reg-names = "smem", "irq-reg-base", "aux-mem1";
@@ -1234,6 +1304,18 @@
compatible = "qcom,ssm";
qcom,channel-name = "SSM_RTR";
};
+
+ sfpb_spinlock: qcom,ipc-spinlock@fd484000 {
+ compatible = "qcom,ipc-spinlock-sfpb";
+ reg = <0xfd484000 0x1000>;
+ qcom,num-locks = <32>;
+ };
+
+ ldrex_spinlock: qcom,ipc-spinlock@fa00000 {
+ compatible = "qcom,ipc-spinlock-ldrex";
+ reg = <0xfa00000 0x200000>;
+ status = "disable";
+ };
};
&gdsc_venus {
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 1880965..e881977 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -162,6 +162,22 @@
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
+ qcom,irqs-detectable;
+ qcom,latency-us = <8000>;
+ qcom,ss-power = <1800>;
+ qcom,energy-overhead = <71950000>;
+ qcom,time-overhead = <15300>;
+ };
+
+ qcom,lpm-level@6 {
+ reg = <0x6>;
+ qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <0>; /* OFF */
qcom,vdd-mem-upper-bound = <950000>; /* SVS SOC */
qcom,vdd-mem-lower-bound = <675000>; /* RETENTION */
qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
diff --git a/arch/arm/boot/dts/msm9625-v1.dtsi b/arch/arm/boot/dts/msm9625-v1.dtsi
index 54fe443..54aa02a 100644
--- a/arch/arm/boot/dts/msm9625-v1.dtsi
+++ b/arch/arm/boot/dts/msm9625-v1.dtsi
@@ -49,3 +49,11 @@
&stm {
qcom,write-64bit;
};
+
+&sfpb_spinlock {
+ status = "disable";
+};
+
+&ldrex_spinlock {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm9625-v2.dtsi b/arch/arm/boot/dts/msm9625-v2.dtsi
index c3c2c49..3ce6844 100644
--- a/arch/arm/boot/dts/msm9625-v2.dtsi
+++ b/arch/arm/boot/dts/msm9625-v2.dtsi
@@ -34,3 +34,11 @@
&ipa_hw {
qcom,ipa-hw-ver = <2>; /* IPA h-w revision */
};
+
+&sfpb_spinlock {
+ status = "disable";
+};
+
+&ldrex_spinlock {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index f22fc28..03eaaf3 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -104,8 +104,8 @@
hsic@f9a15000 {
compatible = "qcom,hsic-host";
reg = <0xf9a15000 0x400>;
- interrupts = <0 136 0>;
- interrupt-names = "core_irq";
+ interrupts = <0 136 0>, <0 148 0>;
+ interrupt-names = "core_irq", "async_irq";
HSIC_VDDCX-supply = <&pm8019_l12>;
HSIC_GDSC-supply = <&gdsc_usb_hsic>;
@@ -515,6 +515,7 @@
qcom,msm-pcm {
compatible = "qcom,msm-pcm-dsp";
+ qcom,msm-pcm-dsp-id = <0>;
};
qcom,msm-pcm-routing {
@@ -633,7 +634,7 @@
qcom,smem@fa00000 {
compatible = "qcom,smem";
reg = <0xfa00000 0x200000>,
- <0xfa006000 0x1000>,
+ <0xf9011000 0x1000>,
<0xfc428000 0x4000>;
reg-names = "smem", "irq-reg-base", "aux-mem1";
@@ -717,6 +718,18 @@
qcom,memblock-remove = <0x1f00000 0x5700000>; /* Address and Size of Hole */
};
+ sfpb_spinlock: qcom,ipc-spinlock@fd484000 {
+ compatible = "qcom,ipc-spinlock-sfpb";
+ reg = <0xfd484000 0x1000>;
+ qcom,num-locks = <32>;
+ };
+
+ ldrex_spinlock: qcom,ipc-spinlock@fa00000 {
+ compatible = "qcom,ipc-spinlock-ldrex";
+ reg = <0xfa00000 0x200000>;
+ status = "disable";
+ };
+
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
diff --git a/arch/arm/boot/dts/skeleton.dtsi b/arch/arm/boot/dts/skeleton.dtsi
index b41d241..f9988cd 100644
--- a/arch/arm/boot/dts/skeleton.dtsi
+++ b/arch/arm/boot/dts/skeleton.dtsi
@@ -9,5 +9,10 @@
#size-cells = <1>;
chosen { };
aliases { };
- memory { device_type = "memory"; reg = <0 0>; };
+ memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "memory";
+ reg = <0 0>;
+ };
};
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 8e948c2..8eac20f 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -374,7 +374,6 @@
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_SHIRQ=y
-# CONFIG_SCHED_DEBUG is not set
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index f2d25ac..e46b835 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -372,7 +372,6 @@
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_FS=y
-# CONFIG_SCHED_DEBUG is not set
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 2e4f84d..14bd1b4 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -45,6 +45,7 @@
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_IPC_LOGGING=y
+CONFIG_MSM_BAM_DMUX=y
CONFIG_MSM_SMP2P=y
CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER=y
@@ -63,6 +64,7 @@
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_ADSP_LOADER=m
+CONFIG_MSM_OCMEM_POWER_DISABLE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
@@ -108,6 +110,12 @@
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_CLS_FW=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_MD=y
@@ -115,6 +123,8 @@
CONFIG_DM_CRYPT=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
+# CONFIG_MSM_RMNET is not set
+CONFIG_MSM_RMNET_BAM=y
CONFIG_WCNSS_CORE=y
CONFIG_WCNSS_CORE_PRONTO=y
CONFIG_WCNSS_MEM_PRE_ALLOC=y
@@ -142,6 +152,9 @@
CONFIG_I2C_QUP=y
CONFIG_WCD9306_CODEC=y
CONFIG_GPIO_QPNP_PIN=y
+CONFIG_HWMON=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_QPNP_CHARGER=y
# CONFIG_HWMON is not set
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
@@ -152,7 +165,14 @@
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
CONFIG_FB=y
-CONFIG_FB_VIRTUAL=y
+CONFIG_FB_MSM=y
+# CONFIG_FB_MSM_BACKLIGHT is not set
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
@@ -177,11 +197,15 @@
CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_QPNP_PWM=y
CONFIG_MSM_IOMMU=y
CONFIG_MSM_IOMMU_PMON=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_MMC_MSM_SPS_SUPPORT=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QPNP=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
CONFIG_RTC_DRV_QPNP=y
@@ -213,6 +237,7 @@
CONFIG_CRYPTO_TWOFISH=y
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
+CONFIG_QPNP_POWER_ON=y
CONFIG_LIBCRC32C=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
@@ -229,3 +254,12 @@
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
CONFIG_THERMAL_MONITOR=y
+CONFIG_MSM_RTB=y
+CONFIG_MSM_RTB_SEPARATE_CPUS=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_TMC=y
+CONFIG_CORESIGHT_TPIU=y
+CONFIG_CORESIGHT_FUNNEL=y
+CONFIG_CORESIGHT_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_EVENT=m
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 828484a..baefac5 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -438,7 +438,6 @@
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-# CONFIG_SCHED_DEBUG is not set
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 7362ea0..8232a8d 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -510,7 +510,6 @@
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-# CONFIG_SCHED_DEBUG is not set
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 5c03630..c6be3c5 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -234,6 +234,7 @@
CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_SYNC=y
CONFIG_SW_SYNC=y
+CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_HAPTIC_ISA1200=y
@@ -392,6 +393,7 @@
CONFIG_MMC_BLOCK_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_MSM=y
CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
@@ -428,6 +430,8 @@
CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE=y
CONFIG_CORESIGHT_EVENT=m
+CONFIG_BIF=y
+CONFIG_BIF_QPNP=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -441,7 +445,6 @@
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-# CONFIG_SCHED_DEBUG is not set
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index faa0471..f77f04f 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -238,6 +238,7 @@
CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_SYNC=y
CONFIG_SW_SYNC=y
+CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_TSPP=m
@@ -400,6 +401,7 @@
CONFIG_MMC_BLOCK_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_MSM=y
CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
@@ -437,6 +439,8 @@
CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE=y
CONFIG_CORESIGHT_EVENT=m
+CONFIG_BIF=y
+CONFIG_BIF_QPNP=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/msm9625-perf_defconfig b/arch/arm/configs/msm9625-perf_defconfig
index 2070f46..1fe528a 100644
--- a/arch/arm/configs/msm9625-perf_defconfig
+++ b/arch/arm/configs/msm9625-perf_defconfig
@@ -172,7 +172,8 @@
CONFIG_KS8851=y
# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_MSM_RMNET is not set
-CONFIG_MSM_RMNET_BAM=y
+# CONFIG_MSM_RMNET_BAM is not set
+CONFIG_MSM_RMNET_WWAN=y
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_SEEQ is not set
# CONFIG_NET_VENDOR_SMSC is not set
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 9a1f872..aa18209 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -172,7 +172,8 @@
CONFIG_KS8851=y
# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_MSM_RMNET is not set
-CONFIG_MSM_RMNET_BAM=y
+# CONFIG_MSM_RMNET_BAM is not set
+CONFIG_MSM_RMNET_WWAN=y
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_SEEQ is not set
# CONFIG_NET_VENDOR_SMSC is not set
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index d021905..584fe0b 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -16,7 +16,6 @@
#include <asm/shmparam.h>
#include <asm/cachetype.h>
#include <asm/outercache.h>
-#include <asm/rodata.h>
#define CACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT)
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index 938be62..c1295c4 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -68,15 +68,18 @@
#define __raw_writeb_no_log(v, a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))
#define __raw_writew_no_log(v, a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))
#define __raw_writel_no_log(v, a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
+#define __raw_writell_no_log(v, a) (__chk_io_ptr(a), *(volatile unsigned long long __force *)(a) = (v))
#define __raw_writeb(v, a) __raw_write_logged((v), (a), b)
#define __raw_writew(v, a) __raw_write_logged((v), (a), w)
#define __raw_writel(v, a) __raw_write_logged((v), (a), l)
+#define __raw_writell(v, a) __raw_write_logged((v), (a), ll)
#define __raw_readb_no_log(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))
#define __raw_readw_no_log(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
#define __raw_readl_no_log(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))
+#define __raw_readll_no_log(a) (__chk_io_ptr(a), *(volatile unsigned long long __force *)(a))
#define __raw_read_logged(a, _l, _t) ({ \
unsigned _t __a; \
@@ -94,6 +97,7 @@
#define __raw_readb(a) __raw_read_logged((a), b, char)
#define __raw_readw(a) __raw_read_logged((a), w, short)
#define __raw_readl(a) __raw_read_logged((a), l, int)
+#define __raw_readll(a) __raw_read_logged((a), ll, long long)
/*
* Architecture ioremap implementation.
@@ -268,8 +272,12 @@
__raw_readw(c)); __r; })
#define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \
__raw_readl(c)); __r; })
+#define readll_relaxed(c) ({ u64 __r = le64_to_cpu((__force __le64) \
+ __raw_readll(c)); __r; })
#define readl_relaxed_no_log(c) ({ u32 __r = le32_to_cpu((__force __le32) \
__raw_readl_no_log(c)); __r; })
+#define readll_relaxed_no_log(c) ({ u64 __r = le64_to_cpu((__force __le64) \
+ __raw_readll_no_log(c)); __r; })
#define writeb_relaxed(v,c) ((void)__raw_writeb(v,c))
@@ -277,16 +285,22 @@
cpu_to_le16(v),c))
#define writel_relaxed(v,c) ((void)__raw_writel((__force u32) \
cpu_to_le32(v),c))
+#define writell_relaxed(v, c) ((void)__raw_writell((__force u64) \
+ cpu_to_le64(v), c))
#define writel_relaxed_no_log(v, c) ((void)__raw_writel_no_log((__force u32) \
cpu_to_le32(v), c))
+#define writell_relaxed_no_log(v, c) ((void)__raw_writell_no_log((__force u64) \
+ cpu_to_le64(v), c))
#define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })
#define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
+#define readll(c) ({ u64 __v = readll_relaxed(c); __iormb(); __v; })
#define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })
#define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })
#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
+#define writell(v, c) ({ __iowmb(); writell_relaxed(v, c); })
#define readsb(p,d,l) __raw_readsb(p,d,l)
#define readsw(p,d,l) __raw_readsw(p,d,l)
@@ -316,22 +330,26 @@
#define iounmap __arm_iounmap
/*
- * io{read,write}{8,16,32} macros
+ * io{read,write}{8,16,32,64} macros
*/
#ifndef ioread8
#define ioread8(p) ({ unsigned int __v = __raw_readb(p); __iormb(); __v; })
#define ioread16(p) ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __iormb(); __v; })
#define ioread32(p) ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __iormb(); __v; })
+#define ioread64(p) ({ unsigned int __v = le64_to_cpu((__force __le64)__raw_readll(p)); __iormb(); __v; })
#define ioread16be(p) ({ unsigned int __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(); __v; })
#define ioread32be(p) ({ unsigned int __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(); __v; })
+#define ioread64be(p) ({ unsigned int __v = be64_to_cpu((__force __be64)__raw_readll(p)); __iormb(); __v; })
#define iowrite8(v,p) ({ __iowmb(); (void)__raw_writeb(v, p); })
#define iowrite16(v,p) ({ __iowmb(); (void)__raw_writew((__force __u16)cpu_to_le16(v), p); })
#define iowrite32(v,p) ({ __iowmb(); (void)__raw_writel((__force __u32)cpu_to_le32(v), p); })
+#define iowrite64(v, p) ({ __iowmb(); (void)__raw_writell((__force __u64)cpu_to_le64(v), p); })
#define iowrite16be(v,p) ({ __iowmb(); (void)__raw_writew((__force __u16)cpu_to_be16(v), p); })
#define iowrite32be(v,p) ({ __iowmb(); (void)__raw_writel((__force __u32)cpu_to_be32(v), p); })
+#define iowrite64be(v, p) ({ __iowmb(); (void)__raw_writell((__force __u64)cpu_to_be64(v), p); })
#define ioread8_rep(p,d,c) __raw_readsb(p,d,c)
#define ioread16_rep(p,d,c) __raw_readsw(p,d,c)
diff --git a/arch/arm/include/asm/rodata.h b/arch/arm/include/asm/rodata.h
deleted file mode 100644
index 8c8add8..0000000
--- a/arch/arm/include/asm/rodata.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * arch/arm/include/asm/rodata.h
- *
- * Copyright (C) 2011 Google, Inc.
- *
- * Author: Colin Cross <ccross@android.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#ifndef _ASMARM_RODATA_H
-#define _ASMARM_RODATA_H
-
-#ifndef __ASSEMBLY__
-
-#ifdef CONFIG_DEBUG_RODATA
-
-int set_memory_rw(unsigned long virt, int numpages);
-int set_memory_ro(unsigned long virt, int numpages);
-
-void mark_rodata_ro(void);
-void set_kernel_text_rw(void);
-void set_kernel_text_ro(void);
-#else
-static inline void set_kernel_text_rw(void) { }
-static inline void set_kernel_text_ro(void) { }
-#endif
-
-#endif
-
-#endif
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index bf17145..df0bf0c 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -13,7 +13,6 @@
*/
#include <linux/ftrace.h>
-#include <linux/module.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
@@ -64,20 +63,6 @@
}
#endif
-int ftrace_arch_code_modify_prepare(void)
-{
- set_kernel_text_rw();
- set_all_modules_text_rw();
- return 0;
-}
-
-int ftrace_arch_code_modify_post_process(void)
-{
- set_all_modules_text_ro();
- set_kernel_text_ro();
- return 0;
-}
-
static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
{
return arm_gen_branch_link(pc, addr);
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 42ed059..1b36410 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -50,13 +50,7 @@
select MSM_REMOTE_SPINLOCK_DEKKERS
select ARCH_SPARSEMEM_ENABLE
select ARCH_HAS_HOLES_MEMORYMODEL
- select MEMORY_HOTPLUG
- select MEMORY_HOTREMOVE
- select ARCH_ENABLE_MEMORY_HOTPLUG
- select ARCH_ENABLE_MEMORY_HOTREMOVE
select MIGRATION
- select ARCH_MEMORY_PROBE
- select ARCH_MEMORY_REMOVE
select MSM_GPIOMUX
select RESERVE_FIRST_PAGE
select MSM_DALRPC
@@ -412,6 +406,7 @@
select CPU_FREQ_GOV_ONDEMAND
select MSM_PIL
select MSM_RUN_QUEUE_STATS
+ select ARM_HAS_SG_CHAIN
config ARCH_MSM8226
bool "MSM8226"
@@ -438,6 +433,7 @@
select MEMORY_HOLE_CARVEOUT
select DONT_MAP_HOLE_AFTER_MEMBANK0
select MSM_BUS_SCALING
+ select ARM_HAS_SG_CHAIN
endmenu
choice
@@ -1037,8 +1033,8 @@
default "0x80200000" if ARCH_MSM8930
default "0x00000000" if ARCH_MSM8974
default "0x00000000" if ARCH_MPQ8092
- default "0x00100000" if ARCH_MSM8226
- default "0x00100000" if ARCH_MSM8610
+ default "0x00000000" if ARCH_MSM8226
+ default "0x00000000" if ARCH_MSM8610
default "0x10000000" if ARCH_FSM9XXX
default "0x00200000" if ARCH_MSM9625
default "0x00200000" if !MSM_STACKED_MEMORY
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index f683b33..02d0b46 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -72,7 +72,7 @@
dtb-$(CONFIG_ARCH_MSM9625) += msm9625-v2-1-cdp.dtb
# MSM8226
- zreladdr-$(CONFIG_ARCH_MSM8226) := 0x00108000
+ zreladdr-$(CONFIG_ARCH_MSM8226) := 0x00008000
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-sim.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-cdp.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-mtp.dtb
@@ -87,6 +87,6 @@
zreladdr-$(CONFIG_ARCH_MPQ8092) := 0x00008000
# MSM8610
- zreladdr-$(CONFIG_ARCH_MSM8610) := 0x00108000
+ zreladdr-$(CONFIG_ARCH_MSM8610) := 0x00008000
dtb-$(CONFIG_ARCH_MSM8610) += msm8610-rumi.dtb
dtb-$(CONFIG_ARCH_MSM8610) += msm8610-sim.dtb
diff --git a/arch/arm/mach-msm/acpuclock-8226.c b/arch/arm/mach-msm/acpuclock-8226.c
index 7dc3a0e..8ba1b39 100644
--- a/arch/arm/mach-msm/acpuclock-8226.c
+++ b/arch/arm/mach-msm/acpuclock-8226.c
@@ -53,13 +53,13 @@
* 3) Depending on Frodo version, may need minimum of LVL_NOM
*/
static struct clkctl_acpu_speed acpu_freq_tbl[] = {
- { 0, 19200, CXO, 0, 0, LVL_LOW, 950000, 0 },
- { 1, 300000, PLL0, 4, 2, LVL_LOW, 950000, 4 },
- { 1, 384000, ACPUPLL, 5, 0, LVL_LOW, 950000, 4 },
- { 1, 600000, PLL0, 4, 0, LVL_NOM, 950000, 6 },
- { 1, 787200, ACPUPLL, 5, 0, LVL_NOM, 1050000, 6 },
- { 1, 998400, ACPUPLL, 5, 0, LVL_HIGH, 1050000, 7 },
- { 1, 1190400, ACPUPLL, 5, 0, LVL_HIGH, 1050000, 7 },
+ { 0, 19200, CXO, 0, 0, 1150000, 1150000, 0 },
+ { 1, 300000, PLL0, 4, 2, 1150000, 1150000, 4 },
+ { 1, 384000, ACPUPLL, 5, 0, 1150000, 1150000, 4 },
+ { 1, 600000, PLL0, 4, 0, 1150000, 1150000, 6 },
+ { 1, 787200, ACPUPLL, 5, 0, 1150000, 1150000, 6 },
+ { 0, 998400, ACPUPLL, 5, 0, 1150000, 1150000, 7 },
+ { 0, 1190400, ACPUPLL, 5, 0, 1150000, 1150000, 7 },
{ 0 }
};
@@ -68,7 +68,7 @@
.current_speed = &(struct clkctl_acpu_speed){ 0 },
.bus_scale = &bus_client_pdata,
/* FIXME regulator doesn't support corners yet */
- .vdd_max_cpu = 1050000,
+ .vdd_max_cpu = 1150000,
.vdd_max_mem = 1150000,
.src_clocks = {
[PLL0].name = "gpll0",
diff --git a/arch/arm/mach-msm/acpuclock-cortex.c b/arch/arm/mach-msm/acpuclock-cortex.c
index 4ac1408..9104f98 100644
--- a/arch/arm/mach-msm/acpuclock-cortex.c
+++ b/arch/arm/mach-msm/acpuclock-cortex.c
@@ -133,7 +133,12 @@
pr_warn("acpu rcg didn't update its configuration\n");
}
-static int set_speed(struct clkctl_acpu_speed *tgt_s)
+/*
+ * This function can be called in both atomic and nonatomic context.
+ * Since regulator APIS can sleep, we cannot always use the clk prepare
+ * unprepare API.
+ */
+static int set_speed(struct clkctl_acpu_speed *tgt_s, bool atomic)
{
int rc = 0;
unsigned int tgt_freq_hz = tgt_s->khz * 1000;
@@ -147,11 +152,19 @@
select_clk_source_div(acpuclk_init_data, cxo_s);
/* Re-program acpu pll */
- clk_disable(tgt);
+ if (atomic)
+ clk_disable(tgt);
+ else
+ clk_disable_unprepare(tgt);
+
rc = clk_set_rate(tgt, tgt_freq_hz);
if (rc)
pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
- BUG_ON(clk_enable(tgt));
+
+ if (atomic)
+ BUG_ON(clk_enable(tgt));
+ else
+ BUG_ON(clk_prepare_enable(tgt));
/* Switch back to acpu pll */
select_clk_source_div(acpuclk_init_data, tgt_s);
@@ -163,7 +176,11 @@
return rc;
}
- rc = clk_enable(tgt);
+ if (atomic)
+ clk_enable(tgt);
+ else
+ clk_prepare_enable(tgt);
+
if (rc) {
pr_err("ACPU PLL enable failed\n");
return rc;
@@ -171,9 +188,17 @@
select_clk_source_div(acpuclk_init_data, tgt_s);
- clk_disable(strt);
+ if (atomic)
+ clk_disable(strt);
+ else
+ clk_disable_unprepare(strt);
+
} else {
- rc = clk_enable(tgt);
+ if (atomic)
+ clk_enable(tgt);
+ else
+ clk_prepare_enable(tgt);
+
if (rc) {
pr_err("%s enable failed\n",
acpuclk_init_data->src_clocks[tgt_s->src].name);
@@ -182,7 +207,11 @@
select_clk_source_div(acpuclk_init_data, tgt_s);
- clk_disable(strt);
+ if (atomic)
+ clk_disable(strt);
+ else
+ clk_disable_unprepare(strt);
+
}
return rc;
@@ -223,8 +252,12 @@
pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
strt_s->khz, tgt_s->khz);
- /* Switch CPU speed. */
- rc = set_speed(tgt_s);
+ /* Switch CPU speed. Flag indicates atomic context */
+ if (reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
+ rc = set_speed(tgt_s, false);
+ else
+ rc = set_speed(tgt_s, true);
+
if (rc)
goto out;
@@ -316,11 +349,6 @@
clk_get(&pdev->dev,
acpuclk_init_data->src_clocks[i].name);
BUG_ON(IS_ERR(acpuclk_init_data->src_clocks[i].clk));
- /*
- * Prepare the PLLs because we enable/disable them
- * in atomic context during power collapse/restore.
- */
- BUG_ON(clk_prepare(acpuclk_init_data->src_clocks[i].clk));
}
/* Improve boot time by ramping up CPU immediately */
@@ -329,8 +357,8 @@
max_cpu_khz = acpuclk_init_data->freq_tbl[i].khz;
/* Initialize regulators */
- rc = increase_vdd(acpuclk_init_data->freq_tbl[i].vdd_cpu,
- acpuclk_init_data->freq_tbl[i].vdd_mem);
+ rc = increase_vdd(acpuclk_init_data->vdd_max_cpu,
+ acpuclk_init_data->vdd_max_mem);
if (rc)
goto err_vdd;
@@ -368,7 +396,6 @@
for (i = 0; i < NUM_SRC; i++) {
if (!acpuclk_init_data->src_clocks[i].name)
continue;
- clk_unprepare(acpuclk_init_data->src_clocks[i].clk);
clk_put(acpuclk_init_data->src_clocks[i].clk);
}
return rc;
diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c
index aa257ef..b2185e3 100644
--- a/arch/arm/mach-msm/avs.c
+++ b/arch/arm/mach-msm/avs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 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
@@ -16,6 +16,7 @@
#include <asm/mach-types.h>
#include <asm/cputype.h>
#include "avs.h"
+#include "spm.h"
u32 avs_get_avscsr(void)
{
@@ -72,7 +73,10 @@
static void avs_disable_local(void *data)
{
+ int cpu = smp_processor_id();
+
avs_set_avscsr(0);
+ msm_spm_set_vdd(cpu, msm_spm_get_vdd(cpu));
}
void avs_enable(int cpu, u32 avsdscr)
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 7c2c463..833b213 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -302,81 +302,56 @@
#define bam_ch_is_in_reset(x) \
(bam_ch[(x)].status & BAM_CH_IN_RESET)
-#define LOG_MESSAGE_MAX_SIZE 80
struct kfifo bam_dmux_state_log;
-static uint32_t bam_dmux_state_logging_disabled;
static int bam_dmux_uplink_vote;
static int bam_dmux_power_state;
-static void bam_dmux_log(const char *fmt, ...)
- __printf(1, 2);
-
-
-#define DMUX_LOG_KERR(fmt...) \
-do { \
- bam_dmux_log(fmt); \
- pr_err(fmt); \
-} while (0)
-
static void *bam_ipc_log_txt;
#define BAM_IPC_LOG_PAGES 5
/**
* Log a state change along with a small message.
- *
* Complete size of messsage is limited to @todo.
+ * Logging is done using IPC Logging infrastructure.
+ *
+ * States
+ * D: 1 = Power collapse disabled
+ * R: 1 = in global reset
+ * P: 1 = BAM is powered up
+ * A: 1 = BAM initialized and ready for data
+ * V: 1 = Uplink vote for power
+ * U: 1 = Uplink active
+ * W: 1 = Uplink Wait-for-ack
+ * A: 1 = Uplink ACK received
+ * #: >=1 On-demand uplink vote
+ * D: 1 = Disconnect ACK active
*/
-static void bam_dmux_log(const char *fmt, ...)
-{
- char buff[LOG_MESSAGE_MAX_SIZE];
- va_list arg_list;
- unsigned long long t_now;
- unsigned long nanosec_rem;
- int len = 0;
- if (bam_dmux_state_logging_disabled)
- return;
+#define BAM_DMUX_LOG(fmt, args...) \
+do { \
+ if (bam_ipc_log_txt) { \
+ ipc_log_string(bam_ipc_log_txt, \
+ "<DMUX> %c%c%c%c %c%c%c%c%d%c " fmt, \
+ a2_pc_disabled ? 'D' : 'd', \
+ in_global_reset ? 'R' : 'r', \
+ bam_dmux_power_state ? 'P' : 'p', \
+ bam_connection_is_active ? 'A' : 'a', \
+ bam_dmux_uplink_vote ? 'V' : 'v', \
+ bam_is_connected ? 'U' : 'u', \
+ wait_for_ack ? 'W' : 'w', \
+ ul_wakeup_ack_completion.done ? 'A' : 'a', \
+ atomic_read(&ul_ondemand_vote), \
+ disconnect_ack ? 'D' : 'd', \
+ args); \
+ } \
+} while (0)
- t_now = sched_clock();
- nanosec_rem = do_div(t_now, 1000000000U);
-
- /*
- * States
- * D: 1 = Power collapse disabled
- * R: 1 = in global reset
- * P: 1 = BAM is powered up
- * A: 1 = BAM initialized and ready for data
- *
- * V: 1 = Uplink vote for power
- * U: 1 = Uplink active
- * W: 1 = Uplink Wait-for-ack
- * A: 1 = Uplink ACK received
- * #: >=1 On-demand uplink vote
- * D: 1 = Disconnect ACK active
- */
- len += scnprintf(buff, sizeof(buff),
- "<DMUX> %u.%09lu %c%c%c%c %c%c%c%c%d%c ",
- (unsigned)t_now, nanosec_rem,
- a2_pc_disabled ? 'D' : 'd',
- in_global_reset ? 'R' : 'r',
- bam_dmux_power_state ? 'P' : 'p',
- bam_connection_is_active ? 'A' : 'a',
- bam_dmux_uplink_vote ? 'V' : 'v',
- bam_is_connected ? 'U' : 'u',
- wait_for_ack ? 'W' : 'w',
- ul_wakeup_ack_completion.done ? 'A' : 'a',
- atomic_read(&ul_ondemand_vote),
- disconnect_ack ? 'D' : 'd'
- );
-
- va_start(arg_list, fmt);
- len += vscnprintf(buff + len, sizeof(buff) - len, fmt, arg_list);
- va_end(arg_list);
- memset(buff + len, 0x0, sizeof(buff) - len);
- if (bam_ipc_log_txt)
- ipc_log_string(bam_ipc_log_txt, buff);
-}
+#define DMUX_LOG_KERR(fmt, args...) \
+do { \
+ BAM_DMUX_LOG(fmt, args); \
+ pr_err(fmt, args); \
+} while (0)
static inline void set_tx_timestamp(struct tx_pkt_info *pkt)
{
@@ -396,12 +371,12 @@
spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
list_for_each_entry(info, &bam_tx_pool, list_node) {
if (!reported) {
- bam_dmux_log("%s: tx pool not empty\n", func);
+ BAM_DMUX_LOG("%s: tx pool not empty\n", func);
if (!in_global_reset)
pr_err("%s: tx pool not empty\n", func);
reported = 1;
}
- bam_dmux_log("%s: node=%p ts=%u.%09lu\n", __func__,
+ BAM_DMUX_LOG("%s: node=%p ts=%u.%09lu\n", __func__,
&info->list_node, info->ts_sec, info->ts_nsec);
if (!in_global_reset)
pr_err("%s: node=%p ts=%u.%09lu\n", __func__,
@@ -529,7 +504,7 @@
mutex_lock(&bam_pdev_mutexlock);
if (in_global_reset) {
- bam_dmux_log("%s: open cid %d aborted due to ssr\n",
+ BAM_DMUX_LOG("%s: open cid %d aborted due to ssr\n",
__func__, rx_hdr->ch_id);
mutex_unlock(&bam_pdev_mutexlock);
queue_rx();
@@ -593,18 +568,18 @@
bam_mux_process_data(rx_skb);
break;
case BAM_MUX_HDR_CMD_OPEN:
- bam_dmux_log("%s: opening cid %d PC enabled\n", __func__,
+ BAM_DMUX_LOG("%s: opening cid %d PC enabled\n", __func__,
rx_hdr->ch_id);
handle_bam_mux_cmd_open(rx_hdr);
if (!(rx_hdr->reserved & ENABLE_DISCONNECT_ACK)) {
- bam_dmux_log("%s: deactivating disconnect ack\n",
+ BAM_DMUX_LOG("%s: deactivating disconnect ack\n",
__func__);
disconnect_ack = 0;
}
dev_kfree_skb_any(rx_skb);
break;
case BAM_MUX_HDR_CMD_OPEN_NO_A2_PC:
- bam_dmux_log("%s: opening cid %d PC disabled\n", __func__,
+ BAM_DMUX_LOG("%s: opening cid %d PC disabled\n", __func__,
rx_hdr->ch_id);
if (!a2_pc_disabled) {
@@ -617,11 +592,11 @@
break;
case BAM_MUX_HDR_CMD_CLOSE:
/* probably should drop pending write */
- bam_dmux_log("%s: closing cid %d\n", __func__,
+ BAM_DMUX_LOG("%s: closing cid %d\n", __func__,
rx_hdr->ch_id);
mutex_lock(&bam_pdev_mutexlock);
if (in_global_reset) {
- bam_dmux_log("%s: close cid %d aborted due to ssr\n",
+ BAM_DMUX_LOG("%s: close cid %d aborted due to ssr\n",
__func__, rx_hdr->ch_id);
mutex_unlock(&bam_pdev_mutexlock);
break;
@@ -1412,7 +1387,7 @@
for (i = 0; i < BAM_DMUX_NUM_CHANNELS; ++i) {
if (bam_ch_is_open(i)) {
bam_ch[i].notify(bam_ch[i].priv, event, data);
- bam_dmux_log("%s: cid=%d, event=%d, data=%lu\n",
+ BAM_DMUX_LOG("%s: cid=%d, event=%d, data=%lu\n",
__func__, i, event, data);
}
}
@@ -1455,11 +1430,11 @@
static void power_vote(int vote)
{
- bam_dmux_log("%s: curr=%d, vote=%d\n", __func__,
+ BAM_DMUX_LOG("%s: curr=%d, vote=%d\n", __func__,
bam_dmux_uplink_vote, vote);
if (bam_dmux_uplink_vote == vote)
- bam_dmux_log("%s: warning - duplicate power vote\n", __func__);
+ BAM_DMUX_LOG("%s: warning - duplicate power vote\n", __func__);
bam_dmux_uplink_vote = vote;
if (vote)
@@ -1473,7 +1448,7 @@
*/
static inline void ul_powerdown(void)
{
- bam_dmux_log("%s: powerdown\n", __func__);
+ BAM_DMUX_LOG("%s: powerdown\n", __func__);
verify_tx_queue_is_empty(__func__);
if (a2_pc_disabled) {
@@ -1585,7 +1560,7 @@
}
if (ul_packet_written || atomic_read(&ul_ondemand_vote)) {
- bam_dmux_log("%s: pkt written %d\n",
+ BAM_DMUX_LOG("%s: pkt written %d\n",
__func__, ul_packet_written);
ul_packet_written = 0;
schedule_delayed_work(&ul_timeout_work,
@@ -1614,7 +1589,7 @@
mutex_lock(&wakeup_lock);
if (bam_is_connected) { /* bam got connected before lock grabbed */
- bam_dmux_log("%s Already awake\n", __func__);
+ BAM_DMUX_LOG("%s Already awake\n", __func__);
mutex_unlock(&wakeup_lock);
return;
}
@@ -1677,35 +1652,35 @@
* instead of waiting
*/
if (wait_for_ack) {
- bam_dmux_log("%s waiting for previous ack\n", __func__);
+ BAM_DMUX_LOG("%s waiting for previous ack\n", __func__);
ret = wait_for_completion_timeout(
&ul_wakeup_ack_completion, HZ);
wait_for_ack = 0;
if (unlikely(ret == 0) && ssrestart_check()) {
mutex_unlock(&wakeup_lock);
- bam_dmux_log("%s timeout previous ack\n", __func__);
+ BAM_DMUX_LOG("%s timeout previous ack\n", __func__);
return;
}
}
INIT_COMPLETION(ul_wakeup_ack_completion);
power_vote(1);
- bam_dmux_log("%s waiting for wakeup ack\n", __func__);
+ BAM_DMUX_LOG("%s waiting for wakeup ack\n", __func__);
ret = wait_for_completion_timeout(&ul_wakeup_ack_completion, HZ);
if (unlikely(ret == 0) && ssrestart_check()) {
mutex_unlock(&wakeup_lock);
- bam_dmux_log("%s timeout wakeup ack\n", __func__);
+ BAM_DMUX_LOG("%s timeout wakeup ack\n", __func__);
return;
}
- bam_dmux_log("%s waiting completion\n", __func__);
+ BAM_DMUX_LOG("%s waiting completion\n", __func__);
ret = wait_for_completion_timeout(&bam_connection_completion, HZ);
if (unlikely(ret == 0) && ssrestart_check()) {
mutex_unlock(&wakeup_lock);
- bam_dmux_log("%s timeout power on\n", __func__);
+ BAM_DMUX_LOG("%s timeout power on\n", __func__);
return;
}
bam_is_connected = 1;
- bam_dmux_log("%s complete\n", __func__);
+ BAM_DMUX_LOG("%s complete\n", __func__);
schedule_delayed_work(&ul_timeout_work,
msecs_to_jiffies(UL_TIMEOUT_DELAY));
mutex_unlock(&wakeup_lock);
@@ -1771,7 +1746,7 @@
/* handle disconnect during active UL */
write_lock_irqsave(&ul_wakeup_lock, flags);
if (bam_is_connected) {
- bam_dmux_log("%s: UL active - forcing powerdown\n", __func__);
+ BAM_DMUX_LOG("%s: UL active - forcing powerdown\n", __func__);
ul_powerdown();
}
write_unlock_irqrestore(&ul_wakeup_lock, flags);
@@ -1817,10 +1792,10 @@
{
int rc;
- bam_dmux_log("%s\n", __func__);
+ BAM_DMUX_LOG("%s\n", __func__);
mutex_lock(&dfab_status_lock);
if (dfab_is_on) {
- bam_dmux_log("%s: dfab is already on\n", __func__);
+ BAM_DMUX_LOG("%s: dfab is already on\n", __func__);
mutex_unlock(&dfab_status_lock);
return;
}
@@ -1842,7 +1817,7 @@
static void unvote_dfab(void)
{
- bam_dmux_log("%s\n", __func__);
+ BAM_DMUX_LOG("%s\n", __func__);
mutex_lock(&dfab_status_lock);
if (!dfab_is_on) {
DMUX_LOG_KERR("%s: dfab is already off\n", __func__);
@@ -1864,7 +1839,7 @@
unsigned long flags;
spin_lock_irqsave(&wakelock_reference_lock, flags);
- bam_dmux_log("%s: ref count = %d\n", __func__,
+ BAM_DMUX_LOG("%s: ref count = %d\n", __func__,
wakelock_reference_count);
if (wakelock_reference_count == 0)
wake_lock(&bam_wakelock);
@@ -1883,7 +1858,7 @@
spin_unlock_irqrestore(&wakelock_reference_lock, flags);
return;
}
- bam_dmux_log("%s: ref count = %d\n", __func__,
+ BAM_DMUX_LOG("%s: ref count = %d\n", __func__,
wakelock_reference_count);
--wakelock_reference_count;
if (wakelock_reference_count == 0)
@@ -1917,7 +1892,7 @@
if (code == SUBSYS_BEFORE_SHUTDOWN) {
in_global_reset = 1;
in_ssr = 1;
- bam_dmux_log("%s: begin\n", __func__);
+ BAM_DMUX_LOG("%s: begin\n", __func__);
flush_workqueue(bam_mux_rx_workqueue);
}
if (code != SUBSYS_AFTER_SHUTDOWN)
@@ -1978,7 +1953,7 @@
}
spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
- bam_dmux_log("%s: complete\n", __func__);
+ BAM_DMUX_LOG("%s: complete\n", __func__);
return NOTIFY_DONE;
}
@@ -2225,7 +2200,7 @@
{
static unsigned int clear_bit; /* 0 = set the bit, else clear bit */
- bam_dmux_log("%s: apps ack %d->%d\n", __func__,
+ BAM_DMUX_LOG("%s: apps ack %d->%d\n", __func__,
clear_bit & 0x1, ~clear_bit & 0x1);
smsm_change_state(SMSM_APPS_STATE,
clear_bit & SMSM_A2_POWER_CONTROL_ACK,
@@ -2241,10 +2216,10 @@
mutex_lock(&smsm_cb_lock);
bam_dmux_power_state = new_state & SMSM_A2_POWER_CONTROL ? 1 : 0;
DBG_INC_A2_POWER_CONTROL_IN_CNT();
- bam_dmux_log("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
+ BAM_DMUX_LOG("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
new_state);
if (last_processed_state == (new_state & SMSM_A2_POWER_CONTROL)) {
- bam_dmux_log("%s: already processed this state\n", __func__);
+ BAM_DMUX_LOG("%s: already processed this state\n", __func__);
mutex_unlock(&smsm_cb_lock);
return;
}
@@ -2252,23 +2227,23 @@
last_processed_state = new_state & SMSM_A2_POWER_CONTROL;
if (bam_mux_initialized && new_state & SMSM_A2_POWER_CONTROL) {
- bam_dmux_log("%s: reconnect\n", __func__);
+ BAM_DMUX_LOG("%s: reconnect\n", __func__);
grab_wakelock();
reconnect_to_bam();
} else if (bam_mux_initialized &&
!(new_state & SMSM_A2_POWER_CONTROL)) {
- bam_dmux_log("%s: disconnect\n", __func__);
+ BAM_DMUX_LOG("%s: disconnect\n", __func__);
disconnect_to_bam();
release_wakelock();
} else if (new_state & SMSM_A2_POWER_CONTROL) {
- bam_dmux_log("%s: init\n", __func__);
+ BAM_DMUX_LOG("%s: init\n", __func__);
grab_wakelock();
if (cpu_is_msm9615())
msm9615_bam_init();
else
bam_init();
} else {
- bam_dmux_log("%s: bad state change\n", __func__);
+ BAM_DMUX_LOG("%s: bad state change\n", __func__);
pr_err("%s: unsupported state change\n", __func__);
}
mutex_unlock(&smsm_cb_lock);
@@ -2279,7 +2254,7 @@
uint32_t new_state)
{
DBG_INC_ACK_IN_CNT();
- bam_dmux_log("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
+ BAM_DMUX_LOG("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
new_state);
complete_all(&ul_wakeup_ack_completion);
}
@@ -2322,12 +2297,12 @@
xo_clk = clk_get(&pdev->dev, "xo");
if (IS_ERR(xo_clk)) {
- bam_dmux_log("%s: did not get xo clock\n", __func__);
+ BAM_DMUX_LOG("%s: did not get xo clock\n", __func__);
xo_clk = NULL;
}
dfab_clk = clk_get(&pdev->dev, "bus_clk");
if (IS_ERR(dfab_clk)) {
- bam_dmux_log("%s: did not get dfab clock\n", __func__);
+ BAM_DMUX_LOG("%s: did not get dfab clock\n", __func__);
dfab_clk = NULL;
} else {
rc = clk_set_rate(dfab_clk, 64000000);
@@ -2434,7 +2409,6 @@
bam_ipc_log_txt = ipc_log_context_create(BAM_IPC_LOG_PAGES, "bam_dmux");
if (!bam_ipc_log_txt) {
pr_err("%s : unable to create IPC Logging Context", __func__);
- bam_dmux_state_logging_disabled = 1;
}
rx_timer_interval = DEFAULT_POLLING_MIN_SLEEP;
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index 0dee8f5..0f88287 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -1729,13 +1729,6 @@
},
};
-static struct gpiomux_setting fsm8064_ep_sync_drsync_cfg = {
- .func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_UP,
- .dir = GPIOMUX_OUT_HIGH,
-};
-
static struct gpiomux_setting fsm8064_ep_sync_input_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_4MA,
@@ -1746,7 +1739,7 @@
{
.gpio = 6, /* GPSPPSIN_DRSYNC */
.settings = {
- [GPIOMUX_SUSPENDED] = &fsm8064_ep_sync_drsync_cfg,
+ [GPIOMUX_SUSPENDED] = &fsm8064_ep_sync_input_cfg,
},
},
{
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index a1ff607..a1ed251 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -137,7 +137,7 @@
PM8921_GPIO_OUTPUT_VIN(14, 1, PM_GPIO_VIN_VPH),
/* PPS_SRC_SEL_N, chooses between WGR7640 PPS source (high) or
* CW GPS module PPS source (low) */
- PM8921_GPIO_OUTPUT_VIN(19, 1, PM_GPIO_VIN_VPH), /* PPS_SRC_SEL_N */
+ PM8921_GPIO_OUTPUT_VIN(19, 0, PM_GPIO_VIN_VPH), /* PPS_SRC_SEL_N */
PM8921_GPIO_OUTPUT_VIN(13, 1, PM_GPIO_VIN_VPH), /* PCIE_CLK_PWR_EN */
PM8921_GPIO_OUTPUT_VIN(37, 1, PM_GPIO_VIN_VPH), /* PCIE_RST_N */
@@ -557,4 +557,7 @@
if (!machine_is_apq8064_mtp() && !machine_is_apq8064_liquid())
apq8064_pm8921_chg_pdata.battery_less_hardware = 1;
+
+ if (machine_is_mpq8064_hrd())
+ apq8064_pm8921_chg_pdata.disable_chg_rmvl_wrkarnd = 1;
}
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 9ed71da..1d07356 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -683,53 +683,6 @@
.paddr_to_memtype = apq8064_paddr_to_memtype,
};
-static int apq8064_memory_bank_size(void)
-{
- return 1<<29;
-}
-
-static void __init locate_unstable_memory(void)
-{
- struct membank *mb = &meminfo.bank[meminfo.nr_banks - 1];
- unsigned long bank_size;
- unsigned long low, high;
-
- bank_size = apq8064_memory_bank_size();
- low = meminfo.bank[0].start;
- high = mb->start + mb->size;
-
- /* Check if 32 bit overflow occured */
- if (high < mb->start)
- high = -PAGE_SIZE;
-
- low &= ~(bank_size - 1);
-
- if (high - low <= bank_size)
- goto no_dmm;
-
-#ifdef CONFIG_ENABLE_DMM
- apq8064_reserve_info.low_unstable_address = mb->start -
- MIN_MEMORY_BLOCK_SIZE + mb->size;
- apq8064_reserve_info.max_unstable_size = MIN_MEMORY_BLOCK_SIZE;
-
- apq8064_reserve_info.bank_size = bank_size;
- pr_info("low unstable address %lx max size %lx bank size %lx\n",
- apq8064_reserve_info.low_unstable_address,
- apq8064_reserve_info.max_unstable_size,
- apq8064_reserve_info.bank_size);
- return;
-#endif
-no_dmm:
- apq8064_reserve_info.low_unstable_address = high;
- apq8064_reserve_info.max_unstable_size = 0;
-}
-
-static int apq8064_change_memory_power(u64 start, u64 size,
- int change_type)
-{
- return soc_change_memory_power(start, size, change_type);
-}
-
static char prim_panel_name[PANEL_NAME_MAX_LEN];
static char ext_panel_name[PANEL_NAME_MAX_LEN];
@@ -766,22 +719,9 @@
msm_reserve();
}
-static void __init place_movable_zone(void)
-{
-#ifdef CONFIG_ENABLE_DMM
- movable_reserved_start = apq8064_reserve_info.low_unstable_address;
- movable_reserved_size = apq8064_reserve_info.max_unstable_size;
- pr_info("movable zone start %lx size %lx\n",
- movable_reserved_start, movable_reserved_size);
-#endif
-}
-
static void __init apq8064_early_reserve(void)
{
reserve_info = &apq8064_reserve_info;
- locate_unstable_memory();
- place_movable_zone();
-
}
#ifdef CONFIG_USB_EHCI_MSM_HSIC
/* Bandwidth requests (zero) if no vote placed */
@@ -3935,8 +3875,6 @@
if (machine_is_apq8064_mtp())
platform_device_register(&mtp_kp_pdev);
- change_memory_power = &apq8064_change_memory_power;
-
if (machine_is_mpq8064_cdp()) {
platform_device_register(&mpq_gpio_keys_pdev);
platform_device_register(&mpq_keypad_device);
@@ -3979,7 +3917,6 @@
#ifdef CONFIG_MSM_CAMERA
apq8064_init_cam();
#endif
- change_memory_power = &apq8064_change_memory_power;
}
MACHINE_START(APQ8064_CDP, "QCT APQ8064 CDP")
@@ -4005,6 +3942,7 @@
.init_early = apq8064_allocate_memory_regions,
.init_very_early = apq8064_early_reserve,
.restart = msm_restart,
+ .smp = &msm8960_smp_ops,
MACHINE_END
MACHINE_START(APQ8064_MTP, "QCT APQ8064 MTP")
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index 8be5525..c1971c1 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -93,6 +93,28 @@
},
};
+static struct gpiomux_setting sd_card_det_active_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting sd_card_det_sleep_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+ .dir = GPIOMUX_IN,
+};
+
+static struct msm_gpiomux_config sd_card_det __initdata = {
+ .gpio = 38,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sd_card_det_active_config,
+ [GPIOMUX_SUSPENDED] = &sd_card_det_sleep_config,
+ },
+};
+
void __init msm8226_init_gpiomux(void)
{
int rc;
@@ -108,4 +130,6 @@
#endif
msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+
+ msm_gpiomux_install(&sd_card_det, 1);
}
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index 2b331d0..79ab428 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -51,6 +51,7 @@
#include "platsmp.h"
#include "spm.h"
#include "lpm_resources.h"
+#include "modem_notifier.h"
static struct memtype_reserve msm8226_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
@@ -102,6 +103,8 @@
*/
void __init msm8226_add_drivers(void)
{
+ msm_init_modem_notifier_list();
+ msm_smd_init();
msm_rpm_driver_init();
msm_lpmrs_module_init();
msm_spm_device_init();
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index fbcc6f1..73b4eb7 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -714,64 +714,9 @@
.paddr_to_memtype = msm8930_paddr_to_memtype,
};
-static int msm8930_memory_bank_size(void)
-{
- return 1<<29;
-}
-
-static void __init locate_unstable_memory(void)
-{
- struct membank *mb = &meminfo.bank[meminfo.nr_banks - 1];
- unsigned long bank_size;
- unsigned long low, high;
-
- bank_size = msm8930_memory_bank_size();
- low = meminfo.bank[0].start;
- high = mb->start + mb->size;
-
- /* Check if 32 bit overflow occured */
- if (high < mb->start)
- high -= PAGE_SIZE;
-
- if (high < MAX_FIXED_AREA_SIZE + MSM8930_FIXED_AREA_START)
- panic("fixed area extends beyond end of memory\n");
-
- low &= ~(bank_size - 1);
-
- if (high - low <= bank_size)
- goto no_dmm;
-
- msm8930_reserve_info.bank_size = bank_size;
-#ifdef CONFIG_ENABLE_DMM
- msm8930_reserve_info.low_unstable_address = mb->start -
- MIN_MEMORY_BLOCK_SIZE + mb->size;
- msm8930_reserve_info.max_unstable_size = MIN_MEMORY_BLOCK_SIZE;
- pr_info("low unstable address %lx max size %lx bank size %lx\n",
- msm8930_reserve_info.low_unstable_address,
- msm8930_reserve_info.max_unstable_size,
- msm8930_reserve_info.bank_size);
- return;
-#endif
-no_dmm:
- msm8930_reserve_info.low_unstable_address = high;
- msm8930_reserve_info.max_unstable_size = 0;
-}
-
-static void __init place_movable_zone(void)
-{
-#ifdef CONFIG_ENABLE_DMM
- movable_reserved_start = msm8930_reserve_info.low_unstable_address;
- movable_reserved_size = msm8930_reserve_info.max_unstable_size;
- pr_info("movable zone start %lx size %lx\n",
- movable_reserved_start, movable_reserved_size);
-#endif
-}
-
static void __init msm8930_early_memory(void)
{
reserve_info = &msm8930_reserve_info;
- locate_unstable_memory();
- place_movable_zone();
}
static char prim_panel_name[PANEL_NAME_MAX_LEN];
@@ -799,12 +744,6 @@
msm_reserve();
}
-static int msm8930_change_memory_power(u64 start, u64 size,
- int change_type)
-{
- return soc_change_memory_power(start, size, change_type);
-}
-
static void __init msm8930_allocate_memory_regions(void)
{
msm8930_allocate_fb_region();
@@ -3022,7 +2961,6 @@
msm8930_init_fb();
slim_register_board_info(msm_slim_devices,
ARRAY_SIZE(msm_slim_devices));
- change_memory_power = &msm8930_change_memory_power;
BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
if (PLATFORM_IS_CHARM25())
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 819ccc5..bd54756 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -806,65 +806,9 @@
.paddr_to_memtype = msm8960_paddr_to_memtype,
};
-static int msm8960_memory_bank_size(void)
-{
- return 1<<29;
-}
-
-static void __init locate_unstable_memory(void)
-{
- struct membank *mb = &meminfo.bank[meminfo.nr_banks - 1];
- unsigned long bank_size;
- unsigned long low, high;
-
- bank_size = msm8960_memory_bank_size();
- msm8960_reserve_info.bank_size = bank_size;
-
- low = meminfo.bank[0].start;
- high = mb->start + mb->size;
-
- /* Check if 32 bit overflow occured */
- if (high < mb->start)
- high = ~0UL;
-
- if (high < MAX_FIXED_AREA_SIZE + MSM8960_FIXED_AREA_START)
- panic("fixed area extends beyond end of memory\n");
-
- low &= ~(bank_size - 1);
-
- if (high - low <= bank_size)
- goto no_dmm;
-
-#ifdef CONFIG_ENABLE_DMM
- msm8960_reserve_info.low_unstable_address = mb->start -
- MIN_MEMORY_BLOCK_SIZE + mb->size;
- msm8960_reserve_info.max_unstable_size = MIN_MEMORY_BLOCK_SIZE;
- pr_info("low unstable address %lx max size %lx bank size %lx\n",
- msm8960_reserve_info.low_unstable_address,
- msm8960_reserve_info.max_unstable_size,
- msm8960_reserve_info.bank_size);
- return;
-#endif
-no_dmm:
- msm8960_reserve_info.low_unstable_address = high;
- msm8960_reserve_info.max_unstable_size = 0;
-}
-
-static void __init place_movable_zone(void)
-{
-#ifdef CONFIG_ENABLE_DMM
- movable_reserved_start = msm8960_reserve_info.low_unstable_address;
- movable_reserved_size = msm8960_reserve_info.max_unstable_size;
- pr_info("movable zone start %lx size %lx\n",
- movable_reserved_start, movable_reserved_size);
-#endif
-}
-
static void __init msm8960_early_memory(void)
{
reserve_info = &msm8960_reserve_info;
- locate_unstable_memory();
- place_movable_zone();
}
static char prim_panel_name[PANEL_NAME_MAX_LEN];
@@ -891,12 +835,6 @@
msm_reserve();
}
-static int msm8960_change_memory_power(u64 start, u64 size,
- int change_type)
-{
- return soc_change_memory_power(start, size, change_type);
-}
-
static void __init msm8960_allocate_memory_regions(void)
{
msm8960_allocate_fb_region();
@@ -3037,7 +2975,10 @@
/* Fixup data that needs to change based on GPU ID */
if (cpu_is_msm8960ab()) {
- kgsl_3d0_pdata->chipid = ADRENO_CHIPID(3, 2, 1, 0);
+ if (SOCINFO_VERSION_MINOR(soc_platform_version) == 0)
+ kgsl_3d0_pdata->chipid = ADRENO_CHIPID(3, 2, 1, 0);
+ else
+ kgsl_3d0_pdata->chipid = ADRENO_CHIPID(3, 2, 1, 1);
/* 8960PRO nominal clock rate is 320Mhz */
kgsl_3d0_pdata->pwrlevel[1].gpu_freq = 320000000;
#ifdef CONFIG_MSM_BUS_SCALING
@@ -3553,7 +3494,6 @@
slim_register_board_info(msm_slim_devices,
ARRAY_SIZE(msm_slim_devices));
msm8960_init_dsps();
- change_memory_power = &msm8960_change_memory_power;
BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
bt_power_init();
if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index 7038ab9..9c9ccaa 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -93,9 +93,9 @@
"qup_scl" },
{ GPIO_CFG(61, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
"qup_sda" },
- { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
"qup_scl" },
- { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
"qup_sda" },
};
@@ -104,9 +104,9 @@
"qup_scl" },
{ GPIO_CFG(61, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
"qup_sda" },
- { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
"qup_scl" },
- { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
"qup_sda" },
};
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index a963c19..b40617c 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -60,6 +60,8 @@
#define gpll0_mm_source_val 5
#define dsipll_750_mm_source_val 1
#define dsipll_667_mm_source_val 1
+#define dsipll0_byte_mm_source_val 1
+#define dsipll0_pixel_mm_source_val 1
#define gpll1_hsic_source_val 4
@@ -1334,17 +1336,6 @@
},
};
-static struct branch_clk gcc_mmss_noc_cfg_ahb_clk = {
- .cbcr_reg = MMSS_NOC_CFG_AHB_CBCR,
- .has_sibling = 1,
- .base = &virt_bases[GCC_BASE],
- .c = {
- .dbg_name = "gcc_mmss_noc_cfg_ahb_clk",
- .ops = &clk_ops_branch,
- CLK_INIT(gcc_mmss_noc_cfg_ahb_clk.c),
- },
-};
-
static struct branch_clk gcc_mss_cfg_ahb_clk = {
.cbcr_reg = MSS_CFG_AHB_CBCR,
.has_sibling = 1,
@@ -1592,7 +1583,6 @@
static struct measure_mux_entry measure_mux_GCC[] = {
{ &gcc_periph_noc_ahb_clk.c, GCC_BASE, 0x0010 },
{ &gcc_noc_conf_xpu_ahb_clk.c, GCC_BASE, 0x0018 },
- { &gcc_mmss_noc_cfg_ahb_clk.c, GCC_BASE, 0x002a },
{ &gcc_mss_cfg_ahb_clk.c, GCC_BASE, 0x0030 },
{ &gcc_mss_q6_bimc_axi_clk.c, GCC_BASE, 0x0031 },
{ &gcc_usb_hsic_ahb_clk.c, GCC_BASE, 0x0058 },
@@ -1697,19 +1687,6 @@
},
};
-static struct pll_clk mmpll2_pll = {
- .mode_reg = (void __iomem *)MMPLL2_PLL_MODE,
- .status_reg = (void __iomem *)MMPLL2_PLL_STATUS,
- .base = &virt_bases[MMSS_BASE],
- .c = {
- .dbg_name = "mmpll2_pll",
- .parent = &xo.c,
- .rate = 900000000,
- .ops = &clk_ops_local_pll,
- CLK_INIT(mmpll2_pll.c),
- },
-};
-
static struct clk_freq_tbl ftbl_camss_csi0_1_clk[] = {
F_MMSS( 100000000, gpll0, 6, 0, 0),
F_MMSS( 200000000, mmpll0_pll, 4, 0, 0),
@@ -1826,21 +1803,36 @@
},
};
-static struct clk_freq_tbl ftbl_mdss_pclk0_clk[] = {
- F_MDSS( 83000000, dsipll_667, 8, 0, 0),
- F_MDSS( 166000000, dsipll_667, 4, 0, 0),
- F_END
+static struct branch_clk mdss_ahb_clk;
+static struct clk dsipll0_byte_clk_src = {
+ .depends = &mdss_ahb_clk.c,
+ .parent = &xo.c,
+ .dbg_name = "dsipll0_byte_clk_src",
+ .ops = &clk_ops_dsi_byte_pll,
+ CLK_INIT(dsipll0_byte_clk_src),
+};
+
+static struct clk dsipll0_pixel_clk_src = {
+ .depends = &mdss_ahb_clk.c,
+ .parent = &xo.c,
+ .dbg_name = "dsipll0_pixel_clk_src",
+ .ops = &clk_ops_dsi_pixel_pll,
+ CLK_INIT(dsipll0_pixel_clk_src),
+};
+
+static struct clk_freq_tbl pixel_freq = {
+ .src_clk = &dsipll0_pixel_clk_src,
+ .div_src_val = BVAL(10, 8, dsipll0_pixel_mm_source_val),
};
static struct rcg_clk pclk0_clk_src = {
.cmd_rcgr_reg = PCLK0_CMD_RCGR,
- .set_rate = set_rate_mnd,
- .freq_tbl = ftbl_mdss_pclk0_clk,
- .current_freq = &rcg_dummy_freq,
+ .current_freq = &pixel_freq,
.base = &virt_bases[MMSS_BASE],
.c = {
+ .parent = &dsipll0_pixel_clk_src,
.dbg_name = "pclk0_clk_src",
- .ops = &clk_ops_rcg_mnd,
+ .ops = &clk_ops_pixel,
VDD_DIG_FMAX_MAP2(LOW, 83330000, NOMINAL, 166670000),
CLK_INIT(pclk0_clk_src.c),
},
@@ -2015,21 +2007,19 @@
},
};
-static struct clk_freq_tbl ftbl_mdss_byte0_clk[] = {
- F_MDSS( 62500000, dsipll_750, 12, 0, 0),
- F_MDSS( 125000000, dsipll_750, 6, 0, 0),
- F_END
+static struct clk_freq_tbl byte_freq = {
+ .src_clk = &dsipll0_byte_clk_src,
+ .div_src_val = BVAL(10, 8, dsipll0_byte_mm_source_val),
};
static struct rcg_clk byte0_clk_src = {
.cmd_rcgr_reg = BYTE0_CMD_RCGR,
- .set_rate = set_rate_hid,
- .freq_tbl = ftbl_mdss_byte0_clk,
- .current_freq = &rcg_dummy_freq,
+ .current_freq = &byte_freq,
.base = &virt_bases[MMSS_BASE],
.c = {
+ .parent = &dsipll0_byte_clk_src,
.dbg_name = "byte0_clk_src",
- .ops = &clk_ops_rcg,
+ .ops = &clk_ops_byte,
VDD_DIG_FMAX_MAP2(LOW, 62500000, NOMINAL, 125000000),
CLK_INIT(byte0_clk_src.c),
},
@@ -2530,17 +2520,6 @@
},
};
-static struct branch_clk mmss_mmssnoc_ahb_clk = {
- .cbcr_reg = MMSS_MMSSNOC_AHB_CBCR,
- .has_sibling = 1,
- .base = &virt_bases[MMSS_BASE],
- .c = {
- .dbg_name = "mmss_mmssnoc_ahb_clk",
- .ops = &clk_ops_branch,
- CLK_INIT(mmss_mmssnoc_ahb_clk.c),
- },
-};
-
static struct branch_clk mmss_mmssnoc_bto_ahb_clk = {
.cbcr_reg = MMSS_MMSSNOC_BTO_AHB_CBCR,
.has_sibling = 1,
@@ -2650,7 +2629,6 @@
};
static struct measure_mux_entry measure_mux_MMSS[] = {
- { &mmss_mmssnoc_ahb_clk.c, MMSS_BASE, 0x0001 },
{ &mmss_mmssnoc_bto_ahb_clk.c, MMSS_BASE, 0x0002 },
{ &mmss_misc_ahb_clk.c, MMSS_BASE, 0x0003 },
{ &mmss_mmssnoc_axi_clk.c, MMSS_BASE, 0x0004 },
@@ -3100,7 +3078,6 @@
CLK_LOOKUP("ocmem_a_clk", ocmemgx_msmbus_a_clk.c, "msm_bus"),
CLK_LOOKUP("bus_clk", mmss_s0_axi_clk.c, "msm_mmss_noc"),
CLK_LOOKUP("bus_a_clk", mmss_s0_axi_clk.c, "msm_mmss_noc"),
- CLK_LOOKUP("iface_clk", gcc_mmss_noc_cfg_ahb_clk.c, ""),
/* CoreSight clocks */
CLK_LOOKUP("core_clk", qdss_clk.c, "fc322000.tmc"),
@@ -3197,7 +3174,6 @@
CLK_LOOKUP("gpll1", gpll1.c, ""),
CLK_LOOKUP("mmpll0", mmpll0_pll.c, ""),
CLK_LOOKUP("mmpll1", mmpll1_pll.c, ""),
- CLK_LOOKUP("mmpll2", mmpll2_pll.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""),
@@ -3236,12 +3212,12 @@
CLK_LOOKUP("pixel_clk", mdss_pclk0_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdss_dsi_clk_ctrl"),
- CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "mdp.0"),
- CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "mdp.0"),
- CLK_LOOKUP("core_clk_src", mdp_clk_src.c, "mdp.0"),
- CLK_LOOKUP("vsync_clk", mdss_vsync_clk.c, "mdp.0"),
- CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdp.0"),
- CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "mdp.0"),
+ CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "fd900000.qcom,mdss_mdp"),
+ CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "fd900000.qcom,mdss_mdp"),
+ CLK_LOOKUP("core_clk_src", mdp_clk_src.c, "fd900000.qcom,mdss_mdp"),
+ CLK_LOOKUP("vsync_clk", mdss_vsync_clk.c, "fd900000.qcom,mdss_mdp"),
+ CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd900000.qcom,mdss_mdp"),
+ CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "fd900000.qcom,mdss_mdp"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
@@ -3368,7 +3344,6 @@
CLK_LOOKUP("cam_gp1_clk", camss_gp1_clk.c, ""),
CLK_LOOKUP("iface_clk", camss_micro_ahb_clk.c, ""),
- CLK_LOOKUP("", mmss_mmssnoc_ahb_clk.c, ""),
CLK_LOOKUP("", mmss_mmssnoc_bto_ahb_clk.c, ""),
CLK_LOOKUP("", mmss_mmssnoc_axi_clk.c, ""),
CLK_LOOKUP("", mmss_s0_axi_clk.c, ""),
@@ -3392,166 +3367,23 @@
.size = ARRAY_SIZE(msm_clocks_8226_rumi),
};
-static struct pll_config_regs gpll0_regs __initdata = {
- .l_reg = (void __iomem *)GPLL0_L_VAL,
- .m_reg = (void __iomem *)GPLL0_M_VAL,
- .n_reg = (void __iomem *)GPLL0_N_VAL,
- .config_reg = (void __iomem *)GPLL0_USER_CTL,
- .mode_reg = (void __iomem *)GPLL0_MODE,
- .base = &virt_bases[GCC_BASE],
-};
-
-/* GPLL0 at 600 MHz, main output enabled. */
-static struct pll_config gpll0_config __initdata = {
- .l = 0x1f,
- .m = 0x1,
- .n = 0x4,
- .vco_val = 0x0,
- .vco_mask = BM(21, 20),
- .pre_div_val = 0x0,
- .pre_div_mask = BM(14, 12),
- .post_div_val = 0x0,
- .post_div_mask = BM(9, 8),
- .mn_ena_val = BIT(24),
- .mn_ena_mask = BIT(24),
- .main_output_val = BIT(0),
- .main_output_mask = BIT(0),
-};
-
-static struct pll_config_regs gpll1_regs __initdata = {
- .l_reg = (void __iomem *)GPLL1_L_VAL,
- .m_reg = (void __iomem *)GPLL1_M_VAL,
- .n_reg = (void __iomem *)GPLL1_N_VAL,
- .config_reg = (void __iomem *)GPLL1_USER_CTL,
- .mode_reg = (void __iomem *)GPLL1_MODE,
- .base = &virt_bases[GCC_BASE],
-};
-
-/* GPLL1 at 480 MHz, main output enabled. */
-static struct pll_config gpll1_config __initdata = {
- .l = 0x19,
- .m = 0x0,
- .n = 0x1,
- .vco_val = 0x0,
- .vco_mask = BM(21, 20),
- .pre_div_val = 0x0,
- .pre_div_mask = BM(14, 12),
- .post_div_val = 0x0,
- .post_div_mask = BM(9, 8),
- .main_output_val = BIT(0),
- .main_output_mask = BIT(0),
-};
-
-static struct pll_config_regs mmpll0_regs __initdata = {
- .l_reg = (void __iomem *)MMPLL0_PLL_L_VAL,
- .m_reg = (void __iomem *)MMPLL0_PLL_M_VAL,
- .n_reg = (void __iomem *)MMPLL0_PLL_N_VAL,
- .config_reg = (void __iomem *)MMPLL0_PLL_USER_CTL,
- .mode_reg = (void __iomem *)MMPLL0_PLL_MODE,
- .base = &virt_bases[MMSS_BASE],
-};
-
-/* MMPLL0 at 800 MHz, main output enabled. */
-static struct pll_config mmpll0_config __initdata = {
- .l = 0x29,
- .m = 0x2,
- .n = 0x3,
- .vco_val = 0x0,
- .vco_mask = BM(21, 20),
- .pre_div_val = 0x0,
- .pre_div_mask = BM(14, 12),
- .post_div_val = 0x0,
- .post_div_mask = BM(9, 8),
- .mn_ena_val = BIT(24),
- .mn_ena_mask = BIT(24),
- .main_output_val = BIT(0),
- .main_output_mask = BIT(0),
-};
-
-static struct pll_config_regs mmpll1_regs __initdata = {
- .l_reg = (void __iomem *)MMPLL1_PLL_L_VAL,
- .m_reg = (void __iomem *)MMPLL1_PLL_M_VAL,
- .n_reg = (void __iomem *)MMPLL1_PLL_N_VAL,
- .config_reg = (void __iomem *)MMPLL1_PLL_USER_CTL,
- .mode_reg = (void __iomem *)MMPLL1_PLL_MODE,
- .base = &virt_bases[MMSS_BASE],
-};
-
-/* MMPLL1 at 1000 MHz, main output enabled. */
-static struct pll_config mmpll1_config __initdata = {
- .l = 0x2C,
- .m = 0x1,
- .n = 0x10,
- .vco_val = 0x0,
- .vco_mask = BM(21, 20),
- .pre_div_val = 0x0,
- .pre_div_mask = BM(14, 12),
- .post_div_val = 0x0,
- .post_div_mask = BM(9, 8),
- .mn_ena_val = BIT(24),
- .mn_ena_mask = BIT(24),
- .main_output_val = BIT(0),
- .main_output_mask = BIT(0),
-};
-
-#define PLL_AUX_OUTPUT_BIT 1
-#define PLL_AUX2_OUTPUT_BIT 2
-
-#define PWR_ON_MASK BIT(31)
-#define EN_REST_WAIT_MASK (0xF << 20)
-#define EN_FEW_WAIT_MASK (0xF << 16)
-#define CLK_DIS_WAIT_MASK (0xF << 12)
-#define SW_OVERRIDE_MASK BIT(2)
-#define HW_CONTROL_MASK BIT(1)
-#define SW_COLLAPSE_MASK BIT(0)
-
-/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
-#define EN_REST_WAIT_VAL (0x2 << 20)
-#define EN_FEW_WAIT_VAL (0x2 << 16)
-#define CLK_DIS_WAIT_VAL (0x2 << 12)
-#define GDSC_TIMEOUT_US 50000
-
-#define PLL_OUTCTRL BIT(0)
-#define PLL_BYPASSNL BIT(1)
-#define PLL_RESET_N BIT(2)
-#define PLL_LOCKED_BIT BIT(16)
-#define ENABLE_WAIT_MAX_LOOPS 200
-
static void __init reg_init(void)
{
u32 regval;
- if (!(readl_relaxed(GCC_REG_BASE(GPLL0_STATUS))
- & gpll0.status_mask))
- configure_sr_hpm_lp_pll(&gpll0_config, &gpll0_regs, 1);
-
- if (!(readl_relaxed(GCC_REG_BASE(GPLL1_STATUS))
- & gpll1.status_mask))
- configure_sr_hpm_lp_pll(&gpll1_config, &gpll1_regs, 1);
-
- configure_sr_hpm_lp_pll(&mmpll0_config, &mmpll0_regs, 1);
- configure_sr_hpm_lp_pll(&mmpll1_config, &mmpll1_regs, 1);
-
- /* Enable GPLL0's aux outputs. */
- regval = readl_relaxed(GCC_REG_BASE(GPLL0_USER_CTL));
- regval |= BIT(PLL_AUX_OUTPUT_BIT) | BIT(PLL_AUX2_OUTPUT_BIT);
- writel_relaxed(regval, GCC_REG_BASE(GPLL0_USER_CTL));
-
/* Vote for GPLL0 to turn on. Needed by acpuclock. */
regval = readl_relaxed(GCC_REG_BASE(APCS_GPLL_ENA_VOTE));
regval |= BIT(0);
writel_relaxed(regval, GCC_REG_BASE(APCS_GPLL_ENA_VOTE));
/*
- * TODO: Confirm that no clocks need to be voted on in this sleep vote
- * register.
+ * No clocks need to be enabled during sleep.
*/
writel_relaxed(0x0, GCC_REG_BASE(APCS_CLOCK_SLEEP_ENA_VOTE));
}
static void __init msm8226_clock_post_init(void)
{
-
/* Set rates for single-rate clocks. */
clk_set_rate(&usb_hs_system_clk_src.c,
usb_hs_system_clk_src.freq_tbl[0].freq_hz);
@@ -3620,18 +3452,12 @@
panic("clock-8226: Unable to get the sr2_pll regulator!");
/*
- * The SR2 PLL is used at boot. Vote to prevent its regulator from
- * being turned off while the PLL is still in use.
+ * These regulators are used at boot. Ensure they stay on
+ * while the clock framework comes online.
*/
regulator_set_voltage(vdd_sr2_reg, 1800000, 1800000);
regulator_enable(vdd_sr2_reg);
- /*
- * TODO: Set a voltage and enable vdd_dig, leaving the voltage high
- * until late_init. This may not be necessary with clock handoff;
- * Investigate this code on a real non-simulator target to determine
- * its necessity.
- */
vote_vdd_level(&vdd_dig, VDD_DIG_HIGH);
regulator_enable(vdd_dig_reg);
@@ -3642,7 +3468,6 @@
* access mmss clock controller registers.
*/
clk_set_rate(&mmssnoc_ahb_a_clk.c, 40000000);
- clk_prepare_enable(&mmssnoc_ahb_a_clk.c);
/*
* Hold an active set vote for CXO; this is because CXO is expected
@@ -3653,18 +3478,12 @@
enable_rpm_scaling();
reg_init();
- /*
- * FIXME remove after bus driver is in place
- * Requires gpll0 to be configured
- */
- clk_set_rate(&axi_clk_src.c, 200000000);
- clk_prepare_enable(&mmss_s0_axi_clk.c);
/*
- * TODO: Enable the gcc_bimc_clk smcbc, which is the parent of thhe
- * mss_gcc_q6_bimc_axi_clk
+ * MDSS needs the ahb clock and needs to init before we register the
+ * lookup table.
*/
- writel_relaxed(0x1, GCC_REG_BASE(0x1118));
+ mdss_clk_ctrl_pre_init(&mdss_ahb_clk.c);
}
static int __init msm8226_clock_late_init(void)
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index bfa9ec0..3587df6 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -809,6 +809,7 @@
static DEFINE_CLK_BRANCH_VOTER(cxo_wlan_clk, &cxo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_pronto_clk, &cxo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_dwc3_clk, &cxo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_ehci_host_clk, &cxo_clk_src.c);
static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = {
F(125000000, gpll0, 1, 5, 24),
@@ -3029,134 +3030,6 @@
.src_clk = &dsipll0_byte_clk_src,
.div_src_val = BVAL(10, 8, dsipll0_byte_mm_source_val),
};
-static struct clk_freq_tbl pixel_freq = {
- .src_clk = &dsipll0_pixel_clk_src,
- .div_src_val = BVAL(10, 8, dsipll0_pixel_mm_source_val),
-};
-static struct clk_ops clk_ops_byte;
-static struct clk_ops clk_ops_pixel;
-
-#define CFG_RCGR_DIV_MASK BM(4, 0)
-#define CMD_RCGR_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x0)
-#define CFG_RCGR_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x4)
-#define M_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x8)
-#define N_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0xC)
-#define MND_MODE_MASK BM(13, 12)
-#define MND_DUAL_EDGE_MODE_BVAL BVAL(13, 12, 0x2)
-#define CFG_RCGR_SRC_SEL_MASK BM(10, 8)
-#define CMD_RCGR_ROOT_STATUS_BIT BIT(31)
-
-static enum handoff byte_rcg_handoff(struct clk *clk)
-{
- struct rcg_clk *rcg = to_rcg_clk(clk);
- u32 div_val;
- unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent);
-
- /* If the pre-divider is used, find the rate after the division */
- div_val = readl_relaxed(CFG_RCGR_REG(rcg)) & CFG_RCGR_DIV_MASK;
- if (div_val > 1)
- pre_div_rate = parent_rate / ((div_val + 1) >> 1);
- else
- pre_div_rate = parent_rate;
-
- clk->rate = pre_div_rate;
-
- if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT)
- return HANDOFF_DISABLED_CLK;
-
- return HANDOFF_ENABLED_CLK;
-}
-
-static int set_rate_byte(struct clk *clk, unsigned long rate)
-{
- struct rcg_clk *rcg = to_rcg_clk(clk);
- struct clk *pll = clk->parent;
- unsigned long source_rate, div;
- int rc;
-
- if (rate == 0)
- return -EINVAL;
-
- rc = clk_set_rate(pll, rate);
- if (rc)
- return rc;
-
- source_rate = clk_round_rate(pll, rate);
- if ((2 * source_rate) % rate)
- return -EINVAL;
-
- div = ((2 * source_rate)/rate) - 1;
- if (div > CFG_RCGR_DIV_MASK)
- return -EINVAL;
-
- byte_freq.div_src_val &= ~CFG_RCGR_DIV_MASK;
- byte_freq.div_src_val |= BVAL(4, 0, div);
- set_rate_hid(rcg, &byte_freq);
-
- return 0;
-}
-
-static enum handoff pixel_rcg_handoff(struct clk *clk)
-{
- struct rcg_clk *rcg = to_rcg_clk(clk);
- u32 div_val, mval, nval, cfg_regval;
- unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent);
-
- cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
-
- /* If the pre-divider is used, find the rate after the division */
- div_val = cfg_regval & CFG_RCGR_DIV_MASK;
- if (div_val > 1)
- pre_div_rate = parent_rate / ((div_val + 1) >> 1);
- else
- pre_div_rate = parent_rate;
-
- clk->rate = pre_div_rate;
-
- /* If MND is used, find the rate after the MND division */
- if ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL) {
- mval = readl_relaxed(M_REG(rcg));
- nval = readl_relaxed(N_REG(rcg));
- if (!nval)
- return HANDOFF_DISABLED_CLK;
- nval = (~nval) + mval;
- clk->rate = (pre_div_rate * mval) / nval;
- }
-
- if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT)
- return HANDOFF_DISABLED_CLK;
-
- return HANDOFF_ENABLED_CLK;
-}
-
-static int set_rate_pixel(struct clk *clk, unsigned long rate)
-{
- struct rcg_clk *rcg = to_rcg_clk(clk);
- struct clk *pll = clk->parent;
- unsigned long source_rate, div;
- int rc;
-
- if (rate == 0)
- return -EINVAL;
-
- rc = clk_set_rate(pll, rate);
- if (rc)
- return rc;
-
- source_rate = clk_round_rate(pll, rate);
- if ((2 * source_rate) % rate)
- return -EINVAL;
-
- div = ((2 * source_rate)/rate) - 1;
- if (div > CFG_RCGR_DIV_MASK)
- return -EINVAL;
-
- pixel_freq.div_src_val &= ~CFG_RCGR_DIV_MASK;
- pixel_freq.div_src_val |= BVAL(4, 0, div);
- set_rate_mnd(rcg, &pixel_freq);
-
- return 0;
-}
static struct rcg_clk byte0_clk_src = {
.cmd_rcgr_reg = BYTE0_CMD_RCGR,
@@ -3323,35 +3196,6 @@
F_END
};
-/*
- * Unlike other clocks, the HDMI rate is adjusted through PLL
- * re-programming. It is also routed through an HID divider.
- */
-static int rcg_clk_set_rate_hdmi(struct clk *c, unsigned long rate)
-{
- struct clk_freq_tbl *nf;
- struct rcg_clk *rcg = to_rcg_clk(c);
- int rc;
-
- for (nf = rcg->freq_tbl; nf->freq_hz != rate; nf++)
- if (nf->freq_hz == FREQ_END) {
- rc = -EINVAL;
- goto out;
- }
-
- rc = clk_set_rate(nf->src_clk, rate);
- if (rc < 0)
- goto out;
- set_rate_hid(rcg, nf);
-
- rcg->current_freq = nf;
- c->parent = nf->src_clk;
-out:
- return rc;
-}
-
-static struct clk_ops clk_ops_rcg_hdmi;
-
static struct rcg_clk extpclk_clk_src = {
.cmd_rcgr_reg = EXTPCLK_CMD_RCGR,
.freq_tbl = ftbl_mdss_extpclk_clk,
@@ -3384,6 +3228,10 @@
},
};
+static struct clk_freq_tbl pixel_freq = {
+ .src_clk = &dsipll0_pixel_clk_src,
+ .div_src_val = BVAL(10, 8, dsipll0_pixel_mm_source_val),
+};
static struct rcg_clk pclk0_clk_src = {
.cmd_rcgr_reg = PCLK0_CMD_RCGR,
@@ -4893,6 +4741,7 @@
CLK_LOOKUP("xo", cxo_wlan_clk.c, "fb000000.qcom,wcnss-wlan"),
CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
CLK_LOOKUP("xo", cxo_dwc3_clk.c, "msm_dwc3"),
+ CLK_LOOKUP("xo", cxo_ehci_host_clk.c, "msm_ehci_host"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
@@ -5033,9 +4882,11 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6e.qcom,camera"),
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "20.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk2_clk_src.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6e.qcom,camera"),
+ CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "20.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk2_clk.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, ""),
@@ -5521,28 +5372,6 @@
writel_relaxed(0x0, GCC_REG_BASE(APCS_CLOCK_SLEEP_ENA_VOTE));
}
-static void __init mdss_clock_setup(void)
-{
- clk_ops_byte = clk_ops_rcg;
- clk_ops_byte.set_rate = set_rate_byte;
- clk_ops_byte.handoff = byte_rcg_handoff;
- clk_ops_byte.get_parent = NULL;
-
- clk_ops_pixel = clk_ops_rcg_mnd;
- clk_ops_pixel.set_rate = set_rate_pixel;
- clk_ops_pixel.handoff = pixel_rcg_handoff;
- clk_ops_pixel.get_parent = NULL;
-
- clk_ops_rcg_hdmi = clk_ops_rcg;
- clk_ops_rcg_hdmi.set_rate = rcg_clk_set_rate_hdmi;
-
- /*
- * MDSS needs the ahb clock and needs to init before we register the
- * lookup table.
- */
- mdss_clk_ctrl_pre_init(&mdss_ahb_clk.c);
-}
-
static void __init msm8974_clock_post_init(void)
{
if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
@@ -5689,7 +5518,11 @@
qup_i2c_clks[i][0]->parent = qup_i2c_clks[i][1];
}
- mdss_clock_setup();
+ /*
+ * MDSS needs the ahb clock and needs to init before we register the
+ * lookup table.
+ */
+ mdss_clk_ctrl_pre_init(&mdss_ahb_clk.c);
}
static int __init msm8974_clock_late_init(void)
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index dd78557..214d1b7 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -624,6 +624,148 @@
return HANDOFF_ENABLED_CLK;
}
+static enum handoff byte_rcg_handoff(struct clk *clk)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ u32 div_val;
+ unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent);
+
+ /* If the pre-divider is used, find the rate after the division */
+ div_val = readl_relaxed(CFG_RCGR_REG(rcg)) & CFG_RCGR_DIV_MASK;
+ if (div_val > 1)
+ pre_div_rate = parent_rate / ((div_val + 1) >> 1);
+ else
+ pre_div_rate = parent_rate;
+
+ clk->rate = pre_div_rate;
+
+ if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT)
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static int set_rate_byte(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk *pll = clk->parent;
+ unsigned long source_rate, div;
+ struct clk_freq_tbl *byte_freq = rcg->current_freq;
+ int rc;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ rc = clk_set_rate(pll, rate);
+ if (rc)
+ return rc;
+
+ source_rate = clk_round_rate(pll, rate);
+ if ((2 * source_rate) % rate)
+ return -EINVAL;
+
+ div = ((2 * source_rate)/rate) - 1;
+ if (div > CFG_RCGR_DIV_MASK)
+ return -EINVAL;
+
+ byte_freq->div_src_val &= ~CFG_RCGR_DIV_MASK;
+ byte_freq->div_src_val |= BVAL(4, 0, div);
+ set_rate_hid(rcg, byte_freq);
+
+ return 0;
+}
+
+static enum handoff pixel_rcg_handoff(struct clk *clk)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ u32 div_val, mval, nval, cfg_regval;
+ unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent);
+
+ cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
+
+ /* If the pre-divider is used, find the rate after the division */
+ div_val = cfg_regval & CFG_RCGR_DIV_MASK;
+ if (div_val > 1)
+ pre_div_rate = parent_rate / ((div_val + 1) >> 1);
+ else
+ pre_div_rate = parent_rate;
+
+ clk->rate = pre_div_rate;
+
+ /* If MND is used, find the rate after the MND division */
+ if ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL) {
+ mval = readl_relaxed(M_REG(rcg));
+ nval = readl_relaxed(N_REG(rcg));
+ if (!nval)
+ return HANDOFF_DISABLED_CLK;
+ nval = (~nval) + mval;
+ clk->rate = (pre_div_rate * mval) / nval;
+ }
+
+ if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT)
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static int set_rate_pixel(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk *pll = clk->parent;
+ unsigned long source_rate, div;
+ struct clk_freq_tbl *pixel_freq = rcg->current_freq;
+ int rc;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ rc = clk_set_rate(pll, rate);
+ if (rc)
+ return rc;
+
+ source_rate = clk_round_rate(pll, rate);
+ if ((2 * source_rate) % rate)
+ return -EINVAL;
+
+ div = ((2 * source_rate)/rate) - 1;
+ if (div > CFG_RCGR_DIV_MASK)
+ return -EINVAL;
+
+ pixel_freq->div_src_val &= ~CFG_RCGR_DIV_MASK;
+ pixel_freq->div_src_val |= BVAL(4, 0, div);
+ set_rate_mnd(rcg, pixel_freq);
+
+ return 0;
+}
+
+/*
+ * Unlike other clocks, the HDMI rate is adjusted through PLL
+ * re-programming. It is also routed through an HID divider.
+ */
+static int rcg_clk_set_rate_hdmi(struct clk *c, unsigned long rate)
+{
+ struct clk_freq_tbl *nf;
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ int rc;
+
+ for (nf = rcg->freq_tbl; nf->freq_hz != rate; nf++)
+ if (nf->freq_hz == FREQ_END) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = clk_set_rate(nf->src_clk, rate);
+ if (rc < 0)
+ goto out;
+ set_rate_hid(rcg, nf);
+
+ rcg->current_freq = nf;
+ c->parent = nf->src_clk;
+out:
+ return rc;
+}
+
+
struct clk_ops clk_ops_empty;
struct clk_ops clk_ops_rcg = {
@@ -644,6 +786,32 @@
.get_parent = rcg_mnd_clk_get_parent,
};
+struct clk_ops clk_ops_pixel = {
+ .enable = rcg_clk_prepare,
+ .set_rate = set_rate_pixel,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = pixel_rcg_handoff,
+};
+
+struct clk_ops clk_ops_byte = {
+ .enable = rcg_clk_prepare,
+ .set_rate = set_rate_byte,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = byte_rcg_handoff,
+};
+
+struct clk_ops clk_ops_rcg_hdmi = {
+ .enable = rcg_clk_prepare,
+ .set_rate = rcg_clk_set_rate_hdmi,
+ .set_rate = rcg_clk_set_rate,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = rcg_clk_handoff,
+ .get_parent = rcg_clk_get_parent,
+};
+
struct clk_ops clk_ops_branch = {
.enable = branch_clk_enable,
.disable = branch_clk_disable,
diff --git a/arch/arm/mach-msm/clock-local2.h b/arch/arm/mach-msm/clock-local2.h
index 2e1b8a9..7ac7bd3 100644
--- a/arch/arm/mach-msm/clock-local2.h
+++ b/arch/arm/mach-msm/clock-local2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -173,6 +173,9 @@
extern struct clk_ops clk_ops_rcg_mnd;
extern struct clk_ops clk_ops_branch;
extern struct clk_ops clk_ops_vote;
+extern struct clk_ops clk_ops_rcg_hdmi;
+extern struct clk_ops clk_ops_byte;
+extern struct clk_ops clk_ops_pixel;
/*
* Clock definition macros
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index c6e93de..c5c4988 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -96,6 +96,8 @@
* @list: List head to link all iommus together
* @clk_reg_virt: Optional clock register virtual address.
* @halt_enabled: Set to 1 if IOMMU halt is supported in the IOMMU, 0 otherwise.
+ * @asid: List of ASID and their usage count (index is ASID value).
+ * @ctx_attach_count: Count of how many context are attached.
*
* A msm_iommu_drvdata holds the global driver data about a single piece
* of an IOMMU hardware instance.
@@ -117,6 +119,8 @@
struct list_head list;
void __iomem *clk_reg_virt;
int halt_enabled;
+ int *asid;
+ unsigned int ctx_attach_count;
};
void msm_iommu_add_drv(struct msm_iommu_drvdata *drv);
diff --git a/arch/arm/mach-msm/include/mach/iommu_perfmon.h b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
index 5a01bee..c03c752 100644
--- a/arch/arm/mach-msm/include/mach/iommu_perfmon.h
+++ b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
@@ -12,6 +12,7 @@
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/list.h>
+#include <linux/irqreturn.h>
#ifndef MSM_IOMMU_PERFMON_H
#define MSM_IOMMU_PERFMON_H
diff --git a/arch/arm/mach-msm/include/mach/ipa.h b/arch/arm/mach-msm/include/mach/ipa.h
index 5ccdf82..f2a4427 100644
--- a/arch/arm/mach-msm/include/mach/ipa.h
+++ b/arch/arm/mach-msm/include/mach/ipa.h
@@ -438,6 +438,9 @@
int (*release_resource)(void);
};
+#define A2_MUX_HDR_NAME_V4_PREF "dmux_hdr_v4_"
+#define A2_MUX_HDR_NAME_V6_PREF "dmux_hdr_v6_"
+
enum a2_mux_event_type {
A2_MUX_RECEIVE,
A2_MUX_WRITE_DONE
@@ -460,26 +463,28 @@
enum a2_mux_event_type event,
unsigned long data);
-#ifdef CONFIG_IPA
-
-/*
- * a2 service
+/**
+ * enum teth_tethering_mode - Tethering mode (Rmnet / MBIM)
*/
-int a2_mux_open_channel(enum a2_mux_logical_channel_id lcid,
- void *user_data,
- a2_mux_notify_cb notify_cb);
+enum teth_tethering_mode {
+ TETH_TETHERING_MODE_RMNET,
+ TETH_TETHERING_MODE_MBIM,
+ TETH_TETHERING_MODE_MAX,
+};
-int a2_mux_close_channel(enum a2_mux_logical_channel_id lcid);
+/**
+ * struct teth_bridge_connect_params - Parameters used in teth_bridge_connect()
+ * @ipa_usb_pipe_hdl: IPA to USB pipe handle, returned from ipa_connect()
+ * @usb_ipa_pipe_hdl: USB to IPA pipe handle, returned from ipa_connect()
+ * @tethering_mode: Rmnet or MBIM
+ */
+struct teth_bridge_connect_params {
+ u32 ipa_usb_pipe_hdl;
+ u32 usb_ipa_pipe_hdl;
+ enum teth_tethering_mode tethering_mode;
+};
-int a2_mux_write(enum a2_mux_logical_channel_id lcid, struct sk_buff *skb);
-
-int a2_mux_is_ch_low(enum a2_mux_logical_channel_id lcid);
-
-int a2_mux_is_ch_full(enum a2_mux_logical_channel_id lcid);
-
-int a2_mux_get_tethered_client_handles(enum a2_mux_logical_channel_id lcid,
- unsigned int *clnt_cons_handle,
- unsigned int *clnt_prod_handle);
+#ifdef CONFIG_IPA
/*
* Connect / Disconnect
@@ -649,6 +654,34 @@
int ipa_rm_inactivity_timer_release_resource(
enum ipa_rm_resource_name resource_name);
+/*
+ * a2 service
+ */
+int a2_mux_open_channel(enum a2_mux_logical_channel_id lcid,
+ void *user_data,
+ a2_mux_notify_cb notify_cb);
+
+int a2_mux_close_channel(enum a2_mux_logical_channel_id lcid);
+
+int a2_mux_write(enum a2_mux_logical_channel_id lcid, struct sk_buff *skb);
+
+int a2_mux_is_ch_low(enum a2_mux_logical_channel_id lcid);
+
+int a2_mux_is_ch_full(enum a2_mux_logical_channel_id lcid);
+
+int a2_mux_get_tethered_client_handles(enum a2_mux_logical_channel_id lcid,
+ unsigned int *clnt_cons_handle,
+ unsigned int *clnt_prod_handle);
+
+/*
+ * Tethering bridge (Rmnet / MBIM)
+ */
+int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr, void **private_data_ptr);
+
+int teth_bridge_disconnect(void);
+
+int teth_bridge_connect(struct teth_bridge_connect_params *connect_params);
+
#else /* CONFIG_IPA */
static inline int a2_mux_open_channel(enum a2_mux_logical_channel_id lcid,
@@ -685,7 +718,6 @@
return -EPERM;
}
-
/*
* Connect / Disconnect
*/
@@ -1051,6 +1083,26 @@
return -EPERM;
}
+/*
+ * Tethering bridge (Rmnetm / MBIM)
+ */
+static inline int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr,
+ void **private_data_ptr)
+{
+ return -EPERM;
+}
+
+static inline int teth_bridge_disconnect(void)
+{
+ return -EPERM;
+}
+
+static inline int teth_bridge_connect(struct teth_bridge_connect_params
+ *connect_params)
+{
+ return -EPERM;
+}
+
#endif /* CONFIG_IPA*/
#endif /* _IPA_H_ */
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index cf68108..d3574c8 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -75,14 +75,9 @@
void clean_and_invalidate_caches(unsigned long, unsigned long, unsigned long);
void clean_caches(unsigned long, unsigned long, unsigned long);
void invalidate_caches(unsigned long, unsigned long, unsigned long);
-int platform_physical_remove_pages(u64, u64);
-int platform_physical_active_pages(u64, u64);
-int platform_physical_low_power_pages(u64, u64);
int msm_get_memory_type_from_name(const char *memtype_name);
unsigned long get_ddr_size(void);
-extern int (*change_memory_power)(u64, u64, int);
-
#if defined(CONFIG_ARCH_MSM_ARM11) || defined(CONFIG_ARCH_MSM_CORTEX_A5)
void write_to_strongly_ordered_memory(void);
void map_page_strongly_ordered(void);
diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h
index a989059..264dad5 100644
--- a/arch/arm/mach-msm/include/mach/msm_memtypes.h
+++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h
@@ -29,7 +29,6 @@
unsigned int get_num_memory_banks(void);
unsigned int get_memory_bank_size(unsigned int);
unsigned int get_memory_bank_start(unsigned int);
-int soc_change_memory_power(u64, u64, int);
enum {
MEMTYPE_NONE = -1,
diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h
index 2748636..a8c7bb7 100644
--- a/arch/arm/mach-msm/include/mach/msm_smd.h
+++ b/arch/arm/mach-msm/include/mach/msm_smd.h
@@ -1,7 +1,7 @@
/* linux/include/asm-arm/arch-msm/msm_smd.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -141,8 +141,8 @@
* @size: size of the region in bytes
*/
struct smd_smem_regions {
- void *phys_addr;
- unsigned size;
+ phys_addr_t phys_addr;
+ resource_size_t size;
};
struct smd_platform {
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
index 02272bc..eb44c40 100644
--- a/arch/arm/mach-msm/iommu_domains.c
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -19,15 +19,15 @@
#include <linux/rbtree.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <asm/sizes.h>
#include <asm/page.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
#include <mach/socinfo.h>
#include <mach/msm_subsystem_map.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
struct msm_iova_data {
struct rb_node node;
diff --git a/arch/arm/mach-msm/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c
index 2cec5c5..a08e7de 100644
--- a/arch/arm/mach-msm/ipc_socket.c
+++ b/arch/arm/mach-msm/ipc_socket.c
@@ -20,6 +20,9 @@
#include <linux/fcntl.h>
#include <linux/gfp.h>
#include <linux/msm_ipc.h>
+#include <linux/sched.h>
+#include <linux/thread_info.h>
+#include <linux/qmi_encdec.h>
#include <asm/string.h>
#include <asm/atomic.h>
@@ -27,16 +30,93 @@
#include <net/sock.h>
#include <mach/msm_ipc_router.h>
+#include <mach/msm_ipc_logging.h>
#include "ipc_router.h"
#include "msm_ipc_router_security.h"
#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
+#define REQ_RESP_IPC_LOG_PAGES 5
+#define IND_IPC_LOG_PAGES 5
+#define IPC_SEND 1
+#define IPC_RECV 2
+#define IPC_REQ_RESP_LOG(level, buf...) \
+do { \
+ if (ipc_req_resp_log_txt) { \
+ ipc_log_string(ipc_req_resp_log_txt, buf); \
+ } \
+} while (0) \
+
+#define IPC_IND_LOG(level, buf...) \
+do { \
+ if (ipc_ind_log_txt) { \
+ ipc_log_string(ipc_ind_log_txt, buf); \
+ } \
+} while (0) \
static int sockets_enabled;
static struct proto msm_ipc_proto;
static const struct proto_ops msm_ipc_proto_ops;
+static void *ipc_req_resp_log_txt;
+static void *ipc_ind_log_txt;
+
+/**
+ * msm_ipc_router_ipc_log() - Pass log data to IPC logging framework
+ * @tran: Identifies the data to be a receive or send.
+ * @ipc_buf: Buffer to extract the log data.
+ * @port_ptr: IPC Router port corresponding to the current log data.
+ *
+ * This function builds the data the would be passed on to the IPC logging
+ * framework. The data that would be passed corresponds to the information
+ * that is exchanged between the IPC Router and user space modules during
+ * request/response/indication transactions.
+ */
+
+static void msm_ipc_router_ipc_log(uint8_t tran,
+ struct sk_buff *ipc_buf, struct msm_ipc_port *port_ptr)
+{
+ struct qmi_header *hdr = (struct qmi_header *)ipc_buf->data;
+
+ /*
+ * IPC Logging format is as below:-
+ * <Name>(Name of the User Space Process):
+ * <PID> (PID of the user space process) :
+ * <TID> (TID of the user space thread) :
+ * <User Space Module>(CLNT or SERV) :
+ * <Opertaion Type> (Transmit) :
+ * <Control Flag> (Req/Resp/Ind) :
+ * <Transaction ID> :
+ * <Message ID> :
+ * <Message Length> :
+ */
+ if (ipc_req_resp_log_txt &&
+ (((uint8_t) hdr->cntl_flag == QMI_REQUEST_CONTROL_FLAG) ||
+ ((uint8_t) hdr->cntl_flag == QMI_RESPONSE_CONTROL_FLAG)) &&
+ (port_ptr->type == CLIENT_PORT ||
+ port_ptr->type == SERVER_PORT)) {
+ IPC_REQ_RESP_LOG(KERN_DEBUG,
+ "%s %d %d %s %s CF:%x TI:%x MI:%x ML:%x",
+ current->comm, current->tgid, current->pid,
+ (port_ptr->type == CLIENT_PORT ? "QCCI" : "QCSI"),
+ (tran == IPC_RECV ? "RX" :
+ (tran == IPC_SEND ? "TX" : "ERR")),
+ (uint8_t)hdr->cntl_flag, hdr->txn_id, hdr->msg_id,
+ hdr->msg_len);
+ } else if (ipc_ind_log_txt &&
+ ((uint8_t)hdr->cntl_flag == QMI_INDICATION_CONTROL_FLAG) &&
+ (port_ptr->type == CLIENT_PORT ||
+ port_ptr->type == SERVER_PORT)) {
+ IPC_IND_LOG(KERN_DEBUG,
+ "%s %d %d %s %s CF:%x TI:%x MI:%x ML:%x",
+ current->comm, current->tgid, current->pid,
+ (port_ptr->type == CLIENT_PORT ? "QCCI" : "QCSI"),
+ (tran == IPC_RECV ? "RX" :
+ (tran == IPC_SEND ? "TX" : "ERR")),
+ (uint8_t)hdr->cntl_flag, hdr->txn_id, hdr->msg_id,
+ hdr->msg_len);
+ }
+}
static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect,
struct iovec const *msg_sect,
@@ -263,6 +343,7 @@
struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
struct sk_buff_head *msg;
+ struct sk_buff *ipc_buf;
int ret;
if (!dest)
@@ -284,7 +365,8 @@
if (port_ptr->type == CLIENT_PORT)
wait_for_irsc_completion();
-
+ ipc_buf = skb_peek(msg);
+ msm_ipc_router_ipc_log(IPC_SEND, ipc_buf, port_ptr);
ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
if (ret == (IPC_ROUTER_HDR_SIZE + total_len))
ret = total_len;
@@ -300,6 +382,7 @@
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
struct sk_buff_head *msg;
+ struct sk_buff *ipc_buf;
long timeout;
int ret;
@@ -344,6 +427,8 @@
}
ret = msm_ipc_router_extract_msg(m, msg);
+ ipc_buf = skb_peek(msg);
+ msm_ipc_router_ipc_log(IPC_RECV, ipc_buf, port_ptr);
msm_ipc_router_release_msg(msg);
msg = NULL;
release_sock(sk);
@@ -518,6 +603,29 @@
.obj_size = sizeof(struct msm_ipc_sock),
};
+/**
+ * msm_ipc_router_ipc_log_init() - Init function for IPC Logging
+ *
+ * Initialize the buffers to be used to provide the log information
+ * pertaining to the request, response and indication data flow that
+ * happens between user and kernel spaces.
+ */
+void msm_ipc_router_ipc_log_init(void)
+{
+ ipc_req_resp_log_txt =
+ ipc_log_context_create(REQ_RESP_IPC_LOG_PAGES, "req_resp");
+ if (!ipc_req_resp_log_txt) {
+ pr_err("%s: Unable to create IPC logging for Req/Resp",
+ __func__);
+ }
+ ipc_ind_log_txt =
+ ipc_log_context_create(IND_IPC_LOG_PAGES, "indication");
+ if (!ipc_ind_log_txt) {
+ pr_err("%s: Unable to create IPC logging for Indications",
+ __func__);
+ }
+}
+
int msm_ipc_router_init_sockets(void)
{
int ret;
@@ -536,6 +644,7 @@
}
sockets_enabled = 1;
+ msm_ipc_router_ipc_log_init();
out_init_sockets:
return ret;
}
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 0c1e279..8fe69d9 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -145,6 +145,7 @@
* regulator's callback functions to prevent
* simultaneous updates to the pmic's phase
* voltage.
+ * @apcs_gcc_base virtual address of the APCS GCC registers
*/
struct pmic_gang_vreg {
const char *name;
@@ -155,6 +156,8 @@
bool pfm_mode;
int pmic_min_uV_for_retention;
bool retention_enabled;
+ bool use_phase_switching;
+ void __iomem *apcs_gcc_base;
};
static struct pmic_gang_vreg *the_gang;
@@ -390,13 +393,17 @@
return 0;
}
-static int set_pmic_gang_phases(int phase_count)
+static int set_pmic_gang_phases(struct pmic_gang_vreg *pvreg, int phase_count)
{
- /*
- * TODO : spm writes for phase control,
- * pmic phase control is not working yet
- */
- return 0;
+ pr_debug("programming phase_count = %d\n", phase_count);
+ if (pvreg->use_phase_switching)
+ /*
+ * note the PMIC sets the phase count to one more than
+ * the value in the register - hence subtract 1 from it
+ */
+ return msm_spm_apcs_set_phase(phase_count - 1);
+ else
+ return 0;
}
static int set_pmic_gang_voltage(struct pmic_gang_vreg *pvreg, int uV)
@@ -547,14 +554,19 @@
int load_uA)
{
struct pmic_gang_vreg *pvreg = from->pvreg;
- int phase_count = DIV_ROUND_UP(load_uA, LOAD_PER_PHASE) - 1;
+ int phase_count = DIV_ROUND_UP(load_uA, LOAD_PER_PHASE);
int rc = 0;
- if (phase_count < 0)
- phase_count = 0;
+ if (phase_count <= 0)
+ phase_count = 1;
+
+ /* Increase phases if it is less than the number of cpus online */
+ if (phase_count < num_online_cpus()) {
+ phase_count = num_online_cpus();
+ }
if (phase_count != pvreg->pmic_phase_count) {
- rc = set_pmic_gang_phases(phase_count);
+ rc = set_pmic_gang_phases(pvreg, phase_count);
if (rc < 0) {
dev_err(&from->rdev->dev,
"%s failed set phase %d rc = %d\n",
@@ -577,32 +589,6 @@
return rc;
}
-static int __devinit pvreg_init(struct platform_device *pdev)
-{
- struct pmic_gang_vreg *pvreg;
-
- pvreg = devm_kzalloc(&pdev->dev,
- sizeof(struct pmic_gang_vreg), GFP_KERNEL);
- if (!pvreg) {
- pr_err("kzalloc failed.\n");
- return -ENOMEM;
- }
-
- pvreg->name = "pmic_gang";
- pvreg->pmic_vmax_uV = PMIC_VOLTAGE_MIN;
- pvreg->pmic_phase_count = 1;
- pvreg->retention_enabled = true;
- pvreg->pmic_min_uV_for_retention = INT_MAX;
-
- mutex_init(&pvreg->krait_power_vregs_lock);
- INIT_LIST_HEAD(&pvreg->krait_power_vregs);
- the_gang = pvreg;
-
- pr_debug("name=%s inited\n", pvreg->name);
-
- return 0;
-}
-
static int krait_power_get_voltage(struct regulator_dev *rdev)
{
struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
@@ -906,12 +892,12 @@
mb();
}
-static void glb_init(struct platform_device *pdev)
+static void glb_init(void __iomem *apcs_gcc_base)
{
/* configure bi-modal switch */
- writel_relaxed(0x0008736E, MSM_APCS_GCC_BASE + PWR_GATE_CONFIG);
+ writel_relaxed(0x0008736E, apcs_gcc_base + PWR_GATE_CONFIG);
/* read kpss version */
- version = readl_relaxed(MSM_APCS_GCC_BASE + VERSION);
+ version = readl_relaxed(apcs_gcc_base + VERSION);
pr_debug("version= 0x%x\n", version);
}
@@ -925,24 +911,6 @@
int ldo_delta_uV;
int cpu_num;
- /* Initialize the pmic gang if it hasn't been initialized already */
- if (the_gang == NULL) {
- rc = pvreg_init(pdev);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "failed to init pmic gang rc = %d\n", rc);
- return rc;
- }
- /* global initializtion */
- glb_init(pdev);
- }
-
- if (dent == NULL) {
- dent = debugfs_create_dir(KRAIT_REGULATOR_DRIVER_NAME, NULL);
- debugfs_create_file("retention_uV",
- 0644, dent, the_gang, &retention_fops);
- }
-
if (pdev->dev.of_node) {
/* Get init_data from device tree. */
init_data = of_get_regulator_init_data(&pdev->dev,
@@ -1139,14 +1107,106 @@
},
};
+static struct of_device_id krait_pdn_match_table[] = {
+ { .compatible = "qcom,krait-pdn", },
+ {}
+};
+
+static int __devinit krait_pdn_probe(struct platform_device *pdev)
+{
+ int rc;
+ bool use_phase_switching = false;
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct pmic_gang_vreg *pvreg;
+ struct resource *res;
+
+ if (!dev->of_node) {
+ dev_err(dev, "device tree information missing\n");
+ return -ENODEV;
+ }
+
+ use_phase_switching = of_property_read_bool(node,
+ "qcom,use-phase-switching");
+ pvreg = devm_kzalloc(&pdev->dev,
+ sizeof(struct pmic_gang_vreg), GFP_KERNEL);
+ if (!pvreg) {
+ pr_err("kzalloc failed.\n");
+ return 0;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apcs_gcc");
+ if (!res) {
+ dev_err(&pdev->dev, "missing apcs gcc base addresses\n");
+ return -EINVAL;
+ }
+
+ pvreg->apcs_gcc_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ if (pvreg->apcs_gcc_base == NULL)
+ return -ENOMEM;
+
+ pvreg->name = "pmic_gang";
+ pvreg->pmic_vmax_uV = PMIC_VOLTAGE_MIN;
+ pvreg->pmic_phase_count = -EINVAL;
+ pvreg->retention_enabled = true;
+ pvreg->pmic_min_uV_for_retention = INT_MAX;
+ pvreg->use_phase_switching = use_phase_switching;
+
+ mutex_init(&pvreg->krait_power_vregs_lock);
+ INIT_LIST_HEAD(&pvreg->krait_power_vregs);
+ the_gang = pvreg;
+
+ pr_debug("name=%s inited\n", pvreg->name);
+
+ /* global initializtion */
+ glb_init(pvreg->apcs_gcc_base);
+
+ rc = of_platform_populate(node, NULL, NULL, dev);
+ if (rc) {
+ dev_err(dev, "failed to add child nodes, rc=%d\n", rc);
+ return rc;
+ }
+
+ dent = debugfs_create_dir(KRAIT_REGULATOR_DRIVER_NAME, NULL);
+ debugfs_create_file("retention_uV",
+ 0644, dent, the_gang, &retention_fops);
+ return 0;
+}
+
+static int __devexit krait_pdn_remove(struct platform_device *pdev)
+{
+ the_gang = NULL;
+ debugfs_remove_recursive(dent);
+ return 0;
+}
+
+static struct platform_driver krait_pdn_driver = {
+ .probe = krait_pdn_probe,
+ .remove = __devexit_p(krait_pdn_remove),
+ .driver = {
+ .name = KRAIT_PDN_DRIVER_NAME,
+ .of_match_table = krait_pdn_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
int __init krait_power_init(void)
{
- return platform_driver_register(&krait_power_driver);
+ int rc = platform_driver_register(&krait_power_driver);
+ if (rc) {
+ pr_err("failed to add %s driver rc = %d\n",
+ KRAIT_REGULATOR_DRIVER_NAME, rc);
+ return rc;
+ }
+ return platform_driver_register(&krait_pdn_driver);
}
static void __exit krait_power_exit(void)
{
platform_driver_unregister(&krait_power_driver);
+ platform_driver_unregister(&krait_pdn_driver);
}
module_exit(krait_power_exit);
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index 806581d..f3bd34a 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -135,29 +135,6 @@
return (void *)addr;
}
-int (*change_memory_power)(u64, u64, int);
-
-int platform_physical_remove_pages(u64 start, u64 size)
-{
- if (!change_memory_power)
- return 0;
- return change_memory_power(start, size, MEMORY_DEEP_POWERDOWN);
-}
-
-int platform_physical_active_pages(u64 start, u64 size)
-{
- if (!change_memory_power)
- return 0;
- return change_memory_power(start, size, MEMORY_ACTIVE);
-}
-
-int platform_physical_low_power_pages(u64 start, u64 size)
-{
- if (!change_memory_power)
- return 0;
- return change_memory_power(start, size, MEMORY_SELF_REFRESH);
-}
-
char *memtype_name[] = {
"SMI_KERNEL",
"SMI",
diff --git a/arch/arm/mach-msm/memory_topology.c b/arch/arm/mach-msm/memory_topology.c
index 772e63e..781cd69 100644
--- a/arch/arm/mach-msm/memory_topology.c
+++ b/arch/arm/mach-msm/memory_topology.c
@@ -66,136 +66,6 @@
}
#endif
-#if (defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_MSM8930)) \
- && defined(CONFIG_ENABLE_DMM)
-static int rpm_change_memory_state(int retention_mask,
- int active_mask)
-{
- int ret;
- struct msm_rpm_iv_pair cmd[2];
- struct msm_rpm_iv_pair status[2];
-
- cmd[0].id = MSM_RPM_ID_DDR_DMM_0;
- cmd[1].id = MSM_RPM_ID_DDR_DMM_1;
-
- status[0].id = MSM_RPM_STATUS_ID_DDR_DMM_0;
- status[1].id = MSM_RPM_STATUS_ID_DDR_DMM_1;
-
- cmd[0].value = retention_mask;
- cmd[1].value = active_mask;
-
- ret = msm_rpm_set(MSM_RPM_CTX_SET_0, cmd, 2);
- if (ret < 0) {
- pr_err("rpm set failed");
- return -EINVAL;
- }
-
- ret = msm_rpm_get_status(status, 2);
- if (ret < 0) {
- pr_err("rpm status failed");
- return -EINVAL;
- }
- if (status[0].value == retention_mask &&
- status[1].value == active_mask)
- return 0;
- else {
- pr_err("rpm failed to change memory state");
- return -EINVAL;
- }
-}
-
-static int switch_memory_state(int mask, int new_state, int start_region,
- int end_region)
-{
- int final_mask = 0;
- int i;
-
- mutex_lock(&mem_regions_mutex);
-
- for (i = start_region; i <= end_region; i++) {
- if (new_state == mem_regions[i].state)
- goto no_change;
- /* All region states must be the same to change them */
- if (mem_regions[i].state != mem_regions[start_region].state)
- goto no_change;
- }
-
- if (new_state == STATE_POWER_DOWN)
- final_mask = mem_regions_mask & mask;
- else if (new_state == STATE_ACTIVE)
- final_mask = mem_regions_mask | ~mask;
- else
- goto no_change;
-
- pr_info("request memory %d to %d state switch (%d->%d)\n",
- start_region, end_region, mem_regions[start_region].state,
- new_state);
- if (rpm_change_memory_state(final_mask, final_mask) == 0) {
- for (i = start_region; i <= end_region; i++)
- mem_regions[i].state = new_state;
- mem_regions_mask = final_mask;
-
- pr_info("completed memory %d to %d state switch to %d\n",
- start_region, end_region, new_state);
- mutex_unlock(&mem_regions_mutex);
- return 0;
- }
-
- pr_err("failed memory %d to %d state switch (%d->%d)\n",
- start_region, end_region, mem_regions[start_region].state,
- new_state);
-
-no_change:
- mutex_unlock(&mem_regions_mutex);
- return -EINVAL;
-}
-#else
-
-static int switch_memory_state(int mask, int new_state, int start_region,
- int end_region)
-{
- return -EINVAL;
-}
-#endif
-
-/* The hotplug code expects the number of bytes that switched state successfully
- * as the return value, so a return value of zero indicates an error
-*/
-int soc_change_memory_power(u64 start, u64 size, int change)
-{
- int i = 0;
- int mask = default_mask;
- u64 end = start + size;
- int start_region = 0;
- int end_region = 0;
-
- if (change != STATE_ACTIVE && change != STATE_POWER_DOWN) {
- pr_info("requested state transition invalid\n");
- return 0;
- }
- /* Find the memory regions that fall within the range */
- for (i = 0; i < nr_mem_regions; i++) {
- if (mem_regions[i].start <= start &&
- mem_regions[i].start >=
- mem_regions[start_region].start) {
- start_region = i;
- }
- if (end <= mem_regions[i].start + mem_regions[i].size) {
- end_region = i;
- break;
- }
- }
-
- /* Set the bitmask for each region in the range */
- for (i = start_region; i <= end_region; i++)
- mask &= ~(0x1 << i);
-
- if (!switch_memory_state(mask, change, start_region, end_region))
- return size;
- else
- return 0;
-}
-
unsigned int get_num_memory_banks(void)
{
return nr_mem_regions;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
index d416c19..3c8348d 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -1960,7 +1960,8 @@
struct msm_bus_bimc_info *binfo =
(struct msm_bus_bimc_info *)hw_data;
- if (!IS_SLAVE(info->node_info->priv_id))
+ if (!IS_SLAVE(info->node_info->priv_id) &&
+ (info->node_info->hw_sel != MSM_BUS_RPM))
msm_bus_bimc_mas_init(binfo, info);
}
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index cd6aaf4..aa42f5b 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -57,7 +57,7 @@
#define RMB_PMI_CODE_LENGTH 0x18
#define VDD_MSS_UV 1050000
-#define MAX_VDD_MX_UV 1050000
+#define MAX_VDD_MX_UV 1150000
#define PROXY_TIMEOUT_MS 10000
#define POLL_INTERVAL_US 50
diff --git a/arch/arm/mach-msm/qdsp5/audio_ac3.c b/arch/arm/mach-msm/qdsp5/audio_ac3.c
index 0363348..199b322 100644
--- a/arch/arm/mach-msm/qdsp5/audio_ac3.c
+++ b/arch/arm/mach-msm/qdsp5/audio_ac3.c
@@ -1,6 +1,6 @@
/* arch/arm/mach-msm/audio_ac3.c
*
- * Copyright (c) 2008-2009, 2011-2012 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2009, 2011-2013 The Linux Foundation. All rights reserved.
*
* This code also borrows from audio_aac.c, which is
* Copyright (C) 2008 Google, Inc.
@@ -186,8 +186,10 @@
static void audac3_dsp_event(void *private, unsigned id, uint16_t *msg);
static void audac3_config_hostpcm(struct audio *audio);
static void audac3_buffer_refresh(struct audio *audio);
+#ifdef CONFIG_HAS_EARLYSUSPEND
static void audac3_post_event(struct audio *audio, int type,
union msm_audio_event_payload payload);
+#endif
static int rmt_put_resource(struct audio *audio)
{
diff --git a/arch/arm/mach-msm/remote_spinlock.c b/arch/arm/mach-msm/remote_spinlock.c
index 4e09a9e..94923a0 100644
--- a/arch/arm/mach-msm/remote_spinlock.c
+++ b/arch/arm/mach-msm/remote_spinlock.c
@@ -196,6 +196,8 @@
/* end swp implementation --------------------------------------------------- */
/* ldrex implementation ----------------------------------------------------- */
+static char *ldrex_compatible_string = "qcom,ipc-spinlock-ldrex";
+
static void __raw_remote_ex_spin_lock(raw_remote_spinlock_t *lock)
{
unsigned long tmp;
@@ -267,7 +269,7 @@
static void *hw_mutex_reg_base;
static DEFINE_MUTEX(hw_map_init_lock);
-static char *compatible_string = "qcom,ipc-spinlock";
+static char *sfpb_compatible_string = "qcom,ipc-spinlock-sfpb";
static int init_hw_mutex(struct device_node *node)
{
@@ -294,7 +296,7 @@
{
struct device_node *node;
- node = of_find_compatible_node(NULL, NULL, compatible_string);
+ node = of_find_compatible_node(NULL, NULL, sfpb_compatible_string);
if (node) {
init_hw_mutex(node);
} else {
@@ -341,7 +343,9 @@
static int __raw_remote_sfpb_spin_trylock(raw_remote_spinlock_t *lock)
{
- return 1;
+ writel_relaxed(SPINLOCK_PID_APPS, lock);
+ smp_mb();
+ return readl_relaxed(lock) == SPINLOCK_PID_APPS;
}
static void __raw_remote_sfpb_spin_unlock(raw_remote_spinlock_t *lock)
@@ -397,6 +401,23 @@
}
+static int dt_node_is_valid(const struct device_node *node)
+{
+ const char *status;
+ int statlen;
+
+ status = of_get_property(node, "status", &statlen);
+ if (status == NULL)
+ return 1;
+
+ if (statlen > 0) {
+ if (!strcmp(status, "okay") || !strcmp(status, "ok"))
+ return 1;
+ }
+
+ return 0;
+}
+
static void initialize_ops(void)
{
struct device_node *node;
@@ -435,23 +456,42 @@
is_hw_lock_type = 1;
break;
case AUTO_MODE:
- node = of_find_compatible_node(NULL, NULL, compatible_string);
- if (node) {
+ /*
+ * of_find_compatible_node() returns a valid pointer even if
+ * the status property is "disabled", so the validity needs
+ * to be checked
+ */
+ node = of_find_compatible_node(NULL, NULL,
+ sfpb_compatible_string);
+ if (node && dt_node_is_valid(node)) {
current_ops.lock = __raw_remote_sfpb_spin_lock;
current_ops.unlock = __raw_remote_sfpb_spin_unlock;
current_ops.trylock = __raw_remote_sfpb_spin_trylock;
current_ops.release = __raw_remote_gen_spin_release;
current_ops.owner = __raw_remote_gen_spin_owner;
is_hw_lock_type = 1;
- } else {
+ break;
+ }
+
+ node = of_find_compatible_node(NULL, NULL,
+ ldrex_compatible_string);
+ if (node && dt_node_is_valid(node)) {
current_ops.lock = __raw_remote_ex_spin_lock;
current_ops.unlock = __raw_remote_ex_spin_unlock;
current_ops.trylock = __raw_remote_ex_spin_trylock;
current_ops.release = __raw_remote_gen_spin_release;
current_ops.owner = __raw_remote_gen_spin_owner;
is_hw_lock_type = 0;
- pr_warn("Falling back to LDREX remote spinlock implementation");
+ break;
}
+
+ current_ops.lock = __raw_remote_ex_spin_lock;
+ current_ops.unlock = __raw_remote_ex_spin_unlock;
+ current_ops.trylock = __raw_remote_ex_spin_trylock;
+ current_ops.release = __raw_remote_gen_spin_release;
+ current_ops.owner = __raw_remote_gen_spin_owner;
+ is_hw_lock_type = 0;
+ pr_warn("Falling back to LDREX remote spinlock implementation");
break;
default:
BUG();
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 10e40b4..cffb211 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -172,13 +172,13 @@
};
struct smem_area {
- void *phys_addr;
- unsigned size;
+ phys_addr_t phys_addr;
+ resource_size_t size;
void __iomem *virt_addr;
};
static uint32_t num_smem_areas;
static struct smem_area *smem_areas;
-static void *smem_range_check(void *base, unsigned offset);
+static void *smem_range_check(phys_addr_t base, unsigned offset);
struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
@@ -2365,11 +2365,11 @@
* @base: physical base address to check
* @offset: offset from the base to get the final address
*/
-static void *smem_range_check(void *base, unsigned offset)
+static void *smem_range_check(phys_addr_t base, unsigned offset)
{
int i;
- void *phys_addr;
- unsigned size;
+ phys_addr_t phys_addr;
+ resource_size_t size;
for (i = 0; i < num_smem_areas; ++i) {
phys_addr = smem_areas[i].phys_addr;
@@ -2464,7 +2464,7 @@
ret = (void *) (MSM_SHARED_RAM_BASE + toc[id].offset);
else
ret = smem_range_check(
- (void *)(toc[id].reserved & BASE_ADDR_MASK),
+ toc[id].reserved & BASE_ADDR_MASK,
toc[id].offset);
} else {
*size = 0;
@@ -3466,10 +3466,10 @@
(unsigned long)(smem_areas[smem_idx].phys_addr),
smem_areas[smem_idx].size);
if (!smem_areas[smem_idx].virt_addr) {
- pr_err("%s: ioremap_nocache() of addr:%p"
- " size: %x\n", __func__,
- smem_areas[smem_idx].phys_addr,
- smem_areas[smem_idx].size);
+ pr_err("%s: ioremap_nocache() of addr: %pa size: %pa\n",
+ __func__,
+ &smem_areas[smem_idx].phys_addr,
+ &smem_areas[smem_idx].size);
err_ret = -ENOMEM;
++smem_idx;
goto smem_failed;
@@ -3712,8 +3712,8 @@
char *key;
struct resource *r;
void *irq_out_base;
- void *aux_mem_base;
- uint32_t aux_mem_size;
+ phys_addr_t aux_mem_base;
+ resource_size_t aux_mem_size;
int temp_string_size = 11; /* max 3 digit count */
char temp_string[temp_string_size];
int count;
@@ -3721,6 +3721,7 @@
int ret;
const char *compatible;
int subnode_num = 0;
+ resource_size_t irq_out_size;
disable_smsm_reset_handshake = 1;
@@ -3730,7 +3731,13 @@
pr_err("%s: missing '%s'\n", __func__, key);
return -ENODEV;
}
- irq_out_base = (void *)(r->start);
+ irq_out_size = resource_size(r);
+ irq_out_base = ioremap_nocache(r->start, irq_out_size);
+ if (!irq_out_base) {
+ pr_err("%s: ioremap_nocache() of irq_out_base addr:%pr size:%pr\n",
+ __func__, &r->start, &irq_out_size);
+ return -ENOMEM;
+ }
SMD_DBG("%s: %s = %p", __func__, key, irq_out_base);
count = 1;
@@ -3766,20 +3773,20 @@
temp_string);
if (!r)
break;
- aux_mem_base = (void *)(r->start);
- aux_mem_size = (uint32_t)(resource_size(r));
- SMD_DBG("%s: %s = %p %x", __func__, temp_string,
- aux_mem_base, aux_mem_size);
+ aux_mem_base = r->start;
+ aux_mem_size = resource_size(r);
+ SMD_DBG("%s: %s = %pa %pa", __func__, temp_string,
+ &aux_mem_base, &aux_mem_size);
smem_areas[count - 1].phys_addr = aux_mem_base;
smem_areas[count - 1].size = aux_mem_size;
smem_areas[count - 1].virt_addr = ioremap_nocache(
(unsigned long)(smem_areas[count-1].phys_addr),
smem_areas[count - 1].size);
if (!smem_areas[count - 1].virt_addr) {
- pr_err("%s: ioremap_nocache() of addr:%p size: %x\n",
+ pr_err("%s: ioremap_nocache() of addr:%pa size: %pa\n",
__func__,
- smem_areas[count - 1].phys_addr,
- smem_areas[count - 1].size);
+ &smem_areas[count - 1].phys_addr,
+ &smem_areas[count - 1].size);
ret = -ENOMEM;
goto free_smem_areas;
}
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index 1820b23..5969a3c 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -47,7 +47,7 @@
struct smd_tty_info {
smd_channel_t *ch;
- struct tty_struct *tty;
+ struct tty_port port;
struct wake_lock wake_lock;
int open_count;
struct tasklet_struct tty_tsklt;
@@ -125,7 +125,7 @@
unsigned char *ptr;
int avail;
struct smd_tty_info *info = (struct smd_tty_info *)param;
- struct tty_struct *tty = info->tty;
+ struct tty_struct *tty = tty_port_tty_get(&info->port);
unsigned long flags;
if (!tty)
@@ -156,6 +156,7 @@
if (avail <= 0) {
mod_timer(&info->buf_req_timer,
jiffies + msecs_to_jiffies(30));
+ tty_kref_put(tty);
return;
}
@@ -173,11 +174,13 @@
/* XXX only when writable and necessary */
tty_wakeup(tty);
+ tty_kref_put(tty);
}
static void smd_tty_notify(void *priv, unsigned event)
{
struct smd_tty_info *info = priv;
+ struct tty_struct *tty;
unsigned long flags;
switch (event) {
@@ -195,8 +198,10 @@
*/
if (smd_write_avail(info->ch)) {
smd_disable_read_intr(info->ch);
- if (info->tty)
- wake_up_interruptible(&info->tty->write_wait);
+ tty = tty_port_tty_get(&info->port);
+ if (tty)
+ wake_up_interruptible(&tty->write_wait);
+ tty_kref_put(tty);
}
spin_lock_irqsave(&info->ra_lock, flags);
if (smd_read_avail(info->ch)) {
@@ -225,9 +230,11 @@
/* schedule task to send TTY_BREAK */
tasklet_hi_schedule(&info->tty_tsklt);
- if (info->tty->index == LOOPBACK_IDX)
+ tty = tty_port_tty_get(&info->port);
+ if (tty->index == LOOPBACK_IDX)
schedule_delayed_work(&loopback_work,
msecs_to_jiffies(1000));
+ tty_kref_put(tty);
break;
}
}
@@ -241,7 +248,8 @@
return (modem_state & ready_state) == ready_state;
}
-static int smd_tty_open(struct tty_struct *tty, struct file *f)
+static int smd_tty_port_activate(struct tty_port *tport,
+ struct tty_struct *tty)
{
int res = 0;
unsigned int n = tty->index;
@@ -306,8 +314,6 @@
}
}
-
- info->tty = tty;
tasklet_init(&info->tty_tsklt, smd_tty_read,
(unsigned long)info);
wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND,
@@ -354,24 +360,27 @@
return res;
}
-static void smd_tty_close(struct tty_struct *tty, struct file *f)
+static void smd_tty_port_shutdown(struct tty_port *tport)
{
- struct smd_tty_info *info = tty->driver_data;
+ struct smd_tty_info *info;
+ struct tty_struct *tty = tty_port_tty_get(tport);
unsigned long flags;
- if (info == 0)
+ info = tty->driver_data;
+ if (info == 0) {
+ tty_kref_put(tty);
return;
+ }
mutex_lock(&smd_tty_lock);
if (--info->open_count == 0) {
spin_lock_irqsave(&info->reset_lock, flags);
info->is_open = 0;
spin_unlock_irqrestore(&info->reset_lock, flags);
- if (info->tty) {
+ if (tty) {
tasklet_kill(&info->tty_tsklt);
wake_lock_destroy(&info->wake_lock);
wake_lock_destroy(&info->ra_wake_lock);
- info->tty = 0;
}
tty->driver_data = 0;
del_timer(&info->buf_req_timer);
@@ -382,6 +391,21 @@
}
}
mutex_unlock(&smd_tty_lock);
+ tty_kref_put(tty);
+}
+
+static int smd_tty_open(struct tty_struct *tty, struct file *f)
+{
+ struct smd_tty_info *info = smd_tty + tty->index;
+
+ return tty_port_open(&info->port, tty, f);
+}
+
+static void smd_tty_close(struct tty_struct *tty, struct file *f)
+{
+ struct smd_tty_info *info = tty->driver_data;
+
+ tty_port_close(&info->port, tty, f);
}
static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len)
@@ -482,6 +506,11 @@
0, SMSM_SMD_LOOPBACK);
}
+static const struct tty_port_operations smd_tty_port_ops = {
+ .shutdown = smd_tty_port_shutdown,
+ .activate = smd_tty_port_activate,
+};
+
static struct tty_operations smd_tty_ops = {
.open = smd_tty_open,
.close = smd_tty_close,
@@ -523,6 +552,7 @@
int ret;
int n;
int idx;
+ struct tty_port *port;
smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
if (smd_tty_driver == 0)
@@ -578,6 +608,10 @@
continue;
}
+ port = &smd_tty[idx].port;
+ tty_port_init(port);
+ port->ops = &smd_tty_port_ops;
+ /* TODO: For kernel >= 3.7 use tty_port_register_device */
tty_register_device(smd_tty_driver, idx, 0);
init_completion(&smd_tty[idx].ch_allocated);
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index 97e1c17..c8e2dd3 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -36,6 +36,7 @@
struct msm_spm_driver_data reg_data;
struct msm_spm_power_modes *modes;
uint32_t num_modes;
+ uint32_t cpu_vdd;
};
struct msm_spm_vdd_info {
@@ -54,6 +55,7 @@
struct msm_spm_vdd_info *info = (struct msm_spm_vdd_info *)data;
dev = &per_cpu(msm_cpu_spm_device, info->cpu);
+ dev->cpu_vdd = info->vlevel;
info->err = msm_spm_drv_set_vdd(&dev->reg_data, info->vlevel);
}
@@ -106,7 +108,7 @@
struct msm_spm_device *dev;
dev = &per_cpu(msm_cpu_spm_device, cpu);
- return msm_spm_drv_get_sts_curr_pmic_data(&dev->reg_data);
+ return dev->cpu_vdd;
}
EXPORT_SYMBOL(msm_spm_get_vdd);
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index 6314e94..d177b05 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -7,7 +7,6 @@
obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \
mmap.o pgd.o mmu.o vmregion.o
-obj-$(CONFIG_DEBUG_RODATA) += rodata.o
ifneq ($(CONFIG_MMU),y)
obj-y += nommu.o
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 0ebc2b9..38da432 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -224,7 +224,7 @@
* allocations. This must be the smallest DMA mask in the system,
* so a successful GFP_DMA allocation will always satisfy this.
*/
-u32 arm_dma_limit;
+phys_addr_t arm_dma_limit;
static void __init arm_adjust_dma_zone(unsigned long *size, unsigned long *hole,
unsigned long dma_size)
@@ -721,9 +721,6 @@
extern u32 dtcm_end;
extern u32 itcm_end;
#endif
-#ifdef CONFIG_FIX_MOVABLE_ZONE
- struct zone *zone;
-#endif
max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map;
@@ -769,14 +766,6 @@
#endif
}
-#ifdef CONFIG_FIX_MOVABLE_ZONE
- for_each_zone(zone) {
- if (zone_idx(zone) == ZONE_MOVABLE)
- total_unmovable_pages = totalram_pages -
- zone->spanned_pages;
- }
-#endif
-
/*
* Since our memory may not be contiguous, calculate the
* real number of pages we have in this system
@@ -898,39 +887,9 @@
__phys_to_pfn(__pa(__init_end)),
"init");
totalram_pages += reclaimed_initmem;
-#ifdef CONFIG_FIX_MOVABLE_ZONE
- total_unmovable_pages += reclaimed_initmem;
-#endif
}
}
-#ifdef CONFIG_MEMORY_HOTPLUG
-int arch_add_memory(int nid, u64 start, u64 size)
-{
- struct pglist_data *pgdata = NODE_DATA(nid);
- struct zone *zone = pgdata->node_zones + ZONE_MOVABLE;
- unsigned long start_pfn = start >> PAGE_SHIFT;
- unsigned long nr_pages = size >> PAGE_SHIFT;
-
- return __add_pages(nid, zone, start_pfn, nr_pages);
-}
-
-int arch_physical_active_memory(u64 start, u64 size)
-{
- return platform_physical_active_pages(start, size);
-}
-
-int arch_physical_remove_memory(u64 start, u64 size)
-{
- return platform_physical_remove_pages(start, size);
-}
-
-int arch_physical_low_power_memory(u64 start, u64 size)
-{
- return platform_physical_low_power_pages(start, size);
-}
-#endif
-
#ifdef CONFIG_BLK_DEV_INITRD
static int keep_initrd;
@@ -945,9 +904,6 @@
__phys_to_pfn(__pa(end)),
"initrd");
totalram_pages += reclaimed_initrd_mem;
-#ifdef CONFIG_FIX_MOVABLE_ZONE
- total_unmovable_pages += reclaimed_initrd_mem;
-#endif
}
}
diff --git a/arch/arm/mm/mm.h b/arch/arm/mm/mm.h
index 8877ddd..21653f2 100644
--- a/arch/arm/mm/mm.h
+++ b/arch/arm/mm/mm.h
@@ -65,9 +65,9 @@
#endif
#ifdef CONFIG_ZONE_DMA
-extern u32 arm_dma_limit;
+extern phys_addr_t arm_dma_limit;
#else
-#define arm_dma_limit ((u32)~0)
+#define arm_dma_limit ((phys_addr_t)~0)
#endif
struct map_desc;
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 8575f78..0e31910 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -604,53 +604,30 @@
return early_alloc_aligned(sz, sz);
}
-static pte_t * __init early_pte_alloc(pmd_t *pmd)
-{
- if (pmd_none(*pmd) || pmd_bad(*pmd))
- return early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
- return pmd_page_vaddr(*pmd);
-}
-
-static void __init early_pte_install(pmd_t *pmd, pte_t *pte, unsigned long prot)
-{
- __pmd_populate(pmd, __pa(pte), prot);
- BUG_ON(pmd_bad(*pmd));
-}
-
-#ifdef CONFIG_HIGHMEM
-static pte_t * __init early_pte_alloc_and_install(pmd_t *pmd,
- unsigned long addr, unsigned long prot)
+static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot)
{
if (pmd_none(*pmd)) {
- pte_t *pte = early_pte_alloc(pmd);
- early_pte_install(pmd, pte, prot);
+ pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
+ __pmd_populate(pmd, __pa(pte), prot);
}
BUG_ON(pmd_bad(*pmd));
return pte_offset_kernel(pmd, addr);
}
-#endif
static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
unsigned long end, unsigned long pfn,
const struct mem_type *type)
{
- pte_t *start_pte = early_pte_alloc(pmd);
- pte_t *pte = start_pte + pte_index(addr);
-
- /* If replacing a section mapping, the whole section must be replaced */
- BUG_ON(pmd_bad(*pmd) && ((addr | end) & ~PMD_MASK));
-
+ pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1);
do {
set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
pfn++;
} while (pte++, addr += PAGE_SIZE, addr != end);
- early_pte_install(pmd, start_pte, type->prot_l1);
}
static void __init alloc_init_section(pud_t *pud, unsigned long addr,
unsigned long end, phys_addr_t phys,
- const struct mem_type *type,
- bool force_pages)
+ const struct mem_type *type)
{
pmd_t *pmd = pmd_offset(pud, addr);
@@ -660,7 +637,7 @@
* L1 entries, whereas PGDs refer to a group of L1 entries making
* up one logical pointer to an L2 table.
*/
- if (type->prot_sect && ((addr | end | phys) & ~SECTION_MASK) == 0 && !force_pages) {
+ if (type->prot_sect && ((addr | end | phys) & ~SECTION_MASK) == 0) {
pmd_t *p = pmd;
#ifndef CONFIG_ARM_LPAE
@@ -684,15 +661,14 @@
}
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
- unsigned long end, unsigned long phys, const struct mem_type *type,
- bool force_pages)
+ unsigned long end, unsigned long phys, const struct mem_type *type)
{
pud_t *pud = pud_offset(pgd, addr);
unsigned long next;
do {
next = pud_addr_end(addr, end);
- alloc_init_section(pud, addr, next, phys, type, force_pages);
+ alloc_init_section(pud, addr, next, phys, type);
phys += next - addr;
} while (pud++, addr = next, addr != end);
}
@@ -766,7 +742,7 @@
* offsets, and we take full advantage of sections and
* supersections.
*/
-static void __init create_mapping(struct map_desc *md, bool force_pages)
+static void __init create_mapping(struct map_desc *md)
{
unsigned long addr, length, end;
phys_addr_t phys;
@@ -818,7 +794,7 @@
do {
unsigned long next = pgd_addr_end(addr, end);
- alloc_init_pud(pgd, addr, next, phys, type, force_pages);
+ alloc_init_pud(pgd, addr, next, phys, type);
phys += next - addr;
addr = next;
@@ -839,7 +815,7 @@
vm = early_alloc_aligned(sizeof(*vm) * nr, __alignof__(*vm));
for (md = io_desc; nr; md++, nr--) {
- create_mapping(md, false);
+ create_mapping(md);
vm->addr = (void *)(md->virtual & PAGE_MASK);
vm->size = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));
vm->phys_addr = __pfn_to_phys(md->pfn);
@@ -964,10 +940,6 @@
find_memory_hole();
#endif
-#if (defined CONFIG_HIGHMEM) && (defined CONFIG_FIX_MOVABLE_ZONE)
- if (movable_reserved_size && __pa(vmalloc_min) > movable_reserved_start)
- vmalloc_min = __va(movable_reserved_start);
-#endif
for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
struct membank *bank = &meminfo.bank[j];
*bank = meminfo.bank[i];
@@ -1199,12 +1171,12 @@
map.virtual = 0xffff0000;
map.length = PAGE_SIZE;
map.type = MT_HIGH_VECTORS;
- create_mapping(&map, false);
+ create_mapping(&map);
if (!vectors_high()) {
map.virtual = 0;
map.type = MT_LOW_VECTORS;
- create_mapping(&map, false);
+ create_mapping(&map);
}
/*
@@ -1224,7 +1196,7 @@
map.virtual = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
map.length = PAGE_SIZE;
map.type = MT_DEVICE_USER_ACCESSIBLE;
- create_mapping(&map, false);
+ create_mapping(&map);
}
}
@@ -1241,7 +1213,7 @@
static void __init kmap_init(void)
{
#ifdef CONFIG_HIGHMEM
- pkmap_page_table = early_pte_alloc_and_install(pmd_off_k(PKMAP_BASE),
+ pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE),
PKMAP_BASE, _PAGE_KERNEL_TABLE);
#endif
}
@@ -1349,14 +1321,12 @@
static void __init map_lowmem(void)
{
struct memblock_region *reg;
- phys_addr_t start;
- phys_addr_t end;
- struct map_desc map;
/* Map all the lowmem memory banks. */
for_each_memblock(memory, reg) {
- start = reg->base;
- end = start + reg->size;
+ phys_addr_t start = reg->base;
+ phys_addr_t end = start + reg->size;
+ struct map_desc map;
if (end > arm_lowmem_limit)
end = arm_lowmem_limit;
@@ -1370,28 +1340,28 @@
map.length = SECTION_SIZE;
map.type = MT_MEMORY;
- create_mapping(&map, false);
+ create_mapping(&map);
map.pfn = __phys_to_pfn(start + SECTION_SIZE);
map.virtual = __phys_to_virt(start + SECTION_SIZE);
map.length = (unsigned long)RX_AREA_END - map.virtual;
map.type = MT_MEMORY_RX;
- create_mapping(&map, false);
+ create_mapping(&map);
map.pfn = __phys_to_pfn(__pa(__start_rodata));
map.virtual = (unsigned long)__start_rodata;
map.length = __init_begin - __start_rodata;
map.type = MT_MEMORY_R;
- create_mapping(&map, false);
+ create_mapping(&map);
map.pfn = __phys_to_pfn(__pa(__init_begin));
map.virtual = (unsigned long)__init_begin;
map.length = __init_data - __init_begin;
map.type = MT_MEMORY;
- create_mapping(&map, false);
+ create_mapping(&map);
map.pfn = __phys_to_pfn(__pa(__init_data));
map.virtual = (unsigned long)__init_data;
@@ -1406,20 +1376,8 @@
map.type = MT_MEMORY;
#endif
- create_mapping(&map, false);
+ create_mapping(&map);
}
-
-#ifdef CONFIG_DEBUG_RODATA
- start = __pa(_stext) & PMD_MASK;
- end = ALIGN(__pa(__end_rodata), PMD_SIZE);
-
- map.pfn = __phys_to_pfn(start);
- map.virtual = __phys_to_virt(start);
- map.length = end - start;
- map.type = MT_MEMORY;
-
- create_mapping(&map, true);
-#endif
}
/*
diff --git a/arch/arm/mm/rodata.c b/arch/arm/mm/rodata.c
deleted file mode 100644
index 9a8eb84..0000000
--- a/arch/arm/mm/rodata.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * linux/arch/arm/mm/rodata.c
- *
- * Copyright (C) 2011 Google, Inc.
- *
- * Author: Colin Cross <ccross@android.com>
- *
- * Based on x86 implementation in arch/x86/mm/init_32.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-
-#include <asm/cache.h>
-#include <asm/pgtable.h>
-#include <asm/rodata.h>
-#include <asm/sections.h>
-#include <asm/tlbflush.h>
-
-#include "mm.h"
-
-static int kernel_set_to_readonly __read_mostly;
-
-#ifdef CONFIG_DEBUG_RODATA_TEST
-static const int rodata_test_data = 0xC3;
-
-static noinline void rodata_test(void)
-{
- int result;
-
- pr_info("%s: attempting to write to read-only section:\n", __func__);
-
- if (*(volatile int *)&rodata_test_data != 0xC3) {
- pr_err("read only data changed before test\n");
- return;
- }
-
- /*
- * Attempt to to write to rodata_test_data, trapping the expected
- * data abort. If the trap executed, result will be 1. If it didn't,
- * result will be 0xFF.
- */
- asm volatile(
- "0: str %[zero], [%[rodata_test_data]]\n"
- " mov %[result], #0xFF\n"
- " b 2f\n"
- "1: mov %[result], #1\n"
- "2:\n"
-
- /* Exception fixup - if store at label 0 faults, jumps to 1 */
- ".pushsection __ex_table, \"a\"\n"
- " .long 0b, 1b\n"
- ".popsection\n"
-
- : [result] "=r" (result)
- : [rodata_test_data] "r" (&rodata_test_data), [zero] "r" (0)
- : "memory"
- );
-
- if (result == 1)
- pr_info("write to read-only section trapped, success\n");
- else
- pr_err("write to read-only section NOT trapped, test failed\n");
-
- if (*(volatile int *)&rodata_test_data != 0xC3)
- pr_err("read only data changed during write\n");
-}
-#else
-static inline void rodata_test(void) { }
-#endif
-
-static int set_page_attributes(unsigned long virt, int numpages,
- pte_t (*f)(pte_t))
-{
- pmd_t *pmd;
- pte_t *pte;
- unsigned long start = virt;
- unsigned long end = virt + (numpages << PAGE_SHIFT);
- unsigned long pmd_end;
-
- while (virt < end) {
- pmd = pmd_off_k(virt);
- pmd_end = min(ALIGN(virt + 1, PMD_SIZE), end);
-
- if ((pmd_val(*pmd) & PMD_TYPE_MASK) != PMD_TYPE_TABLE) {
- pr_err("%s: pmd %p=%08lx for %08lx not page table\n",
- __func__, pmd, pmd_val(*pmd), virt);
- virt = pmd_end;
- continue;
- }
-
- while (virt < pmd_end) {
- pte = pte_offset_kernel(pmd, virt);
- set_pte_ext(pte, f(*pte), 0);
- virt += PAGE_SIZE;
- }
- }
-
- flush_tlb_kernel_range(start, end);
-
- return 0;
-}
-
-int set_memory_ro(unsigned long virt, int numpages)
-{
- return set_page_attributes(virt, numpages, pte_wrprotect);
-}
-EXPORT_SYMBOL(set_memory_ro);
-
-int set_memory_rw(unsigned long virt, int numpages)
-{
- return set_page_attributes(virt, numpages, pte_mkwrite);
-}
-EXPORT_SYMBOL(set_memory_rw);
-
-void set_kernel_text_rw(void)
-{
- unsigned long start = PAGE_ALIGN((unsigned long)_text);
- unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start;
-
- if (!kernel_set_to_readonly)
- return;
-
- pr_debug("Set kernel text: %lx - %lx to read-write\n",
- start, start + size);
-
- set_memory_rw(start, size >> PAGE_SHIFT);
-}
-
-void set_kernel_text_ro(void)
-{
- unsigned long start = PAGE_ALIGN((unsigned long)_text);
- unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start;
-
- if (!kernel_set_to_readonly)
- return;
-
- pr_info_once("Write protecting the kernel text section %lx - %lx\n",
- start, start + size);
-
- pr_debug("Set kernel text: %lx - %lx to read only\n",
- start, start + size);
-
- set_memory_ro(start, size >> PAGE_SHIFT);
-}
-
-void mark_rodata_ro(void)
-{
- kernel_set_to_readonly = 1;
-
- set_kernel_text_ro();
-
- rodata_test();
-}
diff --git a/drivers/Kconfig b/drivers/Kconfig
index a73d713..adead10 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -150,4 +150,6 @@
source "drivers/coresight/Kconfig"
+source "drivers/bif/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index f461e83..d55b035 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -142,3 +142,5 @@
obj-$(CONFIG_MOBICORE_SUPPORT) += gud/
obj-$(CONFIG_CORESIGHT) += coresight/
+
+obj-$(CONFIG_BIF) += bif/
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index 1937db8..3a8bbc5 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -9,6 +9,9 @@
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License or (at your optional) any later version of the license.
+ *
+ * The Linux Foundation chooses to take subject only to the GPLv2 license
+ * terms, and distributes only under these terms.
*/
#define pr_fmt(fmt) "cma: " fmt
@@ -24,6 +27,9 @@
#include <linux/memblock.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/page-isolation.h>
@@ -52,8 +58,9 @@
phys_addr_t base;
unsigned long size;
struct cma *cma;
-} cma_areas[MAX_CMA_AREAS] __initdata;
-static unsigned cma_area_count __initdata;
+ const char *name;
+} cma_areas[MAX_CMA_AREAS];
+static unsigned cma_area_count;
static struct cma_map {
@@ -71,6 +78,20 @@
return NULL;
}
+static struct cma *cma_get_area_by_name(const char *name)
+{
+ int i;
+ if (!name)
+ return NULL;
+
+ for (i = 0; i < cma_area_count; i++)
+ if (cma_areas[i].name && strcmp(cma_areas[i].name, name) == 0)
+ return cma_areas[i].cma;
+ return NULL;
+}
+
+
+
#ifdef CONFIG_CMA_SIZE_MBYTES
#define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES
#else
@@ -181,6 +202,38 @@
return ERR_PTR(ret);
}
+/*****************************************************************************/
+
+#ifdef CONFIG_OF
+int __init cma_fdt_scan(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ phys_addr_t base, size;
+ unsigned long len;
+ __be32 *prop;
+ char *name;
+
+ if (strncmp(uname, "region@", 7) != 0 || depth != 2 ||
+ !of_get_flat_dt_prop(node, "linux,contiguous-region", NULL))
+ return 0;
+
+ prop = of_get_flat_dt_prop(node, "reg", &len);
+ if (!prop || (len != 2 * sizeof(unsigned long)))
+ return 0;
+
+ base = be32_to_cpu(prop[0]);
+ size = be32_to_cpu(prop[1]);
+
+ name = of_get_flat_dt_prop(node, "label", NULL);
+
+ pr_info("Found %s, memory base %lx, size %ld MiB\n", uname,
+ (unsigned long)base, (unsigned long)size / SZ_1M);
+ dma_contiguous_reserve_area(size, &base, 0, name);
+
+ return 0;
+}
+#endif
+
/**
* dma_contiguous_reserve() - reserve area for contiguous memory handling
* @limit: End address of the reserved memory (optional, 0 for any).
@@ -216,9 +269,13 @@
pr_debug("%s: reserving %ld MiB for global area\n", __func__,
(unsigned long)sel_size / SZ_1M);
- if (dma_contiguous_reserve_area(sel_size, &base, limit) == 0)
+ if (dma_contiguous_reserve_area(sel_size, &base, limit, NULL)
+ == 0)
dma_contiguous_def_base = base;
}
+#ifdef CONFIG_OF
+ of_scan_flat_dt(cma_fdt_scan, NULL);
+#endif
};
/**
@@ -236,7 +293,7 @@
* devices.
*/
int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base,
- phys_addr_t limit)
+ phys_addr_t limit, const char *name)
{
phys_addr_t base = *res_base;
phys_addr_t alignment;
@@ -288,6 +345,7 @@
*/
cma_areas[cma_area_count].base = base;
cma_areas[cma_area_count].size = size;
+ cma_areas[cma_area_count].name = name;
cma_area_count++;
*res_base = base;
@@ -323,6 +381,45 @@
return 0;
}
+#ifdef CONFIG_OF
+static void cma_assign_device_from_dt(struct device *dev)
+{
+ struct device_node *node;
+ struct cma *cma;
+ const char *name;
+ u32 value;
+
+ node = of_parse_phandle(dev->of_node, "linux,contiguous-region", 0);
+ if (!node)
+ return;
+ if (of_property_read_u32(node, "reg", &value) && !value)
+ return;
+
+ if (of_property_read_string(node, "label", &name))
+ return;
+
+ cma = cma_get_area_by_name(name);
+ if (!cma)
+ return;
+
+ dev_set_cma_area(dev, cma);
+ pr_info("Assigned CMA region at %lx to %s device\n", (unsigned long)value, dev_name(dev));
+}
+
+static int cma_device_init_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct device *dev = data;
+ if (event == BUS_NOTIFY_ADD_DEVICE && dev->of_node)
+ cma_assign_device_from_dt(dev);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block cma_dev_init_nb = {
+ .notifier_call = cma_device_init_notifier_call,
+};
+#endif
+
static int __init cma_init_reserved_areas(void)
{
struct cma *cma;
@@ -344,6 +441,9 @@
dev_set_cma_area(cma_maps[i].dev, cma);
}
+#ifdef CONFIG_OF
+ bus_register_notifier(&platform_bus_type, &cma_dev_init_nb);
+#endif
return 0;
}
core_initcall(cma_init_reserved_areas);
diff --git a/drivers/bif/Kconfig b/drivers/bif/Kconfig
new file mode 100644
index 0000000..d9828c5
--- /dev/null
+++ b/drivers/bif/Kconfig
@@ -0,0 +1,26 @@
+#
+# BIF framework and drivers
+#
+menuconfig BIF
+ bool "MIPI-BIF support"
+ select CRC_CCITT
+ select BITREVERSE
+ help
+ MIPI-BIF (battery interface) is a one-wire serial interface between a
+ host master device and one or more slave devices which are located in
+ a battery pack or also on the host. Enabling this option allows for
+ BIF consumer drivers to issue transactions via BIF controller drivers.
+
+if BIF
+config BIF_QPNP
+ depends on SPMI
+ depends on OF_SPMI
+ tristate "Qualcomm QPNP BIF support"
+ help
+ This driver supports the QPNP BSI peripheral found inside of Qualcomm
+ QPNP PMIC devices. The BSI peripheral is able to communicate using
+ the BIF protocol. The QPNP BSI driver hooks into the BIF framework.
+ Enable this option in order to provide support for BIF communication
+ on targets which have BSI PMIC peripherals.
+
+endif
diff --git a/drivers/bif/Makefile b/drivers/bif/Makefile
new file mode 100644
index 0000000..7604ca7
--- /dev/null
+++ b/drivers/bif/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for kernel BIF framework.
+#
+obj-$(CONFIG_BIF) += bif-core.o
+obj-$(CONFIG_BIF_QPNP) += qpnp-bsi.o
diff --git a/drivers/bif/bif-core.c b/drivers/bif/bif-core.c
new file mode 100644
index 0000000..e11e6ba4
--- /dev/null
+++ b/drivers/bif/bif-core.c
@@ -0,0 +1,2934 @@
+/* Copyright (c) 2013, 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/bitrev.h>
+#include <linux/crc-ccitt.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <linux/bif/consumer.h>
+#include <linux/bif/driver.h>
+
+/**
+ * struct bif_ctrl_dev - holds controller device specific information
+ * @list: Doubly-linked list parameter linking to other
+ * BIF controllers registered in the system
+ * @desc: Description structure for this BIF controller
+ * @mutex: Mutex lock that is used to ensure mutual
+ * exclusion between transactions performed on the
+ * BIF bus for this controller
+ * @ctrl_dev: Device pointer to the BIF controller device
+ * @driver_data: Private data used by the BIF controller
+ * @selected_sdev: Slave device that is currently selected on
+ * the BIF bus of this controller
+ * @bus_change_notifier: Head of a notifier list containing notifier
+ * blocks that are notified when the battery
+ * presence changes
+ * @enter_irq_mode_work: Work task that is scheduled after a transaction
+ * completes when there are consumers that are
+ * actively monitoring BIF slave interrupts
+ * @irq_count: This is a count of the total number of BIF slave
+ * interrupts that are currently being monitored
+ * for the BIF slaves connected to this BIF
+ * controller
+ * @irq_mode_delay_jiffies: Number of jiffies to wait before scheduling the
+ * enter IRQ mode task. Using a larger value
+ * helps to improve the performance of BIF
+ * consumers that perform many BIF transactions.
+ * Using a smaller value reduces the latency of
+ * BIF slave interrupts.
+ * @battery_present: Cached value of the battery presence. This is
+ * used to filter out spurious presence update
+ * calls when the battery presence state has not
+ * changed.
+ */
+struct bif_ctrl_dev {
+ struct list_head list;
+ struct bif_ctrl_desc *desc;
+ struct mutex mutex;
+ struct device *ctrl_dev;
+ void *driver_data;
+ struct bif_slave_dev *selected_sdev;
+ struct blocking_notifier_head bus_change_notifier;
+ struct delayed_work enter_irq_mode_work;
+ int irq_count;
+ int irq_mode_delay_jiffies;
+ bool battery_present;
+};
+
+/**
+ * struct bif_ctrl - handle used by BIF consumers for bus oriented BIF
+ * operations
+ * @bdev: Pointer to BIF controller device
+ * @exclusive_lock: Flag which indicates that the BIF consumer responsible
+ * for this handle has locked the BIF bus of this
+ * controller. BIF transactions from other consumers are
+ * blocked until the bus is unlocked.
+ */
+struct bif_ctrl {
+ struct bif_ctrl_dev *bdev;
+ bool exclusive_lock;
+};
+
+/**
+ * struct bif_slave_dev - holds BIF slave device information
+ * @list: Doubly-linked list parameter linking to other
+ * BIF slaves that have been enumerated
+ * @bdev: Pointer to the BIF controller device that this
+ * slave is physically connected to
+ * @slave_addr: 8-bit BIF DEV_ADR assigned to this slave
+ * @unique_id: 80-bit BIF unique ID of the slave
+ * @unique_id_bits_known: Number of bits of the UID that are currently
+ * known. This number starts is incremented during
+ * a UID search and must end at 80 if the slave
+ * responds to the search properly.
+ * @present: Boolean value showing if this slave is
+* physically present in the system at a given
+* point in time. The value is set to false if the
+* battery pack containing the slave is
+* disconnected.
+ * @l1_data: BIF DDB L1 data of the slave as read from the
+ * slave's memory
+ * @function_directory: Pointer to the BIF DDB L2 function directory
+ * list as read from the slave's memory
+ * @protocol_function: Pointer to constant protocol function data as
+ * well as software state information if the slave
+ * has a protocol function
+ * @slave_ctrl_function: Pointer to constant slave control function data
+ * as well as software state information if the
+ * slave has a slave control function
+ * @nvm_function: Pointer to constant non-volatile memory function
+ * data as well as software state information if
+ * the slave has a non-volatile memory function
+ *
+ * bif_slave_dev objects are stored indefinitely after enumeration in order to
+ * speed up battery reinsertion. Only a UID check is needed after inserting a
+ * battery assuming it has been enumerated before.
+ *
+ * unique_id bytes are stored such that unique_id[0] = MSB and
+ * unique_id[BIF_UNIQUE_ID_BYTE_LENGTH - 1] = LSB
+ */
+struct bif_slave_dev {
+ struct list_head list;
+ struct bif_ctrl_dev *bdev;
+ u8 slave_addr;
+ u8 unique_id[BIF_UNIQUE_ID_BYTE_LENGTH];
+ int unique_id_bits_known;
+ bool present;
+ struct bif_ddb_l1_data l1_data;
+ struct bif_ddb_l2_data *function_directory;
+ struct bif_protocol_function *protocol_function;
+ struct bif_slave_control_function *slave_ctrl_function;
+ struct bif_nvm_function *nvm_function;
+};
+
+/**
+ * struct bif_slave - handle used by BIF consumers for slave oriented BIF
+ * operations
+ * @ctrl: Consumer BIF controller handle data
+ * @sdev: Pointer to BIF slave device
+ */
+struct bif_slave {
+ struct bif_ctrl ctrl;
+ struct bif_slave_dev *sdev;
+};
+
+/* Number of times to retry a full BIF transaction before returning an error. */
+#define BIF_TRANSACTION_RETRY_COUNT 5
+
+static DEFINE_MUTEX(bif_ctrl_list_mutex);
+static LIST_HEAD(bif_ctrl_list);
+static DEFINE_MUTEX(bif_sdev_list_mutex);
+static LIST_HEAD(bif_sdev_list);
+
+static u8 next_dev_addr = 0x02;
+
+#define DEBUG_PRINT_BUFFER_SIZE 256
+static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
+{
+ int pos = 0;
+ int i;
+
+ for (i = 0; i < buf_len; i++) {
+ pos += scnprintf(str + pos, str_len - pos, "0x%02X", buf[i]);
+ if (i < buf_len - 1)
+ pos += scnprintf(str + pos, str_len - pos, ", ");
+ }
+}
+
+static void bif_print_slave_data(struct bif_slave_dev *sdev)
+{
+ char str[DEBUG_PRINT_BUFFER_SIZE];
+ u8 *uid;
+ int i, j;
+ struct bif_object *object;
+
+ if (sdev->unique_id_bits_known != BIF_UNIQUE_ID_BIT_LENGTH)
+ return;
+
+ uid = sdev->unique_id;
+ pr_debug("BIF slave: 0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
+ uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6],
+ uid[7], uid[8], uid[9]);
+ pr_debug(" present=%d, dev_adr=0x%02X\n", sdev->present,
+ sdev->slave_addr);
+ pr_debug(" revision=0x%02X, level=0x%02X, device class=0x%04X\n",
+ sdev->l1_data.revision, sdev->l1_data.level,
+ sdev->l1_data.device_class);
+ pr_debug(" manufacturer ID=0x%04X, product ID=0x%04X\n",
+ sdev->l1_data.manufacturer_id, sdev->l1_data.product_id);
+ pr_debug(" function directory length=%d\n", sdev->l1_data.length);
+
+ for (i = 0; i < sdev->l1_data.length / 4; i++) {
+ pr_debug(" Function %d: type=0x%02X, version=0x%02X, pointer=0x%04X\n",
+ i, sdev->function_directory[i].function_type,
+ sdev->function_directory[i].function_version,
+ sdev->function_directory[i].function_pointer);
+ }
+
+ if (sdev->nvm_function) {
+ pr_debug(" NVM function: pointer=0x%04X, task=%d, wr_buf_size=%d, nvm_base=0x%04X, nvm_size=%d\n",
+ sdev->nvm_function->nvm_pointer,
+ sdev->nvm_function->slave_control_channel,
+ (sdev->nvm_function->write_buffer_size
+ ? sdev->nvm_function->write_buffer_size : 0),
+ sdev->nvm_function->nvm_base_address,
+ sdev->nvm_function->nvm_size);
+ if (sdev->nvm_function->object_count)
+ pr_debug(" NVM objects:\n");
+ i = 0;
+ list_for_each_entry(object, &sdev->nvm_function->object_list,
+ list) {
+ pr_debug(" Object %d - addr=0x%04X, data len=%d, type=0x%02X, version=0x%02X, manufacturer ID=0x%04X, crc=0x%04X\n",
+ i, object->addr, object->length - 8,
+ object->type, object->version,
+ object->manufacturer_id, object->crc);
+ for (j = 0; j < DIV_ROUND_UP(object->length - 8, 16);
+ j++) {
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE,
+ object->data + j * 16,
+ min(16, object->length - 8 - (j * 16)));
+ pr_debug(" data(0x%04X): %s\n", j * 16,
+ str);
+ }
+ i++;
+ }
+ }
+}
+
+static void bif_print_slaves(void)
+{
+ struct bif_slave_dev *sdev;
+
+ mutex_lock(&bif_sdev_list_mutex);
+
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ /* Skip slaves without fully known UIDs. */
+ if (sdev->unique_id_bits_known != BIF_UNIQUE_ID_BIT_LENGTH)
+ continue;
+ bif_print_slave_data(sdev);
+ }
+
+ mutex_unlock(&bif_sdev_list_mutex);
+}
+
+static struct bif_slave_dev *bif_add_slave(struct bif_ctrl_dev *bdev)
+{
+ struct bif_slave_dev *sdev;
+
+ sdev = kzalloc(sizeof(struct bif_slave_dev), GFP_KERNEL);
+ if (sdev == NULL) {
+ pr_err("Memory allocation failed for bif_slave_dev\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sdev->bdev = bdev;
+ INIT_LIST_HEAD(&sdev->list);
+ list_add_tail(&sdev->list, &bif_sdev_list);
+
+ return sdev;
+}
+
+static void bif_remove_slave(struct bif_slave_dev *sdev)
+{
+ list_del(&sdev->list);
+ if (sdev->bdev->selected_sdev == sdev)
+ sdev->bdev->selected_sdev = NULL;
+
+ if (sdev->slave_ctrl_function)
+ kfree(sdev->slave_ctrl_function->irq_notifier_list);
+ kfree(sdev->slave_ctrl_function);
+ kfree(sdev->protocol_function);
+ kfree(sdev->function_directory);
+
+ kfree(sdev);
+}
+
+/* This function assumes that the uid array is all 0 to start with. */
+static void set_uid_bit(u8 uid[BIF_UNIQUE_ID_BYTE_LENGTH], unsigned int bit,
+ unsigned int value)
+{
+ u8 mask;
+
+ if (bit >= BIF_UNIQUE_ID_BIT_LENGTH)
+ return;
+
+ mask = 1 << (7 - (bit % 8));
+
+ uid[bit / 8] &= ~mask;
+ uid[bit / 8] |= value << (7 - (bit % 8));
+}
+
+static unsigned int get_uid_bit(u8 uid[BIF_UNIQUE_ID_BYTE_LENGTH],
+ unsigned int bit)
+{
+ if (bit >= BIF_UNIQUE_ID_BIT_LENGTH)
+ return 0;
+
+ return (uid[bit / 8] & (1 << (7 - (bit % 8)))) ? 1 : 0;
+}
+
+static void bif_enter_irq_mode_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct bif_ctrl_dev *bdev
+ = container_of(dwork, struct bif_ctrl_dev, enter_irq_mode_work);
+ int rc, i;
+
+ mutex_lock(&bdev->mutex);
+ for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) {
+ rc = bdev->desc->ops->set_bus_state(bdev,
+ BIF_BUS_STATE_INTERRUPT);
+ if (rc == 0)
+ break;
+ }
+ mutex_unlock(&bdev->mutex);
+
+ /* Reschedule the task if the transaction failed. */
+ if (rc) {
+ pr_err("Could not set BIF bus to interrupt mode, rc=%d\n", rc);
+ schedule_delayed_work(&bdev->enter_irq_mode_work,
+ bdev->irq_mode_delay_jiffies);
+ }
+}
+
+static void bif_cancel_irq_mode_work(struct bif_ctrl_dev *bdev)
+{
+ cancel_delayed_work(&bdev->enter_irq_mode_work);
+}
+
+static void bif_schedule_irq_mode_work(struct bif_ctrl_dev *bdev)
+{
+ if (bdev->irq_count > 0 &&
+ bdev->desc->ops->get_bus_state(bdev) != BIF_BUS_STATE_INTERRUPT)
+ schedule_delayed_work(&bdev->enter_irq_mode_work,
+ bdev->irq_mode_delay_jiffies);
+}
+
+static int _bif_select_slave_no_retry(struct bif_slave_dev *sdev)
+{
+ struct bif_ctrl_dev *bdev = sdev->bdev;
+ int rc = 0;
+ int i;
+
+ /* Check if the slave is already selected. */
+ if (sdev->bdev->selected_sdev == sdev)
+ return 0;
+
+ if (sdev->slave_addr) {
+ /* Select using DEV_ADR. */
+ rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_SDA,
+ sdev->slave_addr);
+ if (!rc)
+ sdev->bdev->selected_sdev = sdev;
+ } else if (sdev->unique_id_bits_known == BIF_UNIQUE_ID_BIT_LENGTH) {
+ /* Select using full UID. */
+ for (i = 0; i < BIF_UNIQUE_ID_BYTE_LENGTH - 1; i++) {
+ rc = bdev->desc->ops->bus_transaction(bdev,
+ BIF_TRANS_EDA, sdev->unique_id[i]);
+ if (rc)
+ goto out;
+ }
+
+ rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_SDA,
+ sdev->unique_id[BIF_UNIQUE_ID_BYTE_LENGTH - 1]);
+ if (rc)
+ goto out;
+ } else {
+ pr_err("Cannot select slave because it has neither UID nor DEV_ADR.\n");
+ return -EINVAL;
+ }
+
+ sdev->bdev->selected_sdev = sdev;
+
+ return 0;
+out:
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+}
+
+static int bif_select_slave(struct bif_slave_dev *sdev)
+{
+ int rc = -EPERM;
+ int i;
+
+ for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) {
+ rc = _bif_select_slave_no_retry(sdev);
+ if (rc == 0)
+ break;
+ /* Force slave reselection. */
+ sdev->bdev->selected_sdev = NULL;
+ }
+
+ return rc;
+}
+
+/*
+ * Returns 1 if slave is selected, 0 if slave is not selected, or errno if
+ * error.
+ */
+static int bif_is_slave_selected(struct bif_ctrl_dev *bdev)
+{
+ int rc = -EPERM;
+ int tack, i;
+
+ for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) {
+ /* Attempt a transaction query. */
+ rc = bdev->desc->ops->bus_transaction_read(bdev, BIF_TRANS_BC,
+ BIF_CMD_TQ, &tack);
+ if (rc == 0 || rc == -ETIMEDOUT)
+ break;
+ }
+
+ if (rc == 0)
+ rc = 1;
+ else if (rc == -ETIMEDOUT)
+ rc = 0;
+ else
+ pr_err("BIF bus_transaction_read failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+/* Read from a specified number of consecutive registers. */
+static int _bif_slave_read_no_retry(struct bif_slave_dev *sdev, u16 addr,
+ u8 *buf, int len)
+{
+ struct bif_ctrl_dev *bdev = sdev->bdev;
+ int rc = 0;
+ int i, response;
+
+ rc = bif_select_slave(sdev);
+ if (rc)
+ return rc;
+
+ if (bdev->desc->ops->read_slave_registers) {
+ /*
+ * Use low level slave register read implementation in order to
+ * receive the benefits of BIF burst reads.
+ */
+ rc = bdev->desc->ops->read_slave_registers(bdev, addr, buf,
+ len);
+ if (rc)
+ pr_err("read_slave_registers failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < len; i++) {
+ rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_ERA,
+ addr >> 8);
+ if (rc) {
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = bdev->desc->ops->bus_transaction_read(bdev, BIF_TRANS_RRA,
+ addr & 0xFF, &response);
+ if (rc) {
+ pr_err("bus_transaction_read failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (!(response & BIF_SLAVE_RD_ACK)) {
+ pr_err("BIF register read error=0x%02X\n",
+ response & BIF_SLAVE_RD_ERR);
+ return -EIO;
+ }
+
+ buf[i] = response & BIF_SLAVE_RD_DATA;
+ addr++;
+ }
+
+ return rc;
+}
+
+/*
+ * Read from a specified number of consecutive registers. Retry the transaction
+ * several times in case of communcation failures.
+ */
+static int _bif_slave_read(struct bif_slave_dev *sdev, u16 addr, u8 *buf,
+ int len)
+{
+ int rc = -EPERM;
+ int i;
+
+ for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) {
+ rc = _bif_slave_read_no_retry(sdev, addr, buf, len);
+ if (rc == 0)
+ break;
+ /* Force slave reselection. */
+ sdev->bdev->selected_sdev = NULL;
+ }
+
+ return rc;
+}
+
+/* Write to a specified number of consecutive registers. */
+static int _bif_slave_write_no_retry(struct bif_slave_dev *sdev, u16 addr,
+ u8 *buf, int len)
+{
+ struct bif_ctrl_dev *bdev = sdev->bdev;
+ int rc = 0;
+ int i;
+
+ rc = bif_select_slave(sdev);
+ if (rc)
+ return rc;
+
+ if (bdev->desc->ops->write_slave_registers) {
+ /*
+ * Use low level slave register write implementation in order to
+ * receive the benefits of BIF burst writes.
+ */
+ rc = bdev->desc->ops->write_slave_registers(bdev, addr, buf,
+ len);
+ if (rc)
+ pr_err("write_slave_registers failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_ERA, addr >> 8);
+ if (rc)
+ goto out;
+
+ rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_WRA, addr & 0xFF);
+ if (rc)
+ goto out;
+
+ for (i = 0; i < len; i++) {
+ rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_WD,
+ buf[i]);
+ if (rc)
+ goto out;
+ }
+
+ return 0;
+out:
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+}
+
+/*
+ * Write to a specified number of consecutive registers. Retry the transaction
+ * several times in case of communcation failures.
+ */
+static int _bif_slave_write(struct bif_slave_dev *sdev, u16 addr, u8 *buf,
+ int len)
+{
+ int rc = -EPERM;
+ int i;
+
+ for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) {
+ rc = _bif_slave_write_no_retry(sdev, addr, buf, len);
+ if (rc == 0)
+ break;
+ /* Force slave reselection. */
+ sdev->bdev->selected_sdev = NULL;
+ }
+
+ return rc;
+}
+
+/* Takes a mutex if this consumer is not an exclusive bus user. */
+static void bif_ctrl_lock(struct bif_ctrl *ctrl)
+{
+ if (!ctrl->exclusive_lock) {
+ mutex_lock(&ctrl->bdev->mutex);
+ bif_cancel_irq_mode_work(ctrl->bdev);
+ }
+}
+
+/* Releases a mutex if this consumer is not an exclusive bus user. */
+static void bif_ctrl_unlock(struct bif_ctrl *ctrl)
+{
+ if (!ctrl->exclusive_lock) {
+ bif_schedule_irq_mode_work(ctrl->bdev);
+ mutex_unlock(&ctrl->bdev->mutex);
+ }
+}
+
+static void bif_slave_ctrl_lock(struct bif_slave *slave)
+{
+ bif_ctrl_lock(&slave->ctrl);
+}
+
+static void bif_slave_ctrl_unlock(struct bif_slave *slave)
+{
+ bif_ctrl_unlock(&slave->ctrl);
+}
+
+static int bif_check_task(struct bif_slave *slave, unsigned int task)
+{
+ if (IS_ERR_OR_NULL(slave)) {
+ pr_err("Invalid slave handle.\n");
+ return -EINVAL;
+ } else if (!slave->sdev->bdev) {
+ pr_err("BIF controller has been removed.\n");
+ return -ENXIO;
+ } else if (!slave->sdev->slave_ctrl_function
+ || slave->sdev->slave_ctrl_function->task_count == 0) {
+ pr_err("BIF slave does not support slave control.\n");
+ return -ENODEV;
+ } else if (task >= slave->sdev->slave_ctrl_function->task_count) {
+ pr_err("Requested task: %u greater than max: %u for this slave\n",
+ task, slave->sdev->slave_ctrl_function->task_count);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * bif_request_irq() - request a BIF slave IRQ by slave task number
+ * @slave: BIF slave handle
+ * @task: BIF task number of the IRQ inside of the slave. This
+ * corresponds to the slave control channel specified for a given
+ * BIF function inside of the slave.
+ * @nb: Notifier block to call when the IRQ fires
+ *
+ * This function registers a notifier block to call when the BIF slave interrupt
+ * is triggered and also enables the interrupt. The interrupt is enabled inside
+ * of the BIF slave's slave control function and also the BIF bus is put into
+ * interrupt mode.
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_request_irq(struct bif_slave *slave, unsigned int task,
+ struct notifier_block *nb)
+{
+ int rc;
+ u16 addr;
+ u8 reg, mask;
+
+ rc = bif_check_task(slave, task);
+ if (rc) {
+ pr_err("Invalid slave or task, rc=%d\n", rc);
+ return rc;
+ }
+
+ bif_slave_ctrl_lock(slave);
+
+ rc = blocking_notifier_chain_register(
+ &slave->sdev->slave_ctrl_function->irq_notifier_list[task], nb);
+ if (rc) {
+ pr_err("Notifier registration failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ /* Enable the interrupt within the slave */
+ mask = BIT(task % SLAVE_CTRL_TASKS_PER_SET);
+ addr = SLAVE_CTRL_FUNC_IRQ_EN_ADDR(
+ slave->sdev->slave_ctrl_function->slave_ctrl_pointer, task);
+ if (task / SLAVE_CTRL_TASKS_PER_SET == 0) {
+ /* Set global interrupt enable. */
+ mask |= BIT(0);
+ }
+ rc = _bif_slave_read(slave->sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register read failed, rc=%d\n", rc);
+ goto notifier_unregister;
+ }
+ reg |= mask;
+ rc = _bif_slave_write(slave->sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register write failed, rc=%d\n", rc);
+ goto notifier_unregister;
+ }
+
+ /* Set global interrupt enable if task not in set 0. */
+ if (task / SLAVE_CTRL_TASKS_PER_SET != 0) {
+ mask = BIT(0);
+ addr = SLAVE_CTRL_FUNC_IRQ_EN_ADDR(
+ slave->sdev->slave_ctrl_function->slave_ctrl_pointer, 0);
+ rc = _bif_slave_read(slave->sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register read failed, rc=%d\n", rc);
+ goto notifier_unregister;
+ }
+ reg |= mask;
+ rc = _bif_slave_write(slave->sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register write failed, rc=%d\n", rc);
+ goto notifier_unregister;
+ }
+ }
+
+ rc = slave->sdev->bdev->desc->ops->set_bus_state(slave->sdev->bdev,
+ BIF_BUS_STATE_INTERRUPT);
+ if (rc) {
+ pr_err("Could not set BIF bus to interrupt mode, rc=%d\n", rc);
+ goto notifier_unregister;
+ }
+
+ slave->sdev->bdev->irq_count++;
+done:
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+
+notifier_unregister:
+ blocking_notifier_chain_unregister(
+ &slave->sdev->slave_ctrl_function->irq_notifier_list[task],
+ nb);
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(bif_request_irq);
+
+/**
+ * bif_free_irq() - free a BIF slave IRQ by slave task number
+ * @slave: BIF slave handle
+ * @task: BIF task number of the IRQ inside of the slave. This
+ * corresponds to the slave control channel specified for a given
+ * BIF function inside of the slave.
+ * @nb: Notifier block previously registered with this interrupt
+ *
+ * This function unregisters a notifier block that was previously registered
+ * with bif_request_irq().
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_free_irq(struct bif_slave *slave, unsigned int task,
+ struct notifier_block *nb)
+{
+ int rc;
+ u16 addr;
+ u8 reg;
+
+ rc = bif_check_task(slave, task);
+ if (rc) {
+ pr_err("Invalid slave or task, rc=%d\n", rc);
+ return rc;
+ }
+
+ bif_slave_ctrl_lock(slave);
+
+ /* Disable the interrupt within the slave */
+ reg = BIT(task % SLAVE_CTRL_TASKS_PER_SET);
+ addr = SLAVE_CTRL_FUNC_IRQ_CLEAR_ADDR(
+ slave->sdev->slave_ctrl_function->slave_ctrl_pointer, task);
+ rc = _bif_slave_write(slave->sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register write failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = blocking_notifier_chain_unregister(
+ &slave->sdev->slave_ctrl_function->irq_notifier_list[task], nb);
+ if (rc) {
+ pr_err("Notifier unregistration failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ slave->sdev->bdev->irq_count--;
+
+ if (slave->sdev->bdev->irq_count == 0) {
+ bif_cancel_irq_mode_work(slave->sdev->bdev);
+ } else if (slave->sdev->bdev->irq_count < 0) {
+ pr_err("Unbalanced IRQ free.\n");
+ rc = -EINVAL;
+ slave->sdev->bdev->irq_count = 0;
+ }
+done:
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_free_irq);
+
+/**
+ * bif_trigger_task() - trigger a task within a BIF slave
+ * @slave: BIF slave handle
+ * @task: BIF task inside of the slave to trigger. This corresponds to
+ * the slave control channel specified for a given BIF function
+ * inside of the slave.
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_trigger_task(struct bif_slave *slave, unsigned int task)
+{
+ int rc;
+ u16 addr;
+ u8 reg;
+
+ rc = bif_check_task(slave, task);
+ if (rc) {
+ pr_err("Invalid slave or task, rc=%d\n", rc);
+ return rc;
+ }
+
+ bif_slave_ctrl_lock(slave);
+
+ /* Trigger the task within the slave. */
+ reg = BIT(task % SLAVE_CTRL_TASKS_PER_SET);
+ addr = SLAVE_CTRL_FUNC_TASK_TRIGGER_ADDR(
+ slave->sdev->slave_ctrl_function->slave_ctrl_pointer, task);
+ rc = _bif_slave_write(slave->sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register write failed, rc=%d\n", rc);
+ goto done;
+ }
+
+done:
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_trigger_task);
+
+/**
+ * bif_task_is_busy() - checks the state of a BIF slave task
+ * @slave: BIF slave handle
+ * @task: BIF task inside of the slave to trigger. This corresponds to
+ * the slave control channel specified for a given BIF function
+ * inside of the slave.
+ *
+ * Returns 1 if the task is busy, 0 if it is not busy, and errno on error.
+ */
+int bif_task_is_busy(struct bif_slave *slave, unsigned int task)
+{
+ int rc;
+ u16 addr;
+ u8 reg;
+
+ rc = bif_check_task(slave, task);
+ if (rc) {
+ pr_err("Invalid slave or task, rc=%d\n", rc);
+ return rc;
+ }
+
+ bif_slave_ctrl_lock(slave);
+
+ /* Check the task busy state. */
+ addr = SLAVE_CTRL_FUNC_TASK_BUSY_ADDR(
+ slave->sdev->slave_ctrl_function->slave_ctrl_pointer, task);
+ rc = _bif_slave_read(slave->sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register read failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = (reg & BIT(task % SLAVE_CTRL_TASKS_PER_SET)) ? 1 : 0;
+done:
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_task_is_busy);
+
+static int bif_slave_notify_irqs(struct bif_slave_dev *sdev, int set, u8 val)
+{
+ int rc = 0;
+ int i, task;
+
+ for (i = 0; i < SLAVE_CTRL_TASKS_PER_SET; i++) {
+ if (val & (1 << i)) {
+ task = set * SLAVE_CTRL_TASKS_PER_SET + i;
+
+ rc = blocking_notifier_call_chain(
+ &sdev->slave_ctrl_function->irq_notifier_list[task],
+ task, sdev->bdev);
+ rc = notifier_to_errno(rc);
+ if (rc)
+ pr_err("Notification failed for task %d\n",
+ task);
+ }
+ }
+
+ return rc;
+}
+
+static int bif_slave_handle_irq(struct bif_slave_dev *sdev)
+{
+ struct bif_ctrl_dev *bdev = sdev->bdev;
+ bool resp = false;
+ int rc = 0;
+ int i;
+ u16 addr;
+ u8 reg;
+
+ mutex_lock(&sdev->bdev->mutex);
+ bif_cancel_irq_mode_work(sdev->bdev);
+
+ rc = bif_select_slave(sdev);
+ if (rc) {
+ pr_err("Could not select slave, rc=%d\n", rc);
+ goto done;
+ }
+
+ /* Check overall slave interrupt status. */
+ rc = bdev->desc->ops->bus_transaction_query(bdev, BIF_TRANS_BC,
+ BIF_CMD_ISTS, &resp);
+ if (rc) {
+ pr_err("Could not query slave interrupt status, rc=%d\n", rc);
+ goto done;
+ }
+
+ if (resp) {
+ for (i = 0; i < sdev->slave_ctrl_function->task_count
+ / SLAVE_CTRL_TASKS_PER_SET; i++) {
+ addr = sdev->slave_ctrl_function->slave_ctrl_pointer
+ + 4 * i + 1;
+ rc = _bif_slave_read(sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register read failed, rc=%d\n",
+ rc);
+ goto done;
+ }
+
+ /* Ensure that interrupts are pending in the set. */
+ if (reg != 0x00) {
+ /*
+ * Release mutex before notifying consumers so
+ * that they can use the bus.
+ */
+ mutex_unlock(&sdev->bdev->mutex);
+ rc = bif_slave_notify_irqs(sdev, i, reg);
+ if (rc) {
+ pr_err("BIF slave irq notification failed, rc=%d\n",
+ rc);
+ goto notification_failed;
+ }
+ mutex_lock(&sdev->bdev->mutex);
+
+ rc = bif_select_slave(sdev);
+ if (rc) {
+ pr_err("Could not select slave, rc=%d\n",
+ rc);
+ goto done;
+ }
+
+ /* Clear all interrupts in this set. */
+ rc = _bif_slave_write(sdev, addr, ®, 1);
+ if (rc) {
+ pr_err("BIF slave register write failed, rc=%d\n",
+ rc);
+ goto done;
+ }
+ }
+ }
+ }
+
+done:
+ bif_schedule_irq_mode_work(sdev->bdev);
+ mutex_unlock(&sdev->bdev->mutex);
+notification_failed:
+ if (rc == 0)
+ rc = resp;
+ return rc;
+}
+
+/**
+ * bif_ctrl_notify_slave_irq() - notify the BIF framework that a slave interrupt
+ * was received by a BIF controller
+ * @bdev: BIF controller device pointer
+ *
+ * This function should only be called from a BIF controller driver.
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_ctrl_notify_slave_irq(struct bif_ctrl_dev *bdev)
+{
+ struct bif_slave_dev *sdev;
+ int rc = 0, handled = 0;
+
+ if (IS_ERR_OR_NULL(bdev))
+ return -EINVAL;
+
+ mutex_lock(&bif_sdev_list_mutex);
+
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ if (sdev->bdev == bdev && sdev->present) {
+ rc = bif_slave_handle_irq(sdev);
+ if (rc < 0) {
+ pr_err("Could not handle BIF slave irq, rc=%d\n",
+ rc);
+ break;
+ }
+ handled += rc;
+ }
+ }
+
+ mutex_unlock(&bif_sdev_list_mutex);
+
+ if (handled == 0)
+ pr_info("Spurious BIF slave interrupt detected.\n");
+
+ if (rc > 0)
+ rc = 0;
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_notify_slave_irq);
+
+/**
+ * bif_ctrl_notify_battery_changed() - notify the BIF framework that a battery
+ * pack has been inserted or removed
+ * @bdev: BIF controller device pointer
+ *
+ * This function should only be called from a BIF controller driver.
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_ctrl_notify_battery_changed(struct bif_ctrl_dev *bdev)
+{
+ int rc = 0;
+ int present;
+
+ if (IS_ERR_OR_NULL(bdev))
+ return -EINVAL;
+
+ if (bdev->desc->ops->get_battery_presence) {
+ present = bdev->desc->ops->get_battery_presence(bdev);
+ if (present < 0) {
+ pr_err("Could not determine battery presence, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (bdev->battery_present == !!present)
+ return 0;
+
+ bdev->battery_present = present;
+
+ rc = blocking_notifier_call_chain(&bdev->bus_change_notifier,
+ present ? BIF_BUS_EVENT_BATTERY_INSERTED
+ : BIF_BUS_EVENT_BATTERY_REMOVED, bdev);
+ if (rc)
+ pr_err("Call chain noification failed, rc=%d\n", rc);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_notify_battery_changed);
+
+/**
+ * bif_ctrl_signal_battery_changed() - notify the BIF framework that a battery
+ * pack has been inserted or removed
+ * @ctrl: BIF controller consumer handle
+ *
+ * This function should only be called by a BIF consumer driver on systems where
+ * the BIF controller driver is unable to determine when a battery is inserted
+ * or removed.
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_ctrl_signal_battery_changed(struct bif_ctrl *ctrl)
+{
+ if (IS_ERR_OR_NULL(ctrl))
+ return -EINVAL;
+
+ return bif_ctrl_notify_battery_changed(ctrl->bdev);
+}
+EXPORT_SYMBOL(bif_ctrl_signal_battery_changed);
+
+/**
+ * bif_ctrl_notifier_register() - register a notifier block to be called when
+ * a battery pack is inserted or removed
+ * @ctrl: BIF controller consumer handle
+ *
+ * The value passed into the notifier when it is called is one of
+ * enum bif_bus_event.
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_ctrl_notifier_register(struct bif_ctrl *ctrl, struct notifier_block *nb)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl))
+ return -EINVAL;
+
+ rc = blocking_notifier_chain_register(&ctrl->bdev->bus_change_notifier,
+ nb);
+ if (rc)
+ pr_err("Notifier registration failed, rc=%d\n", rc);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_notifier_register);
+
+/**
+ * bif_ctrl_notifier_unregister() - unregister a battery status change notifier
+ * block that was previously registered
+ * @ctrl: BIF controller consumer handle
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_ctrl_notifier_unregister(struct bif_ctrl *ctrl,
+ struct notifier_block *nb)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl))
+ return -EINVAL;
+
+ rc =
+ blocking_notifier_chain_unregister(&ctrl->bdev->bus_change_notifier,
+ nb);
+ if (rc)
+ pr_err("Notifier unregistration failed, rc=%d\n", rc);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_notifier_unregister);
+
+/**
+ * bif_get_bus_handle() - returns the BIF controller consumer handle associated
+ * with a BIF slave handle
+ * @slave: BIF slave handle
+ *
+ * Note, bif_ctrl_put() should never be called for the pointer output by
+ * bif_get_bus_handle().
+ */
+struct bif_ctrl *bif_get_bus_handle(struct bif_slave *slave)
+{
+ if (IS_ERR_OR_NULL(slave))
+ return ERR_PTR(-EINVAL);
+
+ return &slave->ctrl;
+}
+EXPORT_SYMBOL(bif_get_bus_handle);
+
+/**
+ * bif_ctrl_count() - returns the number of registered BIF controllers
+ */
+int bif_ctrl_count(void)
+{
+ struct bif_ctrl_dev *bdev;
+ int count = 0;
+
+ mutex_lock(&bif_ctrl_list_mutex);
+
+ list_for_each_entry(bdev, &bif_ctrl_list, list) {
+ count++;
+ }
+ mutex_unlock(&bif_ctrl_list_mutex);
+
+ return count;
+}
+EXPORT_SYMBOL(bif_ctrl_count);
+
+/**
+ * bif_ctrl_get_by_id() - get a handle for the id'th BIF controller registered
+ * in the system
+ * @id: Arbitrary number associated with the BIF bus in the system
+ *
+ * id must be in the range [0, bif_ctrl_count() - 1]. This function should only
+ * need to be called by a BIF consumer that is unable to link to a given BIF
+ * controller via a device tree binding.
+ *
+ * Returns a BIF controller consumer handle if successful or an ERR_PTR if not.
+ */
+struct bif_ctrl *bif_ctrl_get_by_id(unsigned int id)
+{
+ struct bif_ctrl_dev *bdev;
+ struct bif_ctrl_dev *bdev_found = NULL;
+ struct bif_ctrl *ctrl = ERR_PTR(-ENODEV);
+
+ mutex_lock(&bif_ctrl_list_mutex);
+
+ list_for_each_entry(bdev, &bif_ctrl_list, list) {
+ if (id == 0) {
+ bdev_found = bdev;
+ break;
+ }
+ id--;
+ }
+ mutex_unlock(&bif_ctrl_list_mutex);
+
+ if (bdev_found) {
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ pr_err("Bus handle allocation failed\n");
+ ctrl = ERR_PTR(-ENOMEM);
+ } else {
+ ctrl->bdev = bdev_found;
+ }
+ }
+
+ return ctrl;
+}
+EXPORT_SYMBOL(bif_ctrl_get_by_id);
+
+/**
+ * bif_ctrl_get() - get a handle for the BIF controller that is linked to the
+ * consumer device in the device tree
+ * @consumer_dev: Pointer to the consumer's device
+ *
+ * In order to use this function, the BIF consumer's device must specify the
+ * "qcom,bif-ctrl" property in its device tree node which points to a BIF
+ * controller device node.
+ *
+ * Returns a BIF controller consumer handle if successful or an ERR_PTR if not.
+ * If the BIF controller linked to the consumer device has not yet probed, then
+ * ERR_PTR(-EPROBE_DEFER) is returned.
+ */
+struct bif_ctrl *bif_ctrl_get(struct device *consumer_dev)
+{
+ struct device_node *ctrl_node = NULL;
+ struct bif_ctrl_dev *bdev_found = NULL;
+ struct bif_ctrl *ctrl = ERR_PTR(-EPROBE_DEFER);
+ struct bif_ctrl_dev *bdev = NULL;
+
+ if (!consumer_dev || !consumer_dev->of_node) {
+ pr_err("Invalid device node\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ctrl_node = of_parse_phandle(consumer_dev->of_node, "qcom,bif-ctrl", 0);
+ if (!ctrl_node) {
+ pr_err("Could not find qcom,bif-ctrl property in %s\n",
+ consumer_dev->of_node->full_name);
+ return ERR_PTR(-ENXIO);
+ }
+
+ mutex_lock(&bif_ctrl_list_mutex);
+ list_for_each_entry(bdev, &bif_ctrl_list, list) {
+ if (bdev->ctrl_dev && bdev->ctrl_dev->of_node == ctrl_node) {
+ bdev_found = bdev;
+ break;
+ }
+ }
+ mutex_unlock(&bif_ctrl_list_mutex);
+
+ if (bdev_found) {
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ pr_err("Bus handle allocation failed\n");
+ ctrl = ERR_PTR(-ENOMEM);
+ } else {
+ ctrl->bdev = bdev_found;
+ }
+ }
+
+ return ctrl;
+}
+EXPORT_SYMBOL(bif_ctrl_get);
+
+/**
+ * bif_ctrl_put() - frees a BIF controller handle
+ * @ctrl: BIF controller consumer handle
+ */
+void bif_ctrl_put(struct bif_ctrl *ctrl)
+{
+ if (!IS_ERR_OR_NULL(ctrl) && ctrl->exclusive_lock)
+ mutex_unlock(&ctrl->bdev->mutex);
+ kfree(ctrl);
+}
+EXPORT_SYMBOL(bif_ctrl_put);
+
+/*
+ * Returns true if all parameters are matched, otherwise false.
+ * function_type and function_version mean that their exists some function in
+ * the slave which has the specified type and subtype. ctrl == NULL is treated
+ * as a wildcard.
+ */
+static bool bif_slave_match(const struct bif_ctrl *ctrl,
+ struct bif_slave_dev *sdev, const struct bif_match_criteria *criteria)
+{
+ int i, type, version;
+
+ if (ctrl && (ctrl->bdev != sdev->bdev))
+ return false;
+
+ if (!sdev->present
+ && (!(criteria->match_mask & BIF_MATCH_IGNORE_PRESENCE)
+ || ((criteria->match_mask & BIF_MATCH_IGNORE_PRESENCE)
+ && !criteria->ignore_presence)))
+ return false;
+
+ if ((criteria->match_mask & BIF_MATCH_MANUFACTURER_ID)
+ && sdev->l1_data.manufacturer_id != criteria->manufacturer_id)
+ return false;
+
+ if ((criteria->match_mask & BIF_MATCH_PRODUCT_ID)
+ && sdev->l1_data.product_id != criteria->product_id)
+ return false;
+
+ if (criteria->match_mask & BIF_MATCH_FUNCTION_TYPE) {
+ if (!sdev->function_directory)
+ return false;
+ for (i = 0; i < sdev->l1_data.length / 4; i++) {
+ type = sdev->function_directory[i].function_type;
+ version = sdev->function_directory[i].function_version;
+ if (type == criteria->function_type &&
+ (version == criteria->function_version
+ || !(criteria->match_mask
+ & BIF_MATCH_FUNCTION_VERSION)))
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * bif_slave_match_count() - returns the number of slaves associated with the
+ * specified BIF controller which fit the matching
+ * criteria
+ * @ctrl: BIF controller consumer handle
+ * @match_criteria: Matching criteria used to filter slaves
+ */
+int bif_slave_match_count(const struct bif_ctrl *ctrl,
+ const struct bif_match_criteria *match_criteria)
+{
+ struct bif_slave_dev *sdev;
+ int count = 0;
+
+ mutex_lock(&bif_sdev_list_mutex);
+
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ if (bif_slave_match(ctrl, sdev, match_criteria))
+ count++;
+ }
+
+ mutex_unlock(&bif_sdev_list_mutex);
+
+ return count;
+}
+EXPORT_SYMBOL(bif_slave_match_count);
+
+/**
+ * bif_slave_match_get() - get a slave handle for the id'th slave associated
+ * with the specified BIF controller which fits the
+ * matching criteria
+ * @ctrl: BIF controller consumer handle
+ * @id: Index into the set of matching slaves
+ * @match_criteria: Matching criteria used to filter slaves
+ *
+ * id must be in the range [0, bif_slave_match_count(ctrl, match_criteria) - 1].
+ *
+ * Returns a BIF slave handle if successful or an ERR_PTR if not.
+ */
+struct bif_slave *bif_slave_match_get(const struct bif_ctrl *ctrl,
+ unsigned int id, const struct bif_match_criteria *match_criteria)
+{
+ struct bif_slave_dev *sdev;
+ struct bif_slave *slave = ERR_PTR(-ENODEV);
+ struct bif_slave_dev *sdev_found = NULL;
+ int count = 0;
+
+ mutex_lock(&bif_sdev_list_mutex);
+
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ if (bif_slave_match(ctrl, sdev, match_criteria))
+ count++;
+ if (count == id + 1) {
+ sdev_found = sdev;
+ break;
+ }
+ }
+
+ mutex_unlock(&bif_sdev_list_mutex);
+
+ if (sdev_found) {
+ slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+ if (!slave) {
+ pr_err("Slave allocation failed\n");
+ slave = ERR_PTR(-ENOMEM);
+ } else {
+ slave->sdev = sdev_found;
+ slave->ctrl.bdev = sdev_found->bdev;
+ }
+ }
+
+ return slave;
+}
+EXPORT_SYMBOL(bif_slave_match_get);
+
+/**
+ * bif_slave_put() - frees a BIF slave handle
+ * @slave: BIF slave handle
+ */
+void bif_slave_put(struct bif_slave *slave)
+{
+ if (!IS_ERR_OR_NULL(slave) && slave->ctrl.exclusive_lock)
+ mutex_unlock(&slave->sdev->bdev->mutex);
+ kfree(slave);
+}
+EXPORT_SYMBOL(bif_slave_put);
+
+/**
+ * bif_slave_find_function() - get the function pointer and version of a
+ * BIF function if it is present on the specified slave
+ * @slave: BIF slave handle
+ * @function: BIF function to search for inside of the slave
+ * @version: If the function is found, then 'version' is set to the
+ * version value of the function
+ * @function_pointer: If the function is found, then 'function_pointer' is set
+ * to the BIF slave address of the function
+ *
+ * Returns 0 for success or errno if an error occurred. If the function is not
+ * found in the slave, then -ENODEV is returned.
+ */
+int bif_slave_find_function(struct bif_slave *slave, u8 function, u8 *version,
+ u16 *function_pointer)
+{
+ int rc = -ENODEV;
+ struct bif_ddb_l2_data *func;
+ int i;
+
+ if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(version)
+ || IS_ERR_OR_NULL(function_pointer)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ func = slave->sdev->function_directory;
+
+ for (i = 0; i < slave->sdev->l1_data.length / 4; i++) {
+ if (function == func[i].function_type) {
+ *version = func[i].function_version;
+ *function_pointer = func[i].function_pointer;
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_slave_find_function);
+
+/**
+ * bif_slave_read() - read contiguous memory values from a BIF slave
+ * @slave: BIF slave handle
+ * @addr: BIF slave address to begin reading at
+ * @buf: Buffer to fill with memory values
+ * @len: Number of byte to read
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_slave_read(struct bif_slave *slave, u16 addr, u8 *buf, int len)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(buf)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ bif_slave_ctrl_lock(slave);
+
+ rc = _bif_slave_read(slave->sdev, addr, buf, len);
+ if (rc)
+ pr_err("BIF slave read failed, rc=%d\n", rc);
+
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_slave_read);
+
+/**
+ * bif_slave_write() - write contiguous memory values to a BIF slave
+ * @slave: BIF slave handle
+ * @addr: BIF slave address to begin writing at
+ * @buf: Buffer containing values to write
+ * @len: Number of byte to write
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_slave_write(struct bif_slave *slave, u16 addr, u8 *buf, int len)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(buf)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ bif_slave_ctrl_lock(slave);
+
+ rc = _bif_slave_write(slave->sdev, addr, buf, len);
+ if (rc)
+ pr_err("BIF slave write failed, rc=%d\n", rc);
+
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_slave_write);
+
+/**
+ * bif_slave_is_present() - check if a slave is currently physically present
+ * in the system
+ * @slave: BIF slave handle
+ *
+ * Returns 1 if the slave is present, 0 if the slave is not present, or errno
+ * if an error occurred.
+ *
+ * This function can be used by BIF consumer drivers to check if their slave
+ * handles are still meaningful after battery reinsertion.
+ */
+int bif_slave_is_present(struct bif_slave *slave)
+{
+ if (IS_ERR_OR_NULL(slave)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ return slave->sdev->present;
+}
+EXPORT_SYMBOL(bif_slave_is_present);
+
+/**
+ * bif_slave_is_selected() - check if a slave is currently selected on the BIF
+ * bus
+ * @slave: BIF slave handle
+ *
+ * Returns 1 if the slave is selected, 0 if the slave is not selected, or errno
+ * if an error occurred.
+ *
+ * This function should not be required under normal circumstances since the
+ * bif-core framework ensures that slaves are always selected when needed.
+ * It would be most useful when used as a helper in conjunction with
+ * bif_ctrl_bus_lock() and the raw transaction functions.
+ */
+int bif_slave_is_selected(struct bif_slave *slave)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(slave)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ if (slave->sdev->bdev->selected_sdev != slave->sdev)
+ return false;
+
+ bif_slave_ctrl_lock(slave);
+ rc = bif_is_slave_selected(slave->sdev->bdev);
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_slave_is_selected);
+
+/**
+ * bif_slave_select() - select a slave on the BIF bus
+ * @slave: BIF slave handle
+ *
+ * Returns 0 on success or errno if an error occurred.
+ *
+ * This function should not be required under normal circumstances since the
+ * bif-core framework ensures that slaves are always selected when needed.
+ * It would be most useful when used as a helper in conjunction with
+ * bif_ctrl_bus_lock() and the raw transaction functions.
+ */
+int bif_slave_select(struct bif_slave *slave)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(slave)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ bif_slave_ctrl_lock(slave);
+ slave->sdev->bdev->selected_sdev = NULL;
+ rc = bif_select_slave(slave->sdev);
+ bif_slave_ctrl_unlock(slave);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_slave_select);
+
+/**
+ * bif_ctrl_raw_transaction() - perform a raw BIF transaction on the bus which
+ * expects no slave response
+ * @ctrl: BIF controller consumer handle
+ * @transaction: BIF transaction to carry out. This should be one of the
+ * values in enum bif_transaction.
+ * @data: 8-bit data to use in the transaction. The meaning of
+ * this data depends upon the transaction that is to be
+ * performed.
+ *
+ * When performing a bus command (BC) transaction, values in enum
+ * bif_bus_command may be used for the data parameter. Additional manufacturer
+ * specific values may also be used in a BC transaction.
+ *
+ * Returns 0 on success or errno if an error occurred.
+ *
+ * This function should only need to be used when BIF transactions are required
+ * that are not handled by the bif-core directly.
+ */
+int bif_ctrl_raw_transaction(struct bif_ctrl *ctrl, int transaction, u8 data)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ bif_ctrl_lock(ctrl);
+
+ rc = ctrl->bdev->desc->ops->bus_transaction(ctrl->bdev, transaction,
+ data);
+ if (rc)
+ pr_err("BIF bus transaction failed, rc=%d\n", rc);
+
+ bif_ctrl_unlock(ctrl);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_raw_transaction);
+
+/**
+ * bif_ctrl_raw_transaction_read() - perform a raw BIF transaction on the bus
+ * which expects an RD or TACK slave response word
+ * @ctrl: BIF controller consumer handle
+ * @transaction: BIF transaction to carry out. This should be one of the
+ * values in enum bif_transaction.
+ * @data: 8-bit data to use in the transaction. The meaning of
+ * this data depends upon the transaction that is to be
+ * performed.
+ * @response: Pointer to an integer which is filled with the 11-bit
+ * slave response word upon success. The 11-bit format is
+ * (MSB to LSB) BCF, ACK, EOT, D7-D0.
+ *
+ * When performing a bus command (BC) transaction, values in enum
+ * bif_bus_command may be used for the data parameter. Additional manufacturer
+ * specific values may also be used in a BC transaction.
+ *
+ * Returns 0 on success or errno if an error occurred.
+ *
+ * This function should only need to be used when BIF transactions are required
+ * that are not handled by the bif-core directly.
+ */
+int bif_ctrl_raw_transaction_read(struct bif_ctrl *ctrl, int transaction,
+ u8 data, int *response)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(response)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ bif_ctrl_lock(ctrl);
+
+ rc = ctrl->bdev->desc->ops->bus_transaction_read(ctrl->bdev,
+ transaction, data, response);
+ if (rc)
+ pr_err("BIF bus transaction failed, rc=%d\n", rc);
+
+ bif_ctrl_unlock(ctrl);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_raw_transaction_read);
+
+/**
+ * bif_ctrl_raw_transaction_query() - perform a raw BIF transaction on the bus
+ * which expects a BQ slave response
+ * @ctrl: BIF controller consumer handle
+ * @transaction: BIF transaction to carry out. This should be one of the
+ * values in enum bif_transaction.
+ * @data: 8-bit data to use in the transaction. The meaning of
+ * this data depends upon the transaction that is to be
+ * performed.
+ * @query_response: Pointer to boolean which is set to true if a BQ pulse
+ * is receieved, or false if no BQ pulse is received before
+ * timing out.
+ *
+ * When performing a bus command (BC) transaction, values in enum
+ * bif_bus_command may be used for the data parameter. Additional manufacturer
+ * specific values may also be used in a BC transaction.
+ *
+ * Returns 0 on success or errno if an error occurred.
+ *
+ * This function should only need to be used when BIF transactions are required
+ * that are not handled by the bif-core directly.
+ */
+int bif_ctrl_raw_transaction_query(struct bif_ctrl *ctrl, int transaction,
+ u8 data, bool *query_response)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(query_response)) {
+ pr_err("Invalid pointer input.\n");
+ return -EINVAL;
+ }
+
+ bif_ctrl_lock(ctrl);
+
+ rc = ctrl->bdev->desc->ops->bus_transaction_query(ctrl->bdev,
+ transaction, data, query_response);
+ if (rc)
+ pr_err("BIF bus transaction failed, rc=%d\n", rc);
+
+ bif_ctrl_unlock(ctrl);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_raw_transaction_query);
+
+/**
+ * bif_ctrl_bus_lock() - lock the BIF bus of a controller for exclusive access
+ * @ctrl: BIF controller consumer handle
+ *
+ * This function should only need to be called in circumstances where a BIF
+ * consumer is issuing special BIF bus commands that have strict ordering
+ * requirements.
+ */
+void bif_ctrl_bus_lock(struct bif_ctrl *ctrl)
+{
+ if (IS_ERR_OR_NULL(ctrl)) {
+ pr_err("Invalid controller handle.\n");
+ return;
+ }
+
+ if (ctrl->exclusive_lock) {
+ pr_err("BIF bus exclusive lock already held\n");
+ return;
+ }
+
+ mutex_lock(&ctrl->bdev->mutex);
+ ctrl->exclusive_lock = true;
+ bif_cancel_irq_mode_work(ctrl->bdev);
+}
+EXPORT_SYMBOL(bif_ctrl_bus_lock);
+
+/**
+ * bif_ctrl_bus_unlock() - lock the BIF bus of a controller that was previously
+ * locked for exclusive access
+ * @ctrl: BIF controller consumer handle
+ *
+ * This function must only be called after first calling bif_ctrl_bus_lock().
+ */
+void bif_ctrl_bus_unlock(struct bif_ctrl *ctrl)
+{
+ if (IS_ERR_OR_NULL(ctrl)) {
+ pr_err("Invalid controller handle.\n");
+ return;
+ }
+
+ if (!ctrl->exclusive_lock) {
+ pr_err("BIF bus exclusive lock not already held\n");
+ return;
+ }
+
+ ctrl->exclusive_lock = false;
+ bif_schedule_irq_mode_work(ctrl->bdev);
+ mutex_unlock(&ctrl->bdev->mutex);
+}
+EXPORT_SYMBOL(bif_ctrl_bus_unlock);
+
+/**
+ * bif_ctrl_measure_rid() - measure the battery pack Rid pull-down resistance
+ * in ohms
+ * @ctrl: BIF controller consumer handle
+ *
+ * Returns the resistance of the Rid resistor in ohms if successful or errno
+ * if an error occurred.
+ */
+int bif_ctrl_measure_rid(struct bif_ctrl *ctrl)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl)) {
+ pr_err("Invalid controller handle.\n");
+ return -ENODEV;
+ }
+
+ if (!ctrl->bdev->desc->ops->get_battery_rid) {
+ pr_err("Cannot measure Rid.\n");
+ return -ENXIO;
+ }
+
+ bif_ctrl_lock(ctrl);
+
+ rc = ctrl->bdev->desc->ops->get_battery_rid(ctrl->bdev);
+ if (rc < 0)
+ pr_err("Error during Rid measurement, rc=%d\n", rc);
+
+ bif_ctrl_unlock(ctrl);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_measure_rid);
+
+/**
+ * bif_ctrl_get_bus_period() - get the BIF bus period (tau_bif) in nanoseconds
+ * @ctrl: BIF controller consumer handle
+ *
+ * Returns the currently configured bus period in nanoseconds if successful or
+ * errno if an error occurred.
+ */
+int bif_ctrl_get_bus_period(struct bif_ctrl *ctrl)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl)) {
+ pr_err("Invalid controller handle.\n");
+ return -ENODEV;
+ }
+
+ if (!ctrl->bdev->desc->ops->get_bus_period) {
+ pr_err("Cannot get the BIF bus period.\n");
+ return -ENXIO;
+ }
+
+ rc = ctrl->bdev->desc->ops->get_bus_period(ctrl->bdev);
+ if (rc < 0)
+ pr_err("Error during bus period retrieval, rc=%d\n", rc);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_get_bus_period);
+
+/**
+ * bif_ctrl_set_bus_period() - set the BIF bus period (tau_bif) in nanoseconds
+ * @ctrl: BIF controller consumer handle
+ * @period_ns: BIF bus period in nanoseconds to use
+ *
+ * If the exact period is not supported by the BIF controller hardware, then the
+ * next larger supported period will be used.
+ *
+ * Returns 0 on success or errno if an error occurred.
+ */
+int bif_ctrl_set_bus_period(struct bif_ctrl *ctrl, int period_ns)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl)) {
+ pr_err("Invalid controller handle.\n");
+ return -ENODEV;
+ }
+
+ if (!ctrl->bdev->desc->ops->set_bus_period) {
+ pr_err("Cannot set the BIF bus period.\n");
+ return -ENXIO;
+ }
+
+ bif_ctrl_lock(ctrl);
+ rc = ctrl->bdev->desc->ops->set_bus_period(ctrl->bdev, period_ns);
+ if (rc)
+ pr_err("Error during bus period configuration, rc=%d\n", rc);
+ bif_ctrl_unlock(ctrl);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_set_bus_period);
+
+/**
+ * bif_ctrl_get_bus_state() - get the current state of the BIF bus
+ * @ctrl: BIF controller consumer handle
+ *
+ * Returns a bus state from enum bif_bus_state if successful or errno if an
+ * error occurred.
+ */
+int bif_ctrl_get_bus_state(struct bif_ctrl *ctrl)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl)) {
+ pr_err("Invalid controller handle.\n");
+ return -ENODEV;
+ }
+
+ rc = ctrl->bdev->desc->ops->get_bus_state(ctrl->bdev);
+ if (rc < 0)
+ pr_err("Error during bus state retrieval, rc=%d\n", rc);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_get_bus_state);
+
+/**
+ * bif_ctrl_set_bus_state() - set the state of the BIF bus
+ * @ctrl: BIF controller consumer handle
+ * @state: State for the BIF bus to enter
+ *
+ * Returns 0 on success or errno if an error occurred.
+ */
+int bif_ctrl_set_bus_state(struct bif_ctrl *ctrl, enum bif_bus_state state)
+{
+ int rc;
+
+ if (IS_ERR_OR_NULL(ctrl)) {
+ pr_err("Invalid controller handle.\n");
+ return -ENODEV;
+ }
+
+ bif_ctrl_lock(ctrl);
+
+ rc = ctrl->bdev->desc->ops->set_bus_state(ctrl->bdev, state);
+ if (rc < 0)
+ pr_err("Error during bus state configuration, rc=%d\n", rc);
+
+ /*
+ * Uncache the selected slave if the new bus state results in the slave
+ * becoming unselected.
+ */
+ if (state == BIF_BUS_STATE_MASTER_DISABLED
+ || state == BIF_BUS_STATE_POWER_DOWN
+ || state == BIF_BUS_STATE_STANDBY)
+ ctrl->bdev->selected_sdev = NULL;
+
+ bif_ctrl_unlock(ctrl);
+
+ return rc;
+}
+EXPORT_SYMBOL(bif_ctrl_set_bus_state);
+
+/*
+ * Check if the specified function is a protocol function and if it is, then
+ * instantiate protocol function data for the slave.
+ */
+static int bif_initialize_protocol_function(struct bif_slave_dev *sdev,
+ struct bif_ddb_l2_data *func)
+{
+ int rc = 0;
+ u8 buf[4];
+
+ /* Ensure that this is a protocol function. */
+ if (func->function_type != BIF_FUNC_PROTOCOL)
+ return 0;
+
+ if (sdev->protocol_function) {
+ pr_err("Duplicate protocol function found for BIF slave; DEV_ADR=0x%02X\n",
+ sdev->slave_addr);
+ return -EPERM;
+ }
+
+ sdev->protocol_function = kzalloc(sizeof(struct bif_protocol_function),
+ GFP_KERNEL);
+ if (!sdev->protocol_function) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+
+ rc = _bif_slave_read(sdev, func->function_pointer, buf, 4);
+ if (rc) {
+ pr_err("Protocol function data read failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ sdev->protocol_function->protocol_pointer = buf[0] << 8 | buf[1];
+ sdev->protocol_function->device_id_pointer = buf[2] << 8 | buf[3];
+ sdev->protocol_function->l2_entry = func;
+
+ rc = _bif_slave_read(sdev, sdev->protocol_function->device_id_pointer,
+ sdev->protocol_function->device_id, BIF_DEVICE_ID_BYTE_LENGTH);
+ if (rc) {
+ pr_err("Device ID read failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Check if this slave does not have a UID value stored. */
+ if (sdev->unique_id_bits_known == 0) {
+ sdev->unique_id_bits_known = BIF_UNIQUE_ID_BIT_LENGTH;
+ /* Fill in UID using manufacturer ID and device ID. */
+ sdev->unique_id[0] = sdev->l1_data.manufacturer_id >> 8;
+ sdev->unique_id[1] = sdev->l1_data.manufacturer_id;
+ memcpy(&sdev->unique_id[2],
+ sdev->protocol_function->device_id,
+ BIF_DEVICE_ID_BYTE_LENGTH);
+ }
+
+ return rc;
+}
+
+/*
+ * Check if the specified function is a slave control function and if it is,
+ * then instantiate slave control function data for the slave.
+ */
+static int bif_initialize_slave_control_function(struct bif_slave_dev *sdev,
+ struct bif_ddb_l2_data *func)
+{
+ int rc = 0;
+ int i;
+ u8 buf[3];
+
+ /* Ensure that this is a slave control function. */
+ if (func->function_type != BIF_FUNC_SLAVE_CONTROL)
+ return 0;
+
+ if (sdev->slave_ctrl_function) {
+ pr_err("Duplicate slave control function found for BIF slave; DEV_ADR=0x%02X\n",
+ sdev->slave_addr);
+ return -EPERM;
+ }
+
+ sdev->slave_ctrl_function
+ = kzalloc(sizeof(struct bif_protocol_function), GFP_KERNEL);
+ if (!sdev->slave_ctrl_function) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+
+ rc = _bif_slave_read(sdev, func->function_pointer, buf, 3);
+ if (rc) {
+ pr_err("Slave control function data read failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ sdev->slave_ctrl_function->slave_ctrl_pointer = buf[0] << 8 | buf[1];
+ sdev->slave_ctrl_function->task_count
+ = buf[2] * SLAVE_CTRL_TASKS_PER_SET;
+ sdev->slave_ctrl_function->l2_entry = func;
+
+ if (sdev->slave_ctrl_function->task_count > 0) {
+ sdev->slave_ctrl_function->irq_notifier_list =
+ kzalloc(sizeof(struct blocking_notifier_head)
+ * sdev->slave_ctrl_function->task_count,
+ GFP_KERNEL);
+ if (!sdev->slave_ctrl_function->irq_notifier_list) {
+ pr_err("out of memory\n");
+ kfree(sdev->slave_ctrl_function);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < sdev->slave_ctrl_function->task_count; i++) {
+ BLOCKING_INIT_NOTIFIER_HEAD(
+ &sdev->slave_ctrl_function->irq_notifier_list[i]);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * bif_crc_ccitt() - calculate the CRC-CCITT CRC value of the data specified
+ * @buffer: Data to calculate the CRC of
+ * @len: Length of the data buffer in bytes
+ *
+ * MIPI-BIF specifies the usage of CRC-CCITT for BIF data objects. This
+ * function performs the CRC calculation while taking into account the bit
+ * ordering used by BIF.
+ */
+u16 bif_crc_ccitt(const u8 *buffer, unsigned int len)
+{
+ u16 crc = 0xFFFF;
+
+ while (len--) {
+ crc = crc_ccitt_byte(crc, bitrev8(*buffer));
+ buffer++;
+ }
+ return bitrev16(crc);
+}
+EXPORT_SYMBOL(bif_crc_ccitt);
+
+static u16 bif_object_crc_ccitt(const struct bif_object *object)
+{
+ u16 crc = 0xFFFF;
+ int i;
+
+ crc = crc_ccitt_byte(crc, bitrev8(object->type));
+ crc = crc_ccitt_byte(crc, bitrev8(object->version));
+ crc = crc_ccitt_byte(crc, bitrev8(object->manufacturer_id >> 8));
+ crc = crc_ccitt_byte(crc, bitrev8(object->manufacturer_id));
+ crc = crc_ccitt_byte(crc, bitrev8(object->length >> 8));
+ crc = crc_ccitt_byte(crc, bitrev8(object->length));
+
+ for (i = 0; i < object->length - 8; i++)
+ crc = crc_ccitt_byte(crc, bitrev8(object->data[i]));
+
+ return bitrev16(crc);
+}
+
+/*
+ * Check if the specified function is an NVM function and if it is, then
+ * instantiate NVM function data for the slave and read all objects.
+ */
+static int bif_initialize_nvm_function(struct bif_slave_dev *sdev,
+ struct bif_ddb_l2_data *func)
+{
+ int rc = 0;
+ int data_len;
+ u8 buf[8], object_type;
+ struct bif_object *object;
+ struct bif_object *temp;
+ u16 addr;
+ u16 crc;
+
+ /* Ensure that this is an NVM function. */
+ if (func->function_type != BIF_FUNC_NVM)
+ return 0;
+
+ if (sdev->nvm_function) {
+ pr_err("Duplicate NVM function found for BIF slave; DEV_ADR=0x%02X\n",
+ sdev->slave_addr);
+ return -EPERM;
+ }
+
+ sdev->nvm_function
+ = kzalloc(sizeof(*sdev->nvm_function), GFP_KERNEL);
+ if (!sdev->nvm_function) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+
+ rc = _bif_slave_read(sdev, func->function_pointer, buf, 8);
+ if (rc) {
+ pr_err("NVM function data read failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ sdev->nvm_function->nvm_pointer = buf[0] << 8 | buf[1];
+ sdev->nvm_function->slave_control_channel = buf[2];
+ sdev->nvm_function->write_buffer_size = buf[3];
+ sdev->nvm_function->nvm_base_address = buf[4] << 8 | buf[5];
+ sdev->nvm_function->nvm_size = buf[6] << 8 | buf[7];
+
+ INIT_LIST_HEAD(&sdev->nvm_function->object_list);
+
+ /* Read object list */
+ addr = sdev->nvm_function->nvm_base_address;
+ rc = _bif_slave_read(sdev, addr, &object_type, 1);
+ if (rc) {
+ pr_err("Slave memory read failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Object type == 0x00 corresponds to the end of the object list. */
+ while (object_type != 0x00) {
+ object = kzalloc(sizeof(*object), GFP_KERNEL);
+ if (!object) {
+ pr_err("out of memory\n");
+ rc = -ENOMEM;
+ goto free_data;
+ }
+ list_add_tail(&object->list, &sdev->nvm_function->object_list);
+
+ rc = _bif_slave_read(sdev, addr + 1, buf + 1, 5);
+ if (rc) {
+ pr_err("Slave memory read of object header failed; addr=0x%04X, len=%d, rc=%d\n",
+ addr + 1, 5, rc);
+ goto free_data;
+ }
+
+ object->addr = addr;
+ object->type = object_type;
+ object->version = buf[1];
+ object->manufacturer_id = buf[2] << 8 | buf[3];
+ object->length = buf[4] << 8 | buf[5];
+
+ if ((object->addr + object->length)
+ > (sdev->nvm_function->nvm_base_address
+ + sdev->nvm_function->nvm_size)) {
+ pr_warn("warning: BIF slave object is not formatted correctly; NVM base=0x%04X, NVM len=%d, object addr=0x%04X, object len=%d\n",
+ sdev->nvm_function->nvm_base_address,
+ sdev->nvm_function->nvm_size,
+ object->addr,
+ object->length);
+ /* Limit object size to remaining NVM size. */
+ object->length = sdev->nvm_function->nvm_size
+ + sdev->nvm_function->nvm_base_address
+ - object->addr;
+ }
+
+ /* Object header + CRC takes up 8 bytes. */
+ data_len = object->length - 8;
+ object->data = kmalloc(data_len, GFP_KERNEL);
+ if (!object->data) {
+ pr_err("out of memory\n");
+ rc = -ENOMEM;
+ goto free_data;
+ }
+
+ rc = _bif_slave_read(sdev, addr + 6, object->data, data_len);
+ if (rc) {
+ pr_err("Slave memory read of object data failed; addr=0x%04X, len=%d, rc=%d\n",
+ addr + 6, data_len, rc);
+ goto free_data;
+ }
+
+ rc = _bif_slave_read(sdev, addr + 6 + data_len, buf, 3);
+ if (rc) {
+ pr_err("Slave memory read of object CRC failed; addr=0x%04X, len=%d, rc=%d\n",
+ addr + 6 + data_len, 3, rc);
+ goto free_data;
+ }
+
+ object->crc = buf[0] << 8 | buf[1];
+ object_type = buf[2];
+ sdev->nvm_function->object_count++;
+
+ crc = bif_object_crc_ccitt(object);
+ if (crc != object->crc)
+ pr_info("BIF object at addr=0x%04X has invalid CRC; crc calc=0x%04X, crc exp=0x%04X\n",
+ object->addr, crc, object->crc);
+
+ addr += object->length;
+ }
+
+ return rc;
+
+free_data:
+ list_for_each_entry_safe(object, temp,
+ &sdev->nvm_function->object_list, list) {
+ list_del(&object->list);
+ kfree(object->data);
+ kfree(object);
+ }
+ kfree(sdev->nvm_function);
+ sdev->nvm_function = NULL;
+ return rc;
+}
+
+static int bif_parse_slave_data(struct bif_slave_dev *sdev)
+{
+ int rc = 0;
+ u8 buf[10];
+ u8 *func_buf;
+ struct bif_ddb_l2_data *func;
+ int function_count, i;
+
+ rc = _bif_slave_read(sdev, BIF_DDB_L1_BASE_ADDR, buf, 10);
+ if (rc) {
+ pr_err("DDB L1 data read failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ sdev->l1_data.revision = buf[0];
+ sdev->l1_data.level = buf[1];
+ sdev->l1_data.device_class = buf[2] << 8 | buf[3];
+ sdev->l1_data.manufacturer_id = buf[4] << 8 | buf[5];
+ sdev->l1_data.product_id = buf[6] << 8 | buf[7];
+ sdev->l1_data.length = buf[8] << 8 | buf[9];
+
+ function_count = sdev->l1_data.length / 4;
+ if (sdev->l1_data.length % 4) {
+ pr_err("Function directory length=%d is invalid\n",
+ sdev->l1_data.length);
+ return -EPROTO;
+ }
+
+ /* No DDB L2 function directory */
+ if (function_count == 0)
+ return 0;
+
+ func_buf = kmalloc(sdev->l1_data.length, GFP_KERNEL);
+ if (!func_buf) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+
+ sdev->function_directory = kzalloc(
+ function_count * sizeof(struct bif_ddb_l2_data), GFP_KERNEL);
+ if (!sdev->function_directory) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+
+ rc = _bif_slave_read(sdev, BIF_DDB_L2_BASE_ADDR, func_buf,
+ sdev->l1_data.length);
+ if (rc) {
+ pr_err("DDB L2 data read failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < function_count; i++) {
+ func = &sdev->function_directory[i];
+ func->function_type = func_buf[i * 4];
+ func->function_version = func_buf[i * 4 + 1];
+ func->function_pointer = func_buf[i * 4 + 2] << 8
+ | func_buf[i * 4 + 3];
+ rc = bif_initialize_protocol_function(sdev, func);
+ if (rc)
+ goto done;
+ rc = bif_initialize_slave_control_function(sdev, func);
+ if (rc)
+ goto done;
+ rc = bif_initialize_nvm_function(sdev, func);
+ if (rc)
+ goto done;
+ }
+done:
+ kfree(func_buf);
+ return rc;
+}
+
+static int bif_add_secondary_slaves(struct bif_slave_dev *primary_slave)
+{
+ int rc = 0;
+ int data_len, i;
+ u16 crc;
+ struct bif_slave_dev *sdev;
+ struct bif_object *object;
+
+ list_for_each_entry(object, &primary_slave->nvm_function->object_list,
+ list) {
+ if (object->type != BIF_OBJ_SEC_SLAVE)
+ continue;
+
+ data_len = object->length - 8;
+ if (data_len % BIF_UNIQUE_ID_BYTE_LENGTH) {
+ pr_info("Invalid secondary slave object found, addr=0x%04X, data len=%d\n",
+ object->addr, data_len);
+ continue;
+ }
+
+ crc = bif_object_crc_ccitt(object);
+ if (crc != object->crc) {
+ pr_info("BIF object at addr=0x%04X has invalid CRC; crc calc=0x%04X, crc exp=0x%04X\n",
+ object->addr, crc, object->crc);
+ continue;
+ }
+
+ for (i = 0; i < data_len / BIF_UNIQUE_ID_BYTE_LENGTH; i++) {
+ sdev = bif_add_slave(primary_slave->bdev);
+ if (IS_ERR(sdev)) {
+ rc = PTR_ERR(sdev);
+ pr_err("bif_add_slave failed, rc=%d\n", rc);
+ return rc;
+ }
+ memcpy(sdev->unique_id,
+ &object->data[i * BIF_UNIQUE_ID_BYTE_LENGTH],
+ BIF_UNIQUE_ID_BYTE_LENGTH);
+ sdev->unique_id_bits_known = BIF_UNIQUE_ID_BIT_LENGTH;
+
+ rc = bif_select_slave(sdev);
+ if (rc) {
+ pr_err("Could not select slave, rc=%d\n", rc);
+ goto free_slave;
+ }
+
+ rc = bif_is_slave_selected(sdev->bdev);
+ if (rc < 0) {
+ pr_err("Transaction failed, rc=%d\n", rc);
+ goto free_slave;
+ } else if (rc == 1) {
+ sdev->present = true;
+ sdev->bdev->selected_sdev = sdev;
+ } else {
+ sdev->present = false;
+ sdev->bdev->selected_sdev = NULL;
+ }
+ }
+ }
+
+ return rc;
+
+free_slave:
+ bif_remove_slave(sdev);
+ return rc;
+}
+
+/*
+ * Performs UID search to identify all slaves attached to the bus. Assumes that
+ * all necessary locks are held.
+ */
+static int bif_perform_uid_search(struct bif_ctrl_dev *bdev)
+{
+ struct bif_slave_dev *sdev;
+ struct bif_slave_dev *new_slave;
+ bool resp[2], resp_dilc;
+ int i;
+ int rc = 0;
+ u8 cmd_probe[2] = {BIF_CMD_DIP0, BIF_CMD_DIP1};
+ u8 cmd_enter[2] = {BIF_CMD_DIE0, BIF_CMD_DIE1};
+
+ /*
+ * Iterate over all partially known UIDs adding new ones as they are
+ * found.
+ */
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ /* Skip slaves with fully known UIDs. */
+ if (sdev->unique_id_bits_known == BIF_UNIQUE_ID_BIT_LENGTH
+ || sdev->bdev != bdev)
+ continue;
+
+ /* Begin a new UID search. */
+ rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_BC,
+ BIF_CMD_DISS);
+ if (rc) {
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Step through all known UID bits (MSB to LSB). */
+ for (i = 0; i < sdev->unique_id_bits_known; i++) {
+ rc = bdev->desc->ops->bus_transaction(bdev,
+ BIF_TRANS_BC,
+ cmd_enter[get_uid_bit(sdev->unique_id, i)]);
+ if (rc) {
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* Step through unknown UID bits. */
+ for (i = sdev->unique_id_bits_known;
+ i < BIF_UNIQUE_ID_BIT_LENGTH; i++) {
+ rc = bdev->desc->ops->bus_transaction_query(bdev,
+ BIF_TRANS_BC, cmd_probe[0], &resp[0]);
+ if (rc) {
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = bdev->desc->ops->bus_transaction_query(bdev,
+ BIF_TRANS_BC, cmd_probe[1], &resp[1]);
+ if (rc) {
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (resp[0] && resp[1]) {
+ /* Create an entry for the new UID branch. */
+ new_slave = bif_add_slave(bdev);
+ if (IS_ERR(new_slave)) {
+ rc = PTR_ERR(sdev);
+ pr_err("bif_add_slave failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ memcpy(new_slave->unique_id, sdev->unique_id,
+ BIF_UNIQUE_ID_BYTE_LENGTH);
+ new_slave->bdev = sdev->bdev;
+
+ set_uid_bit(sdev->unique_id, i, 0);
+ sdev->unique_id_bits_known = i + 1;
+
+ set_uid_bit(new_slave->unique_id, i, 1);
+ new_slave->unique_id_bits_known = i + 1;
+ } else if (resp[0]) {
+ set_uid_bit(sdev->unique_id, i, 0);
+ sdev->unique_id_bits_known = i + 1;
+ } else if (resp[1]) {
+ set_uid_bit(sdev->unique_id, i, 1);
+ sdev->unique_id_bits_known = i + 1;
+ } else {
+ pr_debug("no bus query response received\n");
+ rc = -ENXIO;
+ return rc;
+ }
+
+ rc = bdev->desc->ops->bus_transaction(bdev,
+ BIF_TRANS_BC, cmd_enter[resp[0] ? 0 : 1]);
+ if (rc) {
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = bdev->desc->ops->bus_transaction_query(bdev,
+ BIF_TRANS_BC, BIF_CMD_DILC, &resp_dilc);
+ if (rc) {
+ pr_err("bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (resp_dilc) {
+ sdev->present = true;
+ sdev->bdev->selected_sdev = sdev;
+ rc = bif_parse_slave_data(sdev);
+ } else {
+ pr_err("Slave failed to respond to DILC bus command; its UID is thus unverified.\n");
+ sdev->unique_id_bits_known = 0;
+ rc = -ENXIO;
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Removes slaves from the bif_sdev_list which have the same UID as previous
+ * slaves in the list.
+ */
+static int bif_remove_duplicate_slaves(struct bif_ctrl_dev *bdev)
+{
+ struct bif_slave_dev *sdev;
+ struct bif_slave_dev *last_slave;
+ struct bif_slave_dev *temp;
+
+ list_for_each_entry_safe(last_slave, temp, &bif_sdev_list, list) {
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ if (last_slave == sdev) {
+ break;
+ } else if (memcmp(last_slave->unique_id,
+ sdev->unique_id,
+ BIF_UNIQUE_ID_BYTE_LENGTH) == 0) {
+ bif_remove_slave(last_slave);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int bif_add_all_slaves(struct bif_ctrl_dev *bdev)
+{
+ struct bif_slave_dev *sdev;
+ int rc = 0;
+ int i;
+ bool has_slave = false, is_primary_slave = false;
+
+ mutex_lock(&bif_sdev_list_mutex);
+ mutex_lock(&bdev->mutex);
+
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ if (sdev->bdev == bdev) {
+ has_slave = true;
+ break;
+ }
+ }
+
+ if (!has_slave) {
+ /* Create a single empty slave to start the search algorithm. */
+ sdev = bif_add_slave(bdev);
+ if (IS_ERR(sdev)) {
+ rc = PTR_ERR(sdev);
+ pr_err("bif_add_slave failed, rc=%d\n", rc);
+ goto out;
+ }
+
+ for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) {
+ /* Attempt to select primary slave in battery pack. */
+ rc = bdev->desc->ops->bus_transaction(bdev,
+ BIF_TRANS_SDA, BIF_PRIMARY_SLAVE_DEV_ADR);
+ if (rc == 0)
+ break;
+ }
+ if (rc) {
+ pr_err("BIF bus_transaction failed, rc=%d\n", rc);
+ goto out;
+ }
+
+ /* Check if a slave is selected. */
+ rc = bif_is_slave_selected(bdev);
+ if (rc < 0) {
+ pr_err("BIF bus_transaction failed, rc=%d\n", rc);
+ goto out;
+ } else {
+ is_primary_slave = rc;
+ }
+ }
+
+ if (is_primary_slave) {
+ pr_debug("Using primary slave at DEV_ADR==0x%02X\n",
+ BIF_PRIMARY_SLAVE_DEV_ADR);
+ sdev->bdev->selected_sdev = sdev;
+ sdev->present = true;
+ sdev->slave_addr = BIF_PRIMARY_SLAVE_DEV_ADR;
+ rc = bif_parse_slave_data(sdev);
+ if (rc) {
+ pr_err("Failed to parse primary slave data, rc=%d\n",
+ rc);
+ goto out;
+ }
+ rc = bif_add_secondary_slaves(sdev);
+ if (rc) {
+ pr_err("Failed to add secondary slaves, rc=%d\n", rc);
+ goto out;
+ }
+ } else {
+ pr_debug("Falling back on full UID search.\n");
+ for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) {
+ rc = bif_perform_uid_search(bdev);
+ if (rc == 0)
+ break;
+ }
+ if (rc) {
+ pr_debug("BIF UID search failed, rc=%d\n", rc);
+ goto out;
+ }
+ }
+
+ bif_remove_duplicate_slaves(bdev);
+
+ mutex_unlock(&bdev->mutex);
+ mutex_unlock(&bif_sdev_list_mutex);
+
+ return rc;
+
+out:
+ mutex_unlock(&bdev->mutex);
+ mutex_unlock(&bif_sdev_list_mutex);
+ pr_debug("BIF slave search failed, rc=%d\n", rc);
+ return rc;
+}
+
+static int bif_add_known_slave(struct bif_ctrl_dev *bdev, u8 slave_addr)
+{
+ struct bif_slave_dev *sdev;
+ int rc = 0;
+ int i;
+
+ for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) {
+ /* Attempt to select the slave. */
+ rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_SDA,
+ slave_addr);
+ if (rc == 0)
+ break;
+ }
+ if (rc) {
+ pr_err("BIF bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Check if a slave is selected. */
+ rc = bif_is_slave_selected(bdev);
+ if (rc < 0) {
+ pr_err("BIF bus_transaction failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ sdev = bif_add_slave(bdev);
+ if (IS_ERR(sdev)) {
+ rc = PTR_ERR(sdev);
+ pr_err("bif_add_slave failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ sdev->bdev->selected_sdev = sdev;
+ sdev->present = true;
+ sdev->slave_addr = slave_addr;
+ rc = bif_parse_slave_data(sdev);
+ if (rc) {
+ pr_err("Failed to parse slave data, addr=0x%02X, rc=%d\n",
+ slave_addr, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int bif_add_known_slaves_from_dt(struct bif_ctrl_dev *bdev,
+ struct device_node *of_node)
+{
+ int len = 0;
+ int rc, i;
+ u32 addr;
+ const __be32 *val;
+
+ mutex_lock(&bif_sdev_list_mutex);
+ mutex_lock(&bdev->mutex);
+
+ val = of_get_property(of_node, "qcom,known-device-addresses", &len);
+ len /= sizeof(u32);
+ if (val && len == 0) {
+ pr_err("qcom,known-device-addresses property is invalid\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < len; i++) {
+ addr = be32_to_cpup(val++);
+ if (addr == 0x00 || addr > 0xFF) {
+ rc = -EINVAL;
+ pr_err("qcom,known-device-addresses property contains invalid address=0x%X\n",
+ addr);
+ goto out;
+ }
+ rc = bif_add_known_slave(bdev, addr);
+ if (rc) {
+ pr_err("bif_add_known_slave() failed, rc=%d\n", rc);
+ goto out;
+ }
+ }
+
+out:
+ if (len > 0)
+ bif_remove_duplicate_slaves(bdev);
+
+ mutex_unlock(&bdev->mutex);
+ mutex_unlock(&bif_sdev_list_mutex);
+
+ return rc;
+}
+
+/*
+ * Programs a device address for the specified slave in order to simplify
+ * slave selection in the future.
+ */
+static int bif_assign_slave_dev_addr(struct bif_slave_dev *sdev, u8 dev_addr)
+{
+ int rc;
+ u16 addr;
+
+ if (!sdev->protocol_function) {
+ pr_err("Protocol function not present; cannot set device address.\n");
+ return -ENODEV;
+ }
+
+ addr = PROTOCOL_FUNC_DEV_ADR_ADDR(
+ sdev->protocol_function->protocol_pointer);
+
+ rc = _bif_slave_write(sdev, addr, &dev_addr, 1);
+ if (rc)
+ pr_err("Failed to set slave device address.\n");
+ else
+ sdev->slave_addr = dev_addr;
+
+ return rc;
+}
+
+/* Assigns a unique device address to all slaves which do not have one. */
+static int bif_assign_all_slaves_dev_addr(struct bif_ctrl_dev *bdev)
+{
+ struct bif_slave_dev *sdev;
+ struct bif_slave_dev *sibling;
+ bool duplicate;
+ int rc = 0;
+ u8 dev_addr, first_dev_addr;
+
+ mutex_lock(&bif_sdev_list_mutex);
+ mutex_lock(&bdev->mutex);
+
+ first_dev_addr = next_dev_addr;
+ /*
+ * Iterate over all partially known UIDs adding new ones as they are
+ * found.
+ */
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ /*
+ * Skip slaves without known UIDs, which already have a device
+ * address or which aren't present.
+ */
+ if (sdev->unique_id_bits_known != BIF_UNIQUE_ID_BIT_LENGTH
+ || sdev->slave_addr != 0x00 || !sdev->present)
+ continue;
+
+ do {
+ dev_addr = next_dev_addr;
+ duplicate = false;
+ list_for_each_entry(sibling, &bif_sdev_list, list) {
+ if (sibling->slave_addr == dev_addr) {
+ duplicate = true;
+ break;
+ }
+ }
+
+ next_dev_addr = dev_addr + 1;
+ } while (duplicate && (next_dev_addr != first_dev_addr));
+
+ if (next_dev_addr == first_dev_addr) {
+ pr_err("No more BIF slave device addresses available.\n");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ rc = bif_assign_slave_dev_addr(sdev, dev_addr);
+ if (rc) {
+ pr_err("Failed to set slave address.\n");
+ goto out;
+ }
+ }
+
+ mutex_unlock(&bdev->mutex);
+ mutex_unlock(&bif_sdev_list_mutex);
+
+ return rc;
+
+out:
+ mutex_unlock(&bdev->mutex);
+ mutex_unlock(&bif_sdev_list_mutex);
+ pr_err("BIF slave device address setting failed, rc=%d\n", rc);
+ return rc;
+}
+
+/**
+ * bdev_get_drvdata() - get the private BIF controller driver data
+ * @bdev: BIF controller device pointer
+ */
+void *bdev_get_drvdata(struct bif_ctrl_dev *bdev)
+{
+ return bdev->driver_data;
+}
+EXPORT_SYMBOL(bdev_get_drvdata);
+
+static const char * const battery_label[] = {
+ "unknown",
+ "none",
+ "special 1",
+ "special 2",
+ "special 3",
+ "low cost",
+ "smart",
+};
+
+static const char *bif_get_battery_pack_type(int rid_ohm)
+{
+ const char *label = battery_label[0];
+
+ if (rid_ohm > BIF_BATT_RID_SMART_MAX)
+ label = battery_label[1];
+ else if (rid_ohm >= BIF_BATT_RID_SMART_MIN)
+ label = battery_label[6];
+ else if (rid_ohm >= BIF_BATT_RID_LOW_COST_MIN
+ && rid_ohm <= BIF_BATT_RID_LOW_COST_MAX)
+ label = battery_label[5];
+ else if (rid_ohm >= BIF_BATT_RID_SPECIAL3_MIN
+ && rid_ohm <= BIF_BATT_RID_SPECIAL3_MAX)
+ label = battery_label[4];
+ else if (rid_ohm >= BIF_BATT_RID_SPECIAL2_MIN
+ && rid_ohm <= BIF_BATT_RID_SPECIAL2_MAX)
+ label = battery_label[3];
+ else if (rid_ohm >= BIF_BATT_RID_SPECIAL1_MIN
+ && rid_ohm <= BIF_BATT_RID_SPECIAL1_MAX)
+ label = battery_label[2];
+
+ return label;
+}
+
+/**
+ * bif_ctrl_register() - register a BIF controller with the BIF framework
+ * @bif_desc: Pointer to BIF controller descriptor
+ * @dev: Device pointer of the BIF controller
+ * @driver_data: Private driver data to associate with the BIF controller
+ * @of_node Pointer to the device tree node of the BIF controller
+ *
+ * Returns a BIF controller device pointer for the controller if registration
+ * is successful or an ERR_PTR if an error occurred.
+ */
+struct bif_ctrl_dev *bif_ctrl_register(struct bif_ctrl_desc *bif_desc,
+ struct device *dev, void *driver_data, struct device_node *of_node)
+{
+ struct bif_ctrl_dev *bdev = ERR_PTR(-EINVAL);
+ struct bif_slave_dev *sdev;
+ bool battery_present = false;
+ int rc, rid_ohm;
+
+ if (!bif_desc) {
+ pr_err("Invalid bif_desc specified\n");
+ return bdev;
+ } else if (!bif_desc->name) {
+ pr_err("BIF name missing\n");
+ return bdev;
+ } else if (!bif_desc->ops) {
+ pr_err("BIF operations missing\n");
+ return bdev;
+ } else if (!bif_desc->ops->bus_transaction
+ || !bif_desc->ops->bus_transaction_query
+ || !bif_desc->ops->bus_transaction_read
+ || !bif_desc->ops->get_bus_state
+ || !bif_desc->ops->set_bus_state) {
+ pr_err("BIF operation callback function(s) missing\n");
+ return bdev;
+ }
+
+ bdev = kzalloc(sizeof(struct bif_ctrl_dev), GFP_KERNEL);
+ if (bdev == NULL) {
+ pr_err("Memory allocation failed for bif_ctrl_dev\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mutex_init(&bdev->mutex);
+ INIT_LIST_HEAD(&bdev->list);
+ INIT_DELAYED_WORK(&bdev->enter_irq_mode_work, bif_enter_irq_mode_work);
+ bdev->desc = bif_desc;
+ bdev->ctrl_dev = dev;
+ bdev->driver_data = driver_data;
+ bdev->irq_mode_delay_jiffies = 2;
+
+ mutex_lock(&bif_ctrl_list_mutex);
+ list_add_tail(&bdev->list, &bif_ctrl_list);
+ mutex_unlock(&bif_ctrl_list_mutex);
+
+ rc = bif_add_all_slaves(bdev);
+ if (rc)
+ pr_debug("Search for all slaves failed, rc=%d\n", rc);
+ rc = bif_add_known_slaves_from_dt(bdev, of_node);
+ if (rc)
+ pr_err("Adding slaves based on device tree addressed failed, rc=%d.\n",
+ rc);
+ rc = bif_assign_all_slaves_dev_addr(bdev);
+ if (rc)
+ pr_err("Failed to set slave device address, rc=%d\n", rc);
+
+ bif_print_slaves();
+
+ if (bdev->desc->ops->get_battery_presence) {
+ rc = bdev->desc->ops->get_battery_presence(bdev);
+ if (rc < 0) {
+ pr_err("Could not determine battery presence, rc=%d\n",
+ rc);
+ } else {
+ battery_present = rc;
+ pr_info("Battery pack present = %c\n", rc ? 'Y' : 'N');
+ }
+ }
+
+ if (bdev->desc->ops->get_battery_rid) {
+ rid_ohm = bdev->desc->ops->get_battery_rid(bdev);
+ if (rid_ohm >= 0)
+ pr_info("Battery pack type = %s (Rid=%d ohm)\n",
+ bif_get_battery_pack_type(rid_ohm), rid_ohm);
+ else
+ pr_err("Could not read Rid, rc=%d\n", rid_ohm);
+ }
+
+ list_for_each_entry(sdev, &bif_sdev_list, list) {
+ if (sdev->present) {
+ battery_present = true;
+ break;
+ }
+ }
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&bdev->bus_change_notifier);
+
+ if (battery_present) {
+ bdev->battery_present = true;
+ rc = blocking_notifier_call_chain(&bdev->bus_change_notifier,
+ BIF_BUS_EVENT_BATTERY_INSERTED, bdev);
+ if (rc)
+ pr_err("Call chain noification failed, rc=%d\n", rc);
+ }
+
+ return bdev;
+}
+EXPORT_SYMBOL(bif_ctrl_register);
+
+/**
+ * bif_ctrl_unregister() - unregisters a BIF controller
+ * @bdev: BIF controller device pointer
+ */
+void bif_ctrl_unregister(struct bif_ctrl_dev *bdev)
+{
+ if (bdev) {
+ mutex_lock(&bif_ctrl_list_mutex);
+ list_del(&bdev->list);
+ mutex_unlock(&bif_ctrl_list_mutex);
+ }
+}
+EXPORT_SYMBOL(bif_ctrl_unregister);
diff --git a/drivers/bif/qpnp-bsi.c b/drivers/bif/qpnp-bsi.c
new file mode 100644
index 0000000..5068a21
--- /dev/null
+++ b/drivers/bif/qpnp-bsi.c
@@ -0,0 +1,1765 @@
+/* Copyright (c) 2013, 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/atomic.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spmi.h>
+#include <linux/workqueue.h>
+#include <linux/bif/driver.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+enum qpnp_bsi_irq {
+ QPNP_BSI_IRQ_ERR,
+ QPNP_BSI_IRQ_RX,
+ QPNP_BSI_IRQ_TX,
+ QPNP_BSI_IRQ_COUNT,
+};
+
+enum qpnp_bsi_com_mode {
+ QPNP_BSI_COM_MODE_IRQ,
+ QPNP_BSI_COM_MODE_POLL,
+};
+
+struct qpnp_bsi_chip {
+ struct bif_ctrl_desc bdesc;
+ struct spmi_device *spmi_dev;
+ struct bif_ctrl_dev *bdev;
+ struct work_struct slave_irq_work;
+ u16 base_addr;
+ u16 batt_id_stat_addr;
+ int r_pullup_ohm;
+ int vid_ref_uV;
+ int tau_index;
+ int tau_sampling_mask;
+ enum bif_bus_state state;
+ enum qpnp_bsi_com_mode com_mode;
+ int irq[QPNP_BSI_IRQ_COUNT];
+ atomic_t irq_flag[QPNP_BSI_IRQ_COUNT];
+ int batt_present_irq;
+ enum qpnp_vadc_channels batt_id_adc_channel;
+};
+
+#define QPNP_BSI_DRIVER_NAME "qcom,qpnp-bsi"
+
+enum qpnp_bsi_registers {
+ QPNP_BSI_REG_TYPE = 0x04,
+ QPNP_BSI_REG_SUBTYPE = 0x05,
+ QPNP_BSI_REG_STATUS = 0x08,
+ QPNP_BSI_REG_ENABLE = 0x46,
+ QPNP_BSI_REG_CLEAR_ERROR = 0x4F,
+ QPNP_BSI_REG_FORCE_BCL_LOW = 0x51,
+ QPNP_BSI_REG_TAU_CONFIG = 0x52,
+ QPNP_BSI_REG_MODE = 0x53,
+ QPNP_BSI_REG_RX_TX_ENABLE = 0x54,
+ QPNP_BSI_REG_TX_DATA_LOW = 0x5A,
+ QPNP_BSI_REG_TX_DATA_HIGH = 0x5B,
+ QPNP_BSI_REG_TX_CTRL = 0x5D,
+ QPNP_BSI_REG_RX_DATA_LOW = 0x60,
+ QPNP_BSI_REG_RX_DATA_HIGH = 0x61,
+ QPNP_BSI_REG_RX_SOURCE = 0x62,
+ QPNP_BSI_REG_BSI_ERROR = 0x70,
+};
+
+#define QPNP_BSI_TYPE 0x02
+#define QPNP_BSI_SUBTYPE 0x10
+
+#define QPNP_BSI_STATUS_ERROR 0x10
+#define QPNP_BSI_STATUS_TX_BUSY 0x08
+#define QPNP_BSI_STATUS_RX_BUSY 0x04
+#define QPNP_BSI_STATUS_TX_GO_BUSY 0x02
+#define QPNP_BSI_STATUS_RX_DATA_READY 0x01
+
+#define QPNP_BSI_ENABLE_MASK 0x80
+#define QPNP_BSI_ENABLE 0x80
+#define QPNP_BSI_DISABLE 0x00
+
+#define QPNP_BSI_TAU_CONFIG_SAMPLE_MASK 0x10
+#define QPNP_BSI_TAU_CONFIG_SAMPLE_8X 0x10
+#define QPNP_BSI_TAU_CONFIG_SAMPLE_4X 0x00
+#define QPNP_BSI_TAU_CONFIG_SPEED_MASK 0x07
+
+#define QPNP_BSI_MODE_TX_PULSE_MASK 0x10
+#define QPNP_BSI_MODE_TX_PULSE_INT 0x10
+#define QPNP_BSI_MODE_TX_PULSE_DATA 0x00
+#define QPNP_BSI_MODE_RX_PULSE_MASK 0x08
+#define QPNP_BSI_MODE_RX_PULSE_INT 0x08
+#define QPNP_BSI_MODE_RX_PULSE_DATA 0x00
+#define QPNP_BSI_MODE_TX_PULSE_T_MASK 0x04
+#define QPNP_BSI_MODE_TX_PULSE_T_WAKE 0x04
+#define QPNP_BSI_MODE_TX_PULSE_T_1_TAU 0x00
+#define QPNP_BSI_MODE_RX_FORMAT_MASK 0x02
+#define QPNP_BSI_MODE_RX_FORMAT_17_BIT 0x02
+#define QPNP_BSI_MODE_RX_FORMAT_11_BIT 0x00
+#define QPNP_BSI_MODE_TX_FORMAT_MASK 0x01
+#define QPNP_BSI_MODE_TX_FORMAT_17_BIT 0x01
+#define QPNP_BSI_MODE_TX_FORMAT_11_BIT 0x00
+
+#define QPNP_BSI_TX_ENABLE_MASK 0x80
+#define QPNP_BSI_TX_ENABLE 0x80
+#define QPNP_BSI_TX_DISABLE 0x00
+#define QPNP_BSI_RX_ENABLE_MASK 0x40
+#define QPNP_BSI_RX_ENABLE 0x40
+#define QPNP_BSI_RX_DISABLE 0x00
+
+#define QPNP_BSI_TX_DATA_HIGH_MASK 0x07
+
+#define QPNP_BSI_TX_CTRL_GO 0x01
+
+#define QPNP_BSI_RX_DATA_HIGH_MASK 0x07
+
+#define QPNP_BSI_RX_SRC_LOOPBACK_FLAG 0x10
+
+#define QPNP_BSI_BSI_ERROR_CLEAR 0x80
+
+#define QPNP_SMBB_BAT_IF_BATT_PRES_MASK 0x80
+#define QPNP_SMBB_BAT_IF_BATT_ID_MASK 0x01
+
+#define QPNP_BSI_NUM_CLOCK_PERIODS 8
+
+struct qpnp_bsi_tau {
+ int period_4x_ns[QPNP_BSI_NUM_CLOCK_PERIODS];
+ int period_8x_ns[QPNP_BSI_NUM_CLOCK_PERIODS];
+ int period_4x_us[QPNP_BSI_NUM_CLOCK_PERIODS];
+ int period_8x_us[QPNP_BSI_NUM_CLOCK_PERIODS];
+};
+
+/* Tau BIF clock periods in ns supported by BSI for either 4x or 8x sampling. */
+static const struct qpnp_bsi_tau qpnp_bsi_tau_period = {
+ .period_4x_ns = {
+ 150420, 122080, 61040, 31670, 15830, 7920, 3960, 2080
+ },
+ .period_8x_ns = {
+ 150420, 122080, 63330, 31670, 15830, 7920, 4170, 2080
+ },
+ .period_4x_us = {
+ 151, 122, 61, 32, 16, 8, 4, 2
+ },
+ .period_8x_us = {
+ 151, 122, 64, 32, 16, 8, 4, 2
+ },
+
+};
+#define QPNP_BSI_MIN_CLOCK_SPEED_NS 2080
+#define QPNP_BSI_MAX_CLOCK_SPEED_NS 150420
+
+#define QPNP_BSI_MIN_PULLUP_OHM 1000
+#define QPNP_BSI_MAX_PULLUP_OHM 500000
+#define QPNP_BSI_DEFAULT_PULLUP_OHM 100000
+#define QPNP_BSI_MIN_VID_REF_UV 500000
+#define QPNP_BSI_MAX_VID_REF_UV 5000000
+#define QPNP_BSI_DEFAULT_VID_REF_UV 1800000
+
+/* These have units of tau_bif. */
+#define QPNP_BSI_MAX_TRANSMIT_CYCLES 36
+#define QPNP_BSI_MIN_RECEIVE_CYCLES 24
+#define QPNP_BSI_MAX_BUS_QUERY_CYCLES 17
+
+/*
+ * Maximum time in microseconds for a slave to transition from suspend to active
+ * state.
+ */
+#define QPNP_BSI_MAX_SLAVE_ACTIVIATION_DELAY_US 50
+
+/*
+ * Maximum time in milliseconds for a slave to transition from power down to
+ * active state.
+ */
+#define QPNP_BSI_MAX_SLAVE_POWER_UP_DELAY_MS 10
+
+#define QPNP_BSI_POWER_UP_LOW_DELAY_US 240
+
+/*
+ * Latencies that are used when determining if polling or interrupts should be
+ * used for a given transaction.
+ */
+#define QPNP_BSI_MAX_IRQ_LATENCY_US 170
+#define QPNP_BSI_MAX_BSI_DATA_READ_LATENCY_US 16
+
+static int qpnp_bsi_set_bus_state(struct bif_ctrl_dev *bdev, int state);
+
+static inline int qpnp_bsi_read(struct qpnp_bsi_chip *chip, u16 addr, u8 *buf,
+ int len)
+{
+ int rc;
+
+ rc = spmi_ext_register_readl(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_readl() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+ __func__, chip->spmi_dev->sid, chip->base_addr + addr,
+ len, rc);
+
+ return rc;
+}
+
+static inline int qpnp_bsi_write(struct qpnp_bsi_chip *chip, u16 addr, u8 *buf,
+ int len)
+{
+ int rc;
+
+ rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
+
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_writel() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+ __func__, chip->spmi_dev->sid, chip->base_addr + addr,
+ len, rc);
+
+ return rc;
+}
+
+enum qpnp_bsi_rx_tx_state {
+ QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF,
+ QPNP_BSI_RX_TX_STATE_RX_OFF_TX_DATA,
+ QPNP_BSI_RX_TX_STATE_RX_OFF_TX_INT,
+ QPNP_BSI_RX_TX_STATE_RX_INT_TX_DATA,
+ QPNP_BSI_RX_TX_STATE_RX_DATA_TX_DATA,
+ QPNP_BSI_RX_TX_STATE_RX_INT_TX_OFF,
+};
+
+static int qpnp_bsi_rx_tx_config(struct qpnp_bsi_chip *chip,
+ enum qpnp_bsi_rx_tx_state state)
+{
+ u8 buf[2] = {0, 0};
+ int rc;
+
+ buf[0] = QPNP_BSI_MODE_TX_FORMAT_11_BIT
+ | QPNP_BSI_MODE_RX_FORMAT_11_BIT;
+
+ switch (state) {
+ case QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF:
+ buf[0] |= QPNP_BSI_MODE_TX_PULSE_DATA |
+ QPNP_BSI_MODE_RX_PULSE_DATA;
+ buf[1] = QPNP_BSI_TX_DISABLE | QPNP_BSI_RX_DISABLE;
+ break;
+ case QPNP_BSI_RX_TX_STATE_RX_OFF_TX_DATA:
+ buf[0] |= QPNP_BSI_MODE_TX_PULSE_DATA |
+ QPNP_BSI_MODE_RX_PULSE_DATA;
+ buf[1] = QPNP_BSI_TX_ENABLE | QPNP_BSI_RX_DISABLE;
+ break;
+ case QPNP_BSI_RX_TX_STATE_RX_OFF_TX_INT:
+ buf[0] |= QPNP_BSI_MODE_TX_PULSE_INT |
+ QPNP_BSI_MODE_RX_PULSE_DATA;
+ buf[1] = QPNP_BSI_TX_ENABLE | QPNP_BSI_RX_DISABLE;
+ break;
+ case QPNP_BSI_RX_TX_STATE_RX_INT_TX_DATA:
+ buf[0] |= QPNP_BSI_MODE_TX_PULSE_DATA |
+ QPNP_BSI_MODE_RX_PULSE_INT;
+ buf[1] = QPNP_BSI_TX_ENABLE | QPNP_BSI_RX_ENABLE;
+ break;
+ case QPNP_BSI_RX_TX_STATE_RX_DATA_TX_DATA:
+ buf[0] |= QPNP_BSI_MODE_TX_PULSE_DATA |
+ QPNP_BSI_MODE_RX_PULSE_DATA;
+ buf[1] = QPNP_BSI_TX_ENABLE | QPNP_BSI_RX_ENABLE;
+ break;
+ case QPNP_BSI_RX_TX_STATE_RX_INT_TX_OFF:
+ buf[0] |= QPNP_BSI_MODE_TX_PULSE_DATA |
+ QPNP_BSI_MODE_RX_PULSE_INT;
+ buf[1] = QPNP_BSI_TX_DISABLE | QPNP_BSI_RX_DISABLE;
+ break;
+ default:
+ dev_err(&chip->spmi_dev->dev, "%s: invalid state=%d\n",
+ __func__, state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_bsi_write(chip, QPNP_BSI_REG_MODE, buf, 2);
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static void qpnp_bsi_slave_irq_work(struct work_struct *work)
+{
+ struct qpnp_bsi_chip *chip
+ = container_of(work, struct qpnp_bsi_chip, slave_irq_work);
+ int rc;
+
+ rc = bif_ctrl_notify_slave_irq(chip->bdev);
+ if (rc)
+ pr_err("Could not notify BIF core about slave interrupt, rc=%d\n",
+ rc);
+}
+
+static irqreturn_t qpnp_bsi_isr(int irq, void *data)
+{
+ struct qpnp_bsi_chip *chip = data;
+ bool found = false;
+ int i;
+
+ for (i = 0; i < QPNP_BSI_IRQ_COUNT; i++) {
+ if (irq == chip->irq[i]) {
+ found = true;
+ atomic_cmpxchg(&chip->irq_flag[i], 0, 1);
+
+ /* Check if this is a slave interrupt. */
+ if (i == QPNP_BSI_IRQ_RX
+ && chip->state == BIF_BUS_STATE_INTERRUPT) {
+ /* Slave IRQ makes the bus active. */
+ qpnp_bsi_rx_tx_config(chip,
+ QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF);
+ chip->state = BIF_BUS_STATE_ACTIVE;
+ schedule_work(&chip->slave_irq_work);
+ }
+ }
+ }
+
+ if (!found)
+ pr_err("Unknown interrupt: %d\n", irq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_bsi_batt_present_isr(int irq, void *data)
+{
+ struct qpnp_bsi_chip *chip = data;
+ int rc;
+
+ if (!chip->bdev)
+ return IRQ_HANDLED;
+
+ rc = bif_ctrl_notify_battery_changed(chip->bdev);
+ if (rc)
+ pr_err("Could not notify about battery state change, rc=%d\n",
+ rc);
+
+ return IRQ_HANDLED;
+}
+
+static void qpnp_bsi_set_com_mode(struct qpnp_bsi_chip *chip,
+ enum qpnp_bsi_com_mode mode)
+{
+ int i;
+
+ if (chip->com_mode == mode)
+ return;
+
+ if (mode == QPNP_BSI_COM_MODE_IRQ)
+ for (i = 0; i < QPNP_BSI_IRQ_COUNT; i++)
+ enable_irq(chip->irq[i]);
+ else
+ for (i = 0; i < QPNP_BSI_IRQ_COUNT; i++)
+ disable_irq(chip->irq[i]);
+
+ chip->com_mode = mode;
+}
+
+static inline bool qpnp_bsi_check_irq(struct qpnp_bsi_chip *chip, int irq)
+{
+ return atomic_cmpxchg(&chip->irq_flag[irq], 1, 0);
+}
+
+static void qpnp_bsi_clear_irq_flags(struct qpnp_bsi_chip *chip)
+{
+ int i;
+
+ for (i = 0; i < QPNP_BSI_IRQ_COUNT; i++)
+ atomic_set(&chip->irq_flag[i], 0);
+}
+
+static inline int qpnp_bsi_get_tau_ns(struct qpnp_bsi_chip *chip)
+{
+ if (chip->tau_sampling_mask == QPNP_BSI_TAU_CONFIG_SAMPLE_4X)
+ return qpnp_bsi_tau_period.period_4x_ns[chip->tau_index];
+ else
+ return qpnp_bsi_tau_period.period_8x_ns[chip->tau_index];
+}
+
+static inline int qpnp_bsi_get_tau_us(struct qpnp_bsi_chip *chip)
+{
+ if (chip->tau_sampling_mask == QPNP_BSI_TAU_CONFIG_SAMPLE_4X)
+ return qpnp_bsi_tau_period.period_4x_us[chip->tau_index];
+ else
+ return qpnp_bsi_tau_period.period_8x_us[chip->tau_index];
+}
+
+/* Checks if BSI is in an error state and clears the error if it is. */
+static int qpnp_bsi_clear_bsi_error(struct qpnp_bsi_chip *chip)
+{
+ int rc, delay_us;
+ u8 reg;
+
+ rc = qpnp_bsi_read(chip, QPNP_BSI_REG_BSI_ERROR, ®, 1);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_read() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (reg > 0) {
+ /*
+ * Delay before clearing the BSI error in case a transaction is
+ * still in flight.
+ */
+ delay_us = QPNP_BSI_MAX_TRANSMIT_CYCLES
+ * qpnp_bsi_get_tau_us(chip);
+ udelay(delay_us);
+
+ pr_info("PMIC BSI module in error state, error=%d\n", reg);
+
+ reg = QPNP_BSI_BSI_ERROR_CLEAR;
+ rc = qpnp_bsi_write(chip, QPNP_BSI_REG_CLEAR_ERROR, ®, 1);
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ }
+
+ return rc;
+}
+
+static int qpnp_bsi_get_bsi_error(struct qpnp_bsi_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = qpnp_bsi_read(chip, QPNP_BSI_REG_BSI_ERROR, ®, 1);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_read() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ return reg;
+}
+
+static int qpnp_bsi_wait_for_tx(struct qpnp_bsi_chip *chip, int timeout)
+{
+ int rc = 0;
+
+ /* Wait for TX or ERR IRQ. */
+ while (timeout > 0) {
+ if (qpnp_bsi_check_irq(chip, QPNP_BSI_IRQ_ERR)) {
+ dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
+ __func__, qpnp_bsi_get_bsi_error(chip));
+ return -EIO;
+ }
+
+ if (qpnp_bsi_check_irq(chip, QPNP_BSI_IRQ_TX))
+ break;
+
+ udelay(1);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ rc = -ETIMEDOUT;
+ dev_err(&chip->spmi_dev->dev, "%s: transaction timed out, no interrupts received, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int qpnp_bsi_issue_transaction(struct qpnp_bsi_chip *chip,
+ int transaction, u8 data)
+{
+ int rc;
+ u8 buf[4];
+
+ /* MIPI_BIF_DATA_TX_0 = BIF word bits 7 to 0 */
+ buf[0] = data;
+ /* MIPI_BIF_DATA_TX_1 = BIF word BCF, bits 9 to 8 */
+ buf[1] = transaction & QPNP_BSI_TX_DATA_HIGH_MASK;
+ /* MIPI_BIF_DATA_TX_2 ignored */
+ buf[2] = 0x00;
+ /* MIPI_BIF_TX_CTL bit 0 written to start the transaction. */
+ buf[3] = QPNP_BSI_TX_CTRL_GO;
+
+ /* Write the TX_DATA bytes and initiate the transaction. */
+ rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TX_DATA_LOW, buf, 4);
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+static int qpnp_bsi_issue_transaction_wait_for_tx(struct qpnp_bsi_chip *chip,
+ int transaction, u8 data)
+{
+ int rc, timeout;
+
+ rc = qpnp_bsi_issue_transaction(chip, transaction, data);
+ if (rc)
+ return rc;
+
+ timeout = QPNP_BSI_MAX_TRANSMIT_CYCLES * qpnp_bsi_get_tau_us(chip)
+ + QPNP_BSI_MAX_IRQ_LATENCY_US;
+
+ rc = qpnp_bsi_wait_for_tx(chip, timeout);
+
+ return rc;
+}
+
+static int qpnp_bsi_wait_for_rx(struct qpnp_bsi_chip *chip, int timeout)
+{
+ int rc = 0;
+
+ /* Wait for RX IRQ to indicate that data is ready to read. */
+ while (timeout > 0) {
+ if (qpnp_bsi_check_irq(chip, QPNP_BSI_IRQ_ERR)) {
+ dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
+ __func__, qpnp_bsi_get_bsi_error(chip));
+ return -EIO;
+ }
+
+ if (qpnp_bsi_check_irq(chip, QPNP_BSI_IRQ_RX))
+ break;
+
+ udelay(1);
+ timeout--;
+ }
+
+ if (timeout == 0)
+ rc = -ETIMEDOUT;
+
+ return rc;
+}
+
+static int qpnp_bsi_bus_transaction(struct bif_ctrl_dev *bdev, int transaction,
+ u8 data)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ int rc;
+
+ rc = qpnp_bsi_clear_bsi_error(chip);
+ if (rc)
+ return rc;
+
+ qpnp_bsi_clear_irq_flags(chip);
+
+ qpnp_bsi_set_com_mode(chip, QPNP_BSI_COM_MODE_IRQ);
+
+ rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_DATA);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_issue_transaction_wait_for_tx(chip, transaction, data);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF);
+
+ return rc;
+}
+
+static int qpnp_bsi_bus_transaction_query(struct bif_ctrl_dev *bdev,
+ int transaction, u8 data, bool *query_response)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ int rc, timeout;
+
+ rc = qpnp_bsi_clear_bsi_error(chip);
+ if (rc)
+ return rc;
+
+ qpnp_bsi_clear_irq_flags(chip);
+
+ qpnp_bsi_set_com_mode(chip, QPNP_BSI_COM_MODE_IRQ);
+
+ rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_INT_TX_DATA);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_issue_transaction_wait_for_tx(chip, transaction, data);
+ if (rc)
+ return rc;
+
+ timeout = QPNP_BSI_MAX_BUS_QUERY_CYCLES * qpnp_bsi_get_tau_us(chip)
+ + QPNP_BSI_MAX_IRQ_LATENCY_US;
+
+ rc = qpnp_bsi_wait_for_rx(chip, timeout);
+ if (rc == 0) {
+ *query_response = true;
+ } else if (rc == -ETIMEDOUT) {
+ *query_response = false;
+ rc = 0;
+ }
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF);
+
+ return rc;
+}
+
+static int qpnp_bsi_bus_transaction_read(struct bif_ctrl_dev *bdev,
+ int transaction, u8 data, int *response)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ int rc, timeout;
+ u8 buf[3];
+
+ rc = qpnp_bsi_clear_bsi_error(chip);
+ if (rc)
+ return rc;
+
+ qpnp_bsi_clear_irq_flags(chip);
+
+ qpnp_bsi_set_com_mode(chip, QPNP_BSI_COM_MODE_IRQ);
+
+ rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_DATA_TX_DATA);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_issue_transaction_wait_for_tx(chip, transaction, data);
+ if (rc)
+ return rc;
+
+ timeout = QPNP_BSI_MAX_TRANSMIT_CYCLES * qpnp_bsi_get_tau_us(chip)
+ + QPNP_BSI_MAX_IRQ_LATENCY_US;
+
+ rc = qpnp_bsi_wait_for_rx(chip, timeout);
+ if (rc) {
+ if (rc == -ETIMEDOUT) {
+ /*
+ * No error message is printed in this case in order
+ * to provide silent operation when checking if a slave
+ * is selected using the transaction query bus command.
+ */
+ dev_dbg(&chip->spmi_dev->dev, "%s: transaction timed out, no interrupts received, rc=%d\n",
+ __func__, rc);
+ }
+ return rc;
+ }
+
+ /* Read the RX_DATA bytes. */
+ rc = qpnp_bsi_read(chip, QPNP_BSI_REG_RX_DATA_LOW, buf, 3);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_read() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (buf[2] & QPNP_BSI_RX_SRC_LOOPBACK_FLAG) {
+ rc = -EIO;
+ dev_err(&chip->spmi_dev->dev, "%s: unexpected loopback data read, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ *response = ((int)(buf[1] & QPNP_BSI_RX_DATA_HIGH_MASK) << 8) | buf[0];
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF);
+
+ return 0;
+}
+
+/*
+ * Wait for RX_FLOW_STATUS to be set to 1 which indicates that another BIF word
+ * can be read from PMIC registers.
+ */
+static int qpnp_bsi_wait_for_rx_data(struct qpnp_bsi_chip *chip)
+{
+ int rc = 0;
+ int timeout;
+ u8 reg;
+
+ timeout = QPNP_BSI_MAX_TRANSMIT_CYCLES * qpnp_bsi_get_tau_us(chip);
+
+ /* Wait for RX_FLOW_STATUS == 1 or ERR_FLAG == 1. */
+ while (timeout > 0) {
+ rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (reg & QPNP_BSI_STATUS_ERROR) {
+ dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
+ __func__, qpnp_bsi_get_bsi_error(chip));
+ return -EIO;
+ }
+
+ if (reg & QPNP_BSI_STATUS_RX_DATA_READY) {
+ /* BSI RX has data word latched. */
+ return 0;
+ }
+
+ udelay(1);
+ timeout--;
+ }
+
+ rc = -ETIMEDOUT;
+ dev_err(&chip->spmi_dev->dev, "%s: transaction timed out, RX_FLOW_STATUS never set to 1, rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+/*
+ * Wait for TX_GO_STATUS to be set to 0 which indicates that another BIF word
+ * can be enqueued.
+ */
+static int qpnp_bsi_wait_for_tx_go(struct qpnp_bsi_chip *chip)
+{
+ int rc = 0;
+ int timeout;
+ u8 reg;
+
+ timeout = QPNP_BSI_MAX_TRANSMIT_CYCLES * qpnp_bsi_get_tau_us(chip);
+
+ /* Wait for TX_GO_STATUS == 0 or ERR_FLAG == 1. */
+ while (timeout > 0) {
+ rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (reg & QPNP_BSI_STATUS_ERROR) {
+ dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
+ __func__, qpnp_bsi_get_bsi_error(chip));
+ return -EIO;
+ }
+
+ if (!(reg & QPNP_BSI_STATUS_TX_GO_BUSY)) {
+ /* BSI TX is ready to accept the next word. */
+ return 0;
+ }
+
+ udelay(1);
+ timeout--;
+ }
+
+ rc = -ETIMEDOUT;
+ dev_err(&chip->spmi_dev->dev, "%s: transaction timed out, TX_GO_STATUS never set to 0, rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+/*
+ * Wait for TX_BUSY to be set to 0 which indicates that the TX data has been
+ * successfully transmitted.
+ */
+static int qpnp_bsi_wait_for_tx_idle(struct qpnp_bsi_chip *chip)
+{
+ int rc = 0;
+ int timeout;
+ u8 reg;
+
+ timeout = QPNP_BSI_MAX_TRANSMIT_CYCLES * qpnp_bsi_get_tau_us(chip);
+
+ /* Wait for TX_BUSY == 0 or ERR_FLAG == 1. */
+ while (timeout > 0) {
+ rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (reg & QPNP_BSI_STATUS_ERROR) {
+ dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
+ __func__, qpnp_bsi_get_bsi_error(chip));
+ return -EIO;
+ }
+
+ if (!(reg & QPNP_BSI_STATUS_TX_BUSY)) {
+ /* BSI TX is idle. */
+ return 0;
+ }
+
+ udelay(1);
+ timeout--;
+ }
+
+ rc = -ETIMEDOUT;
+ dev_err(&chip->spmi_dev->dev, "%s: transaction timed out, TX_BUSY never set to 0, rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+/*
+ * For burst read length greater than 1, send necessary RBL and RBE BIF bus
+ * commands.
+ */
+static int qpnp_bsi_send_burst_length(struct qpnp_bsi_chip *chip, int burst_len)
+{
+ int rc = 0;
+
+ /*
+ * Send burst read length bus commands according to the following:
+ *
+ * 256 --> RBL0
+ * 0-255 = 16 * y + x --> RBEy and RBLx
+ * RBE0 does not need to be sent
+ * RBL0 does not need to be sent
+ */
+ if (burst_len == 256) {
+ rc = qpnp_bsi_issue_transaction(chip, BIF_TRANS_BC,
+ BIF_CMD_RBL);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_wait_for_tx_go(chip);
+ if (rc)
+ return rc;
+ } else if (burst_len >= 16) {
+ rc = qpnp_bsi_issue_transaction(chip, BIF_TRANS_BC,
+ BIF_CMD_RBE + (burst_len / 16));
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_wait_for_tx_go(chip);
+ if (rc)
+ return rc;
+ }
+
+ if (burst_len % 16) {
+ rc = qpnp_bsi_issue_transaction(chip, BIF_TRANS_BC,
+ BIF_CMD_RBL + (burst_len % 16));
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_wait_for_tx_go(chip);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+/* Perform validation steps on received BIF data. */
+static int qpnp_bsi_validate_rx_data(struct qpnp_bsi_chip *chip, int response,
+ u8 rx2_data, bool last_word)
+{
+ int err = -EIO;
+
+ if (rx2_data & QPNP_BSI_RX_SRC_LOOPBACK_FLAG) {
+ dev_err(&chip->spmi_dev->dev, "%s: unexpected loopback data read, rc=%d\n",
+ __func__, err);
+ return err;
+ }
+
+ if (!(response & BIF_SLAVE_RD_ACK)) {
+ dev_err(&chip->spmi_dev->dev, "%s: BIF register read error=0x%02X\n",
+ __func__, response & BIF_SLAVE_RD_ERR);
+ return err;
+ }
+
+ if (last_word && !(response & BIF_SLAVE_RD_EOT)) {
+ dev_err(&chip->spmi_dev->dev, "%s: BIF register read error, last RD packet has EOT=0\n",
+ __func__);
+ return err;
+ } else if (!last_word && (response & BIF_SLAVE_RD_EOT)) {
+ dev_err(&chip->spmi_dev->dev, "%s: BIF register read error, RD packet other than last has EOT=1\n",
+ __func__);
+ return err;
+ }
+
+ return 0;
+}
+
+/* Performs all BIF transactions in order to utilize burst reads. */
+static int qpnp_bsi_read_slave_registers(struct bif_ctrl_dev *bdev, u16 addr,
+ u8 *data, int len)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ int response = 0;
+ unsigned long flags;
+ int rc, rc2, i, burst_len;
+ u8 buf[3];
+
+ rc = qpnp_bsi_clear_bsi_error(chip);
+ if (rc)
+ return rc;
+
+ qpnp_bsi_clear_irq_flags(chip);
+
+ qpnp_bsi_set_com_mode(chip, QPNP_BSI_COM_MODE_POLL);
+
+ rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_DATA_TX_DATA);
+ if (rc)
+ return rc;
+
+ while (len > 0) {
+ burst_len = min(len, 256);
+
+ rc = qpnp_bsi_send_burst_length(chip, burst_len);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_issue_transaction(chip, BIF_TRANS_ERA, addr >> 8);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_wait_for_tx_go(chip);
+ if (rc)
+ return rc;
+
+ /* Perform burst read in atomic context. */
+ local_irq_save(flags);
+
+ rc = qpnp_bsi_issue_transaction(chip, BIF_TRANS_RRA,
+ addr & 0xFF);
+ if (rc)
+ goto burst_err;
+
+ for (i = 0; i < burst_len; i++) {
+ rc = qpnp_bsi_wait_for_rx_data(chip);
+ if (rc)
+ goto burst_err;
+
+ /* Read the RX_DATA bytes. */
+ rc = qpnp_bsi_read(chip, QPNP_BSI_REG_RX_DATA_LOW, buf,
+ 3);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_read() failed, rc=%d\n",
+ __func__, rc);
+ goto burst_err;
+ }
+
+ response = ((buf[1] & QPNP_BSI_RX_DATA_HIGH_MASK) << 8)
+ | buf[0];
+
+ rc = qpnp_bsi_validate_rx_data(chip, response, buf[2],
+ i == burst_len - 1);
+ if (rc)
+ goto burst_err;
+
+ data[i] = buf[0];
+ }
+ local_irq_restore(flags);
+
+ addr += burst_len;
+ data += burst_len;
+ len -= burst_len;
+ }
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF);
+
+ return rc;
+
+burst_err:
+ local_irq_restore(flags);
+
+ rc2 = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF);
+ if (rc2 < 0)
+ rc = rc2;
+
+ return rc;
+}
+
+/* Performs all BIF transactions in order to utilize burst writes. */
+static int qpnp_bsi_write_slave_registers(struct bif_ctrl_dev *bdev, u16 addr,
+ const u8 *data, int len)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ unsigned long flags;
+ int rc, rc2, i;
+
+ rc = qpnp_bsi_clear_bsi_error(chip);
+ if (rc)
+ return rc;
+
+ qpnp_bsi_clear_irq_flags(chip);
+
+ qpnp_bsi_set_com_mode(chip, QPNP_BSI_COM_MODE_POLL);
+
+ rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_DATA);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_issue_transaction(chip, BIF_TRANS_ERA, addr >> 8);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_wait_for_tx_go(chip);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_issue_transaction(chip, BIF_TRANS_WRA, addr & 0xFF);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_wait_for_tx_go(chip);
+ if (rc)
+ return rc;
+
+ /* Perform burst write in atomic context. */
+ local_irq_save(flags);
+
+ for (i = 0; i < len; i++) {
+ rc = qpnp_bsi_issue_transaction(chip, BIF_TRANS_WD, data[i]);
+ if (rc)
+ goto burst_err;
+
+ rc = qpnp_bsi_wait_for_tx_go(chip);
+ if (rc)
+ goto burst_err;
+ }
+
+ rc = qpnp_bsi_wait_for_tx_idle(chip);
+ if (rc)
+ goto burst_err;
+
+ local_irq_restore(flags);
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF);
+
+ return rc;
+
+burst_err:
+ local_irq_restore(flags);
+
+ rc2 = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_OFF_TX_OFF);
+ if (rc2 < 0)
+ rc = rc2;
+
+ return rc;
+}
+
+
+static int qpnp_bsi_bus_set_interrupt_mode(struct bif_ctrl_dev *bdev)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ int rc;
+
+ rc = qpnp_bsi_clear_bsi_error(chip);
+ if (rc)
+ return rc;
+
+ qpnp_bsi_clear_irq_flags(chip);
+
+ qpnp_bsi_set_com_mode(chip, QPNP_BSI_COM_MODE_IRQ);
+
+ /*
+ * Temporarily change the bus to active state so that the EINT command
+ * can be issued.
+ */
+ rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_INT_TX_DATA);
+ if (rc)
+ return rc;
+
+ /*
+ * Set the bus state to interrupt mode so that an RX interrupt which
+ * occurs immediately after issuing the EINT command is handled
+ * properly.
+ */
+ chip->state = BIF_BUS_STATE_INTERRUPT;
+
+ /* Send EINT bus command. */
+ rc = qpnp_bsi_issue_transaction_wait_for_tx(chip, BIF_TRANS_BC,
+ BIF_CMD_EINT);
+ if (rc)
+ return rc;
+
+ rc = qpnp_bsi_rx_tx_config(chip, QPNP_BSI_RX_TX_STATE_RX_INT_TX_OFF);
+
+ return rc;
+}
+
+static int qpnp_bsi_bus_set_active_mode(struct bif_ctrl_dev *bdev,
+ int prev_state)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ int rc;
+ u8 buf[2];
+
+ rc = qpnp_bsi_clear_bsi_error(chip);
+ if (rc)
+ return rc;
+
+ buf[0] = QPNP_BSI_MODE_TX_PULSE_INT |
+ QPNP_BSI_MODE_RX_PULSE_DATA;
+ buf[1] = QPNP_BSI_TX_ENABLE | QPNP_BSI_RX_DISABLE;
+
+ if (prev_state == BIF_BUS_STATE_INTERRUPT)
+ buf[0] |= QPNP_BSI_MODE_TX_PULSE_T_1_TAU;
+ else
+ buf[0] |= QPNP_BSI_MODE_TX_PULSE_T_WAKE;
+
+ rc = qpnp_bsi_write(chip, QPNP_BSI_REG_MODE, buf, 2);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ buf[0] = QPNP_BSI_TX_CTRL_GO;
+ /* Initiate BCL low pulse. */
+ rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TX_CTRL, buf, 1);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ switch (prev_state) {
+ case BIF_BUS_STATE_INTERRUPT:
+ udelay(qpnp_bsi_get_tau_us(chip) * 4);
+ break;
+ case BIF_BUS_STATE_STANDBY:
+ udelay(qpnp_bsi_get_tau_us(chip)
+ + QPNP_BSI_MAX_SLAVE_ACTIVIATION_DELAY_US
+ + QPNP_BSI_POWER_UP_LOW_DELAY_US);
+ break;
+ case BIF_BUS_STATE_POWER_DOWN:
+ msleep(QPNP_BSI_MAX_SLAVE_POWER_UP_DELAY_MS);
+ break;
+ }
+
+ return rc;
+}
+
+static int qpnp_bsi_get_bus_state(struct bif_ctrl_dev *bdev)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+
+ return chip->state;
+}
+
+static int qpnp_bsi_set_bus_state(struct bif_ctrl_dev *bdev, int state)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ int rc = 0;
+
+ if (state == chip->state)
+ return 0;
+
+ switch (state) {
+ case BIF_BUS_STATE_MASTER_DISABLED:
+ pr_info("master disable not yet supported.\n");
+ break;
+ case BIF_BUS_STATE_POWER_DOWN:
+ rc = qpnp_bsi_bus_transaction(bdev, BIF_TRANS_BC, BIF_CMD_PDWN);
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: failed to enable power down mode, rc=%d\n",
+ __func__, rc);
+ break;
+ case BIF_BUS_STATE_STANDBY:
+ rc = qpnp_bsi_bus_transaction(bdev, BIF_TRANS_BC, BIF_CMD_STBY);
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: failed to enable standby mode, rc=%d\n",
+ __func__, rc);
+ break;
+ case BIF_BUS_STATE_ACTIVE:
+ rc = qpnp_bsi_bus_set_active_mode(bdev, chip->state);
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: failed to enable active mode, rc=%d\n",
+ __func__, rc);
+ break;
+ case BIF_BUS_STATE_INTERRUPT:
+ /*
+ * qpnp_bsi_bus_set_interrupt_mode() internally sets
+ * chip->state = BIF_BUS_STATE_INTERRUPT immediately before
+ * issuing the EINT command.
+ */
+ rc = qpnp_bsi_bus_set_interrupt_mode(bdev);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: failed to enable interrupt mode, rc=%d\n",
+ __func__, rc);
+ } else if (chip->state == BIF_BUS_STATE_ACTIVE) {
+ /*
+ * A slave interrupt was received immediately after
+ * issuing the EINT command. Therefore, stay in active
+ * communication mode.
+ */
+ state = BIF_BUS_STATE_ACTIVE;
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ dev_err(&chip->spmi_dev->dev, "%s: invalid state=%d\n",
+ __func__, state);
+ }
+
+ if (!rc)
+ chip->state = state;
+
+ return rc;
+}
+
+/* Returns the smallest tau_bif that is greater than or equal to period_ns. */
+static int qpnp_bsi_tau_bif_higher(int period_ns, int sample_mask)
+{
+ const int *supported_period_ns =
+ (sample_mask == QPNP_BSI_TAU_CONFIG_SAMPLE_4X ?
+ qpnp_bsi_tau_period.period_4x_ns :
+ qpnp_bsi_tau_period.period_8x_ns);
+ int smallest_tau_bif = INT_MAX;
+ int i;
+
+ for (i = QPNP_BSI_NUM_CLOCK_PERIODS - 1; i >= 0; i--) {
+ if (period_ns <= supported_period_ns[i]) {
+ smallest_tau_bif = supported_period_ns[i];
+ break;
+ }
+ }
+
+ return smallest_tau_bif;
+}
+
+/* Returns the largest tau_bif that is less than or equal to period_ns. */
+static int qpnp_bsi_tau_bif_lower(int period_ns, int sample_mask)
+{
+ const int *supported_period_ns =
+ (sample_mask == QPNP_BSI_TAU_CONFIG_SAMPLE_4X ?
+ qpnp_bsi_tau_period.period_4x_ns :
+ qpnp_bsi_tau_period.period_8x_ns);
+ int largest_tau_bif = 0;
+ int i;
+
+ for (i = 0; i < QPNP_BSI_NUM_CLOCK_PERIODS; i++) {
+ if (period_ns >= supported_period_ns[i]) {
+ largest_tau_bif = supported_period_ns[i];
+ break;
+ }
+ }
+
+ return largest_tau_bif;
+}
+
+/*
+ * Moves period_ns into allowed range and then sets tau bif to the period that
+ * is greater than or equal to period_ns.
+ */
+static int qpnp_bsi_set_tau_bif(struct qpnp_bsi_chip *chip, int period_ns)
+{
+ const int *supported_period_ns =
+ (chip->tau_sampling_mask == QPNP_BSI_TAU_CONFIG_SAMPLE_4X ?
+ qpnp_bsi_tau_period.period_4x_ns :
+ qpnp_bsi_tau_period.period_8x_ns);
+ int idx = 0;
+ int i, rc;
+ u8 reg;
+
+ if (period_ns < chip->bdesc.bus_clock_min_ns)
+ period_ns = chip->bdesc.bus_clock_min_ns;
+ else if (period_ns > chip->bdesc.bus_clock_max_ns)
+ period_ns = chip->bdesc.bus_clock_max_ns;
+
+ for (i = QPNP_BSI_NUM_CLOCK_PERIODS - 1; i >= 0; i--) {
+ if (period_ns <= supported_period_ns[i]) {
+ idx = i;
+ break;
+ }
+ }
+
+ /* Set the tau BIF clock period and sampling rate. */
+ reg = chip->tau_sampling_mask | idx;
+ rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TAU_CONFIG, ®, 1);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ chip->tau_index = idx;
+
+ return 0;
+}
+
+static int qpnp_bsi_get_bus_period(struct bif_ctrl_dev *bdev)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+
+ return qpnp_bsi_get_tau_ns(chip);
+}
+
+static int qpnp_bsi_set_bus_period(struct bif_ctrl_dev *bdev, int period_ns)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+
+ return qpnp_bsi_set_tau_bif(chip, period_ns);
+}
+
+static int qpnp_bsi_get_battery_rid(struct bif_ctrl_dev *bdev)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ struct qpnp_vadc_result adc_result;
+ int rid_ohm, vid_uV, rc;
+ s64 temp;
+
+ if (chip->batt_id_adc_channel >= ADC_MAX_NUM) {
+ dev_err(&chip->spmi_dev->dev, "%s: no ADC channel specified for Rid measurement\n",
+ __func__);
+ return -ENXIO;
+ }
+
+ rc = qpnp_vadc_read(chip->batt_id_adc_channel, &adc_result);
+ if (!rc) {
+ vid_uV = adc_result.physical;
+
+ if (chip->vid_ref_uV - vid_uV <= 0) {
+ rid_ohm = INT_MAX;
+ } else {
+ temp = (s64)chip->r_pullup_ohm * (s64)vid_uV;
+ do_div(temp, chip->vid_ref_uV - vid_uV);
+ if (temp > INT_MAX)
+ rid_ohm = INT_MAX;
+ else
+ rid_ohm = temp;
+ }
+ } else {
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_vadc_read(%d) failed, rc=%d\n",
+ __func__, chip->batt_id_adc_channel, rc);
+ rid_ohm = rc;
+ }
+
+ return rid_ohm;
+}
+
+/*
+ * Returns 1 if a battery pack is present on the BIF bus, 0 if a battery pack
+ * is not present, or errno if detection fails.
+ *
+ * Battery detection is based upon the idle BCL voltage.
+ */
+static int qpnp_bsi_get_battery_presence(struct bif_ctrl_dev *bdev)
+{
+ struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
+ u8 reg = 0x00;
+ int rc;
+
+ rc = spmi_ext_register_readl(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
+ chip->batt_id_stat_addr, ®, 1);
+ if (rc) {
+ dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_readl() failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ return !!(reg & QPNP_SMBB_BAT_IF_BATT_PRES_MASK);
+}
+
+static struct bif_ctrl_ops qpnp_bsi_ops = {
+ .bus_transaction = qpnp_bsi_bus_transaction,
+ .bus_transaction_query = qpnp_bsi_bus_transaction_query,
+ .bus_transaction_read = qpnp_bsi_bus_transaction_read,
+ .get_bus_state = qpnp_bsi_get_bus_state,
+ .set_bus_state = qpnp_bsi_set_bus_state,
+ .get_bus_period = qpnp_bsi_get_bus_period,
+ .set_bus_period = qpnp_bsi_set_bus_period,
+ .read_slave_registers = qpnp_bsi_read_slave_registers,
+ .write_slave_registers = qpnp_bsi_write_slave_registers,
+ .get_battery_rid = qpnp_bsi_get_battery_rid,
+ .get_battery_presence = qpnp_bsi_get_battery_presence,
+};
+
+/* Load all BSI properties from device tree. */
+static int __devinit qpnp_bsi_parse_dt(struct qpnp_bsi_chip *chip,
+ struct spmi_device *spmi)
+{
+ struct device *dev = &spmi->dev;
+ struct device_node *node = spmi->dev.of_node;
+ struct resource *res;
+ int rc, temp;
+
+ chip->batt_id_adc_channel = ADC_MAX_NUM;
+ rc = of_property_read_u32(node, "qcom,channel-num",
+ &chip->batt_id_adc_channel);
+ if (!rc && (chip->batt_id_adc_channel < 0
+ || chip->batt_id_adc_channel >= ADC_MAX_NUM)) {
+ dev_err(dev, "%s: invalid qcom,channel-num=%d specified\n",
+ __func__, chip->batt_id_adc_channel);
+ return -EINVAL;
+ }
+
+ chip->r_pullup_ohm = QPNP_BSI_DEFAULT_PULLUP_OHM;
+ rc = of_property_read_u32(node, "qcom,pullup-ohms",
+ &chip->r_pullup_ohm);
+ if (!rc && (chip->r_pullup_ohm < QPNP_BSI_MIN_PULLUP_OHM ||
+ chip->r_pullup_ohm > QPNP_BSI_MAX_PULLUP_OHM)) {
+ dev_err(dev, "%s: invalid qcom,pullup-ohms=%d property value\n",
+ __func__, chip->r_pullup_ohm);
+ return -EINVAL;
+ }
+
+ chip->vid_ref_uV = QPNP_BSI_DEFAULT_VID_REF_UV;
+ rc = of_property_read_u32(node, "qcom,vref-microvolts",
+ &chip->vid_ref_uV);
+ if (!rc && (chip->vid_ref_uV < QPNP_BSI_MIN_VID_REF_UV ||
+ chip->vid_ref_uV > QPNP_BSI_MAX_VID_REF_UV)) {
+ dev_err(dev, "%s: invalid qcom,vref-microvolts=%d property value\n",
+ __func__, chip->vid_ref_uV);
+ return -EINVAL;
+ }
+
+ res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM, "bsi-base");
+ if (!res) {
+ dev_err(dev, "%s: node is missing BSI base address\n",
+ __func__);
+ return -EINVAL;
+ }
+ chip->base_addr = res->start;
+
+ res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
+ "batt-id-status");
+ if (!res) {
+ dev_err(dev, "%s: node is missing BATT_ID status address\n",
+ __func__);
+ return -EINVAL;
+ }
+ chip->batt_id_stat_addr = res->start;
+
+ chip->bdesc.name = spmi_get_primary_dev_name(spmi);
+ if (!chip->bdesc.name) {
+ dev_err(dev, "%s: label binding undefined for node %s\n",
+ __func__, spmi->dev.of_node->full_name);
+ return -EINVAL;
+ }
+
+ /* Use maximum range by default. */
+ chip->bdesc.bus_clock_min_ns = QPNP_BSI_MIN_CLOCK_SPEED_NS;
+ chip->bdesc.bus_clock_max_ns = QPNP_BSI_MAX_CLOCK_SPEED_NS;
+ chip->tau_sampling_mask = QPNP_BSI_TAU_CONFIG_SAMPLE_4X;
+
+ rc = of_property_read_u32(node, "qcom,sample-rate", &temp);
+ if (rc == 0) {
+ if (temp == 4) {
+ chip->tau_sampling_mask = QPNP_BSI_TAU_CONFIG_SAMPLE_4X;
+ } else if (temp == 8) {
+ chip->tau_sampling_mask = QPNP_BSI_TAU_CONFIG_SAMPLE_8X;
+ } else {
+ dev_err(dev, "%s: invalid qcom,sample-rate=%d. Only values of 4 and 8 are supported.\n",
+ __func__, temp);
+ return -EINVAL;
+ }
+ }
+
+ rc = of_property_read_u32(node, "qcom,min-clock-period", &temp);
+ if (rc == 0)
+ chip->bdesc.bus_clock_min_ns = qpnp_bsi_tau_bif_higher(temp,
+ chip->tau_sampling_mask);
+
+ rc = of_property_read_u32(node, "qcom,max-clock-period", &temp);
+ if (rc == 0)
+ chip->bdesc.bus_clock_max_ns = qpnp_bsi_tau_bif_lower(temp,
+ chip->tau_sampling_mask);
+
+ if (chip->bdesc.bus_clock_min_ns > chip->bdesc.bus_clock_max_ns) {
+ dev_err(dev, "%s: invalid qcom,min/max-clock-period.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ chip->irq[QPNP_BSI_IRQ_ERR] = spmi_get_irq_byname(spmi, NULL, "err");
+ if (chip->irq[QPNP_BSI_IRQ_ERR] < 0) {
+ dev_err(dev, "%s: node is missing err irq\n", __func__);
+ return chip->irq[QPNP_BSI_IRQ_ERR];
+ }
+
+ chip->irq[QPNP_BSI_IRQ_RX] = spmi_get_irq_byname(spmi, NULL, "rx");
+ if (chip->irq[QPNP_BSI_IRQ_RX] < 0) {
+ dev_err(dev, "%s: node is missing rx irq\n", __func__);
+ return chip->irq[QPNP_BSI_IRQ_RX];
+ }
+
+ chip->irq[QPNP_BSI_IRQ_TX] = spmi_get_irq_byname(spmi, NULL, "tx");
+ if (chip->irq[QPNP_BSI_IRQ_TX] < 0) {
+ dev_err(dev, "%s: node is missing tx irq\n", __func__);
+ return chip->irq[QPNP_BSI_IRQ_TX];
+ }
+
+ chip->batt_present_irq = spmi_get_irq_byname(spmi, NULL,
+ "batt-present");
+ if (chip->batt_present_irq < 0) {
+ dev_err(dev, "%s: node is missing batt-present irq\n",
+ __func__);
+ return chip->batt_present_irq;
+ }
+
+ return rc;
+}
+
+/* Request all BSI and battery presence IRQs and set them as wakeable. */
+static int __devinit qpnp_bsi_init_irqs(struct qpnp_bsi_chip *chip,
+ struct device *dev)
+{
+ int rc;
+
+ rc = devm_request_irq(dev, chip->irq[QPNP_BSI_IRQ_ERR],
+ qpnp_bsi_isr, IRQF_TRIGGER_RISING, "bsi-err", chip);
+ if (rc < 0) {
+ dev_err(dev, "%s: request for bsi-err irq %d failed, rc=%d\n",
+ __func__, chip->irq[QPNP_BSI_IRQ_ERR], rc);
+ return rc;
+ }
+
+ rc = irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_ERR], 1);
+ if (rc < 0) {
+ dev_err(dev, "%s: unable to set bsi-err irq %d as wakeable, rc=%d\n",
+ __func__, chip->irq[QPNP_BSI_IRQ_ERR], rc);
+ return rc;
+ }
+
+ rc = devm_request_irq(dev, chip->irq[QPNP_BSI_IRQ_RX],
+ qpnp_bsi_isr, IRQF_TRIGGER_RISING, "bsi-rx", chip);
+ if (rc < 0) {
+ dev_err(dev, "%s: request for bsi-rx irq %d failed, rc=%d\n",
+ __func__, chip->irq[QPNP_BSI_IRQ_RX], rc);
+ goto set_unwakeable_irq_err;
+ }
+
+ rc = irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_RX], 1);
+ if (rc < 0) {
+ dev_err(dev, "%s: unable to set bsi-rx irq %d as wakeable, rc=%d\n",
+ __func__, chip->irq[QPNP_BSI_IRQ_RX], rc);
+ goto set_unwakeable_irq_err;
+ }
+
+ rc = devm_request_irq(dev, chip->irq[QPNP_BSI_IRQ_TX],
+ qpnp_bsi_isr, IRQF_TRIGGER_RISING, "bsi-tx", chip);
+ if (rc < 0) {
+ dev_err(dev, "%s: request for bsi-tx irq %d failed, rc=%d\n",
+ __func__, chip->irq[QPNP_BSI_IRQ_TX], rc);
+ goto set_unwakeable_irq_rx;
+ }
+
+ rc = irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_TX], 1);
+ if (rc < 0) {
+ dev_err(dev, "%s: unable to set bsi-tx irq %d as wakeable, rc=%d\n",
+ __func__, chip->irq[QPNP_BSI_IRQ_TX], rc);
+ goto set_unwakeable_irq_rx;
+ }
+
+ rc = devm_request_threaded_irq(dev, chip->batt_present_irq, NULL,
+ qpnp_bsi_batt_present_isr,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED
+ | IRQF_ONESHOT,
+ "bsi-batt-present", chip);
+ if (rc < 0) {
+ dev_err(dev, "%s: request for bsi-batt-present irq %d failed, rc=%d\n",
+ __func__, chip->batt_present_irq, rc);
+ goto set_unwakeable_irq_tx;
+ }
+
+ rc = irq_set_irq_wake(chip->batt_present_irq, 1);
+ if (rc < 0) {
+ dev_err(dev, "%s: unable to set bsi-batt-present irq %d as wakeable, rc=%d\n",
+ __func__, chip->batt_present_irq, rc);
+ goto set_unwakeable_irq_tx;
+ }
+
+ return rc;
+
+set_unwakeable_irq_tx:
+ irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_TX], 0);
+set_unwakeable_irq_rx:
+ irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_RX], 0);
+set_unwakeable_irq_err:
+ irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_ERR], 0);
+ return rc;
+}
+
+static void qpnp_bsi_cleanup_irqs(struct qpnp_bsi_chip *chip)
+{
+ irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_ERR], 0);
+ irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_RX], 0);
+ irq_set_irq_wake(chip->irq[QPNP_BSI_IRQ_TX], 0);
+ irq_set_irq_wake(chip->batt_present_irq, 0);
+}
+
+static int __devinit qpnp_bsi_probe(struct spmi_device *spmi)
+{
+ struct device *dev = &spmi->dev;
+ struct qpnp_bsi_chip *chip;
+ int rc;
+ u8 type[2], reg;
+
+ if (!spmi->dev.of_node) {
+ dev_err(dev, "%s: device node missing\n", __func__);
+ return -ENODEV;
+ }
+
+ chip = devm_kzalloc(dev, sizeof(struct qpnp_bsi_chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(dev, "%s: Can't allocate qpnp_bsi\n", __func__);
+ return -ENOMEM;
+ }
+
+ rc = qpnp_bsi_parse_dt(chip, spmi);
+ if (rc) {
+ dev_err(dev, "%s: device tree parsing failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ INIT_WORK(&chip->slave_irq_work, qpnp_bsi_slave_irq_work);
+
+ rc = qpnp_bsi_init_irqs(chip, dev);
+ if (rc) {
+ dev_err(dev, "%s: IRQ initialization failed, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ chip->spmi_dev = spmi;
+ chip->bdesc.ops = &qpnp_bsi_ops;
+ chip->state = BIF_BUS_STATE_POWER_DOWN;
+ chip->com_mode = QPNP_BSI_COM_MODE_IRQ;
+
+ rc = qpnp_bsi_read(chip, QPNP_BSI_REG_TYPE, type, 2);
+ if (rc) {
+ dev_err(dev, "%s: could not read type register, rc=%d\n",
+ __func__, rc);
+ goto cleanup_irqs;
+ }
+
+ if (type[0] != QPNP_BSI_TYPE || type[1] != QPNP_BSI_SUBTYPE) {
+ dev_err(dev, "%s: BSI peripheral is not present; type=0x%02X, subtype=0x%02X\n",
+ __func__, type[0], type[1]);
+ rc = -ENODEV;
+ goto cleanup_irqs;
+ }
+
+ /* Ensure that ADC channel is available if it was specified. */
+ if (chip->batt_id_adc_channel < ADC_MAX_NUM) {
+ rc = qpnp_vadc_is_ready();
+ if (rc) {
+ /* Probe retry, do not print an error message */
+ goto cleanup_irqs;
+ }
+ }
+
+ rc = qpnp_bsi_set_tau_bif(chip, chip->bdesc.bus_clock_min_ns);
+ if (rc) {
+ dev_err(dev, "%s: qpnp_bsi_set_tau_bif() failed, rc=%d\n",
+ __func__, rc);
+ goto cleanup_irqs;
+ }
+
+ /* Enable the BSI module. */
+ reg = QPNP_BSI_ENABLE;
+ rc = qpnp_bsi_write(chip, QPNP_BSI_REG_ENABLE, ®, 1);
+ if (rc) {
+ dev_err(dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
+ __func__, rc);
+ goto cleanup_irqs;
+ }
+
+ chip->bdev = bif_ctrl_register(&chip->bdesc, dev, chip,
+ spmi->dev.of_node);
+ if (IS_ERR(chip->bdev)) {
+ rc = PTR_ERR(chip->bdev);
+ dev_err(dev, "%s: bif_ctrl_register failed, rc=%d\n",
+ __func__, rc);
+ goto cleanup_irqs;
+ }
+
+ dev_set_drvdata(dev, chip);
+
+ return rc;
+
+cleanup_irqs:
+ qpnp_bsi_cleanup_irqs(chip);
+ return rc;
+}
+
+static int __devexit qpnp_bsi_remove(struct spmi_device *spmi)
+{
+ struct qpnp_bsi_chip *chip = dev_get_drvdata(&spmi->dev);
+ dev_set_drvdata(&spmi->dev, NULL);
+
+ if (chip) {
+ bif_ctrl_unregister(chip->bdev);
+ qpnp_bsi_cleanup_irqs(chip);
+ }
+
+ return 0;
+}
+
+static struct of_device_id spmi_match_table[] = {
+ { .compatible = QPNP_BSI_DRIVER_NAME, },
+ {}
+};
+
+static const struct spmi_device_id qpnp_bsi_id[] = {
+ { QPNP_BSI_DRIVER_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spmi, qpnp_bsi_id);
+
+static struct spmi_driver qpnp_bsi_driver = {
+ .driver = {
+ .name = QPNP_BSI_DRIVER_NAME,
+ .of_match_table = spmi_match_table,
+ .owner = THIS_MODULE,
+ },
+ .probe = qpnp_bsi_probe,
+ .remove = __devexit_p(qpnp_bsi_remove),
+ .id_table = qpnp_bsi_id,
+};
+
+static int __init qpnp_bsi_init(void)
+{
+ return spmi_driver_register(&qpnp_bsi_driver);
+}
+
+static void __exit qpnp_bsi_exit(void)
+{
+ spmi_driver_unregister(&qpnp_bsi_driver);
+}
+
+MODULE_DESCRIPTION("QPNP PMIC BSI driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(qpnp_bsi_init);
+module_exit(qpnp_bsi_exit);
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 23dd5a1..0867676 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -149,50 +149,63 @@
void extract_dci_events(unsigned char *buf)
{
- uint16_t event_id, event_id_packet;
- uint8_t *event_mask_ptr, byte_mask, payload_len;
- uint8_t event_data[MAX_EVENT_SIZE], timestamp[8];
- unsigned int byte_index;
- int i, bit_index, length, temp_len;
- int total_event_len, payload_len_field, timestamp_len;
+ uint16_t event_id, event_id_packet, length, temp_len;
+ uint8_t *event_mask_ptr, byte_mask, payload_len, payload_len_field;
+ uint8_t timestamp[8], bit_index, timestamp_len;
+ uint8_t event_data[MAX_EVENT_SIZE];
+ unsigned int byte_index, total_event_len, i;
struct diag_dci_client_tbl *entry;
- length = *(uint16_t *)(buf+1); /* total length of event series */
+ length = *(uint16_t *)(buf + 1); /* total length of event series */
+ if (length == 0) {
+ pr_err("diag: Incoming dci event length is invalid\n");
+ return;
+ }
temp_len = 0;
buf = buf + 3; /* start of event series */
- while (temp_len < length-1) {
- *event_data = EVENT_CMD_CODE;
- event_id_packet = *(uint16_t *)(buf+temp_len);
+ while (temp_len < (length - 1)) {
+ event_id_packet = *(uint16_t *)(buf + temp_len);
event_id = event_id_packet & 0x0FFF; /* extract 12 bits */
if (event_id_packet & 0x8000) {
timestamp_len = 2;
+ memset(timestamp, 0, 8);
} else {
timestamp_len = 8;
- memcpy(timestamp, buf+temp_len+2, 8);
+ memcpy(timestamp, buf + temp_len + 2, timestamp_len);
}
+ /* 13th and 14th bit represent the payload length */
if (((event_id_packet & 0x6000) >> 13) == 3) {
payload_len_field = 1;
payload_len = *(uint8_t *)
- (buf+temp_len+2+timestamp_len);
- memcpy(event_data+13, buf+temp_len+2+timestamp_len, 1);
- memcpy(event_data+14, buf+temp_len+2+timestamp_len+1,
- payload_len);
+ (buf + temp_len + 2 + timestamp_len);
+ if (payload_len < (MAX_EVENT_SIZE - 13)) {
+ /* copy the payload length and the payload */
+ memcpy(event_data + 12, buf + temp_len + 2 +
+ timestamp_len, 1);
+ memcpy(event_data + 13, buf + temp_len + 2 +
+ timestamp_len + 1, payload_len);
+ } else {
+ pr_err("diag: event > %d, payload_len = %d\n",
+ (MAX_EVENT_SIZE - 13), payload_len);
+ return;
+ }
} else {
payload_len_field = 0;
payload_len = (event_id_packet & 0x6000) >> 13;
- if (payload_len < MAX_EVENT_SIZE)
- memcpy(event_data+13,
- buf+temp_len+2+timestamp_len, payload_len);
- else
- pr_alert("diag: event > %d\n", MAX_EVENT_SIZE);
+ /* copy the payload */
+ memcpy(event_data + 12, buf + temp_len + 2 +
+ timestamp_len, payload_len);
}
/* 2 bytes for the event id & timestamp len is hard coded to 8,
as individual events have full timestamp */
- *(uint16_t *)(event_data+1) = 10+payload_len_field+payload_len;
- *(uint16_t *)(event_data+3) = event_id_packet & 0x7FFF;
- memcpy(event_data+5, timestamp, 8);
- total_event_len = 3 + 10 + payload_len_field + payload_len;
- byte_index = event_id/8;
+ *(uint16_t *)(event_data) = 10 +
+ payload_len_field + payload_len;
+ *(uint16_t *)(event_data + 2) = event_id_packet & 0x7FFF;
+ memcpy(event_data + 4, timestamp, 8);
+ /* 2 bytes for the event length field which is added to
+ the event data */
+ total_event_len = 2 + 10 + payload_len_field + payload_len;
+ byte_index = event_id / 8;
bit_index = event_id % 8;
byte_mask = 0x1 << bit_index;
/* parse through event mask tbl of each client and check mask */
@@ -205,7 +218,7 @@
/* copy to client buffer */
if (DCI_CHK_CAPACITY(entry,
4 + total_event_len)) {
- pr_err("diag:DCI event drop\n");
+ pr_err("diag: DCI event drop\n");
driver->dci_client_tbl[i].
dropped_events++;
return;
@@ -214,8 +227,10 @@
received_events++;
*(int *)(entry->dci_data+
entry->data_len) = DCI_EVENT_TYPE;
- memcpy(entry->dci_data+
- entry->data_len+4, event_data, total_event_len);
+ /* 4 bytes for DCI_EVENT_TYPE */
+ memcpy(entry->dci_data +
+ entry->data_len + 4, event_data
+ , total_event_len);
entry->data_len += 4 + total_event_len;
}
}
@@ -228,44 +243,37 @@
{
uint16_t log_code, item_num;
uint8_t equip_id, *log_mask_ptr, byte_mask;
- unsigned int byte_index;
- int i, found = 0;
+ unsigned int i, byte_index, byte_offset = 0;
struct diag_dci_client_tbl *entry;
- log_code = *(uint16_t *)(buf+6);
+ log_code = *(uint16_t *)(buf + 6);
equip_id = LOG_GET_EQUIP_ID(log_code);
item_num = LOG_GET_ITEM_NUM(log_code);
byte_index = item_num/8 + 2;
byte_mask = 0x01 << (item_num % 8);
+ byte_offset = (equip_id * 514) + byte_index;
+ if (byte_offset >= DCI_LOG_MASK_SIZE) {
+ pr_err("diag: Invalid byte_offset %d in dci log\n",
+ byte_offset);
+ return;
+ }
+
/* parse through log mask table of each client and check mask */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
if (driver->dci_client_tbl[i].client) {
entry = &(driver->dci_client_tbl[i]);
log_mask_ptr = entry->dci_log_mask;
- found = 0;
- while (log_mask_ptr) {
- if (*log_mask_ptr == equip_id) {
- found = 1;
- pr_debug("diag: find equip id = %x at %p\n",
- equip_id, log_mask_ptr);
- break;
- } else {
- pr_debug("diag: did not find equip id = %x at %p\n",
- equip_id, log_mask_ptr);
- log_mask_ptr += 514;
- }
- }
- if (!found)
- pr_err("diag: dci equip id not found\n");
- log_mask_ptr = log_mask_ptr + byte_index;
+ if (!log_mask_ptr)
+ return;
+ log_mask_ptr = log_mask_ptr + byte_offset;
if (*log_mask_ptr & byte_mask) {
pr_debug("\t log code %x needed by client %d",
log_code, entry->client->tgid);
/* copy to client buffer */
if (DCI_CHK_CAPACITY(entry,
- 4 + *(uint16_t *)(buf+2))) {
- pr_err("diag:DCI log drop\n");
+ 4 + *(uint16_t *)(buf + 2))) {
+ pr_err("diag: DCI log drop\n");
driver->dci_client_tbl[i].
dropped_logs++;
return;
@@ -273,9 +281,9 @@
driver->dci_client_tbl[i].received_logs++;
*(int *)(entry->dci_data+entry->data_len) =
DCI_LOG_TYPE;
- memcpy(entry->dci_data+entry->data_len+4, buf+4,
- *(uint16_t *)(buf+2));
- entry->data_len += 4 + *(uint16_t *)(buf+2);
+ memcpy(entry->dci_data + entry->data_len + 4,
+ buf + 4, *(uint16_t *)(buf + 2));
+ entry->data_len += 4 + *(uint16_t *)(buf + 2);
}
}
}
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 56d1b91..0f47740 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -22,7 +22,7 @@
#define DCI_EVENT_TYPE -2
#define SET_LOG_MASK 1
#define DISABLE_LOG_MASK 0
-#define MAX_EVENT_SIZE 100
+#define MAX_EVENT_SIZE 512
/* 16 log code categories, each has:
* 1 bytes equip id + 1 dirty byte + 512 byte max log mask
diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c
index b94ea2f..2369c4d 100644
--- a/drivers/char/diag/diagchar_hdlc.c
+++ b/drivers/char/diag/diagchar_hdlc.c
@@ -1,4 +1,5 @@
-/* Copyright (c) 2008-2009, 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2009, 2012-2013, 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
@@ -172,11 +173,14 @@
uint8_t src_byte;
int pkt_bnd = 0;
+ int msg_start;
if (hdlc && hdlc->src_ptr && hdlc->dest_ptr &&
(hdlc->src_size - hdlc->src_idx > 0) &&
(hdlc->dest_size - hdlc->dest_idx > 0)) {
+ msg_start = (hdlc->src_idx == 0) ? 1 : 0;
+
src_ptr = hdlc->src_ptr;
src_ptr = &src_ptr[hdlc->src_idx];
src_length = hdlc->src_size - hdlc->src_idx;
@@ -203,8 +207,16 @@
}
} else if (src_byte == CONTROL_CHAR) {
dest_ptr[len++] = src_byte;
- pkt_bnd = 1;
+ /*
+ * If this is the first byte in the message,
+ * then it is part of the command. Otherwise,
+ * consider it as the last byte of the
+ * message.
+ */
+ if (msg_start && i == 0 && src_length > 1)
+ continue;
i++;
+ pkt_bnd = 1;
break;
} else {
dest_ptr[len++] = src_byte;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 7f4edd1..2aca8cf 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -993,10 +993,18 @@
ret = diag_hdlc_decode(&hdlc);
- if (hdlc.dest_idx < 3) {
- pr_err("diag: Integer underflow in hdlc processing\n");
+ /*
+ * If the message is 3 bytes or less in length then the message is
+ * too short. A message will need 4 bytes minimum, since there are
+ * 2 bytes for the CRC and 1 byte for the ending 0x7e for the hdlc
+ * encoding
+ */
+ if (hdlc.dest_idx < 4) {
+ pr_err_ratelimited("diag: In %s, message is too short, len: %d, dest len: %d\n",
+ __func__, len, hdlc.dest_idx);
return;
}
+
if (ret) {
type = diag_process_apps_pkt(driver->hdlc_buf,
hdlc.dest_idx - 3);
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 3da06a5..1facf24 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -1523,6 +1523,9 @@
}
break;
default:
+ pr_err("Unknown mode of operation %d received, exiting now\n",
+ mode);
+ return -EINVAL;
break;
}
@@ -1686,6 +1689,8 @@
}
break;
default:
+ pr_err("Unknown algorithms %d received, exiting now\n", alg);
+ return -EINVAL;
break;
}
@@ -1893,6 +1898,8 @@
0, NULL);
break;
default:
+ pr_err("Unknown algorithms %d received, exiting now\n", alg);
+ return -EINVAL;
break;
}
@@ -2247,6 +2254,10 @@
if (q_req->mode != QCE_MODE_CCM) {
ivsize = crypto_aead_ivsize(aead);
auth_cmdlistinfo = &pce_dev->ce_sps.cmdlistptr.aead_sha1_hmac;
+ if (auth_cmdlistinfo == NULL) {
+ pr_err("Received NULL cmdlist, exiting now\n");
+ return -EINVAL;
+ }
}
ce_burst_size = pce_dev->ce_sps.ce_burst_size;
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 50d454c..41ab8dc 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -595,6 +595,9 @@
sreq.authklen = qcedev_areq->sha_op_req.authklen;
break;
default:
+ pr_err("Algorithm %d not supported, exiting\n",
+ qcedev_areq->sha_op_req.alg);
+ return -EINVAL;
break;
};
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 7e063ca..05ef87c 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -1175,6 +1175,8 @@
sreq.authklen = SHA_HMAC_KEY_SIZE;
break;
default:
+ pr_err("Algorithm %d not supported, exiting", sha_ctx->alg);
+ ret = -1;
break;
};
ret = qce_process_sha_req(cp->qce, &sreq);
@@ -1240,6 +1242,7 @@
if (rctx->data == NULL) {
pr_err("Mem Alloc fail rctx->data, err %ld\n",
PTR_ERR(rctx->data));
+ kzfree(qreq.assoc);
return -ENOMEM;
}
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index 1ca457f..832a9a1 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -55,7 +55,7 @@
},
{
.id = ION_CP_MM_HEAP_ID,
- .type = ION_HEAP_TYPE_CP,
+ .type = ION_HEAP_TYPE_SECURE_DMA,
.name = ION_MM_HEAP_NAME,
.permission_type = IPT_TYPE_MM_CARVEOUT,
},
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index b7d813c..b1a45bf 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2000,8 +2000,17 @@
/* Is the ring buffer is empty? */
GSL_RB_GET_READPTR(rb, &rb->rptr);
if (!device->active_cnt && (rb->rptr == rb->wptr)) {
- /* Is the core idle? */
- status = is_adreno_rbbm_status_idle(device);
+ /*
+ * Are there interrupts pending? If so then pretend we
+ * are not idle - this avoids the possiblity that we go
+ * to a lower power state without handling interrupts
+ * first.
+ */
+
+ if (!adreno_dev->gpudev->irq_pending(adreno_dev)) {
+ /* Is the core idle? */
+ status = is_adreno_rbbm_status_idle(device);
+ }
}
} else {
status = true;
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index cc6eb16..b1cab9b 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -125,6 +125,7 @@
struct adreno_context *);
irqreturn_t (*irq_handler)(struct adreno_device *);
void (*irq_control)(struct adreno_device *, int);
+ unsigned int (*irq_pending)(struct adreno_device *);
void * (*snapshot)(struct adreno_device *, void *, int *, int);
void (*rb_init)(struct adreno_device *, struct adreno_ringbuffer *);
void (*start)(struct adreno_device *);
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index 952d1f8..6db6e7b 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2013, 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
@@ -1706,34 +1706,6 @@
return;
}
- if (status & CP_INT_CNTL__RB_INT_MASK) {
- /* signal intr completion event */
- unsigned int context_id, timestamp;
- kgsl_sharedmem_readl(&device->memstore, &context_id,
- KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
- current_context));
-
- kgsl_sharedmem_readl(&device->memstore, ×tamp,
- KGSL_MEMSTORE_OFFSET(context_id,
- 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();
- }
-
- KGSL_CMD_WARN(device, "<%d:0x%x> ringbuffer interrupt\n",
- context_id, timestamp);
- }
-
for (i = 0; i < ARRAY_SIZE(kgsl_cp_error_irqs); i++) {
if (status & kgsl_cp_error_irqs[i].mask) {
KGSL_CMD_CRIT(rb->device, "%s\n",
@@ -1840,6 +1812,19 @@
wmb();
}
+static unsigned int a2xx_irq_pending(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ unsigned int rbbm, cp, mh;
+
+ adreno_regread(device, REG_RBBM_INT_CNTL, &rbbm);
+ adreno_regread(device, REG_CP_INT_CNTL, &cp);
+ adreno_regread(device, MH_INTERRUPT_MASK, &mh);
+
+ return ((rbbm & RBBM_INT_MASK) || (cp & CP_INT_MASK) ||
+ (mh & kgsl_mmu_get_int_mask())) ? 1 : 0;
+}
+
static void a2xx_rb_init(struct adreno_device *adreno_dev,
struct adreno_ringbuffer *rb)
{
@@ -2035,6 +2020,7 @@
.ctxt_draw_workaround = a2xx_drawctxt_draw_workaround,
.irq_handler = a2xx_irq_handler,
.irq_control = a2xx_irq_control,
+ .irq_pending = a2xx_irq_pending,
.snapshot = a2xx_snapshot,
.rb_init = a2xx_rb_init,
.busy_cycles = a2xx_busy_cycles,
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index a3739a2..73a7f52 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2591,33 +2591,7 @@
{
struct kgsl_device *device = &adreno_dev->dev;
- if (irq == A3XX_INT_CP_RB_INT) {
- unsigned int context_id, timestamp;
- kgsl_sharedmem_readl(&device->memstore, &context_id,
- KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
- current_context));
-
- kgsl_sharedmem_readl(&device->memstore, ×tamp,
- KGSL_MEMSTORE_OFFSET(context_id,
- 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();
- }
-
- KGSL_CMD_WARN(device, "<%d:0x%x> ringbuffer interrupt\n",
- context_id, timestamp);
- }
-
+ /* Wake up everybody waiting for the interrupt */
wake_up_interruptible_all(&device->wait_queue);
/* Schedule work to free mem and issue ibs */
@@ -2713,6 +2687,15 @@
adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, 0);
}
+static unsigned int a3xx_irq_pending(struct adreno_device *adreno_dev)
+{
+ unsigned int status;
+
+ adreno_regread(&adreno_dev->dev, A3XX_RBBM_INT_0_STATUS, &status);
+
+ return (status & A3XX_INT_MASK) ? 1 : 0;
+}
+
static unsigned int a3xx_busy_cycles(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = &adreno_dev->dev;
@@ -2958,6 +2941,7 @@
.rb_init = a3xx_rb_init,
.irq_control = a3xx_irq_control,
.irq_handler = a3xx_irq_handler,
+ .irq_pending = a3xx_irq_pending,
.busy_cycles = a3xx_busy_cycles,
.start = a3xx_start,
.snapshot = a3xx_snapshot,
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index c43ac51..1d25646 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -570,7 +570,7 @@
total_sizedwords += (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) ? 2 : 0;
/* Add CP_COND_EXEC commands to generate CP_INTERRUPT */
- total_sizedwords += context ? 7 : 0;
+ total_sizedwords += context ? 13 : 0;
if (adreno_is_a3xx(adreno_dev))
total_sizedwords += 7;
@@ -720,7 +720,25 @@
context_id, ref_wait_ts)) >> 2);
GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
/* # of conditional command DWORDs */
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 2);
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, 8);
+
+ /* Clear the ts_cmp_enable for the context */
+ GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_MEM_WRITE, 2));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, gpuaddr +
+ KGSL_MEMSTORE_OFFSET(
+ context_id, ts_cmp_enable));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x0);
+
+ /* Clear the ts_cmp_enable for the global timestamp */
+ GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_MEM_WRITE, 2));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, gpuaddr +
+ KGSL_MEMSTORE_OFFSET(
+ KGSL_MEMSTORE_GLOBAL, ts_cmp_enable));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x0);
+
+ /* Trigger the interrupt */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_INTERRUPT, 1));
GSL_RB_WRITE(ringcmds, rcmd_gpu, CP_INT_CNTL__RB_INT_MASK);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 115fcb7..7ed0b10 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1088,10 +1088,7 @@
result);
/* Fire off any pending suspend operations that are in flight */
-
- INIT_COMPLETION(dev_priv->device->suspend_gate);
- dev_priv->device->active_cnt--;
- complete(&dev_priv->device->suspend_gate);
+ kgsl_active_count_put(dev_priv->device);
return result;
}
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 62316f3..66390fc 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -133,6 +133,7 @@
void *priv;
struct list_head list;
void *owner;
+ unsigned int created;
};
@@ -449,4 +450,23 @@
kref_put(&context->refcount, kgsl_context_destroy);
}
+/**
+ * kgsl_active_count_put - Decrease the device active count
+ * @device: Pointer to a KGSL device
+ *
+ * Decrease the active count for the KGSL device and trigger the suspend_gate
+ * completion if it hits zero
+ */
+static inline void
+kgsl_active_count_put(struct kgsl_device *device)
+{
+ if (device->active_cnt == 1)
+ INIT_COMPLETION(device->suspend_gate);
+
+ device->active_cnt--;
+
+ if (device->active_cnt == 0)
+ complete(&device->suspend_gate);
+}
+
#endif /* __KGSL_DEVICE_H */
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index be9b5eb..6798eed 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -16,6 +16,8 @@
#include <linux/module.h>
#include <kgsl_device.h>
+#include "kgsl_trace.h"
+
static void _add_event_to_list(struct list_head *head, struct kgsl_event *event)
{
struct list_head *n;
@@ -71,6 +73,7 @@
*/
if (timestamp_cmp(cur_ts, ts) >= 0) {
+ trace_kgsl_fire_event(id, ts, 0);
cb(device, priv, id, ts);
return 0;
}
@@ -84,6 +87,9 @@
event->priv = priv;
event->func = cb;
event->owner = owner;
+ event->created = jiffies;
+
+ trace_kgsl_register_event(id, ts);
/* inc refcount to avoid race conditions in cleanup */
if (context)
@@ -106,6 +112,13 @@
} else
_add_event_to_list(&device->events, event);
+ /*
+ * Increase the active count on the device to avoid going into power
+ * saving modes while events are pending
+ */
+
+ device->active_cnt++;
+
queue_work(device->work_queue, &device->ts_expired_ws);
return 0;
}
@@ -137,12 +150,16 @@
* system got before the event was canceled
*/
+ trace_kgsl_fire_event(id, cur, jiffies - event->created);
+
if (event->func)
event->func(device, event->priv, id, cur);
kgsl_context_put(context);
list_del(&event->list);
kfree(event);
+
+ kgsl_active_count_put(device);
}
/* Remove ourselves from the master pending list */
@@ -175,6 +192,10 @@
* the callback knows how far the GPU made it before things went
* explosion
*/
+
+ trace_kgsl_fire_event(KGSL_MEMSTORE_GLOBAL, cur,
+ jiffies - event->created);
+
if (event->func)
event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL,
cur);
@@ -184,6 +205,8 @@
list_del(&event->list);
kfree(event);
+
+ kgsl_active_count_put(device);
}
}
EXPORT_SYMBOL(kgsl_cancel_events);
@@ -207,6 +230,9 @@
* to the timestamp they wanted
*/
+ trace_kgsl_fire_event(id, event->timestamp,
+ jiffies - event->created);
+
if (event->func)
event->func(device, event->priv, id, event->timestamp);
@@ -215,6 +241,8 @@
list_del(&event->list);
kfree(event);
+
+ kgsl_active_count_put(device);
}
}
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index f7818bb..8c4811e 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -693,6 +693,41 @@
)
);
+TRACE_EVENT(kgsl_register_event,
+ TP_PROTO(unsigned int id, unsigned int timestamp),
+ TP_ARGS(id, timestamp),
+ TP_STRUCT__entry(
+ __field(unsigned int, id)
+ __field(unsigned int, timestamp)
+ ),
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->timestamp = timestamp;
+ ),
+ TP_printk(
+ "ctx=%d ts=%d",
+ __entry->id, __entry->timestamp)
+);
+
+TRACE_EVENT(kgsl_fire_event,
+ TP_PROTO(unsigned int id, unsigned int ts,
+ unsigned int age),
+ TP_ARGS(id, ts, age),
+ TP_STRUCT__entry(
+ __field(unsigned int, id)
+ __field(unsigned int, ts)
+ __field(unsigned int, age)
+ ),
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->ts = ts;
+ __entry->age = age;
+ ),
+ TP_printk(
+ "ctx=%d ts=%d age=%u",
+ __entry->id, __entry->ts, __entry->age)
+);
+
#endif /* _KGSL_TRACE_H */
/* This part must be outside protection */
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index 8f03ccb..e578b0e 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -817,9 +817,9 @@
{
int status = -EINVAL;
- /* Don't wait forever, set a max (10 sec) value for now */
+ /* Don't wait forever, set a max of Z180_IDLE_TIMEOUT */
if (msecs == -1)
- msecs = 10 * MSEC_PER_SEC;
+ msecs = Z180_IDLE_TIMEOUT;
mutex_unlock(&device->mutex);
status = z180_wait(device, context, timestamp, msecs);
diff --git a/drivers/gud/mobicore_driver/arm.h b/drivers/gud/mobicore_driver/arm.h
index 0cddc0c..439feed 100644
--- a/drivers/gud/mobicore_driver/arm.h
+++ b/drivers/gud/mobicore_driver/arm.h
@@ -2,6 +2,7 @@
* MobiCore driver module.(interface to the secure world SWD)
*
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/drivers/gud/mobicore_driver/build_tag.h b/drivers/gud/mobicore_driver/build_tag.h
index a6898f1..4abd003 100644
--- a/drivers/gud/mobicore_driver/build_tag.h
+++ b/drivers/gud/mobicore_driver/build_tag.h
@@ -1,5 +1,5 @@
/*
- * <-- Copyright Giesecke & Devrient GmbH 2012-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,4 +26,4 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define MOBICORE_COMPONENT_BUILD_TAG \
- "*** GC_MSM8960_Release_V013 ###"
+ "*** GC_MSM8960_Release_V016 ###"
diff --git a/drivers/gud/mobicore_driver/debug.h b/drivers/gud/mobicore_driver/debug.h
index 0195877..1f9a632 100644
--- a/drivers/gud/mobicore_driver/debug.h
+++ b/drivers/gud/mobicore_driver/debug.h
@@ -2,6 +2,7 @@
* MobiCore driver module.(interface to the secure world SWD)
*
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/drivers/gud/mobicore_driver/fastcall.h b/drivers/gud/mobicore_driver/fastcall.h
index d5f9abc..1c90520 100644
--- a/drivers/gud/mobicore_driver/fastcall.h
+++ b/drivers/gud/mobicore_driver/fastcall.h
@@ -4,6 +4,7 @@
* MobiCore Fast Call interface
*
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/drivers/gud/mobicore_driver/logging.c b/drivers/gud/mobicore_driver/logging.c
index 4160292..1f599f9 100644
--- a/drivers/gud/mobicore_driver/logging.c
+++ b/drivers/gud/mobicore_driver/logging.c
@@ -316,6 +316,7 @@
ret = -EIO;
goto err_stop_kthread;
}
+
set_task_state(log_thread, TASK_INTERRUPTIBLE);
MCDRV_DBG(mcd, "fc_log Logger version %u\n", log_buf->version);
diff --git a/drivers/gud/mobicore_driver/logging.h b/drivers/gud/mobicore_driver/logging.h
index ec7587f..ddb60fb 100644
--- a/drivers/gud/mobicore_driver/logging.h
+++ b/drivers/gud/mobicore_driver/logging.h
@@ -2,6 +2,7 @@
* MobiCore driver module.(interface to the secure world SWD)
*
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/drivers/gud/mobicore_driver/main.c b/drivers/gud/mobicore_driver/main.c
index df5675e..a2b3ad7 100644
--- a/drivers/gud/mobicore_driver/main.c
+++ b/drivers/gud/mobicore_driver/main.c
@@ -28,6 +28,11 @@
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/completion.h>
+#include <linux/fdtable.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+#include <net/af_unix.h>
#include "main.h"
#include "fastcall.h"
@@ -51,6 +56,32 @@
struct device *mcd = &mcd_debug_subname;
+#ifndef FMODE_PATH
+ #define FMODE_PATH 0x0
+#endif
+
+static struct sock *__get_socket(struct file *filp)
+{
+ struct sock *u_sock = NULL;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+
+ /*
+ * Socket ?
+ */
+ if (S_ISSOCK(inode->i_mode) && !(filp->f_mode & FMODE_PATH)) {
+ struct socket *sock = SOCKET_I(inode);
+ struct sock *s = sock->sk;
+
+ /*
+ * PF_UNIX ?
+ */
+ if (s && sock->ops && sock->ops->family == PF_UNIX)
+ u_sock = s;
+ }
+ return u_sock;
+}
+
+
/* MobiCore interrupt context data */
struct mc_context ctx;
@@ -137,8 +168,60 @@
return ret;
}
+bool mc_check_owner_fd(struct mc_instance *instance, int32_t fd)
+{
+#ifndef __ARM_VE_A9X4_STD__
+ struct file *fp;
+ struct sock *s;
+ struct files_struct *files;
+ struct task_struct *peer = NULL;
+ bool ret = false;
+
+ MCDRV_DBG(mcd, "Finding wsm for fd = %d\n", fd);
+ if (!instance)
+ return false;
+
+ if (is_daemon(instance))
+ return true;
+
+ fp = fcheck_files(current->files, fd);
+ s = __get_socket(fp);
+ if (s) {
+ peer = get_pid_task(s->sk_peer_pid, PIDTYPE_PID);
+ MCDRV_DBG(mcd, "Found pid for fd %d\n", peer->pid);
+ }
+ if (peer) {
+ task_lock(peer);
+ files = peer->files;
+ if (!files)
+ goto out;
+ for (fd = 0; fd < files_fdtable(files)->max_fds; fd++) {
+ fp = fcheck_files(files, fd);
+ if (!fp)
+ continue;
+ if (fp->private_data == instance) {
+ MCDRV_DBG(mcd, "Found owner!");
+ ret = true;
+ goto out;
+ }
+
+ }
+ } else {
+ MCDRV_DBG(mcd, "Owner not found!");
+ return false;
+ }
+out:
+ if (peer)
+ task_unlock(peer);
+ if (!ret)
+ MCDRV_DBG(mcd, "Owner not found!");
+ return ret;
+#else
+ return true;
+#endif
+}
static uint32_t mc_find_cont_wsm(struct mc_instance *instance, uint32_t handle,
- uint32_t *phys, uint32_t *len)
+ int32_t fd, uint32_t *phys, uint32_t *len)
{
int ret = 0;
struct mc_buffer *buffer;
@@ -158,9 +241,13 @@
/* search for the given handle in the buffers list */
list_for_each_entry(buffer, &ctx.cont_bufs, list) {
if (buffer->handle == handle) {
- *phys = (uint32_t)buffer->phys;
- *len = buffer->len;
- goto found;
+ if (mc_check_owner_fd(buffer->instance, fd)) {
+ *phys = (uint32_t)buffer->phys;
+ *len = buffer->len;
+ goto found;
+ } else {
+ break;
+ }
}
}
@@ -206,36 +293,34 @@
goto found_buffer;
}
}
+ ret = -EINVAL;
goto err;
found_buffer:
- if (!is_daemon(instance) || buffer->instance != instance)
+ if (!is_daemon(instance) && buffer->instance != instance) {
+ ret = -EPERM;
goto err;
+ }
mutex_unlock(&ctx.bufs_lock);
/* Only unmap if the request is comming from the user space and
* it hasn't already been unmapped */
- if (unlock == false && uaddr != NULL)
+ if (unlock == false && uaddr != NULL) {
#ifndef MC_VM_UNMAP
/* do_munmap must be done with mm->mmap_sem taken */
down_write(&mm->mmap_sem);
ret = do_munmap(mm, (long unsigned int)uaddr, len);
+ up_write(&mm->mmap_sem);
+
+#else
+ ret = vm_munmap((long unsigned int)uaddr, len);
+#endif
if (ret < 0) {
/* Something is not right if we end up here, better not
* clean the buffer so we just leak memory instead of
* creating security issues */
MCDRV_DBG_ERROR(mcd, "Memory can't be unmapped\n");
- }
- up_write(&mm->mmap_sem);
- if (ret < 0)
- return -EINVAL;
-#else
- if (vm_munmap((long unsigned int)uaddr, len) < 0) {
- /* Something is not right if we end up here, better not
- * clean the buffer so we just leak memory instead of
- * creating security issues */
- MCDRV_DBG_ERROR(mcd, "Memory can't be unmapped\n");
return -EINVAL;
}
-#endif
+ }
mutex_lock(&ctx.bufs_lock);
/* search for the given handle in the buffers list */
@@ -247,7 +332,10 @@
goto err;
del_buffer:
- ret = free_buffer(buffer, unlock);
+ if (is_daemon(instance) || buffer->instance == instance)
+ ret = free_buffer(buffer, unlock);
+ else
+ ret = -EPERM;
err:
mutex_unlock(&ctx.bufs_lock);
return ret;
@@ -324,6 +412,7 @@
cbuffer->order = order;
cbuffer->len = len;
cbuffer->instance = instance;
+ cbuffer->uaddr = 0;
/* Refcount +1 because the TLC is requesting it */
atomic_set(&cbuffer->usage, 1);
@@ -511,7 +600,8 @@
return ret;
}
-static uint32_t mc_find_wsm_l2(struct mc_instance *instance, uint32_t handle)
+static uint32_t mc_find_wsm_l2(struct mc_instance *instance,
+ uint32_t handle, int32_t fd)
{
uint32_t ret = 0;
@@ -523,9 +613,7 @@
return 0;
}
- mutex_lock(&instance->lock);
- ret = mc_find_l2_table(instance, handle);
- mutex_unlock(&instance->lock);
+ ret = mc_find_l2_table(handle, fd);
return ret;
}
@@ -569,10 +657,18 @@
/* search for the buffer list. */
list_for_each_entry(buffer, &ctx.cont_bufs, list) {
- if (buffer->phys == paddr)
- goto found;
- else
+ /* Only allow mapping if the client owns it!*/
+ if (buffer->phys == paddr &&
+ buffer->instance == instance) {
+ /* We shouldn't do remap with larger size */
+ if (buffer->len > len)
break;
+ /* We can't allow mapping the buffer twice */
+ if (!buffer->uaddr)
+ goto found;
+ else
+ break;
+ }
}
/* Nothing found return */
mutex_unlock(&ctx.bufs_lock);
@@ -738,16 +834,6 @@
if (ioctl_check_pointer(cmd, uarg))
return -EFAULT;
- if (ctx.mcp) {
- while (ctx.mcp->flags.sleep_mode.SleepReq) {
- ctx.daemon = current;
- set_current_state(TASK_INTERRUPTIBLE);
- /* Back off daemon for a while */
- schedule_timeout(msecs_to_jiffies(DAEMON_BACKOFF_TIME));
- set_current_state(TASK_RUNNING);
- }
- }
-
switch (cmd) {
case MC_IO_INIT: {
struct mc_ioctl_init init;
@@ -798,13 +884,18 @@
ret = mc_clean_wsm_l2(instance);
break;
case MC_IO_RESOLVE_WSM: {
- uint32_t handle, phys;
- if (get_user(handle, uarg))
+ uint32_t phys;
+ struct mc_ioctl_resolv_wsm wsm;
+ if (copy_from_user(&wsm, uarg, sizeof(wsm)))
return -EFAULT;
- phys = mc_find_wsm_l2(instance, handle);
+ phys = mc_find_wsm_l2(instance, wsm.handle, wsm.fd);
if (!phys)
+ return -EINVAL;
+
+ wsm.phys = phys;
+ if (copy_to_user(uarg, &wsm, sizeof(wsm)))
return -EFAULT;
- ret = put_user(phys, uarg);
+ ret = 0;
break;
}
case MC_IO_RESOLVE_CONT_WSM: {
@@ -812,7 +903,8 @@
uint32_t phys = 0, len = 0;
if (copy_from_user(&cont_wsm, uarg, sizeof(cont_wsm)))
return -EFAULT;
- ret = mc_find_cont_wsm(instance, cont_wsm.handle, &phys, &len);
+ ret = mc_find_cont_wsm(instance, cont_wsm.handle, cont_wsm.fd,
+ &phys, &len);
if (!ret) {
cont_wsm.phys = phys;
cont_wsm.length = len;
@@ -980,6 +1072,10 @@
/* Check if some buffers are orphaned. */
list_for_each_entry_safe(buffer, tmp, &ctx.cont_bufs, list) {
+ /* It's safe here to only call free_buffer() without unmapping
+ * because mmap() takes a refcount to the file's fd so only
+ * time we end up here is when everything has been unmaped or
+ * the process called exit() */
if (buffer->instance == instance) {
buffer->instance = NULL;
free_buffer(buffer, false);
@@ -1163,13 +1259,17 @@
return -ENODEV;
}
+ ret = mc_fastcall_init(&ctx);
+ if (ret)
+ goto error;
+
init_completion(&ctx.isr_comp);
/* set up S-SIQ interrupt handler */
ret = request_irq(MC_INTR_SSIQ, mc_ssiq_isr, IRQF_TRIGGER_RISING,
MC_ADMIN_DEVNODE, &ctx);
if (ret != 0) {
MCDRV_DBG_ERROR(mcd, "interrupt request failed\n");
- goto error;
+ goto err_req_irq;
}
#ifdef MC_PM_RUNTIME
@@ -1219,6 +1319,8 @@
misc_deregister(&mc_admin_device);
free_isr:
free_irq(MC_INTR_SSIQ, &ctx);
+err_req_irq:
+ mc_fastcall_destroy();
error:
return ret;
}
@@ -1243,6 +1345,9 @@
misc_deregister(&mc_admin_device);
misc_deregister(&mc_user_device);
+
+ mc_fastcall_destroy();
+
MCDRV_DBG_VERBOSE(mcd, "exit");
}
diff --git a/drivers/gud/mobicore_driver/main.h b/drivers/gud/mobicore_driver/main.h
index 2c316bc..871191e 100644
--- a/drivers/gud/mobicore_driver/main.h
+++ b/drivers/gud/mobicore_driver/main.h
@@ -4,6 +4,7 @@
* Internal structures of the McDrvModule
*
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -143,4 +144,7 @@
/* Free the buffer allocated above */
int mc_free_buffer(struct mc_instance *instance, uint32_t handle);
+/* Check if the other end of the fd owns instance */
+bool mc_check_owner_fd(struct mc_instance *instance, int32_t fd);
+
#endif /* _MC_MAIN_H_ */
diff --git a/drivers/gud/mobicore_driver/mem.c b/drivers/gud/mobicore_driver/mem.c
index 1fe351b..33c51b6 100644
--- a/drivers/gud/mobicore_driver/mem.c
+++ b/drivers/gud/mobicore_driver/mem.c
@@ -618,14 +618,11 @@
return ERR_PTR(ret);
}
-uint32_t mc_find_l2_table(struct mc_instance *instance, uint32_t handle)
+uint32_t mc_find_l2_table(uint32_t handle, int32_t fd)
{
uint32_t ret = 0;
struct mc_l2_table *table = NULL;
- if (WARN(!instance, "No instance data available"))
- return 0;
-
mutex_lock(&mem_ctx.table_lock);
table = find_l2_table(handle);
@@ -635,6 +632,14 @@
goto table_err;
}
+ /* It's safe here not to lock the instance since the owner of
+ * the table will be cleared only with the table lock taken */
+ if (!mc_check_owner_fd(table->owner, fd)) {
+ MCDRV_DBG_ERROR(mcd, "not valid owner%u\n", handle);
+ ret = 0;
+ goto table_err;
+ }
+
ret = table->phys;
table_err:
mutex_unlock(&mem_ctx.table_lock);
diff --git a/drivers/gud/mobicore_driver/mem.h b/drivers/gud/mobicore_driver/mem.h
index a90662a7..397a6cc 100644
--- a/drivers/gud/mobicore_driver/mem.h
+++ b/drivers/gud/mobicore_driver/mem.h
@@ -2,6 +2,7 @@
* MobiCore driver module.(interface to the secure world SWD)
*
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -117,7 +118,7 @@
/* Unlock l2 table. */
int mc_unlock_l2_table(struct mc_instance *instance, uint32_t handle);
/* Return the phys address of l2 table. */
-uint32_t mc_find_l2_table(struct mc_instance *instance, uint32_t handle);
+uint32_t mc_find_l2_table(uint32_t handle, int32_t fd);
/* Release all used l2 tables to Linux memory space */
void mc_release_l2_tables(void);
diff --git a/drivers/gud/mobicore_driver/ops.c b/drivers/gud/mobicore_driver/ops.c
index b44a842..05c80b7 100644
--- a/drivers/gud/mobicore_driver/ops.c
+++ b/drivers/gud/mobicore_driver/ops.c
@@ -28,8 +28,83 @@
#include "fastcall.h"
#include "ops.h"
#include "mem.h"
+#include "pm.h"
#include "debug.h"
+/* MobiCore context data */
+static struct mc_context *ctx;
+
+static inline long smc(union fc_generic *fc)
+{
+ /* If we request sleep yields must be filtered out as they
+ * make no sense */
+ if (ctx->mcp)
+ if (ctx->mcp->flags.sleep_mode.SleepReq) {
+ if (fc->as_in.cmd == MC_SMC_N_YIELD)
+ return MC_FC_RET_ERR_INVALID;
+ }
+ return _smc(fc);
+}
+
+#ifdef MC_FASTCALL_WORKER_THREAD
+
+static struct task_struct *fastcall_thread;
+static DEFINE_KTHREAD_WORKER(fastcall_worker);
+
+struct fastcall_work {
+ struct kthread_work work;
+ void *data;
+};
+
+static void fastcall_work_func(struct kthread_work *work)
+{
+ struct fastcall_work *fc_work =
+ container_of(work, struct fastcall_work, work);
+ smc(fc_work->data);
+}
+
+void mc_fastcall(void *data)
+{
+ struct fastcall_work fc_work = {
+ KTHREAD_WORK_INIT(fc_work.work, fastcall_work_func),
+ .data = data,
+ };
+
+ queue_kthread_work(&fastcall_worker, &fc_work.work);
+ flush_kthread_work(&fc_work.work);
+}
+
+int mc_fastcall_init(struct mc_context *context)
+{
+ int ret = 0;
+
+ ctx = context;
+
+ fastcall_thread = kthread_create(kthread_worker_fn, &fastcall_worker,
+ "mc_fastcall");
+ if (IS_ERR(fastcall_thread)) {
+ ret = PTR_ERR(fastcall_thread);
+ fastcall_thread = NULL;
+ MCDRV_DBG_ERROR(mcd, "cannot create fastcall wq (%d)\n", ret);
+ return ret;
+ }
+
+ /* this thread MUST run on CPU 0 */
+ kthread_bind(fastcall_thread, 0);
+ wake_up_process(fastcall_thread);
+
+ return 0;
+}
+
+void mc_fastcall_destroy(void)
+{
+ if (!IS_ERR_OR_NULL(fastcall_thread)) {
+ kthread_stop(fastcall_thread);
+ fastcall_thread = NULL;
+ }
+}
+#else
+
struct fastcall_work_struct {
struct work_struct work;
void *data;
@@ -39,7 +114,7 @@
{
struct fastcall_work_struct *fc_work =
container_of(work, struct fastcall_work_struct, work);
- _smc(fc_work->data);
+ smc(fc_work->data);
}
void mc_fastcall(void *data)
@@ -53,6 +128,15 @@
flush_work(&work.work);
}
+int mc_fastcall_init(struct mc_context *context)
+{
+ ctx = context;
+ return 0;
+};
+
+void mc_fastcall_destroy(void) {};
+#endif
+
int mc_info(uint32_t ext_info_id, uint32_t *state, uint32_t *ext_info)
{
int ret = 0;
@@ -117,6 +201,21 @@
return ret;
}
+/* call common notify */
+int _nsiq(void)
+{
+ int ret = 0;
+ union fc_generic nsiq;
+ MCDRV_DBG_VERBOSE(mcd, "enter\n");
+
+ memset(&nsiq, 0, sizeof(nsiq));
+ nsiq.as_in.cmd = MC_SMC_N_SIQ;
+ _smc(&nsiq);
+ ret = convert_fc_ret(nsiq.as_out.ret);
+
+ return ret;
+}
+
/* Call the INIT fastcall to setup MobiCore initialization */
int mc_init(uint32_t base, uint32_t nq_offset, uint32_t nq_length,
uint32_t mcp_offset, uint32_t mcp_length)
diff --git a/drivers/gud/mobicore_driver/ops.h b/drivers/gud/mobicore_driver/ops.h
index efe5f05..910c1f4 100644
--- a/drivers/gud/mobicore_driver/ops.h
+++ b/drivers/gud/mobicore_driver/ops.h
@@ -2,6 +2,7 @@
* MobiCore driver module.(interface to the secure world SWD)
*
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -16,6 +17,7 @@
int mc_yield(void);
int mc_nsiq(void);
+int _nsiq(void);
uint32_t mc_get_version(void);
int mc_info(uint32_t ext_info_id, uint32_t *state, uint32_t *ext_info);
@@ -24,4 +26,7 @@
void mc_fastcall(void *data);
+int mc_fastcall_init(struct mc_context *context);
+void mc_fastcall_destroy(void);
+
#endif /* _MC_OPS_H_ */
diff --git a/drivers/gud/mobicore_driver/pm.h b/drivers/gud/mobicore_driver/pm.h
index 067f095..3e73b8b 100644
--- a/drivers/gud/mobicore_driver/pm.h
+++ b/drivers/gud/mobicore_driver/pm.h
@@ -2,6 +2,7 @@
* Header file of MobiCore Driver Kernel Module.
*
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -11,7 +12,24 @@
#ifndef _MC_PM_H_
#define _MC_PM_H_
-/* How much time after resume the daemon should back off */
-#define DAEMON_BACKOFF_TIME 10
+#include "main.h"
+#ifdef MC_BL_NOTIFIER
+#include <asm/bL_switcher.h>
+#endif
+
+
+#define NO_SLEEP_REQ 0
+#define REQ_TO_SLEEP 1
+
+#define NORMAL_EXECUTION 0
+#define READY_TO_SLEEP 1
+
+/* How much time after resume the daemon should backoff */
+#define DAEMON_BACKOFF_TIME 500
+
+/* Initialize Power Management */
+int mc_pm_initialize(struct mc_context *context);
+/* Free all Power Management resources*/
+int mc_pm_free(void);
#endif /* _MC_PM_H_ */
diff --git a/drivers/gud/mobicore_driver/public/mc_linux.h b/drivers/gud/mobicore_driver/public/mc_linux.h
index bb95c26..9c49aef 100644
--- a/drivers/gud/mobicore_driver/public/mc_linux.h
+++ b/drivers/gud/mobicore_driver/public/mc_linux.h
@@ -127,6 +127,20 @@
uint32_t phys;
/* length memory */
uint32_t length;
+ /* fd to owner of the buffer */
+ int32_t fd;
+};
+
+/*
+ * Data exchange structure of the MC_IO_RESOLVE_WSM ioctl command.
+ */
+struct mc_ioctl_resolv_wsm {
+ /* driver handle for buffer */
+ uint32_t handle;
+ /* fd to owner of the buffer */
+ int32_t fd;
+ /* base address of memory */
+ uint32_t phys;
};
@@ -200,12 +214,14 @@
* Get L2 phys address of a buffer handle allocated to the user.
* Only available to the daemon.
*/
-#define MC_IO_RESOLVE_WSM _IOWR(MC_IOC_MAGIC, 15, uint32_t)
+#define MC_IO_RESOLVE_WSM _IOWR(MC_IOC_MAGIC, 15, \
+ struct mc_ioctl_resolv_wsm)
/*
* Get the phys address & length of a allocated contiguous buffer.
* Only available to the daemon */
-#define MC_IO_RESOLVE_CONT_WSM _IOWR(MC_IOC_MAGIC, 16, struct mc_ioctl_execute)
+#define MC_IO_RESOLVE_CONT_WSM _IOWR(MC_IOC_MAGIC, 16, \
+ struct mc_ioctl_resolv_cont_wsm)
/*
* Setup the mem traces when called.
diff --git a/drivers/gud/mobicore_kernelapi/main.c b/drivers/gud/mobicore_kernelapi/main.c
index 73de93a..8943c26 100644
--- a/drivers/gud/mobicore_kernelapi/main.c
+++ b/drivers/gud/mobicore_kernelapi/main.c
@@ -139,29 +139,28 @@
static int __init mcapi_init(void)
{
- /* struct netlink_kernel_cfg netlink_cfg; */
+#if defined MC_NETLINK_COMPAT || defined MC_NETLINK_COMPAT_V37
+ struct netlink_kernel_cfg cfg = {
+ .input = mcapi_callback,
+ };
+#endif
dev_set_name(mc_kapi, "mcapi");
dev_info(mc_kapi, "Mobicore API module initialized!\n");
- /*
- * netlink_cfg.groups = 0;
- * netlink_cfg.flags = 0;
- * netlink_cfg.input = mcapi_callback;
- * netlink_cfg.cb_mutex = NULL;
- * netlink_cfg.bind = NULL;
- */
mod_ctx = kzalloc(sizeof(struct mc_kernelapi_ctx), GFP_KERNEL);
-
+#ifdef MC_NETLINK_COMPAT_V37
+ mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK,
+ &cfg);
+#elif defined MC_NETLINK_COMPAT
+ mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK,
+ THIS_MODULE, &cfg);
+#else
/* start kernel thread */
-
- /*
- * mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK,
- * &netlink_cfg);
- */
mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK, 0,
mcapi_callback, NULL, THIS_MODULE);
+#endif
if (!mod_ctx->sk) {
MCDRV_ERROR(mc_kapi, "register of receive handler failed");
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index ca0a439..a77dacb 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2013, 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
@@ -29,13 +29,14 @@
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/slab.h>
-#include <mach/board.h>
-#include <mach/gpiomux.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_i2c.h>
+#include <linux/of_gpio.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.2");
@@ -131,6 +132,7 @@
#define DEFAULT_CLK_RATE (19200000)
#define I2C_STATUS_CLK_STATE 13
#define QUP_OUT_FIFO_NOT_EMPTY 0x10
+#define I2C_GPIOS_DT_CNT (2) /* sda and scl */
static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"};
@@ -1088,6 +1090,72 @@
return ret;
}
+enum msm_i2c_dt_entry_status {
+ DT_REQUIRED,
+ DT_SUGGESTED,
+ DT_OPTIONAL,
+};
+
+enum msm_i2c_dt_entry_type {
+ DT_U32,
+ DT_GPIO,
+};
+
+struct msm_i2c_dt_to_pdata_map {
+ const char *dt_name;
+ int *ptr_data;
+ enum msm_i2c_dt_entry_status status;
+ enum msm_i2c_dt_entry_type type;
+ int default_val;
+};
+
+int __devinit msm_i2c_rsrcs_dt_to_pdata_map(struct platform_device *pdev,
+ struct msm_i2c_platform_data *pdata, int *gpios)
+{
+ int ret, err = 0;
+ struct device_node *node = pdev->dev.of_node;
+ struct msm_i2c_dt_to_pdata_map *itr;
+ struct msm_i2c_dt_to_pdata_map map[] = {
+ {"qcom,i2c-bus-freq", &pdata->clk_freq , DT_REQUIRED , DT_U32 , 0},
+ {"cell-index" , &pdev->id , DT_REQUIRED , DT_U32 , -1},
+ {"qcom,i2c-src-freq", &pdata->src_clk_rate, DT_SUGGESTED, DT_U32, 0},
+ {"qcom,scl-gpio" , gpios , DT_OPTIONAL , DT_GPIO, -1},
+ {"qcom,sda-gpio" , gpios + 1 , DT_OPTIONAL , DT_GPIO, -1},
+ {NULL , NULL , 0 , 0 , 0},
+ };
+
+ for (itr = map; itr->dt_name ; ++itr) {
+ if (itr->type == DT_GPIO) {
+ ret = of_get_named_gpio(node, itr->dt_name, 0);
+ if (ret >= 0) {
+ *itr->ptr_data = ret;
+ ret = 0;
+ }
+ } else {
+ ret = of_property_read_u32(node, itr->dt_name,
+ itr->ptr_data);
+ }
+
+ dev_dbg(&pdev->dev, "DT entry ret:%d name:%s val:%d\n",
+ ret, itr->dt_name, *itr->ptr_data);
+
+ if (ret) {
+ *itr->ptr_data = itr->default_val;
+
+ if (itr->status < DT_OPTIONAL) {
+ dev_err(&pdev->dev, "Missing '%s' DT entry\n",
+ itr->dt_name);
+
+ /* cont on err to dump all missing entries */
+ if (itr->status == DT_REQUIRED && !err)
+ err = ret;
+ }
+ }
+ }
+
+ return err;
+}
+
static u32
qup_i2c_func(struct i2c_adapter *adap)
{
@@ -1106,28 +1174,23 @@
struct resource *qup_mem, *gsbi_mem, *qup_io, *gsbi_io, *res;
struct resource *in_irq, *out_irq, *err_irq;
struct clk *clk, *pclk;
- int ret = 0;
- int i;
+ int ret = 0;
+ int i;
+ int dt_gpios[I2C_GPIOS_DT_CNT];
+ bool use_device_tree = pdev->dev.of_node;
struct msm_i2c_platform_data *pdata;
gsbi_mem = NULL;
dev_dbg(&pdev->dev, "qup_i2c_probe\n");
- if (pdev->dev.of_node) {
- struct device_node *node = pdev->dev.of_node;
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (use_device_tree) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
- ret = of_property_read_u32(node, "qcom,i2c-bus-freq",
- &pdata->clk_freq);
+
+ ret = msm_i2c_rsrcs_dt_to_pdata_map(pdev, pdata, dt_gpios);
if (ret)
goto get_res_failed;
- ret = of_property_read_u32(node, "cell-index", &pdev->id);
- if (ret)
- goto get_res_failed;
- /* Optional property */
- of_property_read_u32(node, "qcom,i2c-src-freq",
- &pdata->src_clk_rate);
} else
pdata = pdev->dev.platform_data;
@@ -1247,9 +1310,13 @@
}
for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
- res = platform_get_resource_byname(pdev, IORESOURCE_IO,
- i2c_rsrcs[i]);
- dev->i2c_gpios[i] = res ? res->start : -1;
+ if (use_device_tree && i < I2C_GPIOS_DT_CNT) {
+ dev->i2c_gpios[i] = dt_gpios[i];
+ } else {
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ i2c_rsrcs[i]);
+ dev->i2c_gpios[i] = res ? res->start : -1;
+ }
}
platform_set_drvdata(pdev, dev);
@@ -1261,8 +1328,7 @@
if (dev->pdata->src_clk_rate <= 0) {
dev_info(&pdev->dev,
- "No src_clk_rate specified in platfrom data or "
- "qcom,i2c-src-freq in DT\n");
+ "No src_clk_rate specified in platfrom data\n");
dev_info(&pdev->dev, "Using default clock rate %dHz\n",
DEFAULT_CLK_RATE);
dev->pdata->src_clk_rate = DEFAULT_CLK_RATE;
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 899c83b..2c79276 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -82,6 +82,8 @@
#define RMI4_I2C_LOAD_UA 10000
#define RMI4_I2C_LPM_LOAD_UA 10
+#define RMI4_GPIO_SLEEP_LOW_US 10000
+#define RMI4_GPIO_WAIT_HIGH_MS 25
static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr, unsigned char *data,
@@ -1871,15 +1873,60 @@
retval = synaptics_rmi4_regulator_configure(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev, "Failed to configure regulators\n");
- goto err_input_device;
+ goto err_reg_configure;
}
retval = synaptics_rmi4_power_on(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev, "Failed to power on\n");
- goto err_input_device;
+ goto err_power_device;
}
+ if (gpio_is_valid(platform_data->irq_gpio)) {
+ /* configure touchscreen irq gpio */
+ retval = gpio_request(platform_data->irq_gpio, "rmi4_irq_gpio");
+ if (retval) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_query_device;
+ }
+ retval = gpio_direction_input(platform_data->irq_gpio);
+ if (retval) {
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_irq_gpio_req;
+ }
+ } else {
+ dev_err(&client->dev, "irq gpio not provided\n");
+ goto err_query_device;
+ }
+
+ if (gpio_is_valid(platform_data->reset_gpio)) {
+ /* configure touchscreen reset out gpio */
+ retval = gpio_request(platform_data->reset_gpio,
+ "rmi4_reset_gpio");
+ if (retval) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->reset_gpio);
+ goto err_irq_gpio_req;
+ }
+
+ retval = gpio_direction_output(platform_data->reset_gpio, 1);
+ if (retval) {
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->reset_gpio);
+ goto err_reset_gpio_req;
+ }
+
+ gpio_set_value(platform_data->reset_gpio, 0);
+ usleep(RMI4_GPIO_SLEEP_LOW_US);
+ gpio_set_value(platform_data->reset_gpio, 1);
+ msleep(RMI4_GPIO_WAIT_HIGH_MS);
+ }
+
+
init_waitqueue_head(&rmi4_data->wait);
mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex));
@@ -1888,7 +1935,7 @@
dev_err(&client->dev,
"%s: Failed to query device\n",
__func__);
- goto err_query_device;
+ goto err_reset_gpio_req;
}
i2c_set_clientdata(client, rmi4_data);
@@ -1972,9 +2019,6 @@
input_unregister_device(rmi4_data->input_dev);
err_register_input:
-err_query_device:
- synaptics_rmi4_power_on(rmi4_data, false);
- synaptics_rmi4_regulator_configure(rmi4_data, false);
if (!list_empty(&rmi->support_fn_list)) {
list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
@@ -1984,6 +2028,17 @@
kfree(fhandler);
}
}
+err_reset_gpio_req:
+ if (gpio_is_valid(platform_data->reset_gpio))
+ gpio_free(platform_data->reset_gpio);
+err_irq_gpio_req:
+ if (gpio_is_valid(platform_data->irq_gpio))
+ gpio_free(platform_data->irq_gpio);
+err_query_device:
+ synaptics_rmi4_power_on(rmi4_data, false);
+err_power_device:
+ synaptics_rmi4_regulator_configure(rmi4_data, false);
+err_reg_configure:
input_free_device(rmi4_data->input_dev);
rmi4_data->input_dev = NULL;
err_input_device:
@@ -2036,7 +2091,11 @@
kfree(fhandler);
}
}
- input_free_device(rmi4_data->input_dev);
+
+ if (gpio_is_valid(rmi4_data->board->reset_gpio))
+ gpio_free(rmi4_data->board->reset_gpio);
+ if (gpio_is_valid(rmi4_data->board->irq_gpio))
+ gpio_free(rmi4_data->board->irq_gpio);
synaptics_rmi4_power_on(rmi4_data, false);
synaptics_rmi4_regulator_configure(rmi4_data, false);
diff --git a/drivers/iommu/msm_iommu-v1.c b/drivers/iommu/msm_iommu-v1.c
index 15a81ed..24d2854 100644
--- a/drivers/iommu/msm_iommu-v1.c
+++ b/drivers/iommu/msm_iommu-v1.c
@@ -202,7 +202,6 @@
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
int ret = 0;
- int asid;
list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
BUG_ON(!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent);
@@ -215,11 +214,8 @@
if (ret)
goto fail;
- asid = GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base,
- ctx_drvdata->num);
-
SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
- asid | (va & CB_TLBIVA_VA));
+ ctx_drvdata->asid | (va & CB_TLBIVA_VA));
mb();
__sync_tlb(iommu_drvdata->base, ctx_drvdata->num);
__disable_clocks(iommu_drvdata);
@@ -234,7 +230,6 @@
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
int ret = 0;
- int asid;
list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
BUG_ON(!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent);
@@ -246,10 +241,8 @@
if (ret)
goto fail;
- asid = GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base,
- ctx_drvdata->num);
-
- SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num, asid);
+ SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num,
+ ctx_drvdata->asid);
mb();
__sync_tlb(iommu_drvdata->base, ctx_drvdata->num);
__disable_clocks(iommu_drvdata);
@@ -342,69 +335,46 @@
static void msm_iommu_assign_ASID(const struct msm_iommu_drvdata *iommu_drvdata,
struct msm_iommu_ctx_drvdata *curr_ctx,
- phys_addr_t pgtable)
+ struct msm_priv *priv)
{
- struct platform_device *pdev;
- struct device_node *child;
- struct msm_iommu_ctx_drvdata *ctx;
unsigned int found = 0;
void __iomem *base = iommu_drvdata->base;
- struct device_node *iommu_node = iommu_drvdata->dev->of_node;
- unsigned int asid;
+ unsigned int i;
unsigned int ncb = iommu_drvdata->ncb;
+ struct msm_iommu_ctx_drvdata *tmp_drvdata;
/* Find if this page table is used elsewhere, and re-use ASID */
- for_each_child_of_node(iommu_node, child) {
- pdev = of_find_device_by_node(child);
- ctx = dev_get_drvdata(&pdev->dev);
+ if (!list_empty(&priv->list_attached)) {
+ tmp_drvdata = list_first_entry(&priv->list_attached,
+ struct msm_iommu_ctx_drvdata, attached_elm);
- if (ctx->secure_context) {
- of_dev_put(pdev);
- continue;
- }
+ ++iommu_drvdata->asid[tmp_drvdata->asid - 1];
+ curr_ctx->asid = tmp_drvdata->asid;
- if ((ctx != curr_ctx) &&
- (GET_CB_TTBR0_ADDR(base, ctx->num) == pgtable)) {
- SET_CB_CONTEXTIDR_ASID(base, curr_ctx->num, ctx->asid);
- curr_ctx->asid = ctx->asid;
- found = 1;
- of_dev_put(pdev);
- of_node_put(child);
- break;
- }
- of_dev_put(pdev);
+ SET_CB_CONTEXTIDR_ASID(base, curr_ctx->num, curr_ctx->asid);
+ found = 1;
}
/* If page table is new, find an unused ASID */
if (!found) {
- for (asid = 1; asid < ncb + 1; ++asid) {
- found = 0;
- for_each_child_of_node(iommu_node, child) {
- pdev = of_find_device_by_node(child);
- ctx = dev_get_drvdata(&pdev->dev);
+ for (i = 0; i < ncb; ++i) {
+ if (iommu_drvdata->asid[i] == 0) {
+ ++iommu_drvdata->asid[i];
+ curr_ctx->asid = i + 1;
- if (ctx != curr_ctx && ctx->asid == asid) {
- found = 1;
- of_dev_put(pdev);
- of_node_put(child);
- break;
- }
- of_dev_put(pdev);
- }
- if (!found) {
SET_CB_CONTEXTIDR_ASID(base, curr_ctx->num,
- asid);
- curr_ctx->asid = asid;
+ curr_ctx->asid);
+ found = 1;
break;
}
}
- BUG_ON(found);
+ BUG_ON(!found);
}
}
static void __program_context(struct msm_iommu_drvdata *iommu_drvdata,
struct msm_iommu_ctx_drvdata *ctx_drvdata,
- phys_addr_t pgtable, int redirect, bool is_secure)
+ struct msm_priv *priv, bool is_secure)
{
unsigned int prrr, nmrr;
unsigned int pn;
@@ -413,6 +383,7 @@
unsigned int ctx = ctx_drvdata->num;
u32 *sids = ctx_drvdata->sids;
int len = ctx_drvdata->nsid;
+ phys_addr_t pgtable = __pa(priv->pt.fl_table);
__reset_context(base, ctx);
@@ -443,7 +414,7 @@
/* Configure page tables as inner-cacheable and shareable to reduce
* the TLB miss penalty.
*/
- if (redirect) {
+ if (priv->pt.redirect) {
SET_CB_TTBR0_S(base, ctx, 1);
SET_CB_TTBR0_NOS(base, ctx, 1);
SET_CB_TTBR0_IRGN1(base, ctx, 0); /* WB, WA */
@@ -489,7 +460,7 @@
}
- msm_iommu_assign_ASID(iommu_drvdata, ctx_drvdata, pn);
+ msm_iommu_assign_ASID(iommu_drvdata, ctx_drvdata, priv);
/* Enable the MMU */
SET_CB_SCTLR_M(base, ctx, 1);
@@ -535,27 +506,6 @@
mutex_unlock(&msm_iommu_lock);
}
-static int msm_iommu_ctx_attached(struct device *dev)
-{
- struct platform_device *pdev;
- struct device_node *child;
- struct msm_iommu_ctx_drvdata *ctx;
-
- for_each_child_of_node(dev->of_node, child) {
- pdev = of_find_device_by_node(child);
-
- ctx = dev_get_drvdata(&pdev->dev);
- if (ctx->attached_domain) {
- of_dev_put(pdev);
- of_node_put(child);
- return 1;
- }
- of_dev_put(pdev);
- }
-
- return 0;
-}
-
static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
struct msm_priv *priv;
@@ -603,8 +553,8 @@
goto fail;
}
-
- if (!msm_iommu_ctx_attached(dev->parent)) {
+ /* We can only do this once */
+ if (!iommu_drvdata->ctx_attach_count) {
if (!is_secure) {
iommu_halt(iommu_drvdata);
__program_iommu(iommu_drvdata->base);
@@ -624,8 +574,7 @@
iommu_halt(iommu_drvdata);
- __program_context(iommu_drvdata, ctx_drvdata, __pa(priv->pt.fl_table),
- priv->pt.redirect, is_secure);
+ __program_context(iommu_drvdata, ctx_drvdata, priv, is_secure);
iommu_resume(iommu_drvdata);
@@ -633,6 +582,7 @@
list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
ctx_drvdata->attached_domain = domain;
+ ++iommu_drvdata->ctx_attach_count;
mutex_unlock(&msm_iommu_lock);
@@ -671,6 +621,9 @@
is_secure = iommu_drvdata->sec_id != -1;
SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num, ctx_drvdata->asid);
+
+ BUG_ON(iommu_drvdata->asid[ctx_drvdata->asid - 1] == 0);
+ iommu_drvdata->asid[ctx_drvdata->asid - 1]--;
ctx_drvdata->asid = -1;
iommu_halt(iommu_drvdata);
@@ -687,7 +640,8 @@
list_del_init(&ctx_drvdata->attached_elm);
ctx_drvdata->attached_domain = NULL;
-
+ BUG_ON(iommu_drvdata->ctx_attach_count == 0);
+ --iommu_drvdata->ctx_attach_count;
fail:
mutex_unlock(&msm_iommu_lock);
}
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index 3f9f1c4..f994413 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -104,6 +104,15 @@
pr_err("Failed to create %s device\n", child->name);
}
+ drvdata->asid = devm_kzalloc(&pdev->dev, drvdata->ncb * sizeof(int),
+ GFP_KERNEL);
+
+ if (!drvdata->asid) {
+ pr_err("Unable to get memory for asid array\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
ret = of_property_read_string(pdev->dev.of_node, "label",
&drvdata->name);
if (ret)
diff --git a/drivers/iommu/msm_iommu_pagetable.c b/drivers/iommu/msm_iommu_pagetable.c
index 99841cd..b32bd26 100644
--- a/drivers/iommu/msm_iommu_pagetable.c
+++ b/drivers/iommu/msm_iommu_pagetable.c
@@ -357,14 +357,14 @@
return ret;
}
-static unsigned int get_phys_addr(struct scatterlist *sg)
+static phys_addr_t get_phys_addr(struct scatterlist *sg)
{
/*
* Try sg_dma_address first so that we can
* map carveout regions that do not have a
* struct page associated with them.
*/
- unsigned int pa = sg_dma_address(sg);
+ phys_addr_t pa = sg_dma_address(sg);
if (pa == 0)
pa = sg_phys(sg);
return pa;
@@ -380,7 +380,7 @@
int msm_iommu_pagetable_map_range(struct iommu_pt *pt, unsigned int va,
struct scatterlist *sg, unsigned int len, int prot)
{
- unsigned int pa;
+ phys_addr_t pa;
unsigned int offset = 0;
unsigned long *fl_pte;
unsigned long fl_offset;
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
index 4e55bd6..29cf0c1 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -359,26 +359,30 @@
if (ret)
goto fail;
- ret = __enable_clocks(iommu_drvdata);
- if (ret) {
- regulator_disable(iommu_drvdata->gdsc);
- goto fail;
- }
+ /* We can only do this once */
+ if (!iommu_drvdata->ctx_attach_count) {
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret) {
+ regulator_disable(iommu_drvdata->gdsc);
+ goto fail;
+ }
- ret = msm_iommu_sec_program_iommu(iommu_drvdata->sec_id);
+ ret = msm_iommu_sec_program_iommu(iommu_drvdata->sec_id);
- /* bfb settings are always programmed by HLOS */
- program_iommu_bfb_settings(iommu_drvdata->base,
- iommu_drvdata->bfb_settings);
+ /* bfb settings are always programmed by HLOS */
+ program_iommu_bfb_settings(iommu_drvdata->base,
+ iommu_drvdata->bfb_settings);
- __disable_clocks(iommu_drvdata);
- if (ret) {
- regulator_disable(iommu_drvdata->gdsc);
- goto fail;
+ __disable_clocks(iommu_drvdata);
+ if (ret) {
+ regulator_disable(iommu_drvdata->gdsc);
+ goto fail;
+ }
}
list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
ctx_drvdata->attached_domain = domain;
+ ++iommu_drvdata->ctx_attach_count;
mutex_unlock(&msm_iommu_lock);
@@ -410,7 +414,8 @@
ctx_drvdata->attached_domain = NULL;
regulator_disable(iommu_drvdata->gdsc);
-
+ BUG_ON(iommu_drvdata->ctx_attach_count == 0);
+ --iommu_drvdata->ctx_attach_count;
fail:
mutex_unlock(&msm_iommu_lock);
}
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index fb1882c..9e0a147 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -62,7 +62,7 @@
#define WLED_OP_FDBCK_MASK 0x07
#define WLED_OP_FDBCK_BIT_SHFT 0x00
-#define WLED_MAX_LEVEL 255
+#define WLED_MAX_LEVEL 4095
#define WLED_8_BIT_MASK 0xFF
#define WLED_4_BIT_MASK 0x0F
#define WLED_8_BIT_SHFT 0x08
@@ -365,8 +365,8 @@
static int qpnp_wled_set(struct qpnp_led_data *led)
{
- int rc, duty;
- u8 level, val, i, num_wled_strings;
+ int rc, duty, level;
+ u8 val, i, num_wled_strings;
level = led->cdev.brightness;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index dfb8d58..2a0cde9 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -514,6 +514,10 @@
((feed->pid != pid) && (feed->pid != 0x2000)))
continue;
+ if (feed->secure_mode.is_secured &&
+ !dvb_dmx_is_rec_feed(feed))
+ return 0;
+
if (feed->type == DMX_TYPE_TS) {
desired_space = 192; /* upper bound */
ts = &feed->feed.ts;
@@ -593,19 +597,23 @@
if (!feed->feed.ts.is_filtering)
break;
if (feed->ts_type & TS_PACKET) {
- if (feed->ts_type & TS_PAYLOAD_ONLY)
- dvb_dmx_swfilter_payload(feed, buf);
- else
+ if (feed->ts_type & TS_PAYLOAD_ONLY) {
+ if (!feed->secure_mode.is_secured)
+ dvb_dmx_swfilter_payload(feed, buf);
+ } else {
dvb_dmx_swfilter_output_packet(feed,
buf, timestamp);
+ }
}
- if (feed->ts_type & TS_DECODER)
+ if ((feed->ts_type & TS_DECODER) &&
+ !feed->secure_mode.is_secured)
if (feed->demux->write_to_decoder)
feed->demux->write_to_decoder(feed, buf, 188);
break;
case DMX_TYPE_SEC:
- if (!feed->feed.sec.is_filtering)
+ if (!feed->feed.sec.is_filtering ||
+ feed->secure_mode.is_secured)
break;
if (dvb_dmx_swfilter_section_packet(feed, buf) < 0)
feed->feed.sec.seclen = feed->feed.sec.secbufp = 0;
@@ -1212,17 +1220,22 @@
{
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ int ret = 0;
mutex_lock(&dvbdmx->mutex);
- dvbdmxfeed->secure_mode = *secure_mode;
-
if ((dvbdmxfeed->state == DMX_STATE_GO) &&
- dvbdmxfeed->demux->set_secure_mode)
- dvbdmxfeed->demux->set_secure_mode(dvbdmxfeed, secure_mode);
+ dvbdmxfeed->demux->set_secure_mode) {
+ ret = dvbdmxfeed->demux->set_secure_mode(dvbdmxfeed,
+ secure_mode);
+ if (!ret)
+ dvbdmxfeed->secure_mode = *secure_mode;
+ } else {
+ dvbdmxfeed->secure_mode = *secure_mode;
+ }
mutex_unlock(&dvbdmx->mutex);
- return 0;
+ return ret;
}
static int dmx_ts_set_indexing_params(
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index f5f6039..f3dc4b8 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -203,5 +203,85 @@
void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
const u8 timestamp[TIMESTAMP_LEN]);
+/**
+ * dvb_dmx_is_video_feed - Returns whether the PES feed
+ * is video one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is video feed, 0 otherwise.
+ */
+static inline int dvb_dmx_is_video_feed(struct dvb_demux_feed *feed)
+{
+ if (feed->type != DMX_TYPE_TS)
+ return 0;
+
+ if (feed->ts_type & (~TS_DECODER))
+ return 0;
+
+ if ((feed->pes_type == DMX_TS_PES_VIDEO0) ||
+ (feed->pes_type == DMX_TS_PES_VIDEO1) ||
+ (feed->pes_type == DMX_TS_PES_VIDEO2) ||
+ (feed->pes_type == DMX_TS_PES_VIDEO3))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * dvb_dmx_is_pcr_feed - Returns whether the PES feed
+ * is PCR one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is PCR feed, 0 otherwise.
+ */
+static inline int dvb_dmx_is_pcr_feed(struct dvb_demux_feed *feed)
+{
+ if (feed->type != DMX_TYPE_TS)
+ return 0;
+
+ if (feed->ts_type & (~TS_DECODER))
+ return 0;
+
+ if ((feed->pes_type == DMX_TS_PES_PCR0) ||
+ (feed->pes_type == DMX_TS_PES_PCR1) ||
+ (feed->pes_type == DMX_TS_PES_PCR2) ||
+ (feed->pes_type == DMX_TS_PES_PCR3))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * dvb_dmx_is_sec_feed - Returns whether this is a section feed
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is a section feed, 0 otherwise.
+ */
+static inline int dvb_dmx_is_sec_feed(struct dvb_demux_feed *feed)
+{
+ return (feed->type == DMX_TYPE_SEC);
+}
+
+/**
+ * dvb_dmx_is_rec_feed - Returns whether this is a recording feed
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is recording feed, 0 otherwise.
+ */
+static inline int dvb_dmx_is_rec_feed(struct dvb_demux_feed *feed)
+{
+ if (feed->type != DMX_TYPE_TS)
+ return 0;
+
+ if (feed->ts_type & (TS_DECODER | TS_PAYLOAD_ONLY))
+ return 0;
+
+ return 1;
+}
+
#endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index 2bbdc22..e4777e6 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -82,6 +82,15 @@
snapshot config = 4000 * 3000 at 20 fps,
hfr video at 60, 90 and 120 fps.
+config IMX135
+ bool "Sensor IMX135 (BAYER 12M)"
+ depends on MSMB_CAMERA
+ ---help---
+ Sony 12 MP Bayer Sensor with auto focus, uses
+ 4 mipi lanes, preview config = 2104 x 1560 at 49 fps,
+ snapshot config = 4208 x 3120 at 24 fps,
+ Video HDR support.
+
config OV2720
bool "Sensor OV2720 (BAYER 2M)"
depends on MSMB_CAMERA
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index 63ab4bf..6b27048 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -314,7 +314,7 @@
return rc;
set_fmt_fail:
- kfree(sp->vb2_q.drv_priv);
+ kzfree(sp->vb2_q.drv_priv);
return rc;
}
@@ -445,7 +445,7 @@
v4l2_fh_exit(&sp->fh);
}
- kfree(sp);
+ kzfree(sp);
return 0;
}
@@ -479,7 +479,7 @@
{
struct camera_v4l2_private *sp = filep->private_data;
- kfree(sp->vb2_q.drv_priv);
+ kzfree(sp->vb2_q.drv_priv);
vb2_queue_release(&sp->vb2_q);
}
@@ -687,14 +687,14 @@
entity_fail:
media_device_unregister(v4l2_dev->mdev);
media_fail:
- kfree(v4l2_dev->mdev);
+ kzfree(v4l2_dev->mdev);
mdev_fail:
#endif
- kfree(v4l2_dev);
+ kzfree(v4l2_dev);
v4l2_fail:
video_device_release(pvdev->vdev);
video_fail:
- kfree(pvdev);
+ kzfree(pvdev);
init_end:
return rc;
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 1d931df..5385d1e 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -88,8 +88,8 @@
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 1027648000,
- .ib = 1105920000,
+ .ab = 2027648000U,
+ .ib = 2805920000U,
},
};
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 08a4566..f8cddb2 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -129,7 +129,7 @@
if (node->sd == q_node) { \
__q->len--; \
list_del_init(&node->member); \
- kfree(node); \
+ kzfree(node); \
break; \
} \
} \
@@ -146,7 +146,7 @@
if (node == q_node) { \
__q->len--; \
list_del_init(&node->member); \
- kfree(node); \
+ kzfree(node); \
break; \
} \
} \
@@ -165,7 +165,7 @@
if (node) { \
if (&node->member) \
list_del_init(&node->member); \
- kfree(node); \
+ kzfree(node); \
} \
} \
spin_unlock_irqrestore(&__q->lock, flags); \
@@ -288,14 +288,14 @@
list_del_init(&stream->list);
session->stream_q.len--;
- kfree(stream);
+ kzfree(stream);
}
static void msm_sd_unregister_subdev(struct video_device *vdev)
{
struct v4l2_subdev *sd = video_get_drvdata(vdev);
sd->devnode = NULL;
- kfree(vdev);
+ kzfree(vdev);
}
static inline int __msm_sd_register_subdev(struct v4l2_subdev *sd)
@@ -330,7 +330,7 @@
rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner);
if (rc < 0) {
- kfree(vdev);
+ kzfree(vdev);
goto clean_up;
}
@@ -461,9 +461,6 @@
static inline int __msm_sd_close_session_streams(struct v4l2_subdev *sd,
struct msm_sd_close_ioctl *sd_close)
{
- v4l2_subdev_call(sd, core, ioctl,
- MSM_SD_CLOSE_SESSION_AND_STREAM, &sd_close);
-
return 0;
}
@@ -591,7 +588,7 @@
__msm_queue_find_command_ack_q,
&stream_id);
if (WARN_ON(!cmd_ack)) {
- kfree(ret_cmd);
+ kzfree(ret_cmd);
rc = -EFAULT;
break;
}
@@ -713,7 +710,7 @@
*event = cmd->event;
- kfree(cmd);
+ kzfree(cmd);
return rc;
}
@@ -1005,14 +1002,14 @@
entity_fail:
media_device_unregister(msm_v4l2_dev->mdev);
media_fail:
- kfree(msm_v4l2_dev->mdev);
+ kzfree(msm_v4l2_dev->mdev);
mdev_fail:
#endif
video_device_release(pvdev->vdev);
video_fail:
- kfree(pvdev);
+ kzfree(pvdev);
pvdev_fail:
- kfree(msm_v4l2_dev);
+ kzfree(msm_v4l2_dev);
probe_end:
return rc;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index 6ea86ae..079dbb5 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -77,7 +77,7 @@
static void msm_vb2_dma_contig_put_userptr(void *buf_priv)
{
- kfree(buf_priv);
+ kzfree(buf_priv);
}
static struct vb2_mem_ops msm_vb2_get_q_mem_op = {
@@ -105,7 +105,7 @@
unsigned long flags;
stream = msm_get_stream(session_id, stream_id);
- if (!stream)
+ if (IS_ERR_OR_NULL(stream))
return NULL;
spin_lock_irqsave(&stream->stream_lock, flags);
@@ -163,7 +163,7 @@
int rc = 0;
stream = msm_get_stream(session_id, stream_id);
- if (!stream)
+ if (IS_ERR_OR_NULL(stream))
return 0;
spin_lock_irqsave(&stream->stream_lock, flags);
if (vb) {
@@ -172,6 +172,7 @@
/* put buf before buf done */
if (msm_vb2->in_freeq) {
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ msm_vb2->in_freeq = 0;
rc = 0;
} else
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index 637bce3..41234c3 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -293,6 +293,14 @@
goto vbif_remap_failed;
}
+ cpp_dev->cpp_hw_base = ioremap(cpp_dev->cpp_hw_mem->start,
+ resource_size(cpp_dev->cpp_hw_mem));
+ if (!cpp_dev->cpp_hw_base) {
+ rc = -ENOMEM;
+ pr_err("ioremap failed\n");
+ goto cpp_hw_remap_failed;
+ }
+
if (cpp_dev->state != CPP_STATE_BOOT) {
rc = request_irq(cpp_dev->irq->start, msm_cpp_irq,
IRQF_TRIGGER_RISING, "cpp", cpp_dev);
@@ -303,11 +311,19 @@
}
}
+ cpp_dev->hw_info.cpp_hw_version =
+ msm_camera_io_r(cpp_dev->cpp_hw_base);
+ pr_debug("CPP HW Version: 0x%x\n", cpp_dev->hw_info.cpp_hw_version);
+ cpp_dev->hw_info.cpp_hw_caps =
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4);
+ pr_debug("CPP HW Caps: 0x%x\n", cpp_dev->hw_info.cpp_hw_caps);
msm_camera_io_w(0x1, cpp_dev->vbif_base + 0x4);
if (cpp_dev->is_firmware_loaded == 1)
msm_cpp_boot_hw(cpp_dev);
return rc;
req_irq_fail:
+ iounmap(cpp_dev->cpp_hw_base);
+cpp_hw_remap_failed:
iounmap(cpp_dev->vbif_base);
vbif_remap_failed:
iounmap(cpp_dev->base);
@@ -327,6 +343,8 @@
free_irq(cpp_dev->irq->start, cpp_dev);
iounmap(cpp_dev->base);
+ iounmap(cpp_dev->vbif_base);
+ iounmap(cpp_dev->cpp_hw_base);
msm_cam_clk_enable(&cpp_dev->pdev->dev, cpp_clk_info,
cpp_dev->cpp_clk, ARRAY_SIZE(cpp_clk_info), 0);
if (0) {
@@ -336,15 +354,15 @@
}
}
-static void cpp_load_fw(struct cpp_device *cpp_dev)
+static void cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
{
uint32_t i;
uint32_t *ptr_bin = NULL;
int32_t rc = -EFAULT;
const struct firmware *fw = NULL;
- char *fw_name_bin = "cpp_firmware_v1_1_1.fw";
struct device *dev = &cpp_dev->pdev->dev;
+ pr_debug("%s: FW file: %s\n", __func__, fw_name_bin);
rc = request_firmware(&fw, fw_name_bin, dev);
if (rc) {
dev_err(dev, "Failed to locate blob %s from device %p, Error: %d\n",
@@ -690,14 +708,44 @@
struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
int rc = 0;
+ char *fw_name_bin;
mutex_lock(&cpp_dev->mutex);
CPP_DBG("E cmd: %d\n", cmd);
switch (cmd) {
+ case VIDIOC_MSM_CPP_GET_HW_INFO: {
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ &cpp_dev->hw_info,
+ sizeof(struct cpp_hw_info))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ break;
+ }
+
case VIDIOC_MSM_CPP_LOAD_FIRMWARE: {
if (cpp_dev->is_firmware_loaded == 0) {
+ fw_name_bin = kzalloc(ioctl_ptr->len, GFP_KERNEL);
+ if (!fw_name_bin) {
+ pr_err("%s:%d: malloc error\n", __func__,
+ __LINE__);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = (copy_from_user(fw_name_bin,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len) ? -EFAULT : 0);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ kfree(fw_name_bin);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
disable_irq(cpp_dev->irq->start);
- cpp_load_fw(cpp_dev);
+ cpp_load_fw(cpp_dev, fw_name_bin);
+ kfree(fw_name_bin);
enable_irq(cpp_dev->irq->start);
cpp_dev->is_firmware_loaded = 1;
}
@@ -894,6 +942,14 @@
goto ERROR2;
}
+ cpp_dev->cpp_hw_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "cpp_hw");
+ if (!cpp_dev->cpp_hw_mem) {
+ pr_err("no mem resource?\n");
+ rc = -ENODEV;
+ goto ERROR2;
+ }
+
cpp_dev->irq = platform_get_resource_byname(pdev,
IORESOURCE_IRQ, "cpp");
if (!cpp_dev->irq) {
@@ -999,6 +1055,10 @@
iommu_detach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
msm_sd_unregister(&cpp_dev->msm_sd);
release_mem_region(cpp_dev->mem->start, resource_size(cpp_dev->mem));
+ release_mem_region(cpp_dev->vbif_mem->start,
+ resource_size(cpp_dev->vbif_mem));
+ release_mem_region(cpp_dev->cpp_hw_mem->start,
+ resource_size(cpp_dev->cpp_hw_mem));
mutex_destroy(&cpp_dev->mutex);
kfree(cpp_dev->cpp_clk);
kfree(cpp_dev);
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index e8e37ed..0c586ca 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -125,8 +125,10 @@
struct resource *io;
struct resource *vbif_mem;
struct resource *vbif_io;
+ struct resource *cpp_hw_mem;
void __iomem *vbif_base;
void __iomem *base;
+ void __iomem *cpp_hw_base;
struct clk **cpp_clk;
struct regulator *fs_cpp;
struct mutex mutex;
@@ -141,6 +143,7 @@
struct cpp_subscribe_info cpp_subscribe_list[MAX_ACTIVE_CPP_INSTANCE];
uint32_t cpp_open_cnt;
+ struct cpp_hw_info hw_info;
struct msm_device_queue eventData_q; /* V4L2 Event Payload Queue */
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
index f6011ba..6f941f7 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -6,5 +6,6 @@
obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ flash/
obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor.o
obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
+obj-$(CONFIG_IMX135) += imx135.o
obj-$(CONFIG_OV2720) += ov2720.o
obj-$(CONFIG_MT9M114) += mt9m114.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/imx135.c b/drivers/media/platform/msm/camera_v2/sensor/imx135.c
new file mode 100644
index 0000000..c9476ee
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/imx135.c
@@ -0,0 +1,149 @@
+/* Copyright (c) 2013, 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 "msm_sensor.h"
+#define IMX135_SENSOR_NAME "imx135"
+DEFINE_MSM_MUTEX(imx135_mut);
+
+static struct msm_sensor_ctrl_t imx135_s_ctrl;
+
+static struct msm_sensor_power_setting imx135_power_setting[] = {
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VANA,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 0,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_I2C_MUX,
+ .seq_val = 0,
+ .config_val = 0,
+ .delay = 0,
+ },
+};
+
+static struct v4l2_subdev_info imx135_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static const struct i2c_device_id imx135_i2c_id[] = {
+ {IMX135_SENSOR_NAME, (kernel_ulong_t)&imx135_s_ctrl},
+ { }
+};
+
+static struct i2c_driver imx135_i2c_driver = {
+ .id_table = imx135_i2c_id,
+ .probe = msm_sensor_i2c_probe,
+ .driver = {
+ .name = IMX135_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client imx135_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static const struct of_device_id imx135_dt_match[] = {
+ {.compatible = "qcom,imx135", .data = &imx135_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, imx135_dt_match);
+
+static struct platform_driver imx135_platform_driver = {
+ .driver = {
+ .name = "qcom,imx135",
+ .owner = THIS_MODULE,
+ .of_match_table = imx135_dt_match,
+ },
+};
+
+static int32_t imx135_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ const struct of_device_id *match;
+ match = of_match_device(imx135_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init imx135_init_module(void)
+{
+ int32_t rc = 0;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&imx135_platform_driver,
+ imx135_platform_probe);
+ if (!rc)
+ return rc;
+ pr_err("%s:%d rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&imx135_i2c_driver);
+}
+
+static void __exit imx135_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (imx135_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&imx135_s_ctrl);
+ platform_driver_unregister(&imx135_platform_driver);
+ } else
+ i2c_del_driver(&imx135_i2c_driver);
+ return;
+}
+
+static struct msm_sensor_ctrl_t imx135_s_ctrl = {
+ .sensor_i2c_client = &imx135_sensor_i2c_client,
+ .power_setting_array.power_setting = imx135_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(imx135_power_setting),
+ .msm_sensor_mutex = &imx135_mut,
+ .sensor_v4l2_subdev_info = imx135_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(imx135_subdev_info),
+};
+
+module_init(imx135_init_module);
+module_exit(imx135_exit_module);
+MODULE_DESCRIPTION("imx135");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index 9d89a7e..499b36c 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -95,6 +95,10 @@
static int mpq_sdmx_proc_limit = MAX_TS_PACKETS_FOR_SDMX_PROCESS;
module_param(mpq_sdmx_proc_limit, int, S_IRUGO | S_IWUSR);
+/* Debug flag for secure demux process */
+static int mpq_sdmx_debug;
+module_param(mpq_sdmx_debug, int, S_IRUGO | S_IWUSR);
+
/**
* Maximum allowed framing pattern size
@@ -903,6 +907,8 @@
mutex_init(&mpq_demux->mutex);
+ mpq_demux->num_secure_feeds = 0;
+ mpq_demux->num_active_feeds = 0;
mpq_demux->sdmx_filter_count = 0;
mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
@@ -1230,7 +1236,7 @@
return -EINVAL;
}
- if (mpq_dmx_is_video_feed(feed)) {
+ if (dvb_dmx_is_video_feed(feed)) {
struct mpq_video_feed_info *feed_data;
struct mpq_feed *mpq_feed;
struct mpq_streambuffer *stream_buffer;
@@ -1957,9 +1963,10 @@
}
mpq_sdmx_close_session(mpq_demux);
+ mpq_demux->num_secure_feeds--;
}
- if (mpq_dmx_is_video_feed(feed)) {
+ if (dvb_dmx_is_video_feed(feed)) {
ret = mpq_dmx_terminate_video_feed(mpq_feed);
if (ret)
MPQ_DVB_ERR_PRINT(
@@ -1973,6 +1980,7 @@
}
mpq_sdmx_terminate_metadata_buffer(mpq_feed);
+ mpq_demux->num_active_feeds--;
mutex_unlock(&mpq_demux->mutex);
@@ -1982,7 +1990,7 @@
int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
{
- if (mpq_dmx_is_video_feed(feed)) {
+ if (dvb_dmx_is_video_feed(feed)) {
struct mpq_feed *mpq_feed;
struct mpq_video_feed_info *feed_data;
@@ -2056,7 +2064,7 @@
struct mpq_feed *mpq_feed;
int ret = 0;
- if (!mpq_dmx_is_video_feed(feed)) {
+ if (!dvb_dmx_is_video_feed(feed)) {
MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n",
__func__,
feed->pes_type);
@@ -2139,7 +2147,7 @@
int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
{
- if (mpq_dmx_is_video_feed(feed)) {
+ if (dvb_dmx_is_video_feed(feed)) {
struct mpq_feed *mpq_feed;
struct mpq_video_feed_info *feed_data;
struct dvb_ringbuffer *video_buff;
@@ -3087,7 +3095,7 @@
struct mpq_streambuffer *video_buff;
struct mpq_feed *mpq_feed;
- if (!mpq_dmx_is_video_feed(feed)) {
+ if (!dvb_dmx_is_video_feed(feed)) {
MPQ_DVB_ERR_PRINT(
"%s: Invalid feed type %d\n",
__func__,
@@ -3230,67 +3238,6 @@
}
EXPORT_SYMBOL(mpq_dmx_process_pcr_packet);
-int mpq_dmx_set_secure_mode(struct dvb_demux_feed *feed,
- struct dmx_secure_mode *sec_mode)
-{
- struct mpq_feed *mpq_feed;
- struct mpq_demux *mpq_demux;
- int ret;
-
- if (!feed || !feed->priv || !sec_mode) {
- MPQ_DVB_ERR_PRINT(
- "%s: invalid parameters\n",
- __func__);
- return -EINVAL;
- }
-
- MPQ_DVB_DBG_PRINT("%s(%d, %d, %d)\n",
- __func__, sec_mode->pid,
- sec_mode->is_secured,
- sec_mode->key_ladder_id);
-
- mpq_feed = feed->priv;
- mpq_demux = mpq_feed->mpq_demux;
-
- mutex_lock(&mpq_demux->mutex);
-
- /*
- * If secure demux is active, set the KL now,
- * otherwise it will be set when secure-demux is started
- * (when filtering starts).
- */
- if (mpq_demux->sdmx_session_handle !=
- SDMX_INVALID_SESSION_HANDLE) {
- if (sec_mode->is_secured) {
- MPQ_DVB_DBG_PRINT(
- "%s: set key-ladder %d to PID %d\n",
- __func__,
- sec_mode->key_ladder_id,
- sec_mode->pid);
- ret = sdmx_set_kl_ind(mpq_demux->sdmx_session_handle,
- sec_mode->pid, sec_mode->key_ladder_id);
- if (ret) {
- MPQ_DVB_ERR_PRINT(
- "%s: FAILED to set keyladder, ret=%d\n",
- __func__, ret);
- ret = -EINVAL;
- }
- } else {
- MPQ_DVB_DBG_PRINT("%s: setting non-secure mode\n",
- __func__);
- ret = 0;
- }
- } else {
- MPQ_DVB_DBG_PRINT("%s: SDMX not started yet\n", __func__);
- ret = 0;
- }
-
- mutex_unlock(&mpq_demux->mutex);
-
- return ret;
-}
-EXPORT_SYMBOL(mpq_dmx_set_secure_mode);
-
int mpq_sdmx_open_session(struct mpq_demux *mpq_demux)
{
enum sdmx_status ret = SDMX_SUCCESS;
@@ -3409,7 +3356,7 @@
*buf_mode = SDMX_RING_BUF;
- if (mpq_dmx_is_video_feed(feed->dvb_demux_feed)) {
+ if (dvb_dmx_is_video_feed(feed->dvb_demux_feed)) {
if (feed_data->buffer_desc.decoder_buffers_num > 1)
*buf_mode = SDMX_LINEAR_GROUP_BUF;
*num_buffers = feed_data->buffer_desc.decoder_buffers_num;
@@ -3429,8 +3376,8 @@
}
} else {
*num_buffers = 1;
- if (mpq_dmx_is_sec_feed(dvbdmx_feed) ||
- mpq_dmx_is_pcr_feed(dvbdmx_feed)) {
+ if (dvb_dmx_is_sec_feed(dvbdmx_feed) ||
+ dvb_dmx_is_pcr_feed(dvbdmx_feed)) {
buffer = &feed->sdmx_buf;
sdmx_buff = feed->sdmx_buf_handle;
} else {
@@ -3481,18 +3428,18 @@
feed = dvbdmx_feed->priv;
- if (mpq_dmx_is_sec_feed(dvbdmx_feed)) {
+ if (dvb_dmx_is_sec_feed(dvbdmx_feed)) {
feed->filter_type = SDMX_SECTION_FILTER;
if (dvbdmx_feed->feed.sec.check_crc)
filter_flags |= SDMX_FILTER_FLAG_VERIFY_SECTION_CRC;
MPQ_DVB_DBG_PRINT("%s: SDMX_SECTION_FILTER\n", __func__);
- } else if (mpq_dmx_is_pcr_feed(dvbdmx_feed)) {
+ } else if (dvb_dmx_is_pcr_feed(dvbdmx_feed)) {
feed->filter_type = SDMX_PCR_FILTER;
MPQ_DVB_DBG_PRINT("%s: SDMX_PCR_FILTER\n", __func__);
- } else if (mpq_dmx_is_video_feed(dvbdmx_feed)) {
+ } else if (dvb_dmx_is_video_feed(dvbdmx_feed)) {
feed->filter_type = SDMX_SEPARATED_PES_FILTER;
MPQ_DVB_DBG_PRINT("%s: SDMX_SEPARATED_PES_FILTER\n", __func__);
- } else if (mpq_dmx_is_rec_feed(dvbdmx_feed)) {
+ } else if (dvb_dmx_is_rec_feed(dvbdmx_feed)) {
feed->filter_type = SDMX_RAW_FILTER;
switch (dvbdmx_feed->tsp_out_format) {
case (DMX_TSP_FORMAT_188):
@@ -3546,7 +3493,7 @@
/* Meta-data initialization,
* Recording filters do no need meta-data buffers.
*/
- if (mpq_dmx_is_rec_feed(dvbdmx_feed)) {
+ if (dvb_dmx_is_rec_feed(dvbdmx_feed)) {
metadata_buff_desc.base_addr = 0;
metadata_buff_desc.size = 0;
} else {
@@ -3640,6 +3587,63 @@
return ret;
}
+/**
+ * mpq_sdmx_init_feed - initialize secure demux related elements of mpq feed
+ *
+ * @mpq_demux: mpq_demux object
+ * @mpq_feed: mpq_feed object
+ *
+ * Note: the function assumes mpq_demux->mutex locking is done by caller.
+ */
+static int mpq_sdmx_init_feed(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed)
+{
+ int ret;
+
+ ret = mpq_sdmx_open_session(mpq_demux);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_open_session failed, ret=%d\n",
+ __func__, ret);
+
+ ret = -ENODEV;
+ goto init_sdmx_feed_failed;
+ }
+
+ /* PCR and sections have internal buffer for SDMX */
+ if (dvb_dmx_is_pcr_feed(mpq_feed->dvb_demux_feed))
+ ret = mpq_sdmx_alloc_data_buf(mpq_feed, SDMX_PCR_BUFFER_SIZE);
+ else if (dvb_dmx_is_sec_feed(mpq_feed->dvb_demux_feed))
+ ret = mpq_sdmx_alloc_data_buf(mpq_feed,
+ SDMX_SECTION_BUFFER_SIZE);
+ else
+ ret = 0;
+
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s: init buffer failed, ret=%d\n",
+ __func__, ret);
+ goto init_sdmx_feed_failed_free_sdmx;
+ }
+
+ ret = mpq_sdmx_filter_setup(mpq_demux, mpq_feed->dvb_demux_feed);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_filter_setup failed, ret=%d\n",
+ __func__, ret);
+ goto init_sdmx_feed_failed_free_data_buff;
+ }
+
+ mpq_demux->num_secure_feeds++;
+ return 0;
+
+init_sdmx_feed_failed_free_data_buff:
+ mpq_sdmx_free_data_buf(mpq_feed);
+init_sdmx_feed_failed_free_sdmx:
+ mpq_sdmx_close_session(mpq_demux);
+init_sdmx_feed_failed:
+ return ret;
+}
+
int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed)
{
int ret = 0;
@@ -3648,80 +3652,113 @@
mutex_lock(&mpq_demux->mutex);
- if (mpq_dmx_is_video_feed(feed)) {
- ret = mpq_dmx_init_video_feed(mpq_feed);
-
- if (ret) {
- MPQ_DVB_ERR_PRINT(
- "%s: mpq_dmx_init_video_feed failed, ret=%d\n",
- __func__, ret);
- goto init_mpq_feed_failed;
- }
- }
-
mpq_feed->sdmx_buf_handle = NULL;
mpq_feed->metadata_buf_handle = NULL;
mpq_feed->sdmx_filter_handle = SDMX_INVALID_FILTER_HANDLE;
- if (!mpq_sdmx_is_loaded()) {
- /* nothing more to do */
- mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
- mutex_unlock(&mpq_demux->mutex);
- return ret;
+ if (dvb_dmx_is_video_feed(feed)) {
+ ret = mpq_dmx_init_video_feed(mpq_feed);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_init_video_feed failed, ret=%d\n",
+ __func__, ret);
+ goto init_mpq_feed_end;
+ }
}
- /* Further initializations for secure demux */
- ret = mpq_sdmx_open_session(mpq_demux);
+ /*
+ * sdmx is not relevant for recording filters, which always use
+ * regular filters (non-sdmx)
+ */
+ if (!mpq_sdmx_is_loaded() || !feed->secure_mode.is_secured ||
+ dvb_dmx_is_rec_feed(feed)) {
+ if (!mpq_sdmx_is_loaded())
+ mpq_demux->sdmx_session_handle =
+ SDMX_INVALID_SESSION_HANDLE;
+ goto init_mpq_feed_end;
+ }
+
+ /* Initialization of secure demux filters (PES/PCR/Video/Section) */
+ ret = mpq_sdmx_init_feed(mpq_demux, mpq_feed);
if (ret) {
MPQ_DVB_ERR_PRINT(
- "%s: mpq_sdmx_open_session failed, ret=%d\n",
+ "%s: mpq_sdmx_init_feed failed, ret=%d\n",
__func__, ret);
-
- ret = -ENODEV;
- goto init_mpq_feed_failed_free_video;
+ if (dvb_dmx_is_video_feed(feed))
+ mpq_dmx_terminate_video_feed(mpq_feed);
}
- /* PCR and sections have internal buffer for SDMX */
- if (mpq_dmx_is_pcr_feed(feed))
- ret = mpq_sdmx_alloc_data_buf(mpq_feed,
- SDMX_PCR_BUFFER_SIZE);
- else if (mpq_dmx_is_sec_feed(feed))
- ret = mpq_sdmx_alloc_data_buf(mpq_feed,
- SDMX_SECTION_BUFFER_SIZE);
- else
- ret = 0;
-
- if (ret) {
- MPQ_DVB_ERR_PRINT(
- "%s: init buffer failed, ret=%d\n",
- __func__, ret);
- goto init_mpq_feed_failed_free_sdmx;
- }
-
- ret = mpq_sdmx_filter_setup(mpq_demux, feed);
- if (ret) {
- MPQ_DVB_ERR_PRINT(
- "%s: mpq_sdmx_filter_setup failed, ret=%d\n",
- __func__, ret);
- goto init_mpq_feed_failed_free_data_buff;
- }
-
- mutex_unlock(&mpq_demux->mutex);
- return 0;
-
-init_mpq_feed_failed_free_data_buff:
- mpq_sdmx_free_data_buf(mpq_feed);
-init_mpq_feed_failed_free_sdmx:
- mpq_sdmx_close_session(mpq_demux);
-init_mpq_feed_failed_free_video:
- if (mpq_dmx_is_video_feed(feed))
- mpq_dmx_terminate_video_feed(mpq_feed);
-init_mpq_feed_failed:
+init_mpq_feed_end:
+ if (!ret)
+ mpq_demux->num_active_feeds++;
mutex_unlock(&mpq_demux->mutex);
return ret;
}
EXPORT_SYMBOL(mpq_dmx_init_mpq_feed);
+/**
+ * Note: Called only when filter is in "GO" state - after feed has been started.
+ */
+int mpq_dmx_set_secure_mode(struct dvb_demux_feed *feed,
+ struct dmx_secure_mode *sec_mode)
+{
+ struct mpq_feed *mpq_feed;
+ struct mpq_demux *mpq_demux;
+ int ret = 0;
+
+ if (!feed || !feed->priv || !sec_mode) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid parameters\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ MPQ_DVB_DBG_PRINT("%s(%d, %d, %d)\n",
+ __func__, sec_mode->pid,
+ sec_mode->is_secured,
+ sec_mode->key_ladder_id);
+
+ mpq_feed = feed->priv;
+ mpq_demux = mpq_feed->mpq_demux;
+
+ mutex_lock(&mpq_demux->mutex);
+
+ if (feed->secure_mode.is_secured != sec_mode->is_secured) {
+ /*
+ * Switching between secure & non-secure mode is not allowed
+ * while filter is running
+ */
+ MPQ_DVB_ERR_PRINT(
+ "%s: Cannot switch between secure mode while filter is running\n",
+ __func__);
+ mutex_unlock(&mpq_demux->mutex);
+ return -EPERM;
+ }
+
+ /*
+ * Feed is running in secure mode, this secure mode request is to
+ * update the key ladder id
+ */
+ if (feed->secure_mode.pid == sec_mode->pid && sec_mode->is_secured &&
+ feed->secure_mode.key_ladder_id != sec_mode->key_ladder_id &&
+ mpq_demux->sdmx_session_handle != SDMX_INVALID_SESSION_HANDLE) {
+ ret = sdmx_set_kl_ind(mpq_demux->sdmx_session_handle,
+ sec_mode->pid,
+ sec_mode->key_ladder_id);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to set key ladder, ret=%d\n",
+ __func__, ret);
+ ret = -ENODEV;
+ }
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(mpq_dmx_set_secure_mode);
+
static void mpq_sdmx_prepare_filter_status(struct mpq_demux *mpq_demux,
struct sdmx_filter_status *filter_sts,
struct mpq_feed *mpq_feed)
@@ -3742,11 +3779,11 @@
__func__, filter_sts->metadata_fill_count,
filter_sts->metadata_write_offset);
- if (!mpq_dmx_is_video_feed(feed)) {
+ if (!dvb_dmx_is_video_feed(feed)) {
struct dvb_ringbuffer *buffer;
- if (mpq_dmx_is_sec_feed(feed) ||
- mpq_dmx_is_pcr_feed(feed)) {
+ if (dvb_dmx_is_sec_feed(feed) ||
+ dvb_dmx_is_pcr_feed(feed)) {
buffer = (struct dvb_ringbuffer *)
&mpq_feed->sdmx_buf;
} else {
@@ -4421,7 +4458,8 @@
{
struct sdmx_filter_status *sts;
struct mpq_feed *mpq_feed;
- u8 flags = 0; /* MPQ_TODO: EOS handling */
+ /* MPQ_TODO: EOS handling */
+ u8 flags = mpq_sdmx_debug ? SDMX_INPUT_FLAG_DBG_ENABLE : 0;
u32 errors;
u32 status;
u32 prev_read_offset;
@@ -4521,7 +4559,7 @@
int total_bytes_read = 0;
int limit = mpq_sdmx_proc_limit * mpq_demux->demux.ts_packet_size;
- do {
+ while (fill_count >= mpq_demux->demux.ts_packet_size) {
todo = fill_count > limit ? limit : fill_count;
ret = mpq_sdmx_process_buffer(mpq_demux, input, todo,
read_offset);
@@ -4541,7 +4579,7 @@
__func__, ret);
break;
}
- } while (fill_count > 0);
+ }
return total_bytes_read;
}
@@ -4584,6 +4622,7 @@
{
struct dvb_demux *dvb_demux;
struct mpq_demux *mpq_demux;
+ int ret = count;
if (demux == NULL)
return -EINVAL;
@@ -4591,20 +4630,35 @@
dvb_demux = demux->priv;
mpq_demux = dvb_demux->priv;
- if (mpq_sdmx_is_loaded()) {
- /* route through secure demux */
- return mpq_sdmx_write(mpq_demux,
+ /* Route through secure demux - process secure feeds if any exist */
+ if (mpq_sdmx_is_loaded() && mpq_demux->sdmx_filter_count) {
+ ret = mpq_sdmx_write(mpq_demux,
demux->dvr_input.priv_handle,
buf,
count);
- } else {
- /* route through sw filter */
- dvb_dmx_swfilter_format(dvb_demux, buf, count,
- dvb_demux->tsp_format);
- if (signal_pending(current))
- return -EINTR;
- return count;
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_write failed. ret = %d\n",
+ __func__, ret);
+ ret = count;
+ }
}
+
+ /*
+ * Route through sw filter - process non-secure feeds if any exist.
+ * For sw filter, should process the same amount of bytes the sdmx
+ * process managed to consume, unless some sdmx error occurred, for
+ * which should process the whole buffer
+ */
+ if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds) {
+ dvb_dmx_swfilter_format(dvb_demux, buf, ret,
+ dvb_demux->tsp_format);
+ }
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ return ret;
}
EXPORT_SYMBOL(mpq_dmx_write);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
index 2c2420b..7affcc6 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
@@ -362,6 +362,9 @@
* @ion_client: ION demux client used to allocate memory from ION.
* @mutex: Lock used to protect against private feed data
* @feeds: mpq common feed object pool
+ * @num_active_feeds: Number of active mpq feeds
+ * @num_secure_feeds: Number of secure feeds (have a sdmx filter associated)
+ * currently allocated.
* @filters_status: Array holding buffers status for each secure demux filter.
* Used before each call to sdmx_process() to build up to date state.
* @sdmx_session_handle: Secure demux open session handle
@@ -406,6 +409,8 @@
struct ion_client *ion_client;
struct mutex mutex;
struct mpq_feed feeds[MPQ_MAX_DMX_FILES];
+ u32 num_active_feeds;
+ u32 num_secure_feeds;
struct sdmx_filter_status filters_status[MPQ_MAX_DMX_FILES];
int sdmx_session_handle;
int sdmx_session_ref_count;
@@ -615,86 +620,6 @@
int mpq_dmx_process_pcr_packet(struct dvb_demux_feed *feed, const u8 *buf);
/**
- * mpq_dmx_is_video_feed - Returns whether the PES feed
- * is video one.
- *
- * @feed: The feed to be checked.
- *
- * Return 1 if feed is video feed, 0 otherwise.
- */
-static inline int mpq_dmx_is_video_feed(struct dvb_demux_feed *feed)
-{
- if (feed->type != DMX_TYPE_TS)
- return 0;
-
- if (feed->ts_type & (~TS_DECODER))
- return 0;
-
- if ((feed->pes_type == DMX_TS_PES_VIDEO0) ||
- (feed->pes_type == DMX_TS_PES_VIDEO1) ||
- (feed->pes_type == DMX_TS_PES_VIDEO2) ||
- (feed->pes_type == DMX_TS_PES_VIDEO3))
- return 1;
-
- return 0;
-}
-
-/**
- * mpq_dmx_is_pcr_feed - Returns whether the PES feed
- * is PCR one.
- *
- * @feed: The feed to be checked.
- *
- * Return 1 if feed is PCR feed, 0 otherwise.
- */
-static inline int mpq_dmx_is_pcr_feed(struct dvb_demux_feed *feed)
-{
- if (feed->type != DMX_TYPE_TS)
- return 0;
-
- if (feed->ts_type & (~TS_DECODER))
- return 0;
-
- if ((feed->pes_type == DMX_TS_PES_PCR0) ||
- (feed->pes_type == DMX_TS_PES_PCR1) ||
- (feed->pes_type == DMX_TS_PES_PCR2) ||
- (feed->pes_type == DMX_TS_PES_PCR3))
- return 1;
-
- return 0;
-}
-
-/**
- * mpq_dmx_is_sec_feed - Returns whether this is a section feed
- *
- * @feed: The feed to be checked.
- *
- * Return 1 if feed is a section feed, 0 otherwise.
- */
-static inline int mpq_dmx_is_sec_feed(struct dvb_demux_feed *feed)
-{
- return (feed->type == DMX_TYPE_SEC);
-}
-
-/**
- * mpq_dmx_is_rec_feed - Returns whether this is a recording feed
- *
- * @feed: The feed to be checked.
- *
- * Return 1 if feed is recording feed, 0 otherwise.
- */
-static inline int mpq_dmx_is_rec_feed(struct dvb_demux_feed *feed)
-{
- if (feed->type != DMX_TYPE_TS)
- return 0;
-
- if (feed->ts_type & (TS_DECODER | TS_PAYLOAD_ONLY))
- return 0;
-
- return 1;
-}
-
-/**
* mpq_dmx_init_hw_statistics -
* Extend dvb-demux debugfs with HW statistics.
*
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
index b29759c..3d48441 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
@@ -518,10 +518,10 @@
"%s: warnning - len larger than one packet\n",
__func__);
- if (mpq_dmx_is_video_feed(feed))
+ if (dvb_dmx_is_video_feed(feed))
return mpq_dmx_process_video_packet(feed, buf);
- if (mpq_dmx_is_pcr_feed(feed))
+ if (dvb_dmx_is_pcr_feed(feed))
return mpq_dmx_process_pcr_packet(feed, buf);
return 0;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
index 632e864..beb4cce 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
@@ -306,6 +306,30 @@
}
/**
+ * mpq_dmx_tspp_swfilter_desc - helper function
+ *
+ * Takes a tspp buffer descriptor and send it to the SW filter for demuxing,
+ * one TS packet at a time.
+ *
+ * @mpq_demux - mpq demux object
+ * @tspp_data_desc - tspp buffer descriptor
+ */
+static inline void mpq_dmx_tspp_swfilter_desc(struct mpq_demux *mpq_demux,
+ const struct tspp_data_descriptor *tspp_data_desc)
+{
+ u32 notif_size;
+ int i;
+
+ notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
+ for (i = 0; i < notif_size; i++)
+ dvb_dmx_swfilter_packet(&mpq_demux->demux,
+ ((u8 *)tspp_data_desc->virt_base) +
+ i * TSPP_RAW_TTS_SIZE,
+ ((u8 *)tspp_data_desc->virt_base) +
+ i * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
+}
+
+/**
* Demux TS packets from TSPP by secure-demux.
* The fucntion assumes the buffer is physically contiguous
* and that TSPP descriptors are continuous in memory.
@@ -320,37 +344,46 @@
struct sdmx_buff_descr input;
size_t aggregate_len = 0;
size_t aggregate_count = 0;
- phys_addr_t buff_start_addr;
- phys_addr_t buff_current_addr = 0;
+ phys_addr_t buff_start_addr_phys;
+ phys_addr_t buff_current_addr_phys = 0;
+ u32 notif_size;
int i;
while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
if (0 == aggregate_count)
- buff_current_addr = tspp_data_desc->phys_base;
+ buff_current_addr_phys = tspp_data_desc->phys_base;
+ notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[aggregate_count] =
tspp_data_desc->id;
aggregate_len += tspp_data_desc->size;
aggregate_count++;
- mpq_demux->hw_notification_size +=
- tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
+ mpq_demux->hw_notification_size += notif_size;
+
+ /* Let SW filter process only if it might be relevant */
+ if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds)
+ mpq_dmx_tspp_swfilter_desc(mpq_demux, tspp_data_desc);
+
}
if (!aggregate_count)
return;
- buff_start_addr = mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base;
- input.base_addr = (void *)buff_start_addr;
+ buff_start_addr_phys =
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base;
+ input.base_addr = (void *)buff_start_addr_phys;
input.size = mpq_dmx_tspp_info.tsif[tsif].buffer_count *
TSPP_DESCRIPTOR_SIZE;
- MPQ_DVB_DBG_PRINT(
- "%s: Processing %d descriptors: %d bytes at start address 0x%x, read offset %d\n",
- __func__, aggregate_count, aggregate_len,
- (unsigned int)input.base_addr,
- buff_current_addr - buff_start_addr);
+ if (mpq_sdmx_is_loaded() && mpq_demux->sdmx_filter_count) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: SDMX Processing %d descriptors: %d bytes at start address 0x%x, read offset %d\n",
+ __func__, aggregate_count, aggregate_len,
+ (unsigned int)input.base_addr,
+ buff_current_addr_phys - buff_start_addr_phys);
- mpq_sdmx_process(mpq_demux, &input, aggregate_len,
- buff_current_addr - buff_start_addr);
+ mpq_sdmx_process(mpq_demux, &input, aggregate_len,
+ buff_current_addr_phys - buff_start_addr_phys);
+ }
for (i = 0; i < aggregate_count; i++)
tspp_release_buffer(0, channel_id,
@@ -373,7 +406,6 @@
int channel_id;
int ref_count;
int ret;
- int j;
do {
ret = wait_event_interruptible(
@@ -427,13 +459,8 @@
TSPP_RAW_TTS_SIZE;
mpq_demux->hw_notification_size += notif_size;
- for (j = 0; j < notif_size; j++)
- dvb_dmx_swfilter_packet(
- &mpq_demux->demux,
- ((u8 *)tspp_data_desc->virt_base) +
- j * TSPP_RAW_TTS_SIZE,
- ((u8 *)tspp_data_desc->virt_base) +
- j * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
+ mpq_dmx_tspp_swfilter_desc(mpq_demux,
+ tspp_data_desc);
/*
* Notify TSPP that the buffer
* is no longer needed
@@ -1554,10 +1581,10 @@
"%s: warnning - len larger than one packet\n",
__func__);
- if (mpq_dmx_is_video_feed(feed))
+ if (dvb_dmx_is_video_feed(feed))
return mpq_dmx_process_video_packet(feed, buf);
- if (mpq_dmx_is_pcr_feed(feed))
+ if (dvb_dmx_is_pcr_feed(feed))
return mpq_dmx_process_pcr_packet(feed, buf);
return 0;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
index d292992..f9d85aa 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
@@ -52,7 +52,8 @@
#define SDMX_INVALID_FILTER_HANDLE (-1)
/* Input flags */
-#define SDMX_INPUT_FLAG_EOS BIT(0)
+#define SDMX_INPUT_FLAG_EOS BIT(0)
+#define SDMX_INPUT_FLAG_DBG_ENABLE BIT(1)
enum sdmx_buf_mode {
@@ -177,9 +178,7 @@
/* Payload length */
u32 payload_length;
- /* Total metadata length (including this header, plus optional
- * additional metadata.
- */
+ /* Number of meta data bytes immediately following this header */
u32 metadata_length;
};
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 8593760..c8599d5 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -184,24 +184,27 @@
case HAL_BUFFER_OUTPUT:
buffer = HFI_BUFFER_OUTPUT;
break;
- case HAL_BUFFER_OUTPUT2:
- buffer = HFI_BUFFER_OUTPUT;
- break;
case HAL_BUFFER_EXTRADATA_INPUT:
buffer = HFI_BUFFER_EXTRADATA_INPUT;
break;
case HAL_BUFFER_EXTRADATA_OUTPUT:
buffer = HFI_BUFFER_EXTRADATA_OUTPUT;
break;
- case HAL_BUFFER_EXTRADATA_OUTPUT2:
- buffer = HFI_BUFFER_EXTRADATA_OUTPUT2;
- break;
case HAL_BUFFER_INTERNAL_SCRATCH:
buffer = HFI_BUFFER_INTERNAL_SCRATCH;
break;
+ case HAL_BUFFER_INTERNAL_SCRATCH_1:
+ buffer = HFI_BUFFER_INTERNAL_SCRATCH_1;
+ break;
+ case HAL_BUFFER_INTERNAL_SCRATCH_2:
+ buffer = HFI_BUFFER_INTERNAL_SCRATCH_2;
+ break;
case HAL_BUFFER_INTERNAL_PERSIST:
buffer = HFI_BUFFER_INTERNAL_PERSIST;
break;
+ case HAL_BUFFER_INTERNAL_PERSIST_1:
+ buffer = HFI_BUFFER_INTERNAL_PERSIST_1;
+ break;
default:
dprintk(VIDC_ERR, "Invalid buffer :0x%x\n",
hal_buffer);
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 709eafc..be9458d 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -391,12 +391,30 @@
buffreq->buffer[6].buffer_type =
HAL_BUFFER_INTERNAL_SCRATCH;
break;
- case HFI_BUFFER_INTERNAL_PERSIST:
+ case HFI_BUFFER_INTERNAL_SCRATCH_1:
memcpy(&buffreq->buffer[7], hfi_buf_req,
- sizeof(struct hfi_buffer_requirements));
+ sizeof(struct hfi_buffer_requirements));
buffreq->buffer[7].buffer_type =
+ HAL_BUFFER_INTERNAL_SCRATCH_1;
+ break;
+ case HFI_BUFFER_INTERNAL_SCRATCH_2:
+ memcpy(&buffreq->buffer[8], hfi_buf_req,
+ sizeof(struct hfi_buffer_requirements));
+ buffreq->buffer[8].buffer_type =
+ HAL_BUFFER_INTERNAL_SCRATCH_2;
+ break;
+ case HFI_BUFFER_INTERNAL_PERSIST:
+ memcpy(&buffreq->buffer[9], hfi_buf_req,
+ sizeof(struct hfi_buffer_requirements));
+ buffreq->buffer[9].buffer_type =
HAL_BUFFER_INTERNAL_PERSIST;
break;
+ case HFI_BUFFER_INTERNAL_PERSIST_1:
+ memcpy(&buffreq->buffer[10], hfi_buf_req,
+ sizeof(struct hfi_buffer_requirements));
+ buffreq->buffer[10].buffer_type =
+ HAL_BUFFER_INTERNAL_PERSIST_1;
+ break;
default:
dprintk(VIDC_ERR,
"hal_process_sess_get_prop_buf_req: bad_buffer_type: %d",
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
index 125c699..b3bce5f 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.c
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -19,43 +19,86 @@
struct smem_client {
int mem_type;
void *clnt;
+ struct msm_vidc_platform_resources *res;
};
-static int get_device_address(struct ion_client *clnt,
- struct ion_handle *hndl, int domain_num, int partition_num,
- unsigned long align, unsigned long *iova,
- unsigned long *buffer_size, int flags)
+static u32 get_tz_usage(struct smem_client *client, enum hal_buffer buffer_type)
+{
+ int i;
+ struct buffer_usage_set *buffer_usage_set;
+ struct buffer_usage_table *buffer_usage_tbl;
+
+ buffer_usage_set = &client->res->buffer_usage_set;
+ if (!buffer_usage_set) {
+ dprintk(VIDC_DBG, "no buffer usage set present!\n");
+ return 0;
+ }
+
+ for (i = 0; i < buffer_usage_set->count; i++) {
+ buffer_usage_tbl = &buffer_usage_set->buffer_usage_tbl[i];
+ if (buffer_usage_tbl->buffer_type & buffer_type)
+ return buffer_usage_tbl->tz_usage;
+ }
+ dprintk(VIDC_DBG, "No tz usage found for buffer type: %x\n",
+ buffer_type);
+ return 0;
+}
+
+static int get_device_address(struct smem_client *smem_client,
+ struct ion_handle *hndl, unsigned long align,
+ unsigned long *iova, unsigned long *buffer_size,
+ u32 flags, enum hal_buffer buffer_type)
{
int rc = 0;
- if (!iova || !buffer_size || !hndl || !clnt) {
+ int domain, partition;
+ struct ion_client *clnt = NULL;
+
+ if (!iova || !buffer_size || !hndl || !smem_client) {
dprintk(VIDC_ERR, "Invalid params: %p, %p, %p, %p\n",
- clnt, hndl, iova, buffer_size);
+ smem_client, hndl, iova, buffer_size);
return -EINVAL;
}
- dprintk(VIDC_DBG, "domain: %d, partition: %d\n",
- domain_num, partition_num);
+
+ clnt = smem_client->clnt;
+ if (!clnt) {
+ dprintk(VIDC_ERR, "Invalid client");
+ return -EINVAL;
+ }
+
+ rc = msm_smem_get_domain_partition(smem_client, flags, buffer_type,
+ &domain, &partition);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to get domain and partition: %d", rc);
+ goto mem_domain_get_failed;
+ }
+
if (flags & SMEM_SECURE) {
- if (flags & SMEM_INPUT)
- rc = msm_ion_secure_buffer(clnt, hndl, 0x1, 0);
- else
- rc = msm_ion_secure_buffer(clnt, hndl, 0x2, 0);
+ rc = msm_ion_secure_buffer(clnt, hndl,
+ get_tz_usage(smem_client, buffer_type), 0);
if (rc) {
dprintk(VIDC_ERR, "Failed to secure memory\n");
- goto mem_secure_failed;
+ goto mem_domain_get_failed;
}
}
- rc = ion_map_iommu(clnt, hndl, domain_num, partition_num, align,
+ rc = ion_map_iommu(clnt, hndl, domain, partition, align,
0, iova, buffer_size, 0, 0);
- if (rc)
+ if (rc) {
dprintk(VIDC_ERR,
"ion_map_iommu failed(%d).domain: %d,partition: %d\n",
- rc, domain_num, partition_num);
-mem_secure_failed:
+ rc, domain, partition);
+ goto mem_map_failed;
+ }
+
+ return 0;
+mem_map_failed:
+ if (flags & SMEM_SECURE)
+ msm_ion_unsecure_buffer(clnt, hndl);
+mem_domain_get_failed:
return rc;
}
static void put_device_address(struct ion_client *clnt,
- struct ion_handle *hndl, int domain_num, int partition_num, int flags)
+ struct ion_handle *hndl, int domain_num, int partition_num, u32 flags)
{
ion_unmap_iommu(clnt, hndl, domain_num, partition_num);
if (flags & SMEM_SECURE) {
@@ -64,9 +107,8 @@
}
}
-static int ion_user_to_kernel(struct smem_client *client,
- int fd, u32 offset, int domain, int partition,
- struct msm_smem *mem, int flags)
+static int ion_user_to_kernel(struct smem_client *client, int fd, u32 offset,
+ struct msm_smem *mem, enum hal_buffer buffer_type)
{
struct ion_handle *hndl;
unsigned long iova = 0;
@@ -74,6 +116,7 @@
unsigned long ionflags = 0;
int rc = 0;
int align = SZ_4K;
+
hndl = ion_import_dma_buf(client->clnt, fd);
if (IS_ERR_OR_NULL(hndl)) {
dprintk(VIDC_ERR, "Failed to get handle: %p, %d, %d, %p\n",
@@ -82,9 +125,6 @@
goto fail_import_fd;
}
mem->kvaddr = NULL;
- mem->domain = domain;
- mem->partition_num = partition;
- mem->flags = flags;
rc = ion_handle_get_flags(client->clnt, hndl, &ionflags);
if (rc) {
dprintk(VIDC_ERR, "Failed to get ion flags: %d\n", rc);
@@ -99,11 +139,14 @@
goto fail_map;
}
}
- if (flags & SMEM_SECURE)
+
+ mem->flags = ionflags;
+ mem->buffer_type = buffer_type;
+ if (mem->flags & SMEM_SECURE)
align = ALIGN(align, SZ_1M);
- rc = get_device_address(client->clnt, hndl, mem->domain,
- mem->partition_num, align, &iova, &buffer_size, flags);
+ rc = get_device_address(client, hndl, align, &iova, &buffer_size,
+ mem->flags, buffer_type);
if (rc) {
dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc);
goto fail_device_address;
@@ -125,49 +168,40 @@
return rc;
}
-static int alloc_ion_mem(struct smem_client *client, size_t size,
- u32 align, u32 flags, int domain, int partition,
- struct msm_smem *mem, int map_kernel)
+static int alloc_ion_mem(struct smem_client *client, size_t size, u32 align,
+ u32 flags, enum hal_buffer buffer_type, struct msm_smem *mem,
+ int map_kernel)
{
struct ion_handle *hndl;
unsigned long iova = 0;
unsigned long buffer_size = 0;
- unsigned long ionflags = 0;
unsigned long heap_mask = 0;
int rc = 0;
- if (flags & SMEM_CACHED)
- ionflags = ION_SET_CACHED(ionflags);
- else
- ionflags = ION_SET_UNCACHED(ionflags);
align = ALIGN(align, SZ_4K);
size = ALIGN(size, SZ_4K);
if (flags & SMEM_SECURE) {
- ionflags |= ION_SECURE;
size = ALIGN(size, SZ_1M);
align = ALIGN(align, SZ_1M);
}
- heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
- if (!(flags & SMEM_SECURE))
- heap_mask |= ION_HEAP(ION_IOMMU_HEAP_ID);
+ heap_mask = ION_HEAP(ION_IOMMU_HEAP_ID);
+ if (flags & SMEM_SECURE)
+ heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
- dprintk(VIDC_DBG, "domain: %d, partition: %d\n",
- domain, partition);
- hndl = ion_alloc(client->clnt, size, align, heap_mask, ionflags);
+ hndl = ion_alloc(client->clnt, size, align, heap_mask, flags);
if (IS_ERR_OR_NULL(hndl)) {
dprintk(VIDC_ERR,
- "Failed to allocate shared memory = %p, %d, %d, 0x%lx\n",
- client, size, align, ionflags);
+ "Failed to allocate shared memory = %p, %d, %d, 0x%x\n",
+ client, size, align, flags);
rc = -ENOMEM;
goto fail_shared_mem_alloc;
}
mem->mem_type = client->mem_type;
mem->smem_priv = hndl;
- mem->domain = domain;
- mem->partition_num = partition;
mem->flags = flags;
+ mem->buffer_type = buffer_type;
if (map_kernel) {
mem->kvaddr = ion_map_kernel(client->clnt, hndl);
if (!mem->kvaddr) {
@@ -179,8 +213,8 @@
} else
mem->kvaddr = NULL;
- rc = get_device_address(client->clnt, hndl, mem->domain,
- mem->partition_num, align, &iova, &buffer_size, flags);
+ rc = get_device_address(client, hndl, align, &iova, &buffer_size,
+ flags, buffer_type);
if (rc) {
dprintk(VIDC_ERR, "Failed to get device address: %d\n",
rc);
@@ -202,10 +236,18 @@
static void free_ion_mem(struct smem_client *client, struct msm_smem *mem)
{
+ int domain, partition, rc;
+
+ rc = msm_smem_get_domain_partition((void *)client, mem->flags,
+ mem->buffer_type, &domain, &partition);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to get domain, partition: %d", rc);
+ return;
+ }
+
if (mem->device_addr)
put_device_address(client->clnt,
- mem->smem_priv, mem->domain,
- mem->partition_num, mem->flags);
+ mem->smem_priv, domain, partition, mem->flags);
if (mem->kvaddr)
ion_unmap_kernel(client->clnt, mem->smem_priv);
if (mem->smem_priv)
@@ -227,7 +269,7 @@
}
struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
- int domain, int partition, int flags)
+ enum hal_buffer buffer_type)
{
struct smem_client *client = clt;
int rc = 0;
@@ -243,8 +285,7 @@
}
switch (client->mem_type) {
case SMEM_ION:
- rc = ion_user_to_kernel(clt, fd, offset,
- domain, partition, mem, flags);
+ rc = ion_user_to_kernel(clt, fd, offset, mem, buffer_type);
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
@@ -337,7 +378,8 @@
return rc;
}
-void *msm_smem_new_client(enum smem_type mtype)
+void *msm_smem_new_client(enum smem_type mtype,
+ struct msm_vidc_platform_resources *res)
{
struct smem_client *client = NULL;
void *clnt = NULL;
@@ -354,6 +396,7 @@
if (client) {
client->mem_type = mtype;
client->clnt = clnt;
+ client->res = res;
}
} else {
dprintk(VIDC_ERR, "Failed to create new client: mtype = %d\n",
@@ -363,7 +406,7 @@
};
struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags,
- int domain, int partition, int map_kernel)
+ enum hal_buffer buffer_type, int map_kernel)
{
struct smem_client *client;
int rc = 0;
@@ -385,8 +428,8 @@
}
switch (client->mem_type) {
case SMEM_ION:
- rc = alloc_ion_mem(client, size, align, flags,
- domain, partition, mem, map_kernel);
+ rc = alloc_ion_mem(client, size, align, flags, buffer_type,
+ mem, map_kernel);
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
@@ -436,3 +479,36 @@
}
kfree(client);
}
+
+int msm_smem_get_domain_partition(void *clt, u32 flags, enum hal_buffer
+ buffer_type, int *domain_num, int *partition_num)
+{
+ struct smem_client *client = clt;
+ struct iommu_set *iommu_group_set = &client->res->iommu_group_set;
+ int i;
+ bool is_secure = (flags & SMEM_SECURE);
+ struct iommu_info *iommu_map;
+
+ *domain_num = -1;
+ *partition_num = -1;
+ if (!iommu_group_set) {
+ dprintk(VIDC_DBG, "no iommu group set present!\n");
+ return -ENOENT;
+ }
+
+ for (i = 0; i < iommu_group_set->count; i++) {
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ if ((iommu_map->is_secure == is_secure) &&
+ (iommu_map->buffer_type & buffer_type)) {
+ *domain_num = iommu_map->domain;
+ *partition_num = 0;
+ if ((buffer_type & HAL_BUFFER_INTERNAL_CMD_QUEUE) &&
+ (iommu_map->npartitions == 2))
+ *partition_num = 1;
+ break;
+ }
+ }
+ dprintk(VIDC_DBG, "domain: %d, partition: %d found!\n",
+ *domain_num, *partition_num);
+ return 0;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_smem.h b/drivers/media/platform/msm/vidc/msm_smem.h
index d1c8293..b80d63e 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.h
+++ b/drivers/media/platform/msm/vidc/msm_smem.h
@@ -15,15 +15,32 @@
#include <linux/types.h>
#include <linux/msm_ion.h>
+#include "msm_vidc_resources.h"
+
+#define HAL_BUFFER_MAX 0xb
enum smem_type {
SMEM_ION,
};
enum smem_prop {
- SMEM_CACHED = 0x1,
- SMEM_SECURE = 0x2,
- SMEM_INPUT = 0x4,
+ SMEM_CACHED = ION_FLAG_CACHED,
+ SMEM_SECURE = ION_SECURE,
+};
+
+enum hal_buffer {
+ HAL_BUFFER_INPUT = 0x1,
+ HAL_BUFFER_OUTPUT = 0x2,
+ HAL_BUFFER_OUTPUT2 = 0x2,
+ HAL_BUFFER_EXTRADATA_INPUT = 0x4,
+ HAL_BUFFER_EXTRADATA_OUTPUT = 0x8,
+ HAL_BUFFER_EXTRADATA_OUTPUT2 = 0x8,
+ HAL_BUFFER_INTERNAL_SCRATCH = 0x10,
+ HAL_BUFFER_INTERNAL_SCRATCH_1 = 0x20,
+ HAL_BUFFER_INTERNAL_SCRATCH_2 = 0x40,
+ HAL_BUFFER_INTERNAL_PERSIST = 0x80,
+ HAL_BUFFER_INTERNAL_PERSIST_1 = 0x100,
+ HAL_BUFFER_INTERNAL_CMD_QUEUE = 0x200,
};
struct msm_smem {
@@ -31,10 +48,9 @@
size_t size;
void *kvaddr;
unsigned long device_addr;
- int domain;
- int partition_num;
- int flags;
+ u32 flags;
void *smem_priv;
+ enum hal_buffer buffer_type;
};
enum smem_cache_ops {
@@ -43,14 +59,17 @@
SMEM_CACHE_CLEAN_INVALIDATE,
};
-
-void *msm_smem_new_client(enum smem_type mtype);
+void *msm_smem_new_client(enum smem_type mtype,
+ struct msm_vidc_platform_resources *res);
struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags,
- int domain, int partition, int map_kernel);
+ enum hal_buffer buffer_type, int map_kernel);
void msm_smem_free(void *clt, struct msm_smem *mem);
void msm_smem_delete_client(void *clt);
-struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset, int
- domain, int partition, int flags);
int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
enum smem_cache_ops);
+struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
+ enum hal_buffer buffer_type);
+int msm_smem_clean_invalidate(void *clt, struct msm_smem *mem);
+int msm_smem_get_domain_partition(void *clt, u32 flags, enum hal_buffer
+ buffer_type, int *domain_num, int *partition_num);
#endif
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 181b2b6..4f8c257 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -227,7 +227,7 @@
rc = -ENOMEM;
goto fail_nomem;
}
- v4l2_inst->mem_client = msm_smem_new_client(SMEM_ION);
+ v4l2_inst->mem_client = msm_smem_new_client(SMEM_ION, &core->resources);
if (!v4l2_inst->mem_client) {
dprintk(VIDC_ERR, "Failed to create memory client\n");
rc = -ENOMEM;
@@ -400,9 +400,8 @@
struct msm_v4l2_vid_inst *v4l2_inst;
int plane = 0;
int i, rc = 0;
- int smem_flags = 0;
- int domain;
struct hfi_device *hdev;
+ enum hal_buffer buffer_type;
vidc_inst = get_vidc_inst(file, fh);
v4l2_inst = get_v4l2_inst(file, fh);
@@ -432,7 +431,7 @@
goto exit;
}
for (i = 0; i < b->length; ++i) {
- smem_flags = 0;
+ buffer_type = HAL_BUFFER_OUTPUT;
if (EXTRADATA_IDX(b->length) &&
(i == EXTRADATA_IDX(b->length)) &&
!b->m.planes[i].length) {
@@ -449,18 +448,8 @@
kfree(binfo);
goto exit;
}
- if ((vidc_inst->mode == VIDC_SECURE)
- && (!EXTRADATA_IDX(b->length)
- || (i != EXTRADATA_IDX(b->length)))) {
- smem_flags |= SMEM_SECURE;
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, CP_MAP);
- } else
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, NS_MAP);
-
if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- smem_flags |= SMEM_INPUT;
+ buffer_type = HAL_BUFFER_INPUT;
temp = get_same_fd_buffer(&v4l2_inst->registered_bufs,
b->m.planes[i].reserved[0], &plane);
@@ -476,9 +465,9 @@
binfo->handle[i] = NULL;
} else {
handle = msm_smem_user_to_kernel(v4l2_inst->mem_client,
- b->m.planes[i].reserved[0],
- b->m.planes[i].reserved[1],
- domain, 0, smem_flags);
+ b->m.planes[i].reserved[0],
+ b->m.planes[i].reserved[1],
+ buffer_type);
if (!handle) {
dprintk(VIDC_ERR,
"Failed to get device buffer address\n");
@@ -796,13 +785,6 @@
res->load_freq_tbl = NULL;
}
-static inline void msm_vidc_free_iommu_maps(
- struct msm_vidc_platform_resources *res)
-{
- kfree(res->iommu_maps);
- res->iommu_maps = NULL;
-}
-
static inline void msm_vidc_free_reg_table(
struct msm_vidc_platform_resources *res)
{
@@ -828,6 +810,20 @@
}
}
+static inline void msm_vidc_free_iommu_groups(
+ struct msm_vidc_platform_resources *res)
+{
+ kfree(res->iommu_group_set.iommu_maps);
+ res->iommu_group_set.iommu_maps = NULL;
+}
+
+static inline void msm_vidc_free_buffer_usage_table(
+ struct msm_vidc_platform_resources *res)
+{
+ kfree(res->buffer_usage_set.buffer_usage_tbl);
+ res->buffer_usage_set.buffer_usage_tbl = NULL;
+}
+
static int msm_vidc_load_freq_table(struct msm_vidc_platform_resources *res)
{
int rc = 0;
@@ -861,60 +857,6 @@
return rc;
}
-static int msm_vidc_load_iommu_maps(struct msm_vidc_platform_resources *res)
-{
- int rc = 0;
- int num_elements = 0;
- int i;
- struct platform_device *pdev = res->pdev;
- char *names[MAX_MAP] = {
- [CP_MAP] = "qcom,vidc-cp-map",
- [NS_MAP] = "qcom,vidc-ns-map",
- };
- char *contexts[MAX_MAP] = {
- [CP_MAP] = "venus_cp",
- [NS_MAP] = "venus_ns",
- };
-
-
- res->iommu_maps = kzalloc(MAX_MAP * sizeof(*res->iommu_maps),
- GFP_KERNEL);
- if (!res->iommu_maps) {
- dprintk(VIDC_ERR, "%s Failed to alloc iommu_maps\n", __func__);
- return -ENOMEM;
- }
-
- res->iommu_maps_size = MAX_MAP;
- for (i = 0; i < MAX_MAP; i++) {
- num_elements = get_u32_array_num_elements(pdev, names[i]);
- if ((num_elements == 0)) {
- if (i == NS_MAP) {
- dprintk(VIDC_ERR,
- "Domain not found in dtsi file :%s\n",
- names[i]);
- goto error;
- } else
- continue;
- }
- memcpy(&res->iommu_maps[i].name, names[i],
- strlen(names[i]));
- memcpy(&res->iommu_maps[i].ctx, contexts[i],
- strlen(contexts[i]));
-
- if (of_property_read_u32_array(pdev->dev.of_node, names[i],
- res->iommu_maps[i].addr_range, num_elements * 2)) {
- dprintk(VIDC_ERR, "Failed to read iommu map :%s\n",
- names[i]);
- rc = -EINVAL;
- goto error;
- }
- }
- return rc;
-error:
- msm_vidc_free_iommu_maps(res);
- return rc;
-}
-
static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res)
{
struct reg_set *reg_set;
@@ -932,7 +874,7 @@
reg_set->reg_tbl = kzalloc(reg_set->count *
sizeof(*(reg_set->reg_tbl)), GFP_KERNEL);
if (!reg_set->reg_tbl) {
- dprintk(VIDC_ERR, "%s Failed to alloc temp\n",
+ dprintk(VIDC_ERR, "%s Failed to alloc register table\n",
__func__);
return -ENOMEM;
}
@@ -1091,6 +1033,153 @@
return rc;
}
+static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res)
+{
+ int rc = 0;
+ struct platform_device *pdev = res->pdev;
+ struct device_node *ctx_node;
+ struct iommu_set *iommu_group_set = &res->iommu_group_set;
+ int array_size;
+ int i;
+ struct iommu_info *iommu_map;
+ u32 *buffer_types = NULL;
+
+ if (!of_get_property(pdev->dev.of_node, "qcom,iommu-groups",
+ &array_size)) {
+ dprintk(VIDC_ERR, "Could not find iommu_groups property\n");
+ iommu_group_set->count = 0;
+ rc = -ENOENT;
+ goto err_no_of_node;
+ }
+
+ iommu_group_set->count = array_size / sizeof(u32);
+ if (iommu_group_set->count == 0) {
+ dprintk(VIDC_ERR, "No group present in iommu_groups\n");
+ rc = -ENOENT;
+ goto err_no_of_node;
+ }
+
+ iommu_group_set->iommu_maps = kzalloc(iommu_group_set->count *
+ sizeof(*(iommu_group_set->iommu_maps)), GFP_KERNEL);
+ if (!iommu_group_set->iommu_maps) {
+ dprintk(VIDC_ERR, "%s Failed to alloc iommu_maps\n",
+ __func__);
+ rc = -ENOMEM;
+ goto err_no_of_node;
+ }
+
+ buffer_types = kzalloc(iommu_group_set->count * sizeof(*buffer_types),
+ GFP_KERNEL);
+ if (!buffer_types) {
+ dprintk(VIDC_ERR,
+ "%s Failed to alloc iommu group buffer types\n",
+ __func__);
+ rc = -ENOMEM;
+ goto err_load_groups;
+ }
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,iommu-group-buffer-types", buffer_types,
+ iommu_group_set->count);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s Failed to read iommu group buffer types\n", __func__);
+ goto err_load_groups;
+ }
+
+ for (i = 0; i < iommu_group_set->count; i++) {
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ ctx_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,iommu-groups", i);
+ if (!ctx_node) {
+ dprintk(VIDC_ERR, "Unable to parse phandle : %u\n", i);
+ rc = -EBADHANDLE;
+ goto err_load_groups;
+ }
+
+ rc = of_property_read_string(ctx_node, "label",
+ &(iommu_map->name));
+ if (rc) {
+ dprintk(VIDC_ERR, "Could not find label property\n");
+ goto err_load_groups;
+ }
+
+ if (!of_get_property(ctx_node, "qcom,virtual-addr-pool",
+ &array_size)) {
+ dprintk(VIDC_ERR,
+ "Could not find any addr pool for group : %s\n",
+ iommu_map->name);
+ rc = -EBADHANDLE;
+ goto err_load_groups;
+ }
+
+ iommu_map->npartitions = array_size / sizeof(u32) / 2;
+
+ rc = of_property_read_u32_array(ctx_node,
+ "qcom,virtual-addr-pool",
+ (u32 *)iommu_map->addr_range,
+ iommu_map->npartitions * 2);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Could not read addr pool for group : %s\n",
+ iommu_map->name);
+ goto err_load_groups;
+ }
+
+ iommu_map->buffer_type = buffer_types[i];
+ iommu_map->is_secure =
+ of_property_read_bool(ctx_node, "qcom,secure-domain");
+ }
+ kfree(buffer_types);
+ return 0;
+err_load_groups:
+ kfree(buffer_types);
+ msm_vidc_free_iommu_groups(res);
+err_no_of_node:
+ return rc;
+}
+
+static int msm_vidc_load_buffer_usage_table(
+ struct msm_vidc_platform_resources *res)
+{
+ int rc = 0;
+ struct platform_device *pdev = res->pdev;
+ struct buffer_usage_set *buffer_usage_set = &res->buffer_usage_set;
+
+ buffer_usage_set->count = get_u32_array_num_elements(
+ pdev, "qcom,buffer-type-tz-usage-table");
+ if (buffer_usage_set->count == 0) {
+ dprintk(VIDC_DBG, "no elements in buffer usage set\n");
+ return 0;
+ }
+
+ buffer_usage_set->buffer_usage_tbl = kzalloc(buffer_usage_set->count *
+ sizeof(*(buffer_usage_set->buffer_usage_tbl)),
+ GFP_KERNEL);
+ if (!buffer_usage_set->buffer_usage_tbl) {
+ dprintk(VIDC_ERR, "%s Failed to alloc buffer usage table\n",
+ __func__);
+ rc = -ENOMEM;
+ goto err_load_buf_usage;
+ }
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,buffer-type-tz-usage-table",
+ (u32 *)buffer_usage_set->buffer_usage_tbl,
+ buffer_usage_set->count *
+ (sizeof(*buffer_usage_set->buffer_usage_tbl)/sizeof(u32)));
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to read buffer usage table\n");
+ goto err_load_buf_usage;
+ }
+
+ return 0;
+err_load_buf_usage:
+ msm_vidc_free_buffer_usage_table(res);
+ return rc;
+}
+
+
static int read_platform_resources_from_dt(
struct msm_vidc_platform_resources *res)
{
@@ -1117,29 +1206,36 @@
dprintk(VIDC_ERR, "Failed to load freq table: %d\n", rc);
goto err_load_freq_table;
}
- rc = msm_vidc_load_iommu_maps(res);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to load iommu maps: %d\n", rc);
- goto err_load_iommu_maps;
- }
rc = msm_vidc_load_reg_table(res);
if (rc) {
dprintk(VIDC_ERR, "Failed to load reg table: %d\n", rc);
goto err_load_reg_table;
}
-
rc = msm_vidc_load_bus_vectors(res);
if (rc) {
dprintk(VIDC_ERR, "Failed to load bus vectors: %d\n", rc);
goto err_load_bus_vectors;
}
+ rc = msm_vidc_load_iommu_groups(res);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to load iommu groups: %d\n", rc);
+ goto err_load_iommu_groups;
+ }
+ rc = msm_vidc_load_buffer_usage_table(res);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to load buffer usage table: %d\n", rc);
+ goto err_load_buffer_usage_table;
+ }
return rc;
+err_load_buffer_usage_table:
+ msm_vidc_free_iommu_groups(res);
+err_load_iommu_groups:
+ msm_vidc_free_bus_vectors(res);
err_load_bus_vectors:
msm_vidc_free_reg_table(res);
err_load_reg_table:
- msm_vidc_free_iommu_maps(res);
-err_load_iommu_maps:
msm_vidc_free_freq_table(res);
err_load_freq_table:
return rc;
@@ -1151,7 +1247,6 @@
struct resource *kres = NULL;
struct platform_device *pdev = res->pdev;
struct msm_vidc_v4l2_platform_data *pdata = pdev->dev.platform_data;
- int64_t start, size;
int c = 0, rc = 0;
if (!pdata) {
@@ -1182,33 +1277,6 @@
res->load_freq_tbl[c].load = pdata->load_table[c][0];
res->load_freq_tbl[c].freq = pdata->load_table[c][1];
}
-
- res->iommu_maps = kzalloc(MAX_MAP *
- sizeof(*res->iommu_maps), GFP_KERNEL);
- if (!res->iommu_maps) {
- dprintk(VIDC_ERR, "%s Failed to alloc iommu_maps\n",
- __func__);
- kfree(res->load_freq_tbl);
- return -ENOMEM;
- }
-
- res->iommu_maps_size = MAX_MAP;
-
- start = pdata->iommu_table[MSM_VIDC_V4L2_IOMMU_MAP_CP][0];
- size = pdata->iommu_table[MSM_VIDC_V4L2_IOMMU_MAP_CP][1];
- res->iommu_maps[CP_MAP] = (struct msm_vidc_iommu_info) {
- .addr_range = {(u32) start, (u32) size},
- .name = "qcom,vidc-cp-map",
- .ctx = "venus_cp",
- };
-
- start = pdata->iommu_table[MSM_VIDC_V4L2_IOMMU_MAP_NS][0];
- size = pdata->iommu_table[MSM_VIDC_V4L2_IOMMU_MAP_NS][1];
- res->iommu_maps[NS_MAP] = (struct msm_vidc_iommu_info) {
- .addr_range = {(u32) start, (u32) size},
- .name = "qcom,vidc-ns-map",
- .ctx = "venus_ns",
- };
return rc;
}
@@ -1365,9 +1433,10 @@
v4l2_device_unregister(&core->v4l2_dev);
msm_vidc_free_freq_table(&core->resources);
- msm_vidc_free_iommu_maps(&core->resources);
msm_vidc_free_reg_table(&core->resources);
msm_vidc_free_bus_vectors(&core->resources);
+ msm_vidc_free_iommu_groups(&core->resources);
+ msm_vidc_free_buffer_usage_table(&core->resources);
kfree(core);
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index ae98afb..5966d12 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -584,6 +584,7 @@
int rc = 0;
int ret;
int i;
+ struct hal_buffer_requirements *buff_req_buffer;
if (!inst || !f || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR,
"Invalid input, inst = %p, format = %p\n", inst, f);
@@ -636,12 +637,26 @@
dprintk(VIDC_WARN,
"Color format not recognized\n");
}
- f->fmt.pix_mp.plane_fmt[0].sizeimage =
- inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+ buff_req_buffer =
+ get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+ if (buff_req_buffer)
+ f->fmt.pix_mp.plane_fmt[0].sizeimage =
+ buff_req_buffer->buffer_size;
+ else
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = 0;
+
extra_idx = EXTRADATA_IDX(fmt->num_planes);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
- f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
- inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
+ buff_req_buffer =
+ get_buff_req_buffer(inst,
+ HAL_BUFFER_EXTRADATA_OUTPUT);
+ if (buff_req_buffer)
+ f->fmt.pix_mp.plane_fmt[extra_idx].
+ sizeimage =
+ buff_req_buffer->buffer_size;
+ else
+ f->fmt.pix_mp.plane_fmt[extra_idx].
+ sizeimage = 0;
}
for (i = 0; i < fmt->num_planes; ++i)
inst->bufq[CAPTURE_PORT].
@@ -708,6 +723,7 @@
int rc = 0;
int ret = 0;
int i;
+ struct hal_buffer_requirements *buff_req_buffer;
if (!inst || !f) {
dprintk(VIDC_ERR,
"Invalid input, inst = %p, format = %p\n", inst, f);
@@ -743,12 +759,24 @@
f->fmt.pix_mp.width);
}
} else {
- f->fmt.pix_mp.plane_fmt[0].sizeimage =
- inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+ buff_req_buffer =
+ get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+ if (buff_req_buffer)
+ f->fmt.pix_mp.plane_fmt[0].sizeimage =
+ buff_req_buffer->buffer_size;
+ else
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = 0;
extra_idx = EXTRADATA_IDX(fmt->num_planes);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
- f->fmt.pix_mp.plane_fmt[1].sizeimage =
- inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
+ buff_req_buffer =
+ get_buff_req_buffer(inst,
+ HAL_BUFFER_EXTRADATA_OUTPUT);
+ if (buff_req_buffer)
+ f->fmt.pix_mp.plane_fmt[1].sizeimage =
+ buff_req_buffer->buffer_size;
+ else
+ f->fmt.pix_mp.plane_fmt[1].sizeimage =
+ 0;
}
}
f->fmt.pix_mp.num_planes = fmt->num_planes;
@@ -893,9 +921,16 @@
break;
}
mutex_lock(&inst->lock);
+ bufreq = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+ if (!bufreq) {
+ dprintk(VIDC_ERR,
+ "No buffer requirement for buffer type %x\n",
+ HAL_BUFFER_OUTPUT);
+ rc = -EINVAL;
+ break;
+ }
if (*num_buffers && *num_buffers >
- inst->buff_req.buffer[HAL_BUFFER_OUTPUT].
- buffer_count_actual) {
+ bufreq->buffer_count_actual) {
struct hal_buffer_count_actual new_buf_count;
enum hal_property property_id =
HAL_PARAM_BUFFER_COUNT_ACTUAL;
@@ -906,22 +941,25 @@
inst->session, property_id, &new_buf_count);
}
- bufreq = &inst->buff_req.buffer[HAL_BUFFER_OUTPUT];
if (bufreq->buffer_count_actual > *num_buffers)
*num_buffers = bufreq->buffer_count_actual;
else
- bufreq->buffer_count_actual = *num_buffers ;
+ bufreq->buffer_count_actual = *num_buffers;
mutex_unlock(&inst->lock);
dprintk(VIDC_DBG, "count = %d, size = %d, alignment = %d\n",
inst->buff_req.buffer[1].buffer_count_actual,
inst->buff_req.buffer[1].buffer_size,
inst->buff_req.buffer[1].buffer_alignment);
- sizes[0] = inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+ sizes[0] = bufreq->buffer_size;
extra_idx =
EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
- sizes[extra_idx] =
- inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
+ bufreq = get_buff_req_buffer(inst,
+ HAL_BUFFER_EXTRADATA_OUTPUT);
+ if (bufreq)
+ sizes[extra_idx] = bufreq->buffer_size;
+ else
+ sizes[extra_idx] = 0;
}
break;
default:
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 042900e..218987e 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -82,18 +82,16 @@
return rc;
}
-int msm_vidc_get_iommu_maps(void *instance,
- struct msm_vidc_iommu_info maps[MAX_MAP])
+int msm_vidc_get_iommu_domain_partition(void *instance, u32 flags,
+ enum v4l2_buf_type buf_type, int *domain, int *partition)
{
struct msm_vidc_inst *inst = instance;
- struct hfi_device *hdev;
- if (!inst || !maps || !inst->core || !inst->core->device)
+ if (!inst || !inst->core || !inst->core->device)
return -EINVAL;
- hdev = inst->core->device;
-
- return call_hfi_op(hdev, iommu_get_map, hdev->hfi_device_data, maps);
+ return msm_comm_get_domain_partition(inst, flags, buf_type, domain,
+ partition);
}
int msm_vidc_querycap(void *instance, struct v4l2_capability *cap)
@@ -436,7 +434,8 @@
i <= SESSION_MSG_INDEX(SESSION_MSG_END); i++) {
init_completion(&inst->completions[i]);
}
- inst->mem_client = msm_smem_new_client(SMEM_ION);
+ inst->mem_client = msm_smem_new_client(SMEM_ION,
+ &inst->core->resources);
if (!inst->mem_client) {
dprintk(VIDC_ERR, "Failed to create memory client\n");
goto fail_mem_client;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index d43e5ba..e55c0f1 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -193,7 +193,7 @@
}
struct buf_queue *msm_comm_get_vb2q(
- struct msm_vidc_inst *inst, enum v4l2_buf_type type)
+ struct msm_vidc_inst *inst, enum v4l2_buf_type type)
{
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return &inst->bufq[CAPTURE_PORT];
@@ -417,7 +417,7 @@
memcpy(&inst->buff_req, response->data,
sizeof(struct buffer_requirements));
mutex_unlock(&inst->lock);
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < HAL_BUFFER_MAX; i++) {
dprintk(VIDC_DBG,
"buffer type: %d, count : %d, size: %d\n",
inst->buff_req.buffer[i].buffer_type,
@@ -1123,8 +1123,8 @@
}
mutex_lock(&core->lock);
core->state = VIDC_CORE_UNINIT;
- call_hfi_op(hdev, unload_fw, hdev->hfi_device_data);
mutex_unlock(&core->lock);
+ call_hfi_op(hdev, unload_fw, hdev->hfi_device_data);
msm_comm_unvote_buses(core, DDR_MEM|OCMEM_MEM);
}
core_already_uninited:
@@ -1443,6 +1443,186 @@
return flipped_state;
}
+struct hal_buffer_requirements *get_buff_req_buffer(
+ struct msm_vidc_inst *inst, enum hal_buffer buffer_type)
+{
+ int i;
+ for (i = 0; i < HAL_BUFFER_MAX; i++) {
+ if (inst->buff_req.buffer[i].buffer_type == buffer_type)
+ return &inst->buff_req.buffer[i];
+ }
+ return NULL;
+}
+
+static int set_scratch_buffers(struct msm_vidc_inst *inst,
+ enum hal_buffer buffer_type)
+{
+ int rc = 0;
+ struct msm_smem *handle;
+ struct internal_buf *binfo;
+ struct vidc_buffer_addr_info buffer_info;
+ u32 smem_flags = 0;
+ struct hal_buffer_requirements *scratch_buf;
+ int i;
+ struct hfi_device *hdev;
+
+ hdev = inst->core->device;
+
+ scratch_buf = get_buff_req_buffer(inst, buffer_type);
+ if (!scratch_buf) {
+ dprintk(VIDC_DBG,
+ "This scratch buffer not required, buffer_type: %x\n",
+ buffer_type);
+ return 0;
+ }
+ dprintk(VIDC_DBG,
+ "scratch: num = %d, size = %d\n",
+ scratch_buf->buffer_count_actual,
+ scratch_buf->buffer_size);
+
+ if (inst->mode == VIDC_SECURE)
+ smem_flags |= SMEM_SECURE;
+
+ if (scratch_buf->buffer_size) {
+ for (i = 0; i < scratch_buf->buffer_count_actual;
+ i++) {
+ handle = msm_smem_alloc(inst->mem_client,
+ scratch_buf->buffer_size, 1, smem_flags,
+ buffer_type, 0);
+ if (!handle) {
+ dprintk(VIDC_ERR,
+ "Failed to allocate scratch memory\n");
+ rc = -ENOMEM;
+ goto err_no_mem;
+ }
+ rc = msm_smem_cache_operations(inst->mem_client,
+ handle, SMEM_CACHE_CLEAN);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to clean cache may cause undefined behavior\n");
+ }
+ binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+ if (!binfo) {
+ dprintk(VIDC_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto fail_kzalloc;
+ }
+ binfo->handle = handle;
+ buffer_info.buffer_size = scratch_buf->buffer_size;
+ buffer_info.buffer_type = buffer_type;
+ binfo->buffer_type = buffer_type;
+ buffer_info.num_buffers = 1;
+ buffer_info.align_device_addr = handle->device_addr;
+ dprintk(VIDC_DBG, "Scratch buffer address: %x",
+ buffer_info.align_device_addr);
+ rc = call_hfi_op(hdev, session_set_buffers,
+ (void *) inst->session, &buffer_info);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "vidc_hal_session_set_buffers failed");
+ goto fail_set_buffers;
+ }
+ mutex_lock(&inst->lock);
+ list_add_tail(&binfo->list, &inst->internalbufs);
+ mutex_unlock(&inst->lock);
+ }
+ }
+ return rc;
+fail_set_buffers:
+ kfree(binfo);
+fail_kzalloc:
+ msm_smem_free(inst->mem_client, handle);
+err_no_mem:
+ return rc;
+}
+
+static int set_persist_buffers(struct msm_vidc_inst *inst,
+ enum hal_buffer buffer_type)
+{
+ int rc = 0;
+ struct msm_smem *handle;
+ struct internal_buf *binfo;
+ struct vidc_buffer_addr_info buffer_info;
+ u32 smem_flags = 0;
+ struct hal_buffer_requirements *persist_buf;
+ int i;
+ struct hfi_device *hdev;
+
+ hdev = inst->core->device;
+
+ persist_buf = get_buff_req_buffer(inst, buffer_type);
+ if (!persist_buf) {
+ dprintk(VIDC_DBG,
+ "This persist buffer not required, buffer_type: %x\n",
+ buffer_type);
+ return 0;
+ }
+
+ dprintk(VIDC_DBG,
+ "persist: num = %d, size = %d\n",
+ persist_buf->buffer_count_actual,
+ persist_buf->buffer_size);
+ if (!list_empty(&inst->persistbufs)) {
+ dprintk(VIDC_ERR,
+ "Persist buffers already allocated\n");
+ return rc;
+ }
+
+ if (inst->mode == VIDC_SECURE)
+ smem_flags |= SMEM_SECURE;
+
+ if (persist_buf->buffer_size) {
+ for (i = 0; i < persist_buf->buffer_count_actual; i++) {
+ handle = msm_smem_alloc(inst->mem_client,
+ persist_buf->buffer_size, 1, smem_flags,
+ buffer_type, 0);
+ if (!handle) {
+ dprintk(VIDC_ERR,
+ "Failed to allocate persist memory\n");
+ rc = -ENOMEM;
+ goto err_no_mem;
+ }
+ rc = msm_smem_cache_operations(inst->mem_client,
+ handle, SMEM_CACHE_CLEAN);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to clean cache may cause undefined behavior\n");
+ }
+ binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+ if (!binfo) {
+ dprintk(VIDC_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto fail_kzalloc;
+ }
+ binfo->handle = handle;
+ buffer_info.buffer_size = persist_buf->buffer_size;
+ buffer_info.buffer_type = buffer_type;
+ binfo->buffer_type = buffer_type;
+ buffer_info.num_buffers = 1;
+ buffer_info.align_device_addr = handle->device_addr;
+ dprintk(VIDC_DBG, "Persist buffer address: %x",
+ buffer_info.align_device_addr);
+ rc = call_hfi_op(hdev, session_set_buffers,
+ (void *) inst->session, &buffer_info);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "vidc_hal_session_set_buffers failed");
+ goto fail_set_buffers;
+ }
+ mutex_lock(&inst->lock);
+ list_add_tail(&binfo->list, &inst->persistbufs);
+ mutex_unlock(&inst->lock);
+ }
+ }
+ return rc;
+fail_set_buffers:
+ kfree(binfo);
+fail_kzalloc:
+ msm_smem_free(inst->mem_client, handle);
+err_no_mem:
+ return rc;
+}
+
int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
{
int rc = 0;
@@ -1723,6 +1903,7 @@
mutex_unlock(&inst->sync_lock);
return rc;
}
+
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
{
struct msm_smem *handle;
@@ -1755,7 +1936,7 @@
list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
- buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
+ buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
@@ -1819,7 +2000,7 @@
list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
- buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
+ buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
@@ -1885,178 +2066,50 @@
int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
- struct msm_smem *handle;
- struct internal_buf *binfo;
- struct vidc_buffer_addr_info buffer_info;
- int domain;
- unsigned long smem_flags = 0;
- struct hal_buffer_requirements *scratch_buf;
- int i;
- struct hfi_device *hdev;
-
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- hdev = inst->core->device;
-
- scratch_buf =
- &inst->buff_req.buffer[HAL_BUFFER_INTERNAL_SCRATCH];
- dprintk(VIDC_DBG,
- "scratch: num = %d, size = %d\n",
- scratch_buf->buffer_count_actual,
- scratch_buf->buffer_size);
if (msm_comm_release_scratch_buffers(inst))
dprintk(VIDC_WARN, "Failed to release scratch buffers\n");
- if (inst->mode == VIDC_SECURE) {
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, CP_MAP);
- smem_flags |= SMEM_SECURE;
- } else
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, NS_MAP);
- if (scratch_buf->buffer_size) {
- for (i = 0; i < scratch_buf->buffer_count_actual;
- i++) {
- handle = msm_smem_alloc(inst->mem_client,
- scratch_buf->buffer_size, 1, smem_flags,
- domain, 0, 0);
- if (!handle) {
- dprintk(VIDC_ERR,
- "Failed to allocate scratch memory\n");
- rc = -ENOMEM;
- goto err_no_mem;
- }
- rc = msm_smem_cache_operations(inst->mem_client,
- handle, SMEM_CACHE_CLEAN);
- if (rc) {
- dprintk(VIDC_WARN,
- "Failed to clean cache may cause undefined behavior\n");
- }
- binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
- if (!binfo) {
- dprintk(VIDC_ERR, "Out of memory\n");
- rc = -ENOMEM;
- goto fail_kzalloc;
- }
- binfo->handle = handle;
- buffer_info.buffer_size = scratch_buf->buffer_size;
- buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
- buffer_info.num_buffers = 1;
- buffer_info.align_device_addr = handle->device_addr;
- dprintk(VIDC_DBG, "Scratch buffer address: %x",
- buffer_info.align_device_addr);
- rc = call_hfi_op(hdev, session_set_buffers,
- (void *) inst->session, &buffer_info);
- if (rc) {
- dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed");
- goto fail_set_buffers;
- }
- mutex_lock(&inst->lock);
- list_add_tail(&binfo->list, &inst->internalbufs);
- mutex_unlock(&inst->lock);
- }
- }
+ rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH);
+ if (rc)
+ goto error;
+
+ rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_1);
+ if (rc)
+ goto error;
+
+ rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_2);
+ if (rc)
+ goto error;
+
return rc;
-fail_set_buffers:
- kfree(binfo);
-fail_kzalloc:
- msm_smem_free(inst->mem_client, handle);
-err_no_mem:
+error:
+ msm_comm_release_scratch_buffers(inst);
return rc;
}
int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
- struct msm_smem *handle;
- struct internal_buf *binfo;
- struct vidc_buffer_addr_info buffer_info;
- unsigned long flags;
- unsigned long smem_flags = 0;
- int domain;
- struct hal_buffer_requirements *persist_buf;
- int i;
- struct hfi_device *hdev;
-
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- hdev = inst->core->device;
+ rc = set_persist_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST);
+ if (rc)
+ goto error;
- persist_buf =
- &inst->buff_req.buffer[HAL_BUFFER_INTERNAL_PERSIST];
- dprintk(VIDC_DBG,
- "persist: num = %d, size = %d\n",
- persist_buf->buffer_count_actual,
- persist_buf->buffer_size);
- if (!list_empty(&inst->persistbufs)) {
- dprintk(VIDC_ERR,
- "Persist buffers already allocated\n");
- return rc;
- }
-
- if (inst->mode == VIDC_SECURE) {
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, CP_MAP);
- flags |= SMEM_SECURE;
- } else
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, NS_MAP);
-
- if (persist_buf->buffer_size) {
- for (i = 0; i < persist_buf->buffer_count_actual; i++) {
- handle = msm_smem_alloc(inst->mem_client,
- persist_buf->buffer_size, 1, smem_flags,
- domain, 0, 0);
- if (!handle) {
- dprintk(VIDC_ERR,
- "Failed to allocate persist memory\n");
- rc = -ENOMEM;
- goto err_no_mem;
- }
- rc = msm_smem_cache_operations(inst->mem_client,
- handle, SMEM_CACHE_CLEAN);
- if (rc) {
- dprintk(VIDC_WARN,
- "Failed to clean cache may cause undefined behavior\n");
- }
- binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
- if (!binfo) {
- dprintk(VIDC_ERR, "Out of memory\n");
- rc = -ENOMEM;
- goto fail_kzalloc;
- }
- binfo->handle = handle;
- buffer_info.buffer_size = persist_buf->buffer_size;
- buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
- buffer_info.num_buffers = 1;
- buffer_info.align_device_addr = handle->device_addr;
- dprintk(VIDC_DBG, "Persist buffer address: %x",
- buffer_info.align_device_addr);
- rc = call_hfi_op(hdev, session_set_buffers,
- (void *) inst->session, &buffer_info);
- if (rc) {
- dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed");
- goto fail_set_buffers;
- }
- mutex_lock(&inst->lock);
- list_add_tail(&binfo->list, &inst->persistbufs);
- mutex_unlock(&inst->lock);
- }
- }
+ rc = set_persist_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST_1);
+ if (rc)
+ goto error;
return rc;
-fail_set_buffers:
- kfree(binfo);
-fail_kzalloc:
- msm_smem_free(inst->mem_client, handle);
-err_no_mem:
+error:
+ msm_comm_release_persist_buffers(inst);
return rc;
}
@@ -2245,6 +2298,41 @@
return ret;
};
+int msm_comm_get_domain_partition(struct msm_vidc_inst *inst, u32 flags,
+ enum v4l2_buf_type buf_type, int *domain, int *partition)
+{
+ struct hfi_device *hdev;
+ u32 hal_buffer_type = 0;
+ if (!inst || !inst->core || !inst->core->device)
+ return -EINVAL;
+
+ hdev = inst->core->device;
+
+ /*
+ * TODO: Due to the way in which the underlying smem mechanism
+ * maps buffer types to corresponding IOMMU domains, we need to
+ * pass in HAL_BUFFER_OUTPUT for input buffers (and vice versa)
+ * so that buffers are mapped into the correct domains. In the
+ * future, we should try to remove this workaround.
+ */
+ switch (buf_type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ hal_buffer_type = (inst->session_type == MSM_VIDC_ENCODER) ?
+ HAL_BUFFER_INPUT : HAL_BUFFER_OUTPUT;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ hal_buffer_type = (inst->session_type == MSM_VIDC_ENCODER) ?
+ HAL_BUFFER_OUTPUT : HAL_BUFFER_INPUT;
+ break;
+ default:
+ dprintk(VIDC_ERR, "v4l2 buf type not found %d\n", buf_type);
+ return -ENOTSUPP;
+ }
+ return call_hfi_op(hdev, iommu_get_domain_partition,
+ hdev->hfi_device_data, flags, hal_buffer_type, domain,
+ partition);
+};
+
int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
enum hal_ssr_trigger_type type)
{
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index 69f41c7..4f3deb6 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -39,6 +39,10 @@
int msm_comm_force_cleanup(struct msm_vidc_inst *inst);
enum hal_extradata_id msm_comm_get_hal_extradata_index(
enum v4l2_mpeg_vidc_extradata index);
+int msm_comm_get_domain_partition(struct msm_vidc_inst *inst, u32 flags,
+ enum v4l2_buf_type buf_type, int *domain, int *partition);
+struct hal_buffer_requirements *get_buff_req_buffer(
+ struct msm_vidc_inst *inst, u32 buffer_type);
#define IS_PRIV_CTRL(idx) (\
(V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_MPEG) && \
V4L2_CTRL_DRIVER_PRIV(idx))
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index c03a4c4..8238d42 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -92,6 +92,7 @@
struct internal_buf {
struct list_head list;
+ enum hal_buffer buffer_type;
struct msm_smem *handle;
};
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index 8fc6452..54c0878 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -32,6 +32,36 @@
int count;
};
+struct addr_range {
+ u32 start;
+ u32 size;
+};
+
+struct iommu_info {
+ const char *name;
+ u32 buffer_type;
+ struct iommu_group *group;
+ int domain;
+ bool is_secure;
+ struct addr_range addr_range[2];
+ int npartitions;
+};
+
+struct iommu_set {
+ struct iommu_info *iommu_maps;
+ u32 count;
+};
+
+struct buffer_usage_table {
+ u32 buffer_type;
+ u32 tz_usage;
+};
+
+struct buffer_usage_set {
+ struct buffer_usage_table *buffer_usage_tbl;
+ u32 count;
+};
+
struct msm_vidc_platform_resources {
uint32_t fw_base_addr;
uint32_t register_base;
@@ -39,10 +69,10 @@
uint32_t irq;
struct load_freq_table *load_freq_tbl;
uint32_t load_freq_tbl_size;
- struct msm_vidc_iommu_info *iommu_maps;
- uint32_t iommu_maps_size;
struct reg_set reg_set;
struct msm_bus_scale_pdata *bus_pdata;
+ struct iommu_set iommu_group_set;
+ struct buffer_usage_set buffer_usage_set;
struct platform_device *pdev;
};
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c
index 25cc239..88dc4fe 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.c
+++ b/drivers/media/platform/msm/vidc/q6_hfi.c
@@ -1113,25 +1113,14 @@
return 0;
}
-static int q6_hfi_get_domain(void *dev, enum msm_vidc_io_maps iomap)
+static int q6_hfi_iommu_get_domain_partition(void *dev, u32 flags,
+ u32 buffer_type, int *domain, int *partition)
{
(void)dev;
- (void)iomap;
dprintk(VIDC_ERR, "Not implemented: %s", __func__);
- return 0;
-}
-
-static int q6_hfi_iommu_get_map(void *dev,
- struct msm_vidc_iommu_info maps[MAX_MAP])
-{
- (void)dev;
- (void)maps;
-
- dprintk(VIDC_ERR, "Not implemented: %s", __func__);
-
- return 0;
+ return -ENOTSUPP;
}
static int q6_hfi_iommu_attach(void *dev)
@@ -1230,8 +1219,7 @@
hdev->alloc_ocmem = q6_hfi_alloc_ocmem;
hdev->free_ocmem = q6_hfi_free_ocmem;
hdev->is_ocmem_present = q6_hfi_is_ocmem_present;
- hdev->get_domain = q6_hfi_get_domain;
- hdev->iommu_get_map = q6_hfi_iommu_get_map;
+ hdev->iommu_get_domain_partition = q6_hfi_iommu_get_domain_partition;
hdev->load_fw = q6_hfi_load_fw;
hdev->unload_fw = q6_hfi_unload_fw;
hdev->get_fw_info = q6_hfi_get_fw_info;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index af8b761..8c30b6c 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -333,7 +333,7 @@
}
static int venus_hfi_alloc(void *mem, void *clnt, u32 size, u32 align,
- u32 flags, int domain)
+ u32 flags, u32 usage)
{
struct vidc_mem_addr *vmem;
struct msm_smem *alloc;
@@ -346,7 +346,7 @@
vmem = (struct vidc_mem_addr *)mem;
dprintk(VIDC_INFO, "start to alloc: size:%d, Flags: %d", size, flags);
- alloc = msm_smem_alloc(clnt, size, align, flags, domain, 1, 1);
+ alloc = msm_smem_alloc(clnt, size, align, flags, usage, 1);
dprintk(VIDC_DBG, "Alloc done");
if (!alloc) {
dprintk(VIDC_ERR, "Alloc failed\n");
@@ -632,6 +632,7 @@
struct hfi_mem_map_table *qdss;
struct hfi_mem_map *mem_map;
int num_entries = sizeof(venus_qdss_entries)/(2 * sizeof(u32));
+ int domain, partition;
if (device->qdss.mem_data) {
qdss = (struct hfi_mem_map_table *)
@@ -641,11 +642,12 @@
(u32 *)((u32)device->qdss.align_device_addr +
sizeof(struct hfi_mem_map_table));
mem_map = (struct hfi_mem_map *)(qdss + 1);
+ msm_smem_get_domain_partition(device->hal_client, 0,
+ HAL_BUFFER_INTERNAL_CMD_QUEUE, &domain, &partition);
for (i = 0; i < num_entries; i++) {
msm_iommu_unmap_contig_buffer(
(unsigned long)(mem_map[i].virtual_addr),
- device->resources.io_map[NS_MAP].domain,
- 1, SZ_4K);
+ domain, partition, SZ_4K);
}
venus_hfi_free(device->hal_client, device->qdss.mem_data);
}
@@ -674,7 +676,7 @@
device->hal_client = NULL;
}
static int venus_hfi_get_qdss_iommu_virtual_addr(struct hfi_mem_map *mem_map,
- int domain)
+ int domain, int partition)
{
int i;
int rc = 0;
@@ -703,14 +705,13 @@
for (--i; i >= 0; i--) {
msm_iommu_unmap_contig_buffer(
(unsigned long)(mem_map[i].virtual_addr),
- domain, 1, SZ_4K);
+ domain, partition, SZ_4K);
}
}
return rc;
}
-static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev,
- int domain)
+static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev)
{
struct hfi_queue_table_header *q_tbl_hdr;
struct hfi_queue_header *q_hdr;
@@ -723,10 +724,11 @@
struct vidc_mem_addr *mem_addr;
int offset = 0;
int num_entries = sizeof(venus_qdss_entries)/(2 * sizeof(u32));
+ int domain, partition;
mem_addr = &dev->mem_addr;
rc = venus_hfi_alloc((void *) mem_addr,
- dev->hal_client, QUEUE_SIZE, 1,
- 0, domain);
+ dev->hal_client, QUEUE_SIZE, 1, 0,
+ HAL_BUFFER_INTERNAL_CMD_QUEUE);
if (rc) {
dprintk(VIDC_ERR, "iface_q_table_alloc_fail");
goto fail_alloc_queue;
@@ -752,8 +754,8 @@
}
rc = venus_hfi_alloc((void *) mem_addr,
- dev->hal_client, QDSS_SIZE, 1,
- 0, domain);
+ dev->hal_client, QDSS_SIZE, 1, 0,
+ HAL_BUFFER_INTERNAL_CMD_QUEUE);
if (rc) {
dprintk(VIDC_WARN,
"qdss_alloc_fail: QDSS messages logging will not work");
@@ -765,8 +767,8 @@
dev->qdss.mem_data = mem_addr->mem_data;
}
rc = venus_hfi_alloc((void *) mem_addr,
- dev->hal_client, SFR_SIZE, 1,
- 0, domain);
+ dev->hal_client, SFR_SIZE, 1, 0,
+ HAL_BUFFER_INTERNAL_CMD_QUEUE);
if (rc) {
dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work");
dev->sfr.align_device_addr = NULL;
@@ -824,7 +826,9 @@
(u32 *) ((u32)dev->qdss.align_device_addr +
sizeof(struct hfi_mem_map_table));
mem_map = (struct hfi_mem_map *)(qdss + 1);
- rc = venus_hfi_get_qdss_iommu_virtual_addr(mem_map, domain);
+ msm_smem_get_domain_partition(dev->hal_client, 0,
+ HAL_BUFFER_INTERNAL_CMD_QUEUE, &domain, &partition);
+ rc = venus_hfi_get_qdss_iommu_virtual_addr(mem_map, domain, partition);
if (rc) {
dprintk(VIDC_ERR,
"IOMMU mapping failed, Freeing qdss memdata");
@@ -940,7 +944,7 @@
venus_hfi_set_registers(dev);
if (!dev->hal_client) {
- dev->hal_client = msm_smem_new_client(SMEM_ION);
+ dev->hal_client = msm_smem_new_client(SMEM_ION, dev->res);
if (dev->hal_client == NULL) {
dprintk(VIDC_ERR, "Failed to alloc ION_Client");
rc = -ENODEV;
@@ -951,8 +955,7 @@
dev->hal_data->device_base_addr,
(u32) dev->hal_data->register_base_addr);
- rc = venus_hfi_interface_queues_init(dev,
- dev->resources.io_map[NS_MAP].domain);
+ rc = venus_hfi_interface_queues_init(dev);
if (rc) {
dprintk(VIDC_ERR, "failed to init queues");
rc = -ENOMEM;
@@ -2038,68 +2041,69 @@
static int venus_hfi_register_iommu_domains(struct venus_hfi_device *device,
struct msm_vidc_platform_resources *res)
{
- struct msm_iova_partition partition[2];
- struct msm_iova_layout layout;
- int rc = 0;
- int i;
- struct msm_vidc_iommu_info *io_map;
+ struct iommu_domain *domain;
+ int rc = 0, i = 0;
+ struct iommu_set *iommu_group_set;
+ struct iommu_info *iommu_map;
- if (!device)
+ if (!device || !res)
return -EINVAL;
- io_map = device->resources.io_map;
+ iommu_group_set = &device->res->iommu_group_set;
- strlcpy(io_map[CP_MAP].name, "vidc-cp-map",
- sizeof(io_map[CP_MAP].name));
- strlcpy(io_map[CP_MAP].ctx, "venus_cp",
- sizeof(io_map[CP_MAP].ctx));
- strlcpy(io_map[NS_MAP].name, "vidc-ns-map",
- sizeof(io_map[NS_MAP].name));
- strlcpy(io_map[NS_MAP].ctx, "venus_ns",
- sizeof(io_map[NS_MAP].ctx));
-
- for (i = 0; i < MAX_MAP; i++) {
- if (!res->iommu_maps[i].addr_range[1])
- continue;
- memcpy(io_map[i].addr_range, &res->iommu_maps[i].addr_range,
- sizeof(u32) * 2);
-
- partition[0].start = io_map[i].addr_range[0];
- if (i == NS_MAP) {
- partition[0].size =
- io_map[i].addr_range[1] - SHARED_QSIZE;
- partition[1].start =
- partition[0].start + io_map[i].addr_range[1]
- - SHARED_QSIZE;
- partition[1].size = SHARED_QSIZE;
- layout.npartitions = 2;
- layout.is_secure = 0;
- } else {
- partition[0].size = io_map[i].addr_range[1];
- layout.npartitions = 1;
- layout.is_secure = 1;
+ for (i = 0; i < iommu_group_set->count; i++) {
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ iommu_map->group = iommu_group_find(iommu_map->name);
+ if (!iommu_map->group) {
+ dprintk(VIDC_ERR, "Failed to find group :%s\n",
+ iommu_map->name);
+ goto fail_group;
}
- layout.partitions = &partition[0];
- layout.client_name = io_map[i].name;
- layout.domain_flags = 0;
- dprintk(VIDC_DBG, "Registering domain 1 with: %lx, %lx, %s\n",
- partition[0].start, partition[0].size,
- layout.client_name);
- dprintk(VIDC_DBG, "Registering domain 2 with: %lx, %lx, %s\n",
- partition[1].start, partition[1].size,
- layout.client_name);
- io_map[i].domain = msm_register_domain(&layout);
- if (io_map[i].domain < 0) {
- dprintk(VIDC_ERR, "Failed to register cp domain\n");
- rc = -EINVAL;
- break;
+ domain = iommu_group_get_iommudata(iommu_map->group);
+ if (!domain) {
+ dprintk(VIDC_ERR,
+ "Failed to get domain data for group %p",
+ iommu_map->group);
+ goto fail_group;
+ }
+ iommu_map->domain = msm_find_domain_no(domain);
+ if (iommu_map->domain < 0) {
+ dprintk(VIDC_ERR,
+ "Failed to get domain index for domain %p",
+ domain);
+ goto fail_group;
}
}
- /* There is no api provided as msm_unregister_domain, so
- * we are not able to unregister the previously
- * registered domains if any domain registration fails.*/
- BUG_ON(i < MAX_MAP);
return rc;
+
+fail_group:
+ for (--i; i >= 0; i--) {
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ if (iommu_map->group)
+ iommu_group_put(iommu_map->group);
+ iommu_map->group = NULL;
+ iommu_map->domain = -1;
+ }
+ return -EINVAL;
+}
+
+static void venus_hfi_deregister_iommu_domains(struct venus_hfi_device *device)
+{
+ struct iommu_set *iommu_group_set;
+ struct iommu_info *iommu_map;
+ int i = 0;
+
+ if (!device)
+ return;
+
+ iommu_group_set = &device->res->iommu_group_set;
+ for (i = 0; i < iommu_group_set->count; i++) {
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ if (iommu_map->group)
+ iommu_group_put(iommu_map->group);
+ iommu_map->group = NULL;
+ iommu_map->domain = -1;
+ }
}
static void venus_hfi_deinit_bus(struct venus_hfi_device *device)
@@ -2462,6 +2466,7 @@
static void venus_hfi_deinit_resources(struct venus_hfi_device *device)
{
venus_hfi_deinit_ocmem(device);
+ venus_hfi_deregister_iommu_domains(device);
venus_hfi_deinit_bus(device);
venus_hfi_deinit_clocks(device);
}
@@ -2471,39 +2476,39 @@
int rc;
struct iommu_domain *domain;
int i;
- struct msm_vidc_iommu_info *io_map;
- struct device *dev;
+ struct iommu_set *iommu_group_set;
+ struct iommu_group *group;
+ struct iommu_info *iommu_map;
- if (!device)
+ if (!device || !device->res)
return -EINVAL;
- for (i = 0; i < MAX_MAP; i++) {
- io_map = &device->resources.io_map[i];
- if (!io_map->domain)
- continue;
- dev = msm_iommu_get_ctx(io_map->ctx);
- domain = msm_get_iommu_domain(io_map->domain);
+ iommu_group_set = &device->res->iommu_group_set;
+ for (i = 0; i < iommu_group_set->count; i++) {
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ group = iommu_map->group;
+ domain = msm_get_iommu_domain(iommu_map->domain);
if (IS_ERR_OR_NULL(domain)) {
dprintk(VIDC_ERR,
- "Failed to get domain: %s\n", io_map->name);
+ "Failed to get domain: %s\n", iommu_map->name);
rc = PTR_ERR(domain);
break;
}
- rc = iommu_attach_device(domain, dev);
+ rc = iommu_attach_group(domain, group);
if (rc) {
dprintk(VIDC_ERR,
- "IOMMU attach failed: %s\n", io_map->name);
+ "IOMMU attach failed: %s\n", iommu_map->name);
break;
}
}
- if (i < MAX_MAP) {
+ if (i < iommu_group_set->count) {
i--;
for (; i >= 0; i--) {
- io_map = &device->resources.io_map[i];
- dev = msm_iommu_get_ctx(io_map->ctx);
- domain = msm_get_iommu_domain(io_map->domain);
- if (dev && domain)
- iommu_detach_device(domain, dev);
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ group = iommu_map->group;
+ domain = msm_get_iommu_domain(iommu_map->domain);
+ if (group && domain)
+ iommu_detach_group(domain, group);
}
}
return rc;
@@ -2511,51 +2516,40 @@
static void venus_hfi_iommu_detach(struct venus_hfi_device *device)
{
- struct device *dev;
+ struct iommu_group *group;
struct iommu_domain *domain;
- struct msm_vidc_iommu_info *io_map;
+ struct iommu_set *iommu_group_set;
+ struct iommu_info *iommu_map;
int i;
- if (!device) {
+ if (!device || !device->res) {
dprintk(VIDC_ERR, "Invalid paramter: %p\n", device);
return;
}
- for (i = 0; i < MAX_MAP; i++) {
- io_map = &device->resources.io_map[i];
- dev = msm_iommu_get_ctx(io_map->ctx);
- domain = msm_get_iommu_domain(io_map->domain);
- if (dev && domain)
- iommu_detach_device(domain, dev);
+ iommu_group_set = &device->res->iommu_group_set;
+ for (i = 0; i < iommu_group_set->count; i++) {
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ group = iommu_map->group;
+ domain = msm_get_iommu_domain(iommu_map->domain);
+ if (group && domain)
+ iommu_detach_group(domain, group);
}
}
-static int venus_hfi_get_domain(void *dev, enum msm_vidc_io_maps iomap)
+static int venus_hfi_iommu_get_domain_partition(void *dev, u32 flags,
+ u32 buffer_type, int *domain, int *partition)
{
struct venus_hfi_device *device = dev;
- if (!device || iomap < CP_MAP || iomap >= MAX_MAP) {
- dprintk(VIDC_ERR, "%s: Invalid parameter: %p iomap: %d\n",
- __func__, device, iomap);
- return -EINVAL;
- }
- return device->resources.io_map[iomap].domain;
-}
-static int venus_hfi_iommu_get_map(void *dev,
- struct msm_vidc_iommu_info maps[MAX_MAP])
-{
- int i = 0;
- struct venus_hfi_device *device = dev;
-
- if (!device || !maps) {
- dprintk(VIDC_ERR, "%s: Invalid param device: %p maps: %p\n",
- __func__, device, maps);
+ if (!device) {
+ dprintk(VIDC_ERR, "%s: Invalid param device: %p\n",
+ __func__, device);
return -EINVAL;
}
- for (i = 0; i < MAX_MAP; i++)
- maps[i] = device->resources.io_map[i];
-
+ msm_smem_get_domain_partition(device->hal_client, flags, buffer_type,
+ domain, partition);
return 0;
}
@@ -2564,22 +2558,36 @@
struct tzbsp_memprot memprot;
unsigned int resp = 0;
int rc = 0;
- struct msm_vidc_iommu_info *io_map;
+ struct iommu_set *iommu_group_set;
+ struct iommu_info *iommu_map;
+ int i;
+
if (!device)
return -EINVAL;
- io_map = device->resources.io_map;
- if (!io_map) {
- dprintk(VIDC_ERR, "invalid params: %p\n", io_map);
+ iommu_group_set = &device->res->iommu_group_set;
+ if (!iommu_group_set) {
+ dprintk(VIDC_ERR, "invalid params: %p\n", iommu_group_set);
return -EINVAL;
}
- if (!io_map[CP_MAP].addr_range[1])
- return 0;
+
memprot.cp_start = 0x0;
- memprot.cp_size = io_map[CP_MAP].addr_range[0] +
- io_map[CP_MAP].addr_range[1];
- memprot.cp_nonpixel_start = 0;
- memprot.cp_nonpixel_size = 0;
+ memprot.cp_size = 0x0;
+ memprot.cp_nonpixel_start = 0x0;
+ memprot.cp_nonpixel_size = 0x0;
+
+ for (i = 0; i < iommu_group_set->count; i++) {
+ iommu_map = &iommu_group_set->iommu_maps[i];
+ if (strcmp(iommu_map->name, "venus_ns") == 0)
+ memprot.cp_size = iommu_map->addr_range[0].start;
+
+ if (strcmp(iommu_map->name, "venus_sec_non_pixel") == 0) {
+ memprot.cp_nonpixel_start =
+ iommu_map->addr_range[0].start;
+ memprot.cp_nonpixel_size =
+ iommu_map->addr_range[0].size;
+ }
+ }
rc = scm_call(SCM_SVC_CP, TZBSP_MEM_PROTECT_VIDEO_VAR, &memprot,
sizeof(memprot), &resp, sizeof(resp));
@@ -2833,8 +2841,7 @@
hdev->alloc_ocmem = venus_hfi_alloc_ocmem;
hdev->free_ocmem = venus_hfi_free_ocmem;
hdev->is_ocmem_present = venus_hfi_is_ocmem_present;
- hdev->get_domain = venus_hfi_get_domain;
- hdev->iommu_get_map = venus_hfi_iommu_get_map;
+ hdev->iommu_get_domain_partition = venus_hfi_iommu_get_domain_partition;
hdev->load_fw = venus_hfi_load_fw;
hdev->unload_fw = venus_hfi_unload_fw;
hdev->get_fw_info = venus_hfi_get_fw_info;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.h b/drivers/media/platform/msm/vidc/venus_hfi.h
index 2ffb9d4..7a96ff4 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.h
+++ b/drivers/media/platform/msm/vidc/venus_hfi.h
@@ -164,7 +164,6 @@
struct venus_resources {
struct msm_vidc_fw fw;
- struct msm_vidc_iommu_info io_map[MAX_MAP];
struct venus_core_clock clock[VCODEC_MAX_CLKS];
struct venus_bus_info bus_info;
struct on_chip_mem ocmem;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 75594b3..8b3e7cb 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -53,6 +53,8 @@
#define HFI_BUFFER_EXTRADATA_INPUT (HFI_OX_BASE + 0x2)
#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_OX_BASE + 0x3)
#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_OX_BASE + 0x4)
+#define HFI_BUFFER_INTERNAL_SCRATCH_1 (HFI_OX_BASE + 0x5)
+#define HFI_BUFFER_INTERNAL_SCRATCH_2 (HFI_OX_BASE + 0x6)
#define HFI_BUFFER_MODE_STATIC (HFI_OX_BASE + 0x1)
#define HFI_BUFFER_MODE_RING (HFI_OX_BASE + 0x2)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index d06ea51..fad29f1 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -18,6 +18,7 @@
#include <linux/types.h>
#include <media/msm_vidc.h>
#include "msm_vidc_resources.h"
+#include "msm_smem.h"
#define CONTAINS(__a, __sz, __t) ({\
int __rc = __t >= __a && \
@@ -375,18 +376,6 @@
HAL_UNUSED_MVC_LEVEL = 0x10000000,
};
-enum hal_buffer {
- HAL_BUFFER_INPUT,
- HAL_BUFFER_OUTPUT,
- HAL_BUFFER_OUTPUT2,
- HAL_BUFFER_EXTRADATA_INPUT,
- HAL_BUFFER_EXTRADATA_OUTPUT,
- HAL_BUFFER_EXTRADATA_OUTPUT2,
- HAL_BUFFER_INTERNAL_SCRATCH,
- HAL_BUFFER_INTERNAL_PERSIST,
- HAL_BUFFER_MAX
-};
-
struct hal_frame_rate {
enum hal_buffer buffer_type;
u32 frame_rate;
@@ -1058,9 +1047,8 @@
int (*alloc_ocmem)(void *dev, unsigned long size);
int (*free_ocmem)(void *dev);
int (*is_ocmem_present)(void *dev);
- int (*get_domain)(void *dev, enum msm_vidc_io_maps iomap);
- int (*iommu_get_map)(void *dev,
- struct msm_vidc_iommu_info maps[MAX_MAP]);
+ int (*iommu_get_domain_partition)(void *dev, u32 flags, u32 buffer_type,
+ int *domain_num, int *partition_num);
int (*load_fw)(void *dev);
void (*unload_fw)(void *dev);
int (*get_fw_info)(void *dev, enum fw_info info);
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 37c051e..01c5e0b 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -179,6 +179,7 @@
#define HFI_BUFFER_OUTPUT (HFI_COMMON_BASE + 0x2)
#define HFI_BUFFER_OUTPUT2 (HFI_COMMON_BASE + 0x3)
#define HFI_BUFFER_INTERNAL_PERSIST (HFI_COMMON_BASE + 0x4)
+#define HFI_BUFFER_INTERNAL_PERSIST_1 (HFI_COMMON_BASE + 0x5)
struct hfi_buffer_info {
u32 buffer_addr;
diff --git a/drivers/media/platform/msm/wfd/enc-venus-subdev.c b/drivers/media/platform/msm/wfd/enc-venus-subdev.c
index b41ece6..40362f0 100644
--- a/drivers/media/platform/msm/wfd/enc-venus-subdev.c
+++ b/drivers/media/platform/msm/wfd/enc-venus-subdev.c
@@ -46,7 +46,6 @@
bool callback_thread_running;
struct completion dq_complete, cmd_complete;
bool secure;
- int domain;
};
int venc_load_fw(struct v4l2_subdev *sd)
@@ -252,18 +251,6 @@
return msm_vidc_s_ctrl(inst->vidc_context, &ctrl);
}
-static long get_iommu_domain(struct venc_inst *inst)
-{
- struct msm_vidc_iommu_info maps[MAX_MAP];
- int rc = msm_vidc_get_iommu_maps(inst->vidc_context, maps);
- if (rc) {
- WFD_MSG_ERR("Failed to retreive domain mappings\n");
- return rc;
- }
-
- return maps[inst->secure ? CP_MAP : NS_MAP].domain;
-}
-
static long venc_open(struct v4l2_subdev *sd, void *arg)
{
struct venc_inst *inst = NULL;
@@ -316,12 +303,6 @@
goto vidc_subscribe_fail;
}
- inst->domain = get_iommu_domain(inst);
- if (inst->domain < 0) {
- WFD_MSG_ERR("Failed to get domain\n");
- goto vidc_subscribe_fail;
- }
-
inst->callback_thread = kthread_run(venc_vidc_callback_thread, inst,
"venc_vidc_callback_thread");
if (IS_ERR(inst->callback_thread)) {
@@ -641,7 +622,9 @@
struct mem_region *mregion)
{
int rc = 0;
- unsigned long size = 0, align_req = 0;
+ unsigned long size = 0, align_req = 0, flags = 0;
+ int domain = 0, partition = 0;
+
if (!mregion) {
rc = -EINVAL;
goto venc_map_fail;
@@ -663,6 +646,12 @@
goto venc_map_fail;
}
+ rc = ion_handle_get_flags(venc_ion_client, mregion->ion_handle, &flags);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get ion flags %d\n", rc);
+ goto venc_map_fail;
+ }
+
if (!inst->secure) {
mregion->kvaddr = ion_map_kernel(venc_ion_client,
mregion->ion_handle);
@@ -685,10 +674,16 @@
}
}
- rc = ion_map_iommu(venc_ion_client, mregion->ion_handle,
- inst->domain, 0, align_req, 0,
- (unsigned long *)&mregion->paddr, &size, 0, 0);
+ rc = msm_vidc_get_iommu_domain_partition(inst->vidc_context,
+ flags, BUF_TYPE_OUTPUT, &domain, &partition);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get domain for output buffer\n");
+ goto venc_domain_fail;
+ }
+ rc = ion_map_iommu(venc_ion_client, mregion->ion_handle,
+ domain, partition, align_req, 0,
+ (unsigned long *)&mregion->paddr, &size, 0, 0);
if (rc) {
WFD_MSG_ERR("Failed to map into iommu\n");
goto venc_map_iommu_map_fail;
@@ -700,8 +695,8 @@
return 0;
venc_map_iommu_size_fail:
ion_unmap_iommu(venc_ion_client, mregion->ion_handle,
- inst->domain, 0);
-
+ domain, partition);
+venc_domain_fail:
if (inst->secure)
msm_ion_unsecure_buffer(venc_ion_client, mregion->ion_handle);
venc_map_iommu_map_fail:
@@ -714,12 +709,28 @@
static int venc_unmap_user_to_kernel(struct venc_inst *inst,
struct mem_region *mregion)
{
+ unsigned long flags = 0;
+ int domain = 0, partition = 0, rc = 0;
+
if (!mregion || !mregion->ion_handle)
return 0;
+ rc = ion_handle_get_flags(venc_ion_client, mregion->ion_handle, &flags);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get ion flags %d\n", rc);
+ return rc;
+ }
+
+ rc = msm_vidc_get_iommu_domain_partition(inst->vidc_context,
+ flags, BUF_TYPE_OUTPUT, &domain, &partition);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get domain for input buffer\n");
+ return rc;
+ }
+
if (mregion->paddr) {
ion_unmap_iommu(venc_ion_client, mregion->ion_handle,
- inst->domain, 0);
+ domain, partition);
mregion->paddr = NULL;
}
@@ -731,7 +742,7 @@
if (inst->secure)
msm_ion_unsecure_buffer(venc_ion_client, mregion->ion_handle);
- return 0;
+ return rc;
}
static long venc_set_output_buffer(struct v4l2_subdev *sd, void *arg)
@@ -1145,7 +1156,8 @@
{
struct mem_region_map *mmap = arg;
struct mem_region *mregion = NULL;
- unsigned long rc = 0, size = 0, align_req = 0;
+ unsigned long size = 0, align_req = 0, flags = 0;
+ int domain = 0, partition = 0, rc = 0;
void *paddr = NULL;
struct venc_inst *inst = NULL;
@@ -1167,21 +1179,34 @@
goto venc_map_bad_align;
}
+ rc = ion_handle_get_flags(mmap->ion_client, mregion->ion_handle,
+ &flags);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get ion flags %d\n", rc);
+ goto venc_map_bad_align;
+ }
+
if (inst->secure) {
rc = msm_ion_secure_buffer(mmap->ion_client,
- mregion->ion_handle, VIDEO_PIXEL, 0);
+ mregion->ion_handle, VIDEO_PIXEL, 0);
if (rc) {
WFD_MSG_ERR("Failed to secure input buffer\n");
goto venc_map_bad_align;
}
}
- rc = ion_map_iommu(mmap->ion_client, mregion->ion_handle,
- inst->domain, 0, align_req, 0, (unsigned long *)&paddr,
- &size, 0, 0);
-
+ rc = msm_vidc_get_iommu_domain_partition(inst->vidc_context,
+ flags, BUF_TYPE_INPUT, &domain, &partition);
if (rc) {
- WFD_MSG_ERR("Failed to get physical addr %ld\n", rc);
+ WFD_MSG_ERR("Failed to get domain for output buffer\n");
+ goto venc_map_domain_fail;
+ }
+
+ rc = ion_map_iommu(mmap->ion_client, mregion->ion_handle,
+ domain, partition, align_req, 0,
+ (unsigned long *)&paddr, &size, 0, 0);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get physical addr %d\n", rc);
paddr = NULL;
goto venc_map_bad_align;
} else if (size < mregion->size) {
@@ -1191,12 +1216,12 @@
}
mregion->paddr = paddr;
- return 0;
+ return rc;
venc_map_iommu_size_fail:
ion_unmap_iommu(venc_ion_client, mregion->ion_handle,
- inst->domain, 0);
-
+ domain, partition);
+venc_map_domain_fail:
if (inst->secure)
msm_ion_unsecure_buffer(mmap->ion_client, mregion->ion_handle);
venc_map_bad_align:
@@ -1208,6 +1233,8 @@
struct mem_region_map *mmap = arg;
struct mem_region *mregion = NULL;
struct venc_inst *inst = NULL;
+ unsigned long flags = 0;
+ int domain = 0, partition = 0, rc = 0;
if (!sd) {
WFD_MSG_ERR("Subdevice required for %s\n", __func__);
@@ -1220,14 +1247,28 @@
inst = (struct venc_inst *)sd->dev_priv;
mregion = mmap->mregion;
+ rc = ion_handle_get_flags(mmap->ion_client,
+ mregion->ion_handle, &flags);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get ion flags %d\n", rc);
+ return rc;
+ }
+
+ rc = msm_vidc_get_iommu_domain_partition(inst->vidc_context,
+ flags, BUF_TYPE_INPUT, &domain, &partition);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get domain for input buffer\n");
+ return rc;
+ }
+
if (mregion->paddr)
ion_unmap_iommu(mmap->ion_client, mregion->ion_handle,
- inst->domain, 0);
+ domain, partition);
if (inst->secure)
msm_ion_unsecure_buffer(mmap->ion_client, mregion->ion_handle);
- return 0;
+ return rc;
}
static long venc_set_framerate_mode(struct v4l2_subdev *sd,
@@ -1239,24 +1280,6 @@
return 0;
}
-static long secure_toggle(struct venc_inst *inst, bool secure)
-{
- if (inst->secure == secure)
- return 0;
-
- if (!list_empty(&inst->registered_input_bufs.list) ||
- !list_empty(&inst->registered_output_bufs.list)) {
- WFD_MSG_ERR(
- "Attempt to (un)secure encoder not allowed after registering buffers"
- );
- return -EEXIST;
- }
-
- inst->secure = secure;
- inst->domain = get_iommu_domain(inst);
- return 0;
-}
-
static long venc_secure(struct v4l2_subdev *sd)
{
struct venc_inst *inst = NULL;
@@ -1269,9 +1292,18 @@
}
inst = sd->dev_priv;
- rc = secure_toggle(inst, true);
- if (rc) {
- WFD_MSG_ERR("Failed to toggle into secure mode\n");
+
+ if (!list_empty(&inst->registered_input_bufs.list) ||
+ !list_empty(&inst->registered_output_bufs.list)) {
+ WFD_MSG_ERR(
+ "Attempt to (un)secure encoder not allowed after registering buffers"
+ );
+ rc = -EEXIST;
+ }
+
+ if (inst->secure) {
+ /* Nothing to do! */
+ rc = 0;
goto secure_fail;
}
@@ -1282,9 +1314,8 @@
goto secure_fail;
}
- return 0;
+ inst->secure = true;
secure_fail:
- secure_toggle(sd->dev_priv, false);
return rc;
}
diff --git a/drivers/media/platform/msm/wfd/vsg-subdev.c b/drivers/media/platform/msm/wfd/vsg-subdev.c
index bbe2b7b..90b1957 100644
--- a/drivers/media/platform/msm/wfd/vsg-subdev.c
+++ b/drivers/media/platform/msm/wfd/vsg-subdev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -101,8 +101,8 @@
list_for_each_entry(temp, &context->busy_queue.node, node) {
if (++count > MAX_BUFS_BUSY_WITH_ENC) {
- WFD_MSG_WARN("Skipping encode, too many "
- "buffers with encoder");
+ WFD_MSG_WARN(
+ "Skipping encode, too many buffers with encoder\n");
goto err_skip_encode;
}
}
@@ -158,8 +158,11 @@
&& can_release) {
vsg_release_input_buffer(context,
old_last_buffer);
- kfree(old_last_buffer);
}
+
+ if (last_buf_with_us)
+ kfree(old_last_buffer);
+
}
vsg_set_last_buffer(context, buf_info);
}
@@ -406,8 +409,8 @@
if (!is_last_buffer &&
!(temp->flags & VSG_NEVER_RELEASE)) {
vsg_release_input_buffer(context, temp);
- kfree(temp);
}
+ kfree(temp);
}
}
@@ -458,8 +461,8 @@
static long vsg_return_ip_buffer(struct v4l2_subdev *sd, void *arg)
{
struct vsg_context *context = NULL;
- struct vsg_buf_info *buf_info, *last_buffer,
- *expected_buffer;
+ struct vsg_buf_info *buf_info = NULL, *last_buffer = NULL,
+ *expected_buffer = NULL;
int rc = 0;
if (!arg || !sd) {
@@ -473,8 +476,10 @@
buf_info = (struct vsg_buf_info *)arg;
last_buffer = context->last_buffer;
- expected_buffer = list_first_entry(&context->busy_queue.node,
- struct vsg_buf_info, node);
+ if (!list_empty(&context->busy_queue.node)) {
+ expected_buffer = list_first_entry(&context->busy_queue.node,
+ struct vsg_buf_info, node);
+ }
WFD_MSG_DBG("Return frame with paddr %p\n",
(void *)buf_info->mdp_buf_info.paddr);
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index 11a8f4d..bbc18a4 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -49,6 +49,7 @@
static char utf_8_flag;
static char rt_ert_flag;
static char formatting_dir;
+static DEFINE_MUTEX(iris_fm);
module_param(rds_buf, uint, 0);
MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
@@ -1167,6 +1168,7 @@
DECLARE_WAITQUEUE(wait, current);
+ mutex_lock(&iris_fm);
hdev->req_status = HCI_REQ_PEND;
add_wait_queue(&hdev->req_wait_q, &wait);
@@ -1178,8 +1180,10 @@
remove_wait_queue(&hdev->req_wait_q, &wait);
- if (signal_pending(current))
+ if (signal_pending(current)) {
+ mutex_unlock(&iris_fm);
return -EINTR;
+ }
switch (hdev->req_status) {
case HCI_REQ_DONE:
@@ -1197,6 +1201,7 @@
}
hdev->req_status = hdev->req_result = 0;
+ mutex_unlock(&iris_fm);
return err;
}
diff --git a/drivers/media/radio/radio-tavarua.c b/drivers/media/radio/radio-tavarua.c
index a79009b..d3ddeef 100644
--- a/drivers/media/radio/radio-tavarua.c
+++ b/drivers/media/radio/radio-tavarua.c
@@ -1354,13 +1354,16 @@
/* Set channel spacing */
switch (region) {
case TAVARUA_REGION_US:
- if (adie_type_bahma) {
+ if ((adie_type_bahma) && (bahama_version == 0x09)) {
FMDBG("Adie type : Bahama\n");
/*
Configuring all 200KHZ spaced regions as 100KHz due to
change in the new Bahma FM SoC search algorithm.
*/
value = FM_CH_SPACE_100KHZ;
+ } else if ((adie_type_bahma) && (bahama_version == 0x0a)) {
+ FMDBG("Adie type : Bahama B1\n");
+ value = FM_CH_SPACE_200KHZ;
} else {
FMDBG("Adie type : Marimba\n");
value = FM_CH_SPACE_200KHZ;
@@ -1368,7 +1371,7 @@
break;
case TAVARUA_REGION_JAPAN:
case TAVARUA_REGION_OTHER:
- if (adie_type_bahma) {
+ if ((adie_type_bahma) && (bahama_version == 0x09)) {
FMDBG("Adie type : Bahama\n");
FMDBG("%s: Configuring the channel-spacing as 50KHz"
"for the Region : %d", __func__, region);
@@ -1377,6 +1380,9 @@
change in the new Bahma FM SoC search algorithm.
*/
value = FM_CH_SPACE_50KHZ;
+ } else if ((adie_type_bahma) && (bahama_version == 0x0a)) {
+ FMDBG("Adie type : Bahama B1\n");
+ value = FM_CH_SPACE_100KHZ;
} else {
FMDBG("Adie type : Marimba\n");
value = FM_CH_SPACE_100KHZ;
diff --git a/drivers/misc/qseecom_kernel.h b/drivers/misc/qseecom_kernel.h
index 0c93ef2..c6c8fc9 100644
--- a/drivers/misc/qseecom_kernel.h
+++ b/drivers/misc/qseecom_kernel.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -14,6 +14,12 @@
#define __QSEECOM_KERNEL_H_
#include <linux/types.h>
+
+#define QSEECOM_ALIGN_SIZE 0x40
+#define QSEECOM_ALIGN_MASK (QSEECOM_ALIGN_SIZE - 1)
+#define QSEECOM_ALIGN(x) \
+ ((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK))
+
/*
* struct qseecom_handle -
* Handle to the qseecom device for kernel clients
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index a037c17..cf6f97c 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3016,14 +3016,27 @@
{
struct mmc_blk_data *part_md;
struct mmc_blk_data *md = mmc_get_drvdata(card);
+ int rc = 0;
if (md) {
- mmc_queue_suspend(&md->queue);
+ rc = mmc_queue_suspend(&md->queue);
+ if (rc)
+ goto out;
list_for_each_entry(part_md, &md->part, part) {
- mmc_queue_suspend(&part_md->queue);
+ rc = mmc_queue_suspend(&part_md->queue);
+ if (rc)
+ goto out_resume;
}
}
- return 0;
+ goto out;
+
+ out_resume:
+ mmc_queue_resume(&md->queue);
+ list_for_each_entry(part_md, &md->part, part) {
+ mmc_queue_resume(&part_md->queue);
+ }
+ out:
+ return rc;
}
static int mmc_blk_resume(struct mmc_card *card)
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 64ece67..65a1322 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -435,10 +435,11 @@
* complete any outstanding requests. This ensures that we
* won't suspend while a request is being processed.
*/
-void mmc_queue_suspend(struct mmc_queue *mq)
+int mmc_queue_suspend(struct mmc_queue *mq)
{
struct request_queue *q = mq->queue;
unsigned long flags;
+ int rc = 0;
if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
mq->flags |= MMC_QUEUE_SUSPENDED;
@@ -447,8 +448,20 @@
blk_stop_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
- down(&mq->thread_sem);
+ rc = down_trylock(&mq->thread_sem);
+ if (rc) {
+ /*
+ * Failed to take the lock so better to abort the
+ * suspend because mmcqd thread is processing requests.
+ */
+ mq->flags &= ~MMC_QUEUE_SUSPENDED;
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ rc = -EBUSY;
+ }
}
+ return rc;
}
/**
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 99c3c60..9280d1b 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -60,7 +60,7 @@
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
const char *);
extern void mmc_cleanup_queue(struct mmc_queue *);
-extern void mmc_queue_suspend(struct mmc_queue *);
+extern int mmc_queue_suspend(struct mmc_queue *);
extern void mmc_queue_resume(struct mmc_queue *);
extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index be4315e..b395fc8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1907,6 +1907,14 @@
mmc_host_clk_release(host);
}
+void mmc_power_cycle(struct mmc_host *host)
+{
+ mmc_power_off(host);
+ /* Wait at least 1 ms according to SD spec */
+ mmc_delay(1);
+ mmc_power_up(host);
+}
+
/*
* Cleanup when the last reference to the bus operator is dropped.
*/
@@ -2571,7 +2579,7 @@
if (!host->bus_ops->power_restore)
return -EOPNOTSUPP;
- if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
+ if (!(host->caps & MMC_CAP_HW_RESET))
return -EOPNOTSUPP;
if (!card)
@@ -2583,7 +2591,10 @@
mmc_host_clk_hold(host);
mmc_set_clock(host, host->f_init);
- host->ops->hw_reset(host);
+ if (mmc_card_sd(card))
+ mmc_power_cycle(host);
+ else if (host->ops->hw_reset)
+ host->ops->hw_reset(host);
/* If the reset has happened, then a status command will fail */
if (check) {
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 6fa51e0..153c821 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -48,6 +48,7 @@
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
void mmc_power_off(struct mmc_host *host);
+void mmc_power_cycle(struct mmc_host *host);
static inline void mmc_delay(unsigned int ms)
{
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index da38122..2c1e11a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -458,6 +458,19 @@
config MMC_TMIO_CORE
tristate
+config MMC_SDHCI_MSM
+ tristate "Qualcomm SDHCI Controller Support"
+ depends on ARCH_MSM
+ depends on MMC_SDHCI_PLTFM
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ support present in MSM SOCs from Qualcomm. The controller
+ supports SD/MMC/SDIO devices.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_MSM
tristate "Qualcomm SDCC Controller Support"
depends on MMC && ARCH_MSM
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 843ce06..c0232fa 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -56,6 +56,7 @@
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
+obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
new file mode 100644
index 0000000..f0706e83
--- /dev/null
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -0,0 +1,1779 @@
+/*
+ * drivers/mmc/host/sdhci-msm.c - Qualcomm MSM SDHCI Platform
+ * driver source file
+ *
+ * Copyright (c) 2012-2013, 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/module.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/gfp.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/mmc/mmc.h>
+#include <mach/gpio.h>
+
+#include "sdhci-pltfm.h"
+
+#define SDHCI_VER_100 0x2B
+#define CORE_HC_MODE 0x78
+#define HC_MODE_EN 0x1
+
+#define CORE_POWER 0x0
+#define CORE_SW_RST (1 << 7)
+
+#define CORE_PWRCTL_STATUS 0xDC
+#define CORE_PWRCTL_MASK 0xE0
+#define CORE_PWRCTL_CLEAR 0xE4
+#define CORE_PWRCTL_CTL 0xE8
+
+#define CORE_PWRCTL_BUS_OFF 0x01
+#define CORE_PWRCTL_BUS_ON (1 << 1)
+#define CORE_PWRCTL_IO_LOW (1 << 2)
+#define CORE_PWRCTL_IO_HIGH (1 << 3)
+
+#define CORE_PWRCTL_BUS_SUCCESS 0x01
+#define CORE_PWRCTL_BUS_FAIL (1 << 1)
+#define CORE_PWRCTL_IO_SUCCESS (1 << 2)
+#define CORE_PWRCTL_IO_FAIL (1 << 3)
+
+#define INT_MASK 0xF
+#define MAX_PHASES 16
+
+#define CORE_DLL_LOCK (1 << 7)
+#define CORE_DLL_EN (1 << 16)
+#define CORE_CDR_EN (1 << 17)
+#define CORE_CK_OUT_EN (1 << 18)
+#define CORE_CDR_EXT_EN (1 << 19)
+#define CORE_DLL_PDN (1 << 29)
+#define CORE_DLL_RST (1 << 30)
+#define CORE_DLL_CONFIG 0x100
+#define CORE_DLL_TEST_CTL 0x104
+#define CORE_DLL_STATUS 0x108
+
+#define CORE_VENDOR_SPEC 0x10C
+#define CORE_CLK_PWRSAVE (1 << 1)
+#define CORE_IO_PAD_PWR_SWITCH (1 << 16)
+
+static const u32 tuning_block_64[] = {
+ 0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
+ 0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
+ 0xF0FFF0FF, 0x3CCCFC0F, 0xCFCC33CC, 0xEEFFEFFF,
+ 0xFDFFFDFF, 0xFFBFFFDF, 0xFFF7FFBB, 0xDE7B7FF7
+};
+
+static const u32 tuning_block_128[] = {
+ 0xFF00FFFF, 0x0000FFFF, 0xCCCCFFFF, 0xCCCC33CC,
+ 0xCC3333CC, 0xFFFFCCCC, 0xFFFFEEFF, 0xFFEEEEFF,
+ 0xFFDDFFFF, 0xDDDDFFFF, 0xBBFFFFFF, 0xBBFFFFFF,
+ 0xFFFFFFBB, 0xFFFFFF77, 0x77FF7777, 0xFFEEDDBB,
+ 0x00FFFFFF, 0x00FFFFFF, 0xCCFFFF00, 0xCC33CCCC,
+ 0x3333CCCC, 0xFFCCCCCC, 0xFFEEFFFF, 0xEEEEFFFF,
+ 0xDDFFFFFF, 0xDDFFFFFF, 0xFFFFFFDD, 0xFFFFFFBB,
+ 0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
+};
+
+/* This structure keeps information per regulator */
+struct sdhci_msm_reg_data {
+ /* voltage regulator handle */
+ struct regulator *reg;
+ /* regulator name */
+ const char *name;
+ /* voltage level to be set */
+ u32 low_vol_level;
+ u32 high_vol_level;
+ /* Load values for low power and high power mode */
+ u32 lpm_uA;
+ u32 hpm_uA;
+
+ /* is this regulator enabled? */
+ bool is_enabled;
+ /* is this regulator needs to be always on? */
+ bool is_always_on;
+ /* is low power mode setting required for this regulator? */
+ bool lpm_sup;
+};
+
+/*
+ * This structure keeps information for all the
+ * regulators required for a SDCC slot.
+ */
+struct sdhci_msm_slot_reg_data {
+ /* keeps VDD/VCC regulator info */
+ struct sdhci_msm_reg_data *vdd_data;
+ /* keeps VDD IO regulator info */
+ struct sdhci_msm_reg_data *vdd_io_data;
+};
+
+struct sdhci_msm_gpio {
+ u32 no;
+ const char *name;
+ bool is_enabled;
+};
+
+struct sdhci_msm_gpio_data {
+ struct sdhci_msm_gpio *gpio;
+ u8 size;
+};
+
+struct sdhci_msm_pad_pull {
+ enum msm_tlmm_pull_tgt no;
+ u32 val;
+};
+
+struct sdhci_msm_pad_pull_data {
+ struct sdhci_msm_pad_pull *on;
+ struct sdhci_msm_pad_pull *off;
+ u8 size;
+};
+
+struct sdhci_msm_pad_drv {
+ enum msm_tlmm_hdrive_tgt no;
+ u32 val;
+};
+
+struct sdhci_msm_pad_drv_data {
+ struct sdhci_msm_pad_drv *on;
+ struct sdhci_msm_pad_drv *off;
+ u8 size;
+};
+
+struct sdhci_msm_pad_data {
+ struct sdhci_msm_pad_pull_data *pull;
+ struct sdhci_msm_pad_drv_data *drv;
+};
+
+
+struct sdhci_msm_pin_data {
+ /*
+ * = 1 if controller pins are using gpios
+ * = 0 if controller has dedicated MSM pads
+ */
+ u8 is_gpio;
+ bool cfg_sts;
+ struct sdhci_msm_gpio_data *gpio_data;
+ struct sdhci_msm_pad_data *pad_data;
+};
+
+struct sdhci_msm_pltfm_data {
+ /* Supported UHS-I Modes */
+ u32 caps;
+
+ /* More capabilities */
+ u32 caps2;
+
+ unsigned long mmc_bus_width;
+ u32 max_clk;
+ struct sdhci_msm_slot_reg_data *vreg_data;
+ bool nonremovable;
+ struct sdhci_msm_pin_data *pin_data;
+};
+
+struct sdhci_msm_host {
+ void __iomem *core_mem; /* MSM SDCC mapped address */
+ struct clk *clk; /* main SD/MMC bus clock */
+ struct clk *pclk; /* SDHC peripheral bus clock */
+ struct clk *bus_clk; /* SDHC bus voter clock */
+ struct sdhci_msm_pltfm_data *pdata;
+ struct mmc_host *mmc;
+ struct sdhci_pltfm_data sdhci_msm_pdata;
+ wait_queue_head_t pwr_irq_wait;
+};
+
+enum vdd_io_level {
+ /* set vdd_io_data->low_vol_level */
+ VDD_IO_LOW,
+ /* set vdd_io_data->high_vol_level */
+ VDD_IO_HIGH,
+ /*
+ * set whatever there in voltage_level (third argument) of
+ * sdhci_msm_set_vdd_io_vol() function.
+ */
+ VDD_IO_SET_LEVEL,
+};
+
+/* MSM platform specific tuning */
+static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host,
+ u8 poll)
+{
+ int rc = 0;
+ u32 wait_cnt = 50;
+ u8 ck_out_en = 0;
+ struct mmc_host *mmc = host->mmc;
+
+ /* poll for CK_OUT_EN bit. max. poll time = 50us */
+ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
+ CORE_CK_OUT_EN);
+
+ while (ck_out_en != poll) {
+ if (--wait_cnt == 0) {
+ pr_err("%s: %s: CK_OUT_EN bit is not %d\n",
+ mmc_hostname(mmc), __func__, poll);
+ rc = -ETIMEDOUT;
+ goto out;
+ }
+ udelay(1);
+
+ ck_out_en = !!(readl_relaxed(host->ioaddr +
+ CORE_DLL_CONFIG) & CORE_CK_OUT_EN);
+ }
+out:
+ return rc;
+}
+
+static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
+{
+ int rc = 0;
+ u8 grey_coded_phase_table[] = {0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
+ 0xC, 0xD, 0xF, 0xE, 0xA, 0xB, 0x9,
+ 0x8};
+ unsigned long flags;
+ u32 config;
+ struct mmc_host *mmc = host->mmc;
+
+ pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
+ spin_lock_irqsave(&host->lock, flags);
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
+ config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
+ rc = msm_dll_poll_ck_out_en(host, 0);
+ if (rc)
+ goto err_out;
+
+ /*
+ * Write the selected DLL clock output phase (0 ... 15)
+ * to CDR_SELEXT bit field of DLL_CONFIG register.
+ */
+ writel_relaxed(((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ & ~(0xF << 20))
+ | (grey_coded_phase_table[phase] << 20)),
+ host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
+ rc = msm_dll_poll_ck_out_en(host, 1);
+ if (rc)
+ goto err_out;
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config |= CORE_CDR_EN;
+ config &= ~CORE_CDR_EXT_EN;
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+ goto out;
+
+err_out:
+ pr_err("%s: %s: Failed to set DLL phase: %d\n",
+ mmc_hostname(mmc), __func__, phase);
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
+ return rc;
+}
+
+/*
+ * Find out the greatest range of consecuitive selected
+ * DLL clock output phases that can be used as sampling
+ * setting for SD3.0 UHS-I card read operation (in SDR104
+ * timing mode) or for eMMC4.5 card read operation (in HS200
+ * timing mode).
+ * Select the 3/4 of the range and configure the DLL with the
+ * selected DLL clock output phase.
+ */
+
+static int msm_find_most_appropriate_phase(struct sdhci_host *host,
+ u8 *phase_table, u8 total_phases)
+{
+ int ret;
+ u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
+ u8 phases_per_row[MAX_PHASES] = {0};
+ int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
+ int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
+ bool phase_0_found = false, phase_15_found = false;
+ struct mmc_host *mmc = host->mmc;
+
+ pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
+ if (!total_phases || (total_phases > MAX_PHASES)) {
+ pr_err("%s: %s: invalid argument: total_phases=%d\n",
+ mmc_hostname(mmc), __func__, total_phases);
+ return -EINVAL;
+ }
+
+ for (cnt = 0; cnt < total_phases; cnt++) {
+ ranges[row_index][col_index] = phase_table[cnt];
+ phases_per_row[row_index] += 1;
+ col_index++;
+
+ if ((cnt + 1) == total_phases) {
+ continue;
+ /* check if next phase in phase_table is consecutive or not */
+ } else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
+ row_index++;
+ col_index = 0;
+ }
+ }
+
+ if (row_index >= MAX_PHASES)
+ return -EINVAL;
+
+ /* Check if phase-0 is present in first valid window? */
+ if (!ranges[0][0]) {
+ phase_0_found = true;
+ phase_0_raw_index = 0;
+ /* Check if cycle exist between 2 valid windows */
+ for (cnt = 1; cnt <= row_index; cnt++) {
+ if (phases_per_row[cnt]) {
+ for (i = 0; i < phases_per_row[cnt]; i++) {
+ if (ranges[cnt][i] == 15) {
+ phase_15_found = true;
+ phase_15_raw_index = cnt;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* If 2 valid windows form cycle then merge them as single window */
+ if (phase_0_found && phase_15_found) {
+ /* number of phases in raw where phase 0 is present */
+ u8 phases_0 = phases_per_row[phase_0_raw_index];
+ /* number of phases in raw where phase 15 is present */
+ u8 phases_15 = phases_per_row[phase_15_raw_index];
+
+ if (phases_0 + phases_15 >= MAX_PHASES)
+ /*
+ * If there are more than 1 phase windows then total
+ * number of phases in both the windows should not be
+ * more than or equal to MAX_PHASES.
+ */
+ return -EINVAL;
+
+ /* Merge 2 cyclic windows */
+ i = phases_15;
+ for (cnt = 0; cnt < phases_0; cnt++) {
+ ranges[phase_15_raw_index][i] =
+ ranges[phase_0_raw_index][cnt];
+ if (++i >= MAX_PHASES)
+ break;
+ }
+
+ phases_per_row[phase_0_raw_index] = 0;
+ phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
+ }
+
+ for (cnt = 0; cnt <= row_index; cnt++) {
+ if (phases_per_row[cnt] > curr_max) {
+ curr_max = phases_per_row[cnt];
+ selected_row_index = cnt;
+ }
+ }
+
+ i = ((curr_max * 3) / 4);
+ if (i)
+ i--;
+
+ ret = (int)ranges[selected_row_index][i];
+
+ if (ret >= MAX_PHASES) {
+ ret = -EINVAL;
+ pr_err("%s: %s: invalid phase selected=%d\n",
+ mmc_hostname(mmc), __func__, ret);
+ }
+
+ pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
+ return ret;
+}
+
+static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
+{
+ u32 mclk_freq = 0;
+
+ /* Program the MCLK value to MCLK_FREQ bit field */
+ if (host->clock <= 112000000)
+ mclk_freq = 0;
+ else if (host->clock <= 125000000)
+ mclk_freq = 1;
+ else if (host->clock <= 137000000)
+ mclk_freq = 2;
+ else if (host->clock <= 150000000)
+ mclk_freq = 3;
+ else if (host->clock <= 162000000)
+ mclk_freq = 4;
+ else if (host->clock <= 175000000)
+ mclk_freq = 5;
+ else if (host->clock <= 187000000)
+ mclk_freq = 6;
+ else if (host->clock <= 200000000)
+ mclk_freq = 7;
+
+ writel_relaxed(((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ & ~(7 << 24)) | (mclk_freq << 24)),
+ host->ioaddr + CORE_DLL_CONFIG);
+}
+
+/* Initialize the DLL (Programmable Delay Line ) */
+static int msm_init_cm_dll(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ int rc = 0;
+ unsigned long flags;
+ u32 wait_cnt;
+
+ pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
+ spin_lock_irqsave(&host->lock, flags);
+
+ /*
+ * Make sure that clock is always enabled when DLL
+ * tuning is in progress. Keeping PWRSAVE ON may
+ * turn off the clock. So let's disable the PWRSAVE
+ * here and re-enable it once tuning is completed.
+ */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ & ~CORE_CLK_PWRSAVE),
+ host->ioaddr + CORE_VENDOR_SPEC);
+
+ /* Write 1 to DLL_RST bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Write 1 to DLL_PDN bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+ msm_cm_dll_set_freq(host);
+
+ /* Write 0 to DLL_RST bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ & ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Write 0 to DLL_PDN bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ & ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set DLL_EN bit to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set CK_OUT_EN bit to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ wait_cnt = 50;
+ /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
+ while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
+ CORE_DLL_LOCK)) {
+ /* max. wait for 50us sec for LOCK bit to be set */
+ if (--wait_cnt == 0) {
+ pr_err("%s: %s: DLL failed to LOCK\n",
+ mmc_hostname(mmc), __func__);
+ rc = -ETIMEDOUT;
+ goto out;
+ }
+ /* wait for 1us before polling again */
+ udelay(1);
+ }
+
+out:
+ /* re-enable PWRSAVE */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) |
+ CORE_CLK_PWRSAVE),
+ host->ioaddr + CORE_VENDOR_SPEC);
+ spin_unlock_irqrestore(&host->lock, flags);
+ pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
+ return rc;
+}
+
+int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ unsigned long flags;
+ u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
+ const u32 *tuning_block_pattern = tuning_block_64;
+ int size = sizeof(tuning_block_64); /* Tuning pattern size in bytes */
+ int rc;
+ struct mmc_host *mmc = host->mmc;
+
+ pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
+ /* Tuning is only required for SDR104 modes */
+ spin_lock_irqsave(&host->lock, flags);
+
+ if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
+ (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
+ tuning_block_pattern = tuning_block_128;
+ size = sizeof(tuning_block_128);
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /* first of all reset the tuning block */
+ rc = msm_init_cm_dll(host);
+ if (rc)
+ goto out;
+
+ data_buf = kmalloc(size, GFP_KERNEL);
+ if (!data_buf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ phase = 0;
+ do {
+ struct mmc_command cmd = {0};
+ struct mmc_data data = {0};
+ struct mmc_request mrq = {
+ .cmd = &cmd,
+ .data = &data
+ };
+ struct scatterlist sg;
+
+ /* set the phase in delay line hw block */
+ rc = msm_config_cm_dll_phase(host, phase);
+ if (rc)
+ goto kfree;
+
+ cmd.opcode = opcode;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = size;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.timeout_ns = 1000 * 1000 * 1000; /* 1 sec */
+
+ data.sg = &sg;
+ data.sg_len = 1;
+ sg_init_one(&sg, data_buf, size);
+ memset(data_buf, 0, size);
+ mmc_wait_for_req(mmc, &mrq);
+
+ if (!cmd.error && !data.error &&
+ !memcmp(data_buf, tuning_block_pattern, size)) {
+ /* tuning is successful at this tuning point */
+ tuned_phases[tuned_phase_cnt++] = phase;
+ pr_debug("%s: %s: found good phase = %d\n",
+ mmc_hostname(mmc), __func__, phase);
+ }
+ } while (++phase < 16);
+
+ if (tuned_phase_cnt) {
+ rc = msm_find_most_appropriate_phase(host, tuned_phases,
+ tuned_phase_cnt);
+ if (rc < 0)
+ goto kfree;
+ else
+ phase = (u8)rc;
+
+ /*
+ * Finally set the selected phase in delay
+ * line hw block.
+ */
+ rc = msm_config_cm_dll_phase(host, phase);
+ if (rc)
+ goto kfree;
+ pr_debug("%s: %s: finally setting the tuning phase to %d\n",
+ mmc_hostname(mmc), __func__, phase);
+ } else {
+ /* tuning failed */
+ pr_err("%s: %s: no tuning point found\n",
+ mmc_hostname(mmc), __func__);
+ rc = -EAGAIN;
+ }
+
+kfree:
+ kfree(data_buf);
+out:
+ pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
+ return rc;
+}
+
+static int sdhci_msm_setup_gpio(struct sdhci_msm_pltfm_data *pdata, bool enable)
+{
+ struct sdhci_msm_gpio_data *curr;
+ int i, ret = 0;
+
+ curr = pdata->pin_data->gpio_data;
+ for (i = 0; i < curr->size; i++) {
+ if (!gpio_is_valid(curr->gpio[i].no)) {
+ ret = -EINVAL;
+ pr_err("%s: Invalid gpio = %d\n", __func__,
+ curr->gpio[i].no);
+ goto free_gpios;
+ }
+ if (enable) {
+ ret = gpio_request(curr->gpio[i].no,
+ curr->gpio[i].name);
+ if (ret) {
+ pr_err("%s: gpio_request(%d, %s) failed %d\n",
+ __func__, curr->gpio[i].no,
+ curr->gpio[i].name, ret);
+ goto free_gpios;
+ }
+ curr->gpio[i].is_enabled = true;
+ } else {
+ gpio_free(curr->gpio[i].no);
+ curr->gpio[i].is_enabled = false;
+ }
+ }
+ return ret;
+
+free_gpios:
+ for (i--; i >= 0; i--) {
+ gpio_free(curr->gpio[i].no);
+ curr->gpio[i].is_enabled = false;
+ }
+ return ret;
+}
+
+static int sdhci_msm_setup_pad(struct sdhci_msm_pltfm_data *pdata, bool enable)
+{
+ struct sdhci_msm_pad_data *curr;
+ int i;
+
+ curr = pdata->pin_data->pad_data;
+ for (i = 0; i < curr->drv->size; i++) {
+ if (enable)
+ msm_tlmm_set_hdrive(curr->drv->on[i].no,
+ curr->drv->on[i].val);
+ else
+ msm_tlmm_set_hdrive(curr->drv->off[i].no,
+ curr->drv->off[i].val);
+ }
+
+ for (i = 0; i < curr->pull->size; i++) {
+ if (enable)
+ msm_tlmm_set_pull(curr->pull->on[i].no,
+ curr->pull->on[i].val);
+ else
+ msm_tlmm_set_pull(curr->pull->off[i].no,
+ curr->pull->off[i].val);
+ }
+
+ return 0;
+}
+
+static int sdhci_msm_setup_pins(struct sdhci_msm_pltfm_data *pdata, bool enable)
+{
+ int ret = 0;
+
+ if (!pdata->pin_data || (pdata->pin_data->cfg_sts == enable))
+ return 0;
+ if (pdata->pin_data->is_gpio)
+ ret = sdhci_msm_setup_gpio(pdata, enable);
+ else
+ ret = sdhci_msm_setup_pad(pdata, enable);
+
+ if (!ret)
+ pdata->pin_data->cfg_sts = enable;
+
+ return ret;
+}
+
+static int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name,
+ u32 **out, int *len, u32 size)
+{
+ int ret = 0;
+ struct device_node *np = dev->of_node;
+ size_t sz;
+ u32 *arr = NULL;
+
+ if (!of_get_property(np, prop_name, len)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ sz = *len = *len / sizeof(*arr);
+ if (sz <= 0 || (size > 0 && (sz != size))) {
+ dev_err(dev, "%s invalid size\n", prop_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ arr = devm_kzalloc(dev, sz * sizeof(*arr), GFP_KERNEL);
+ if (!arr) {
+ dev_err(dev, "%s failed allocating memory\n", prop_name);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = of_property_read_u32_array(np, prop_name, arr, sz);
+ if (ret < 0) {
+ dev_err(dev, "%s failed reading array %d\n", prop_name, ret);
+ goto out;
+ }
+ *out = arr;
+out:
+ if (ret)
+ *len = 0;
+ return ret;
+}
+
+#define MAX_PROP_SIZE 32
+static int sdhci_msm_dt_parse_vreg_info(struct device *dev,
+ struct sdhci_msm_reg_data **vreg_data, const char *vreg_name)
+{
+ int len, ret = 0;
+ const __be32 *prop;
+ char prop_name[MAX_PROP_SIZE];
+ struct sdhci_msm_reg_data *vreg;
+ struct device_node *np = dev->of_node;
+
+ snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name);
+ if (!of_parse_phandle(np, prop_name, 0)) {
+ dev_err(dev, "No vreg data found for %s\n", vreg_name);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg) {
+ dev_err(dev, "No memory for vreg: %s\n", vreg_name);
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ vreg->name = vreg_name;
+
+ snprintf(prop_name, MAX_PROP_SIZE,
+ "qcom,%s-always-on", vreg_name);
+ if (of_get_property(np, prop_name, NULL))
+ vreg->is_always_on = true;
+
+ snprintf(prop_name, MAX_PROP_SIZE,
+ "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,%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",
+ prop ? "invalid format" : "no", prop_name);
+ } else {
+ vreg->low_vol_level = be32_to_cpup(&prop[0]);
+ vreg->high_vol_level = be32_to_cpup(&prop[1]);
+ }
+
+ snprintf(prop_name, MAX_PROP_SIZE,
+ "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",
+ prop ? "invalid format" : "no", prop_name);
+ } else {
+ vreg->lpm_uA = be32_to_cpup(&prop[0]);
+ vreg->hpm_uA = be32_to_cpup(&prop[1]);
+ }
+
+ *vreg_data = vreg;
+ dev_dbg(dev, "%s: %s %s vol=[%d %d]uV, curr=[%d %d]uA\n",
+ vreg->name, vreg->is_always_on ? "always_on," : "",
+ vreg->lpm_sup ? "lpm_sup," : "", vreg->low_vol_level,
+ vreg->high_vol_level, vreg->lpm_uA, vreg->hpm_uA);
+
+ return ret;
+}
+
+/* GPIO/Pad data extraction */
+static int sdhci_msm_dt_get_pad_pull_info(struct device *dev, int id,
+ struct sdhci_msm_pad_pull_data **pad_pull_data)
+{
+ int ret = 0, base = 0, len, i;
+ u32 *tmp;
+ struct sdhci_msm_pad_pull_data *pull_data;
+ struct sdhci_msm_pad_pull *pull;
+
+ switch (id) {
+ case 1:
+ base = TLMM_PULL_SDC1_CLK;
+ break;
+ case 2:
+ base = TLMM_PULL_SDC2_CLK;
+ break;
+ case 3:
+ base = TLMM_PULL_SDC3_CLK;
+ break;
+ case 4:
+ base = TLMM_PULL_SDC4_CLK;
+ break;
+ default:
+ dev_err(dev, "%s: Invalid slot id\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pull_data = devm_kzalloc(dev, sizeof(struct sdhci_msm_pad_pull_data),
+ GFP_KERNEL);
+ if (!pull_data) {
+ dev_err(dev, "No memory for msm_mmc_pad_pull_data\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ pull_data->size = 3; /* array size for clk, cmd, data */
+
+ /* Allocate on, off configs for clk, cmd, data */
+ pull = devm_kzalloc(dev, 2 * pull_data->size *\
+ sizeof(struct sdhci_msm_pad_pull), GFP_KERNEL);
+ if (!pull) {
+ dev_err(dev, "No memory for msm_mmc_pad_pull\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ pull_data->on = pull;
+ pull_data->off = pull + pull_data->size;
+
+ ret = sdhci_msm_dt_get_array(dev, "qcom,pad-pull-on",
+ &tmp, &len, pull_data->size);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < len; i++) {
+ pull_data->on[i].no = base + i;
+ pull_data->on[i].val = tmp[i];
+ dev_dbg(dev, "%s: val[%d]=0x%x\n", __func__,
+ i, pull_data->on[i].val);
+ }
+
+ ret = sdhci_msm_dt_get_array(dev, "qcom,pad-pull-off",
+ &tmp, &len, pull_data->size);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < len; i++) {
+ pull_data->off[i].no = base + i;
+ pull_data->off[i].val = tmp[i];
+ dev_dbg(dev, "%s: val[%d]=0x%x\n", __func__,
+ i, pull_data->off[i].val);
+ }
+
+ *pad_pull_data = pull_data;
+out:
+ return ret;
+}
+
+static int sdhci_msm_dt_get_pad_drv_info(struct device *dev, int id,
+ struct sdhci_msm_pad_drv_data **pad_drv_data)
+{
+ int ret = 0, base = 0, len, i;
+ u32 *tmp;
+ struct sdhci_msm_pad_drv_data *drv_data;
+ struct sdhci_msm_pad_drv *drv;
+
+ switch (id) {
+ case 1:
+ base = TLMM_HDRV_SDC1_CLK;
+ break;
+ case 2:
+ base = TLMM_HDRV_SDC2_CLK;
+ break;
+ case 3:
+ base = TLMM_HDRV_SDC3_CLK;
+ break;
+ case 4:
+ base = TLMM_HDRV_SDC4_CLK;
+ break;
+ default:
+ dev_err(dev, "%s: Invalid slot id\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ drv_data = devm_kzalloc(dev, sizeof(struct sdhci_msm_pad_drv_data),
+ GFP_KERNEL);
+ if (!drv_data) {
+ dev_err(dev, "No memory for msm_mmc_pad_drv_data\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ drv_data->size = 3; /* array size for clk, cmd, data */
+
+ /* Allocate on, off configs for clk, cmd, data */
+ drv = devm_kzalloc(dev, 2 * drv_data->size *\
+ sizeof(struct sdhci_msm_pad_drv), GFP_KERNEL);
+ if (!drv) {
+ dev_err(dev, "No memory msm_mmc_pad_drv\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ drv_data->on = drv;
+ drv_data->off = drv + drv_data->size;
+
+ ret = sdhci_msm_dt_get_array(dev, "qcom,pad-drv-on",
+ &tmp, &len, drv_data->size);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < len; i++) {
+ drv_data->on[i].no = base + i;
+ drv_data->on[i].val = tmp[i];
+ dev_dbg(dev, "%s: val[%d]=0x%x\n", __func__,
+ i, drv_data->on[i].val);
+ }
+
+ ret = sdhci_msm_dt_get_array(dev, "qcom,pad-drv-off",
+ &tmp, &len, drv_data->size);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < len; i++) {
+ drv_data->off[i].no = base + i;
+ drv_data->off[i].val = tmp[i];
+ dev_dbg(dev, "%s: val[%d]=0x%x\n", __func__,
+ i, drv_data->off[i].val);
+ }
+
+ *pad_drv_data = drv_data;
+out:
+ return ret;
+}
+
+#define GPIO_NAME_MAX_LEN 32
+static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
+ struct sdhci_msm_pltfm_data *pdata)
+{
+ int ret = 0, id = 0, cnt, i;
+ struct sdhci_msm_pin_data *pin_data;
+ struct device_node *np = dev->of_node;
+
+ pin_data = devm_kzalloc(dev, sizeof(*pin_data), GFP_KERNEL);
+ if (!pin_data) {
+ dev_err(dev, "No memory for pin_data\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cnt = of_gpio_count(np);
+ if (cnt > 0) {
+ pin_data->is_gpio = true;
+ pin_data->gpio_data = devm_kzalloc(dev,
+ sizeof(struct sdhci_msm_gpio_data), GFP_KERNEL);
+ if (!pin_data->gpio_data) {
+ dev_err(dev, "No memory for gpio_data\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ pin_data->gpio_data->size = cnt;
+ pin_data->gpio_data->gpio = devm_kzalloc(dev, cnt *
+ sizeof(struct sdhci_msm_gpio), GFP_KERNEL);
+
+ if (!pin_data->gpio_data->gpio) {
+ dev_err(dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < cnt; i++) {
+ const char *name = NULL;
+ char result[GPIO_NAME_MAX_LEN];
+ pin_data->gpio_data->gpio[i].no = of_get_gpio(np, i);
+ of_property_read_string_index(np,
+ "qcom,gpio-names", i, &name);
+
+ snprintf(result, GPIO_NAME_MAX_LEN, "%s-%s",
+ dev_name(dev), name ? name : "?");
+ pin_data->gpio_data->gpio[i].name = result;
+ dev_dbg(dev, "%s: gpio[%s] = %d\n", __func__,
+ pin_data->gpio_data->gpio[i].name,
+ pin_data->gpio_data->gpio[i].no);
+ }
+ } else {
+ pin_data->pad_data =
+ devm_kzalloc(dev,
+ sizeof(struct sdhci_msm_pad_data),
+ GFP_KERNEL);
+ if (!pin_data->pad_data) {
+ dev_err(dev,
+ "No memory for pin_data->pad_data\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = of_alias_get_id(np, "sdhc");
+ if (ret < 0) {
+ dev_err(dev, "Failed to get slot index %d\n", ret);
+ goto out;
+ }
+ id = ret;
+
+ ret = sdhci_msm_dt_get_pad_pull_info(
+ dev, id, &pin_data->pad_data->pull);
+ if (ret)
+ goto out;
+ ret = sdhci_msm_dt_get_pad_drv_info(
+ dev, id, &pin_data->pad_data->drv);
+ if (ret)
+ goto out;
+
+ }
+ pdata->pin_data = pin_data;
+out:
+ if (ret)
+ dev_err(dev, "%s failed with err %d\n", __func__, ret);
+ return ret;
+}
+
+/* Parse platform data */
+static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
+{
+ struct sdhci_msm_pltfm_data *pdata = NULL;
+ struct device_node *np = dev->of_node;
+ u32 bus_width = 0;
+ int len, i;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "failed to allocate memory for platform data\n");
+ goto out;
+ }
+
+ 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)
+ pdata->mmc_bus_width = MMC_CAP_4_BIT_DATA;
+ else {
+ dev_notice(dev, "invalid bus-width, default to 1-bit mode\n");
+ pdata->mmc_bus_width = 0;
+ }
+
+ pdata->vreg_data = devm_kzalloc(dev, sizeof(struct
+ sdhci_msm_slot_reg_data),
+ GFP_KERNEL);
+ if (!pdata->vreg_data) {
+ dev_err(dev, "failed to allocate memory for vreg data\n");
+ goto out;
+ }
+
+ if (sdhci_msm_dt_parse_vreg_info(dev, &pdata->vreg_data->vdd_data,
+ "vdd")) {
+ dev_err(dev, "failed parsing vdd data\n");
+ goto out;
+ }
+ if (sdhci_msm_dt_parse_vreg_info(dev,
+ &pdata->vreg_data->vdd_io_data,
+ "vdd-io")) {
+ dev_err(dev, "failed parsing vdd-io data\n");
+ goto out;
+ }
+
+ if (sdhci_msm_dt_parse_gpio_info(dev, pdata)) {
+ dev_err(dev, "failed parsing gpio data\n");
+ goto out;
+ }
+
+ of_property_read_u32(np, "qcom,max-clk-rate", &pdata->max_clk);
+
+ 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,bus-speed-mode", i, &name);
+ if (!name)
+ continue;
+
+ if (!strncmp(name, "HS200_1p8v", sizeof("HS200_1p8v")))
+ pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+ else if (!strncmp(name, "HS200_1p2v", sizeof("HS200_1p2v")))
+ pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
+ else if (!strncmp(name, "DDR_1p8v", sizeof("DDR_1p8v")))
+ pdata->caps |= MMC_CAP_1_8V_DDR
+ | MMC_CAP_UHS_DDR50;
+ else if (!strncmp(name, "DDR_1p2v", sizeof("DDR_1p2v")))
+ pdata->caps |= MMC_CAP_1_2V_DDR
+ | MMC_CAP_UHS_DDR50;
+ }
+
+ if (of_get_property(np, "qcom,nonremovable", NULL))
+ pdata->nonremovable = true;
+
+ return pdata;
+out:
+ return NULL;
+}
+
+/* Regulator utility functions */
+static int sdhci_msm_vreg_init_reg(struct device *dev,
+ struct sdhci_msm_reg_data *vreg)
+{
+ int ret = 0;
+
+ /* check if regulator is already initialized? */
+ if (vreg->reg)
+ goto out;
+
+ /* Get the regulator handle */
+ vreg->reg = devm_regulator_get(dev, vreg->name);
+ if (IS_ERR(vreg->reg)) {
+ ret = PTR_ERR(vreg->reg);
+ pr_err("%s: devm_regulator_get(%s) failed. ret=%d\n",
+ __func__, vreg->name, ret);
+ goto out;
+ }
+
+ /* sanity check */
+ if (!vreg->high_vol_level || !vreg->hpm_uA) {
+ pr_err("%s: %s invalid constraints specified\n",
+ __func__, vreg->name);
+ ret = -EINVAL;
+ }
+
+out:
+ return ret;
+}
+
+static void sdhci_msm_vreg_deinit_reg(struct sdhci_msm_reg_data *vreg)
+{
+ if (vreg->reg)
+ devm_regulator_put(vreg->reg);
+}
+
+static int sdhci_msm_vreg_set_optimum_mode(struct sdhci_msm_reg_data
+ *vreg, int uA_load)
+{
+ int ret = 0;
+
+ /*
+ * regulators that do not support regulator_set_voltage also
+ * do not support regulator_set_optimum_mode
+ */
+ ret = regulator_set_optimum_mode(vreg->reg, uA_load);
+ if (ret < 0)
+ pr_err("%s: regulator_set_optimum_mode(reg=%s,uA_load=%d) failed. ret=%d\n",
+ __func__, vreg->name, uA_load, ret);
+ else
+ /*
+ * regulator_set_optimum_mode() can return non zero
+ * value even for success case.
+ */
+ ret = 0;
+ return ret;
+}
+
+static int sdhci_msm_vreg_set_voltage(struct sdhci_msm_reg_data *vreg,
+ int min_uV, int max_uV)
+{
+ int ret = 0;
+
+ ret = regulator_set_voltage(vreg->reg, min_uV, max_uV);
+ if (ret) {
+ pr_err("%s: regulator_set_voltage(%s)failed. min_uV=%d,max_uV=%d,ret=%d\n",
+ __func__, vreg->name, min_uV, max_uV, ret);
+ }
+
+ return ret;
+}
+
+static int sdhci_msm_vreg_enable(struct sdhci_msm_reg_data *vreg)
+{
+ int ret = 0;
+
+ /* Put regulator in HPM (high power mode) */
+ ret = sdhci_msm_vreg_set_optimum_mode(vreg, vreg->hpm_uA);
+ if (ret < 0)
+ return ret;
+
+ if (!vreg->is_enabled) {
+ /* Set voltage level */
+ ret = sdhci_msm_vreg_set_voltage(vreg, vreg->high_vol_level,
+ vreg->high_vol_level);
+ if (ret)
+ return ret;
+ }
+ ret = regulator_enable(vreg->reg);
+ if (ret) {
+ pr_err("%s: regulator_enable(%s) failed. ret=%d\n",
+ __func__, vreg->name, ret);
+ return ret;
+ }
+ vreg->is_enabled = true;
+ return ret;
+}
+
+static int sdhci_msm_vreg_disable(struct sdhci_msm_reg_data *vreg)
+{
+ int ret = 0;
+
+ /* Never disable regulator marked as always_on */
+ if (vreg->is_enabled && !vreg->is_always_on) {
+ ret = regulator_disable(vreg->reg);
+ if (ret) {
+ pr_err("%s: regulator_disable(%s) failed. ret=%d\n",
+ __func__, vreg->name, ret);
+ goto out;
+ }
+ vreg->is_enabled = false;
+
+ ret = sdhci_msm_vreg_set_optimum_mode(vreg, 0);
+ if (ret < 0)
+ goto out;
+
+ /* Set min. voltage level to 0 */
+ ret = sdhci_msm_vreg_set_voltage(vreg, 0, vreg->high_vol_level);
+ if (ret)
+ goto out;
+ } else if (vreg->is_enabled && vreg->is_always_on) {
+ if (vreg->lpm_sup) {
+ /* Put always_on regulator in LPM (low power mode) */
+ ret = sdhci_msm_vreg_set_optimum_mode(vreg,
+ vreg->lpm_uA);
+ if (ret < 0)
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+static int sdhci_msm_setup_vreg(struct sdhci_msm_pltfm_data *pdata,
+ bool enable, bool is_init)
+{
+ int ret = 0, i;
+ struct sdhci_msm_slot_reg_data *curr_slot;
+ struct sdhci_msm_reg_data *vreg_table[2];
+
+ curr_slot = pdata->vreg_data;
+ if (!curr_slot) {
+ pr_debug("%s: vreg info unavailable,assuming the slot is powered by always on domain\n",
+ __func__);
+ goto out;
+ }
+
+ vreg_table[0] = curr_slot->vdd_data;
+ vreg_table[1] = curr_slot->vdd_io_data;
+
+ for (i = 0; i < ARRAY_SIZE(vreg_table); i++) {
+ if (vreg_table[i]) {
+ if (enable)
+ ret = sdhci_msm_vreg_enable(vreg_table[i]);
+ else
+ ret = sdhci_msm_vreg_disable(vreg_table[i]);
+ if (ret)
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+/*
+ * Reset vreg by ensuring it is off during probe. A call
+ * to enable vreg is needed to balance disable vreg
+ */
+static int sdhci_msm_vreg_reset(struct sdhci_msm_pltfm_data *pdata)
+{
+ int ret;
+
+ ret = sdhci_msm_setup_vreg(pdata, 1, true);
+ if (ret)
+ return ret;
+ ret = sdhci_msm_setup_vreg(pdata, 0, true);
+ return ret;
+}
+
+/* This init function should be called only once for each SDHC slot */
+static int sdhci_msm_vreg_init(struct device *dev,
+ struct sdhci_msm_pltfm_data *pdata,
+ bool is_init)
+{
+ int ret = 0;
+ struct sdhci_msm_slot_reg_data *curr_slot;
+ struct sdhci_msm_reg_data *curr_vdd_reg, *curr_vdd_io_reg;
+
+ curr_slot = pdata->vreg_data;
+ if (!curr_slot)
+ goto out;
+
+ curr_vdd_reg = curr_slot->vdd_data;
+ curr_vdd_io_reg = curr_slot->vdd_io_data;
+
+ if (!is_init)
+ /* Deregister all regulators from regulator framework */
+ goto vdd_io_reg_deinit;
+
+ /*
+ * Get the regulator handle from voltage regulator framework
+ * and then try to set the voltage level for the regulator
+ */
+ if (curr_vdd_reg) {
+ ret = sdhci_msm_vreg_init_reg(dev, curr_vdd_reg);
+ if (ret)
+ goto out;
+ }
+ if (curr_vdd_io_reg) {
+ ret = sdhci_msm_vreg_init_reg(dev, curr_vdd_io_reg);
+ if (ret)
+ goto vdd_reg_deinit;
+ }
+ ret = sdhci_msm_vreg_reset(pdata);
+ if (ret)
+ dev_err(dev, "vreg reset failed (%d)\n", ret);
+ goto out;
+
+vdd_io_reg_deinit:
+ if (curr_vdd_io_reg)
+ sdhci_msm_vreg_deinit_reg(curr_vdd_io_reg);
+vdd_reg_deinit:
+ if (curr_vdd_reg)
+ sdhci_msm_vreg_deinit_reg(curr_vdd_reg);
+out:
+ return ret;
+}
+
+
+static int sdhci_msm_set_vdd_io_vol(struct sdhci_msm_pltfm_data *pdata,
+ enum vdd_io_level level,
+ unsigned int voltage_level)
+{
+ int ret = 0;
+ int set_level;
+ struct sdhci_msm_reg_data *vdd_io_reg;
+
+ if (!pdata->vreg_data)
+ return ret;
+
+ vdd_io_reg = pdata->vreg_data->vdd_io_data;
+ if (vdd_io_reg && vdd_io_reg->is_enabled) {
+ switch (level) {
+ case VDD_IO_LOW:
+ set_level = vdd_io_reg->low_vol_level;
+ break;
+ case VDD_IO_HIGH:
+ set_level = vdd_io_reg->high_vol_level;
+ break;
+ case VDD_IO_SET_LEVEL:
+ set_level = voltage_level;
+ break;
+ default:
+ pr_err("%s: invalid argument level = %d",
+ __func__, level);
+ ret = -EINVAL;
+ return ret;
+ }
+ ret = sdhci_msm_vreg_set_voltage(vdd_io_reg, set_level,
+ set_level);
+ }
+ return ret;
+}
+
+static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
+{
+ struct sdhci_host *host = (struct sdhci_host *)data;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u8 irq_status = 0;
+ u8 irq_ack = 0;
+ int ret = 0;
+
+ irq_status = readb_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
+ pr_debug("%s: Received IRQ(%d), status=0x%x\n",
+ mmc_hostname(msm_host->mmc), irq, irq_status);
+
+ /* Clear the interrupt */
+ writeb_relaxed(irq_status, (msm_host->core_mem + CORE_PWRCTL_CLEAR));
+ /*
+ * SDHC has core_mem and hc_mem device memory and these memory
+ * addresses do not fall within 1KB region. Hence, any update to
+ * core_mem address space would require an mb() to ensure this gets
+ * completed before its next update to registers within hc_mem.
+ */
+ mb();
+
+ /* Handle BUS ON/OFF*/
+ if (irq_status & CORE_PWRCTL_BUS_ON) {
+ ret = sdhci_msm_setup_vreg(msm_host->pdata, true, false);
+ if (!ret)
+ ret = sdhci_msm_setup_pins(msm_host->pdata, true);
+ if (ret)
+ irq_ack |= CORE_PWRCTL_BUS_FAIL;
+ else
+ irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
+ }
+ if (irq_status & CORE_PWRCTL_BUS_OFF) {
+ ret = sdhci_msm_setup_vreg(msm_host->pdata, false, false);
+ if (!ret)
+ ret = sdhci_msm_setup_pins(msm_host->pdata, false);
+ if (ret)
+ irq_ack |= CORE_PWRCTL_BUS_FAIL;
+ else
+ irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
+ }
+ /* Handle IO LOW/HIGH */
+ if (irq_status & CORE_PWRCTL_IO_LOW) {
+ /* Switch voltage Low */
+ ret = sdhci_msm_set_vdd_io_vol(msm_host->pdata, VDD_IO_LOW, 0);
+ if (ret)
+ irq_ack |= CORE_PWRCTL_IO_FAIL;
+ else
+ irq_ack |= CORE_PWRCTL_IO_SUCCESS;
+ }
+ if (irq_status & CORE_PWRCTL_IO_HIGH) {
+ /* Switch voltage High */
+ ret = sdhci_msm_set_vdd_io_vol(msm_host->pdata, VDD_IO_HIGH, 0);
+ if (ret)
+ irq_ack |= CORE_PWRCTL_IO_FAIL;
+ else
+ irq_ack |= CORE_PWRCTL_IO_SUCCESS;
+ }
+
+ /* ACK status to the core */
+ writeb_relaxed(irq_ack, (msm_host->core_mem + CORE_PWRCTL_CTL));
+ /*
+ * SDHC has core_mem and hc_mem device memory and these memory
+ * addresses do not fall within 1KB region. Hence, any update to
+ * core_mem address space would require an mb() to ensure this gets
+ * completed before its next update to registers within hc_mem.
+ */
+ mb();
+
+ if (irq_status & CORE_PWRCTL_IO_HIGH)
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
+ ~CORE_IO_PAD_PWR_SWITCH),
+ host->ioaddr + CORE_VENDOR_SPEC);
+ if (irq_status & CORE_PWRCTL_IO_LOW)
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) |
+ CORE_IO_PAD_PWR_SWITCH),
+ host->ioaddr + CORE_VENDOR_SPEC);
+ mb();
+
+ pr_debug("%s: Handled IRQ(%d), ret=%d, ack=0x%x\n",
+ mmc_hostname(msm_host->mmc), irq, ret, irq_ack);
+ wake_up_interruptible(&msm_host->pwr_irq_wait);
+ return IRQ_HANDLED;
+}
+
+/* This function returns the max. current supported by VDD rail in mA */
+static unsigned int sdhci_msm_get_vreg_vdd_max_current(struct sdhci_msm_host
+ *host)
+{
+ struct sdhci_msm_slot_reg_data *curr_slot = host->pdata->vreg_data;
+ if (!curr_slot)
+ return 0;
+ if (curr_slot->vdd_data)
+ return curr_slot->vdd_data->hpm_uA / 1000;
+ else
+ return 0;
+}
+
+static void sdhci_msm_check_power_status(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret = 0;
+
+ pr_debug("%s: %s: power status before waiting 0x%x\n",
+ mmc_hostname(host->mmc), __func__,
+ readb_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL));
+
+ ret = wait_event_interruptible(msm_host->pwr_irq_wait,
+ (readb_relaxed(msm_host->core_mem +
+ CORE_PWRCTL_CTL)) != 0x0);
+ if (ret)
+ pr_warning("%s: %s: returned due to error %d\n",
+ mmc_hostname(host->mmc), __func__, ret);
+ pr_debug("%s: %s: ret %d power status after handling power IRQ 0x%x\n",
+ mmc_hostname(host->mmc), __func__, ret,
+ readb_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL));
+}
+
+static void sdhci_msm_toggle_cdr(struct sdhci_host *host, bool enable)
+{
+ if (enable)
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ CORE_DLL_CONFIG) | CORE_CDR_EN),
+ host->ioaddr + CORE_DLL_CONFIG);
+ else
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ CORE_DLL_CONFIG) & ~CORE_CDR_EN),
+ host->ioaddr + CORE_DLL_CONFIG);
+}
+
+static struct sdhci_ops sdhci_msm_ops = {
+ .check_power_status = sdhci_msm_check_power_status,
+ .execute_tuning = sdhci_msm_execute_tuning,
+ .toggle_cdr = sdhci_msm_toggle_cdr,
+};
+
+static int __devinit sdhci_msm_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_msm_host *msm_host;
+ struct resource *core_memres = NULL;
+ int ret = 0, pwr_irq = 0, dead = 0;
+ u32 vdd_max_current;
+ u32 host_version;
+
+ pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
+ msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
+ GFP_KERNEL);
+ if (!msm_host) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ init_waitqueue_head(&msm_host->pwr_irq_wait);
+
+ msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
+ host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata);
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ goto out;
+ }
+
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->priv = msm_host;
+ msm_host->mmc = host->mmc;
+
+ /* Extract platform data */
+ if (pdev->dev.of_node) {
+ msm_host->pdata = sdhci_msm_populate_pdata(&pdev->dev);
+ if (!msm_host->pdata) {
+ dev_err(&pdev->dev, "DT parsing error\n");
+ goto pltfm_free;
+ }
+ } else {
+ dev_err(&pdev->dev, "No device tree node\n");
+ goto pltfm_free;
+ }
+
+ /* Setup Clocks */
+
+ /* Setup SDCC bus voter clock. */
+ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ if (!IS_ERR_OR_NULL(msm_host->bus_clk)) {
+ /* Vote for max. clk rate for max. performance */
+ ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
+ if (ret)
+ goto pltfm_free;
+ ret = clk_prepare_enable(msm_host->bus_clk);
+ if (ret)
+ goto pltfm_free;
+ }
+
+ /* Setup main peripheral bus clock */
+ msm_host->pclk = devm_clk_get(&pdev->dev, "iface_clk");
+ if (!IS_ERR(msm_host->pclk)) {
+ ret = clk_prepare_enable(msm_host->pclk);
+ if (ret)
+ goto bus_clk_disable;
+ }
+
+ /* Setup SDC MMC clock */
+ msm_host->clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(msm_host->clk)) {
+ ret = PTR_ERR(msm_host->clk);
+ goto pclk_disable;
+ }
+
+ ret = clk_prepare_enable(msm_host->clk);
+ if (ret)
+ goto pclk_disable;
+
+ /* Setup regulators */
+ ret = sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, true);
+ if (ret) {
+ dev_err(&pdev->dev, "Regulator setup failed (%d)\n", ret);
+ goto clk_disable;
+ }
+
+ /* Reset the core and Enable SDHC mode */
+ core_memres = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "core_mem");
+ msm_host->core_mem = devm_ioremap(&pdev->dev, core_memres->start,
+ resource_size(core_memres));
+
+ if (!msm_host->core_mem) {
+ dev_err(&pdev->dev, "Failed to remap registers\n");
+ ret = -ENOMEM;
+ goto vreg_deinit;
+ }
+
+ /* Set SW_RST bit in POWER register (Offset 0x0) */
+ writel_relaxed(CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+ /* Set HC_MODE_EN bit in HC_MODE register */
+ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
+
+ /*
+ * Following are the deviations from SDHC spec v3.0 -
+ * 1. Card detection is handled using separate GPIO.
+ * 2. Bus power control is handled by interacting with PMIC.
+ */
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
+ host->quirks2 |= SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING;
+
+ host_version = readl_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
+ dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
+ host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
+ SDHCI_VENDOR_VER_SHIFT));
+ if (((host_version & SDHCI_VENDOR_VER_MASK) >>
+ SDHCI_VENDOR_VER_SHIFT) == SDHCI_VER_100) {
+ /*
+ * Add 40us delay in interrupt handler when
+ * operating at initialization frequency(400KHz).
+ */
+ host->quirks2 |= SDHCI_QUIRK2_SLOW_INT_CLR;
+ /*
+ * Set Software Reset for DAT line in Software
+ * Reset Register (Bit 2).
+ */
+ host->quirks2 |= SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT;
+ }
+
+ /* Setup PWRCTL irq */
+ pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
+ if (pwr_irq < 0) {
+ dev_err(&pdev->dev, "Failed to get pwr_irq by name (%d)\n",
+ pwr_irq);
+ goto vreg_deinit;
+ }
+ ret = devm_request_threaded_irq(&pdev->dev, pwr_irq, NULL,
+ sdhci_msm_pwr_irq, IRQF_ONESHOT,
+ dev_name(&pdev->dev), host);
+ if (ret) {
+ dev_err(&pdev->dev, "Request threaded irq(%d) failed (%d)\n",
+ pwr_irq, ret);
+ goto vreg_deinit;
+ }
+
+ /* Enable pwr irq interrupts */
+ writel_relaxed(INT_MASK, (msm_host->core_mem + CORE_PWRCTL_MASK));
+
+ /* Set host capabilities */
+ msm_host->mmc->caps |= msm_host->pdata->mmc_bus_width;
+ msm_host->mmc->caps |= msm_host->pdata->caps;
+
+ vdd_max_current = sdhci_msm_get_vreg_vdd_max_current(msm_host);
+ if (vdd_max_current >= 800)
+ msm_host->mmc->caps |= MMC_CAP_MAX_CURRENT_800;
+ else if (vdd_max_current >= 600)
+ msm_host->mmc->caps |= MMC_CAP_MAX_CURRENT_600;
+ else if (vdd_max_current >= 400)
+ msm_host->mmc->caps |= MMC_CAP_MAX_CURRENT_400;
+ else
+ msm_host->mmc->caps |= MMC_CAP_MAX_CURRENT_200;
+
+ if (vdd_max_current > 150)
+ msm_host->mmc->caps |= MMC_CAP_SET_XPC_180 |
+ MMC_CAP_SET_XPC_300|
+ MMC_CAP_SET_XPC_330;
+
+ msm_host->mmc->caps |= MMC_CAP_HW_RESET;
+ msm_host->mmc->caps2 |= msm_host->pdata->caps2;
+ msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR;
+ msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
+ msm_host->mmc->caps2 |= (MMC_CAP2_BOOTPART_NOACC |
+ MMC_CAP2_DETECT_ON_ERR);
+ msm_host->mmc->caps2 |= MMC_CAP2_SANITIZE;
+ msm_host->mmc->caps2 |= MMC_CAP2_CACHE_CTRL;
+ msm_host->mmc->caps2 |= MMC_CAP2_INIT_BKOPS;
+ msm_host->mmc->caps2 |= MMC_CAP2_POWEROFF_NOTIFY;
+
+ if (msm_host->pdata->nonremovable)
+ msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+
+ ret = sdhci_add_host(host);
+ if (ret) {
+ dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
+ goto vreg_deinit;
+ }
+
+ /* Set core clk rate, optionally override from dts */
+ if (msm_host->pdata->max_clk)
+ host->max_clk = msm_host->pdata->max_clk;
+ ret = clk_set_rate(msm_host->clk, host->max_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret);
+ goto remove_host;
+ }
+
+ /* Successful initialization */
+ goto out;
+
+remove_host:
+ dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+ sdhci_remove_host(host, dead);
+vreg_deinit:
+ sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
+clk_disable:
+ if (!IS_ERR(msm_host->clk))
+ clk_disable_unprepare(msm_host->clk);
+pclk_disable:
+ if (!IS_ERR(msm_host->pclk))
+ clk_disable_unprepare(msm_host->pclk);
+bus_clk_disable:
+ if (!IS_ERR_OR_NULL(msm_host->bus_clk))
+ clk_disable_unprepare(msm_host->bus_clk);
+pltfm_free:
+ sdhci_pltfm_free(pdev);
+out:
+ pr_debug("%s: Exit %s\n", dev_name(&pdev->dev), __func__);
+ return ret;
+}
+
+static int __devexit sdhci_msm_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_pltfm_data *pdata = msm_host->pdata;
+ int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) ==
+ 0xffffffff);
+
+ pr_debug("%s: %s\n", dev_name(&pdev->dev), __func__);
+ sdhci_remove_host(host, dead);
+ sdhci_pltfm_free(pdev);
+ sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
+ if (!IS_ERR(msm_host->clk))
+ clk_disable_unprepare(msm_host->clk);
+ if (!IS_ERR(msm_host->pclk))
+ clk_disable_unprepare(msm_host->pclk);
+ if (!IS_ERR_OR_NULL(msm_host->bus_clk))
+ clk_disable_unprepare(msm_host->bus_clk);
+ if (pdata->pin_data)
+ sdhci_msm_setup_pins(pdata, false);
+ return 0;
+}
+
+static const struct of_device_id sdhci_msm_dt_match[] = {
+ {.compatible = "qcom,sdhci-msm"},
+};
+MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
+
+static struct platform_driver sdhci_msm_driver = {
+ .probe = sdhci_msm_probe,
+ .remove = __devexit_p(sdhci_msm_remove),
+ .driver = {
+ .name = "sdhci_msm",
+ .owner = THIS_MODULE,
+ .of_match_table = sdhci_msm_dt_match,
+ },
+};
+
+module_platform_driver(sdhci_msm_driver);
+
+MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 8c2bea09..b9bd3a1 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2049,6 +2049,9 @@
controllers do not like that. */
sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA);
+ } else {
+ if (host->quirks2 & SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT)
+ sdhci_reset(host, SDHCI_RESET_DATA);
}
host->mrq = NULL;
@@ -2135,6 +2138,16 @@
SDHCI_INT_INDEX))
host->cmd->error = -EILSEQ;
+ if (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING) {
+ if ((host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
+ (host->cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
+ if (intmask & SDHCI_INT_CRC) {
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ host->cmd->error = 0;
+ }
+ }
+ }
+
if (host->cmd->error) {
tasklet_schedule(&host->finish_tasklet);
return;
@@ -2162,6 +2175,16 @@
* fall through and take the SDHCI_INT_RESPONSE */
}
+ if (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING) {
+ if ((host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
+ (host->cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
+ if (intmask & SDHCI_INT_CRC) {
+ sdhci_finish_command(host);
+ return;
+ }
+ }
+ }
+
if (intmask & SDHCI_INT_RESPONSE)
sdhci_finish_command(host);
}
@@ -2349,12 +2372,18 @@
if (intmask & SDHCI_INT_CMD_MASK) {
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
SDHCI_INT_STATUS);
+ if ((host->quirks2 & SDHCI_QUIRK2_SLOW_INT_CLR) &&
+ (host->clock <= 400000))
+ udelay(40);
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
}
if (intmask & SDHCI_INT_DATA_MASK) {
sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
SDHCI_INT_STATUS);
+ if ((host->quirks2 & SDHCI_QUIRK2_SLOW_INT_CLR) &&
+ (host->clock <= 400000))
+ udelay(40);
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
}
diff --git a/drivers/net/ethernet/msm/Kconfig b/drivers/net/ethernet/msm/Kconfig
index e15f4a9..4e95614 100644
--- a/drivers/net/ethernet/msm/Kconfig
+++ b/drivers/net/ethernet/msm/Kconfig
@@ -42,6 +42,16 @@
help
Debug stats on wakeup counts.
+config MSM_RMNET_WWAN
+ tristate "MSM RMNET WWAN Network Device"
+ depends on IPA
+ default n
+ help
+ WWAN Network Driver
+ Provides an API to embedded
+ applications to send and receive
+ the data to/from A2
+
config QFEC
tristate "QFEC ethernet driver"
select MII
diff --git a/drivers/net/ethernet/msm/Makefile b/drivers/net/ethernet/msm/Makefile
index e152ec7..0afa00f 100644
--- a/drivers/net/ethernet/msm/Makefile
+++ b/drivers/net/ethernet/msm/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_MSM_RMNET) += msm_rmnet.o
+obj-$(CONFIG_MSM_RMNET_WWAN) += msm_rmnet_wwan.o
obj-$(CONFIG_MSM_RMNET_SDIO) += msm_rmnet_sdio.o
obj-$(CONFIG_MSM_RMNET_BAM) += msm_rmnet_bam.o
obj-$(CONFIG_MSM_RMNET_SMUX) += msm_rmnet_smux.o
diff --git a/drivers/net/ethernet/msm/msm_rmnet_wwan.c b/drivers/net/ethernet/msm/msm_rmnet_wwan.c
new file mode 100644
index 0000000..fe1ac46
--- /dev/null
+++ b/drivers/net/ethernet/msm/msm_rmnet_wwan.c
@@ -0,0 +1,736 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/*
+ * WWAN Network Interface.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wakelock.h>
+#include <linux/msm_rmnet.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <net/pkt_sched.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <mach/ipa.h>
+
+#define WWAN_DEV_NAME "rmnet%d"
+#define WWAN_METADATA_MASK 0x00FF0000
+#define IPA_RM_INACTIVITY_TIMER 1000
+#define WWAN_DEVICE_COUNT (8)
+#define WWAN_DATA_LEN 2000
+#define HEADROOM_FOR_A2_MUX 8 /* for mux header */
+#define TAILROOM 8 /* for padding by mux layer */
+
+enum wwan_device_status {
+ WWAN_DEVICE_INACTIVE = 0,
+ WWAN_DEVICE_ACTIVE = 1
+};
+static enum ipa_rm_resource_name
+ ipa_rm_resource_by_ch_id[WWAN_DEVICE_COUNT] = {
+ IPA_RM_RESOURCE_WWAN_0_PROD,
+ IPA_RM_RESOURCE_WWAN_1_PROD,
+ IPA_RM_RESOURCE_WWAN_2_PROD,
+ IPA_RM_RESOURCE_WWAN_3_PROD,
+ IPA_RM_RESOURCE_WWAN_4_PROD,
+ IPA_RM_RESOURCE_WWAN_5_PROD,
+ IPA_RM_RESOURCE_WWAN_6_PROD,
+ IPA_RM_RESOURCE_WWAN_7_PROD
+};
+static enum a2_mux_logical_channel_id
+ a2_mux_lcid_by_ch_id[WWAN_DEVICE_COUNT] = {
+ A2_MUX_WWAN_0,
+ A2_MUX_WWAN_1,
+ A2_MUX_WWAN_2,
+ A2_MUX_WWAN_3,
+ A2_MUX_WWAN_4,
+ A2_MUX_WWAN_5,
+ A2_MUX_WWAN_6,
+ A2_MUX_WWAN_7
+};
+
+/**
+ * struct wwan_private - WWAN private data
+ * @stats: iface statistics
+ * @ch_id: channel id
+ * @lock: spinlock for mutual exclusion
+ * @device_status: holds device status
+ *
+ * WWAN private - holds all relevant info about WWAN driver
+ */
+struct wwan_private {
+ struct net_device_stats stats;
+ uint32_t ch_id;
+ spinlock_t lock;
+ struct completion resource_granted_completion;
+ enum wwan_device_status device_status;
+};
+
+static struct net_device *netdevs[WWAN_DEVICE_COUNT];
+
+static __be16 wwan_ip_type_trans(struct sk_buff *skb)
+{
+ __be16 protocol = 0;
+ /* Determine L3 protocol */
+ switch (skb->data[0] & 0xf0) {
+ case 0x40:
+ protocol = htons(ETH_P_IP);
+ break;
+ case 0x60:
+ protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ pr_err("[%s] %s() L3 protocol decode error: 0x%02x",
+ skb->dev->name, __func__, skb->data[0] & 0xf0);
+ /* skb will be dropped in upper layer for unknown protocol */
+ break;
+ }
+ return protocol;
+}
+
+/**
+ * a2_mux_recv_notify() - Deliver an RX packet to network stack
+ *
+ * @skb: skb to be delivered
+ * @dev: network device
+ *
+ * Return codes:
+ * None
+ */
+static void a2_mux_recv_notify(void *dev, struct sk_buff *skb)
+{
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+
+ skb->dev = dev;
+ skb->protocol = wwan_ip_type_trans(skb);
+ wwan_ptr->stats.rx_packets++;
+ wwan_ptr->stats.rx_bytes += skb->len;
+ pr_debug("[%s] Rx packet #%lu len=%d\n",
+ skb->dev->name,
+ wwan_ptr->stats.rx_packets, skb->len);
+ netif_rx(skb);
+}
+
+/**
+ * wwan_send_packet() - Deliver a TX packet to A2 MUX driver.
+ *
+ * @skb: skb to be delivered
+ * @dev: network device
+ *
+ * Return codes:
+ * 0: success
+ * -EAGAIN: A2 MUX is not ready to send the skb. try later
+ * -EFAULT: A2 MUX rejected the skb
+ * -EPREM: Unknown error
+ */
+static int wwan_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+ int ret;
+
+ dev->trans_start = jiffies;
+ ret = a2_mux_write(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id], skb);
+ if (ret != 0 && ret != -EAGAIN && ret != -EFAULT) {
+ pr_err("[%s] %s: write returned error %d",
+ dev->name, __func__, ret);
+ return -EPERM;
+ }
+ return ret;
+}
+
+/**
+ * a2_mux_write_done() - Update device statistics and start
+ * network stack queue is was stop and A2 MUX queue is below low
+ * watermark.
+ *
+ * @dev: network device
+ * @skb: skb to be delivered
+ *
+ * Return codes:
+ * None
+ */
+static void a2_mux_write_done(void *dev, struct sk_buff *skb)
+{
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+ unsigned long flags;
+
+ pr_debug("%s: write complete\n", __func__);
+ wwan_ptr->stats.tx_packets++;
+ wwan_ptr->stats.tx_bytes += skb->len;
+ pr_debug("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+ ((struct net_device *)(dev))->name, wwan_ptr->stats.tx_packets,
+ skb->len, skb->mark);
+ dev_kfree_skb_any(skb);
+ spin_lock_irqsave(&wwan_ptr->lock, flags);
+ if (netif_queue_stopped(dev) &&
+ a2_mux_is_ch_low(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id])) {
+ pr_debug("%s: Low WM hit, waking queue=%p\n",
+ __func__, skb);
+ netif_wake_queue(dev);
+ }
+ spin_unlock_irqrestore(&wwan_ptr->lock, flags);
+}
+
+/**
+ * a2_mux_notify() - Callback function for A2 MUX events Handles
+ * A2_MUX_RECEIVE and A2_MUX_WRITE_DONE events.
+ *
+ * @dev: network device
+ * @event: A2 MUX event
+ * @data: Additional data provided by A2 MUX
+ *
+ * Return codes:
+ * None
+ */
+static void a2_mux_notify(void *dev, enum a2_mux_event_type event,
+ unsigned long data)
+{
+ struct sk_buff *skb = (struct sk_buff *)data;
+
+ switch (event) {
+ case A2_MUX_RECEIVE:
+ if (!skb) {
+ pr_err("[%s] %s: No skb received",
+ ((struct net_device *)dev)->name, __func__);
+ return;
+ }
+ a2_mux_recv_notify(dev, skb);
+ break;
+ case A2_MUX_WRITE_DONE:
+ a2_mux_write_done(dev, skb);
+ break;
+ default:
+ pr_err("%s: unknown event %d\n", __func__, event);
+ break;
+ }
+}
+
+/**
+ * ipa_rm_resource_granted() - Called upon
+ * IPA_RM_RESOURCE_GRANTED event. Wakes up queue is was stopped.
+ *
+ * @work: work object supplied ny workqueue
+ *
+ * Return codes:
+ * None
+ */
+static void ipa_rm_resource_granted(void *dev)
+{
+ netif_wake_queue(dev);
+}
+/**
+ * ipa_rm_notify() - Callback function for RM events. Handles
+ * IPA_RM_RESOURCE_GRANTED and IPA_RM_RESOURCE_RELEASED events.
+ * IPA_RM_RESOURCE_GRANTED is handled in the context of shared
+ * workqueue.
+ *
+ * @dev: network device
+ * @event: IPA RM event
+ * @data: Additional data provided by IPA RM
+ *
+ * Return codes:
+ * None
+ */
+static void ipa_rm_notify(void *dev, enum ipa_rm_event event,
+ unsigned long data)
+{
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+
+ pr_debug("%s: event %d\n", __func__, event);
+ switch (event) {
+ case IPA_RM_RESOURCE_GRANTED:
+ if (wwan_ptr->device_status == WWAN_DEVICE_INACTIVE) {
+ complete_all(&wwan_ptr->resource_granted_completion);
+ break;
+ }
+ ipa_rm_resource_granted(dev);
+ break;
+ case IPA_RM_RESOURCE_RELEASED:
+ break;
+ default:
+ pr_err("%s: unknown event %d\n", __func__, event);
+ break;
+ }
+}
+
+static int wwan_register_to_ipa(struct net_device *dev)
+{
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+ struct ipa_tx_intf tx_properties = {0};
+ struct ipa_ioc_tx_intf_prop tx_ioc_properties[2] = { {0}, {0} };
+ struct ipa_ioc_tx_intf_prop *tx_ipv4_property;
+ struct ipa_ioc_tx_intf_prop *tx_ipv6_property;
+ struct ipa_rx_intf rx_properties = {0};
+ struct ipa_ioc_rx_intf_prop rx_ioc_properties[2] = { {0}, {0} };
+ struct ipa_ioc_rx_intf_prop *rx_ipv4_property;
+ struct ipa_ioc_rx_intf_prop *rx_ipv6_property;
+ int ret = 0;
+
+ pr_debug("[%s] %s:\n", dev->name, __func__);
+ tx_properties.prop = tx_ioc_properties;
+ tx_ipv4_property = &tx_properties.prop[0];
+ tx_ipv4_property->ip = IPA_IP_v4;
+ tx_ipv4_property->dst_pipe = IPA_CLIENT_A2_EMBEDDED_CONS;
+ snprintf(tx_ipv4_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d",
+ A2_MUX_HDR_NAME_V4_PREF,
+ a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]);
+ tx_ipv6_property = &tx_properties.prop[1];
+ tx_ipv6_property->ip = IPA_IP_v6;
+ tx_ipv6_property->dst_pipe = IPA_CLIENT_A2_EMBEDDED_CONS;
+ snprintf(tx_ipv6_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d",
+ A2_MUX_HDR_NAME_V6_PREF,
+ a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]);
+ tx_properties.num_props = 2;
+ rx_properties.prop = rx_ioc_properties;
+ rx_ipv4_property = &rx_properties.prop[0];
+ rx_ipv4_property->ip = IPA_IP_v4;
+ rx_ipv4_property->attrib.attrib_mask |= IPA_FLT_META_DATA;
+ rx_ipv4_property->attrib.meta_data = wwan_ptr->ch_id;
+ rx_ipv4_property->attrib.meta_data_mask = WWAN_METADATA_MASK;
+ rx_ipv4_property->src_pipe = IPA_CLIENT_A2_EMBEDDED_PROD;
+ rx_ipv6_property = &rx_properties.prop[1];
+ rx_ipv6_property->ip = IPA_IP_v6;
+ rx_ipv6_property->attrib.attrib_mask |= IPA_FLT_META_DATA;
+ rx_ipv6_property->attrib.meta_data = wwan_ptr->ch_id;
+ rx_ipv6_property->attrib.meta_data_mask = WWAN_METADATA_MASK;
+ rx_ipv6_property->src_pipe = IPA_CLIENT_A2_EMBEDDED_PROD;
+ rx_properties.num_props = 2;
+ ret = ipa_register_intf(dev->name, &tx_properties, &rx_properties);
+ if (ret) {
+ pr_err("[%s] %s: ipa_register_intf failed %d\n", dev->name,
+ __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int __wwan_open(struct net_device *dev)
+{
+ int r;
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+
+ pr_debug("[%s] __wwan_open()\n", dev->name);
+ if (wwan_ptr->device_status != WWAN_DEVICE_ACTIVE) {
+ INIT_COMPLETION(wwan_ptr->resource_granted_completion);
+ r = ipa_rm_inactivity_timer_request_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
+ if (r < 0 && r != -EINPROGRESS) {
+ pr_err("%s: ipa rm timer request resource failed %d\n",
+ __func__, r);
+ return -ENODEV;
+ }
+ if (r == -EINPROGRESS) {
+ wait_for_completion(
+ &wwan_ptr->resource_granted_completion);
+ }
+ r = a2_mux_open_channel(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id],
+ dev, a2_mux_notify);
+ if (r < 0) {
+ pr_err("%s: ch=%d failed with rc %d\n",
+ __func__, wwan_ptr->ch_id, r);
+ ipa_rm_inactivity_timer_release_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
+ return -ENODEV;
+ }
+ ipa_rm_inactivity_timer_release_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
+ r = wwan_register_to_ipa(dev);
+ if (r < 0) {
+ pr_err("%s: ch=%d failed to register to IPA rc %d\n",
+ __func__, wwan_ptr->ch_id, r);
+ return -ENODEV;
+ }
+ }
+ wwan_ptr->device_status = WWAN_DEVICE_ACTIVE;
+ return 0;
+}
+
+/**
+ * wwan_open() - Opens the wwan network interface. Opens logical
+ * channel on A2 MUX driver and starts the network stack queue
+ *
+ * @dev: network device
+ *
+ * Return codes:
+ * 0: success
+ * -ENODEV: Error while opening logical channel on A2 MUX driver
+ */
+static int wwan_open(struct net_device *dev)
+{
+ int rc = 0;
+
+ pr_debug("[%s] wwan_open()\n", dev->name);
+ rc = __wwan_open(dev);
+ if (rc == 0)
+ netif_start_queue(dev);
+ return rc;
+}
+
+
+static int __wwan_close(struct net_device *dev)
+{
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+ int rc = 0;
+
+ if (wwan_ptr->device_status == WWAN_DEVICE_ACTIVE) {
+ wwan_ptr->device_status = WWAN_DEVICE_INACTIVE;
+ /* do not close wwan port once up, this causes
+ remote side to hang if tried to open again */
+ INIT_COMPLETION(wwan_ptr->resource_granted_completion);
+ rc = ipa_rm_inactivity_timer_request_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
+ if (rc < 0 && rc != -EINPROGRESS) {
+ pr_err("%s: ipa rm timer request resource failed %d\n",
+ __func__, rc);
+ return -ENODEV;
+ }
+ if (rc == -EINPROGRESS) {
+ wait_for_completion(
+ &wwan_ptr->resource_granted_completion);
+ }
+ rc = a2_mux_close_channel(
+ a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]);
+ if (rc) {
+ pr_err("[%s] %s: a2_mux_close_channel failed %d\n",
+ dev->name, __func__, rc);
+ ipa_rm_inactivity_timer_release_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
+ return rc;
+ }
+ ipa_rm_inactivity_timer_release_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
+ rc = ipa_deregister_intf(dev->name);
+ if (rc) {
+ pr_err("[%s] %s: ipa_deregister_intf failed %d\n",
+ dev->name, __func__, rc);
+ return rc;
+ }
+ return rc;
+ } else
+ return -EBADF;
+}
+
+/**
+ * wwan_stop() - Stops the wwan network interface. Closes
+ * logical channel on A2 MUX driver and stops the network stack
+ * queue
+ *
+ * @dev: network device
+ *
+ * Return codes:
+ * 0: success
+ * -ENODEV: Error while opening logical channel on A2 MUX driver
+ */
+static int wwan_stop(struct net_device *dev)
+{
+ pr_debug("[%s] wwan_stop()\n", dev->name);
+ __wwan_close(dev);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static int wwan_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (0 > new_mtu || WWAN_DATA_LEN < new_mtu)
+ return -EINVAL;
+ pr_debug("[%s] MTU change: old=%d new=%d\n",
+ dev->name, dev->mtu, new_mtu);
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+/**
+ * wwan_xmit() - Transmits an skb. In charge of asking IPA
+ * RM needed resources. In case that IPA RM is not ready, then
+ * the skb is saved for tranmitting as soon as IPA RM resources
+ * are granted.
+ *
+ * @skb: skb to be transmitted
+ * @dev: network device
+ *
+ * Return codes:
+ * 0: success
+ * NETDEV_TX_BUSY: Error while transmitting the skb. Try again
+ * later
+ * -EFAULT: Error while transmitting the skb
+ */
+static int wwan_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+ unsigned long flags;
+ int ret = 0;
+
+ if (netif_queue_stopped(dev)) {
+ pr_err("[%s]fatal: wwan_xmit called when netif_queue stopped\n",
+ dev->name);
+ return 0;
+ }
+ ret = ipa_rm_inactivity_timer_request_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
+ if (ret == -EINPROGRESS) {
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+ }
+ if (ret) {
+ pr_err("[%s] fatal: ipa rm timer request resource failed %d\n",
+ dev->name, ret);
+ return -EFAULT;
+ }
+ ret = wwan_send_packet(skb, dev);
+ if (ret == -EPERM) {
+ ret = NETDEV_TX_BUSY;
+ goto exit;
+ }
+ /*
+ * detected SSR a bit early. shut some things down now, and leave
+ * the rest to the main ssr handling code when that happens later
+ */
+ if (ret == -EFAULT) {
+ netif_carrier_off(dev);
+ dev_kfree_skb_any(skb);
+ ret = 0;
+ goto exit;
+ }
+ if (ret == -EAGAIN) {
+ /*
+ * This should not happen
+ * EAGAIN means we attempted to overflow the high watermark
+ * Clearly the queue is not stopped like it should be, so
+ * stop it and return BUSY to the TCP/IP framework. It will
+ * retry this packet with the queue is restarted which happens
+ * in the write_done callback when the low watermark is hit.
+ */
+ netif_stop_queue(dev);
+ ret = NETDEV_TX_BUSY;
+ goto exit;
+ }
+ spin_lock_irqsave(&wwan_ptr->lock, flags);
+ if (a2_mux_is_ch_full(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id])) {
+ netif_stop_queue(dev);
+ pr_debug("%s: High WM hit, stopping queue=%p\n",
+ __func__, skb);
+ }
+ spin_unlock_irqrestore(&wwan_ptr->lock, flags);
+exit:
+ ipa_rm_inactivity_timer_release_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
+ return ret;
+}
+
+static struct net_device_stats *wwan_get_stats(struct net_device *dev)
+{
+ struct wwan_private *wwan_ptr = netdev_priv(dev);
+ return &wwan_ptr->stats;
+}
+
+static void wwan_tx_timeout(struct net_device *dev)
+{
+ pr_warning("[%s] wwan_tx_timeout()\n", dev->name);
+}
+
+/**
+ * wwan_ioctl() - I/O control for wwan network driver.
+ *
+ * @dev: network device
+ * @ifr: ignored
+ * @cmd: cmd to be excecuded. can be one of the following:
+ * WWAN_IOCTL_OPEN - Open the network interface
+ * WWAN_IOCTL_CLOSE - Close the network interface
+ *
+ * Return codes:
+ * 0: success
+ * NETDEV_TX_BUSY: Error while transmitting the skb. Try again
+ * later
+ * -EFAULT: Error while transmitting the skb
+ */
+static int wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ int rc = 0;
+
+ switch (cmd) {
+ case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
+ break;
+ case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
+ ifr->ifr_ifru.ifru_data = (void *) RMNET_MODE_LLP_IP;
+ break;
+ case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
+ break;
+ case RMNET_IOCTL_FLOW_ENABLE:
+ tc_qdisc_flow_control(dev, (u32)ifr->ifr_data, 1);
+ pr_debug("[%s] %s: enabled flow", dev->name, __func__);
+ break;
+ case RMNET_IOCTL_FLOW_DISABLE:
+ tc_qdisc_flow_control(dev, (u32)ifr->ifr_data, 0);
+ pr_debug("[%s] %s: disabled flow", dev->name, __func__);
+ break;
+ case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
+ /* QoS disabled */
+ ifr->ifr_ifru.ifru_data = (void *) 0;
+ break;
+ case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
+ ifr->ifr_ifru.ifru_data = (void *) RMNET_MODE_LLP_IP;
+ break;
+ case RMNET_IOCTL_OPEN: /* Open transport port */
+ rc = __wwan_open(dev);
+ pr_debug("[%s] wwan_ioctl(): open transport port\n",
+ dev->name);
+ break;
+ case RMNET_IOCTL_CLOSE: /* Close transport port */
+ rc = __wwan_close(dev);
+ pr_debug("[%s] wwan_ioctl(): close transport port\n",
+ dev->name);
+ break;
+ default:
+ pr_err("[%s] error: wwan_ioct called for unsupported cmd[%d]",
+ dev->name, cmd);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static const struct net_device_ops wwan_ops_ip = {
+ .ndo_open = wwan_open,
+ .ndo_stop = wwan_stop,
+ .ndo_start_xmit = wwan_xmit,
+ .ndo_get_stats = wwan_get_stats,
+ .ndo_tx_timeout = wwan_tx_timeout,
+ .ndo_do_ioctl = wwan_ioctl,
+ .ndo_change_mtu = wwan_change_mtu,
+ .ndo_set_mac_address = 0,
+ .ndo_validate_addr = 0,
+};
+
+/**
+ * wwan_setup() - Setups the wwan network driver.
+ *
+ * @dev: network device
+ *
+ * Return codes:
+ * None
+ */
+static void wwan_setup(struct net_device *dev)
+{
+ dev->netdev_ops = &wwan_ops_ip;
+ ether_setup(dev);
+ /* set this after calling ether_setup */
+ dev->header_ops = 0; /* No header */
+ dev->type = ARPHRD_RAWIP;
+ dev->hard_header_len = 0;
+ dev->mtu = WWAN_DATA_LEN;
+ dev->addr_len = 0;
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ dev->needed_headroom = HEADROOM_FOR_A2_MUX;
+ dev->needed_tailroom = TAILROOM;
+ dev->watchdog_timeo = 1000;
+}
+
+/**
+ * wwan_init() - Initialized the module and registers as a
+ * network interface to the network stack
+ *
+ * Return codes:
+ * 0: success
+ * -ENOMEM: No memory available
+ * -EFAULT: Internal error
+ */
+static int __init wwan_init(void)
+{
+ int ret;
+ struct net_device *dev;
+ struct wwan_private *wwan_ptr;
+ unsigned n;
+ struct ipa_rm_create_params ipa_rm_params;
+
+ pr_info("%s: WWAN devices[%d]\n", __func__, WWAN_DEVICE_COUNT);
+ for (n = 0; n < WWAN_DEVICE_COUNT; n++) {
+ dev = alloc_netdev(sizeof(struct wwan_private),
+ WWAN_DEV_NAME, wwan_setup);
+ if (!dev) {
+ pr_err("%s: no memory for netdev %d\n", __func__, n);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ netdevs[n] = dev;
+ wwan_ptr = netdev_priv(dev);
+ wwan_ptr->ch_id = n;
+ spin_lock_init(&wwan_ptr->lock);
+ init_completion(&wwan_ptr->resource_granted_completion);
+ memset(&ipa_rm_params, 0, sizeof(struct ipa_rm_create_params));
+ ipa_rm_params.name = ipa_rm_resource_by_ch_id[n];
+ ipa_rm_params.reg_params.user_data = dev;
+ ipa_rm_params.reg_params.notify_cb = ipa_rm_notify;
+ ret = ipa_rm_create_resource(&ipa_rm_params);
+ if (ret) {
+ pr_err("%s: unable to create resourse %d in IPA RM\n",
+ __func__, ipa_rm_resource_by_ch_id[n]);
+ goto fail;
+ }
+ ret = ipa_rm_inactivity_timer_init(ipa_rm_resource_by_ch_id[n],
+ IPA_RM_INACTIVITY_TIMER);
+ if (ret) {
+ pr_err("%s: ipa rm timer init failed %d on ins %d\n",
+ __func__, ret, n);
+ goto fail;
+ }
+ ret = ipa_rm_add_dependency(ipa_rm_resource_by_ch_id[n],
+ IPA_RM_RESOURCE_A2_CONS);
+ if (ret) {
+ pr_err("%s: unable to add dependency %d rc=%d\n",
+ __func__, n, ret);
+ goto fail;
+ }
+ ret = register_netdev(dev);
+ if (ret) {
+ pr_err("%s: unable to register netdev %d rc=%d\n",
+ __func__, n, ret);
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ for (n = 0; n < WWAN_DEVICE_COUNT; n++) {
+ if (!netdevs[n])
+ break;
+ unregister_netdev(netdevs[n]);
+ ipa_rm_inactivity_timer_destroy(ipa_rm_resource_by_ch_id[n]);
+ free_netdev(netdevs[n]);
+ netdevs[n] = NULL;
+ }
+ return ret;
+}
+late_initcall(wwan_init);
+
+void wwan_cleanup(void)
+{
+ unsigned n;
+
+ pr_info("%s: WWAN devices[%d]\n", __func__, WWAN_DEVICE_COUNT);
+ for (n = 0; n < WWAN_DEVICE_COUNT; n++) {
+ unregister_netdev(netdevs[n]);
+ ipa_rm_inactivity_timer_destroy(ipa_rm_resource_by_ch_id[n]);
+ free_netdev(netdevs[n]);
+ netdevs[n] = NULL;
+ }
+}
+
+MODULE_DESCRIPTION("WWAN Network Interface");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wireless/wcnss/wcnss_vreg.c b/drivers/net/wireless/wcnss/wcnss_vreg.c
index 6a315d2..0aa9677 100644
--- a/drivers/net/wireless/wcnss/wcnss_vreg.c
+++ b/drivers/net/wireless/wcnss/wcnss_vreg.c
@@ -156,8 +156,15 @@
goto fail;
}
- /* power on thru SSR should not set NV bit,
- * during SSR, NV bin is downloaded by WLAN driver
+ /* Enable IRIS XO */
+ rc = clk_prepare_enable(clk);
+ if (rc) {
+ pr_err("clk enable failed\n");
+ goto fail;
+ }
+ /* NV bit is set to indicate that platform driver is capable
+ * of doing NV download. SSR should not set NV bit; during
+ * SSR NV bin is downloaded by WLAN driver.
*/
if (!wcnss_cold_boot_done()) {
pr_debug("wcnss: Indicate NV bin download\n");
@@ -168,13 +175,6 @@
}
pmu_conf_reg = msm_wcnss_base + pmu_offset;
-
- /* Enable IRIS XO */
- rc = clk_prepare_enable(clk);
- if (rc) {
- pr_err("clk enable failed\n");
- goto fail;
- }
writel_relaxed(0, pmu_conf_reg);
reg = readl_relaxed(pmu_conf_reg);
reg |= WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP |
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index ed4e246..867aec1 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -58,16 +58,35 @@
#define MSM_RIVA_CCU_BASE 0x03200800
-#define CCU_INVALID_ADDR_OFFSET 0x100
-#define CCU_LAST_ADDR0_OFFSET 0x104
-#define CCU_LAST_ADDR1_OFFSET 0x108
-#define CCU_LAST_ADDR2_OFFSET 0x10c
+#define CCU_RIVA_INVALID_ADDR_OFFSET 0x100
+#define CCU_RIVA_LAST_ADDR0_OFFSET 0x104
+#define CCU_RIVA_LAST_ADDR1_OFFSET 0x108
+#define CCU_RIVA_LAST_ADDR2_OFFSET 0x10c
#define MSM_PRONTO_A2XB_BASE 0xfb100400
-#define A2XB_CFG_OFFSET 0x00
-#define A2XB_INT_SRC_OFFSET 0x0c
+#define A2XB_CFG_OFFSET 0x00
+#define A2XB_INT_SRC_OFFSET 0x0c
+#define A2XB_TSTBUS_CTRL_OFFSET 0x14
+#define A2XB_TSTBUS_OFFSET 0x18
#define A2XB_ERR_INFO_OFFSET 0x1c
+#define WCNSS_TSTBUS_CTRL_EN BIT(0)
+#define WCNSS_TSTBUS_CTRL_AXIM (0x02 << 1)
+#define WCNSS_TSTBUS_CTRL_CMDFIFO (0x03 << 1)
+#define WCNSS_TSTBUS_CTRL_WRFIFO (0x04 << 1)
+#define WCNSS_TSTBUS_CTRL_RDFIFO (0x05 << 1)
+#define WCNSS_TSTBUS_CTRL_CTRL (0x07 << 1)
+#define WCNSS_TSTBUS_CTRL_AXIM_CFG0 (0x00 << 6)
+#define WCNSS_TSTBUS_CTRL_AXIM_CFG1 (0x01 << 6)
+#define WCNSS_TSTBUS_CTRL_CTRL_CFG0 (0x00 << 12)
+#define WCNSS_TSTBUS_CTRL_CTRL_CFG1 (0x01 << 12)
+
+#define MSM_PRONTO_CCPU_BASE 0xfb205050
+#define CCU_PRONTO_INVALID_ADDR_OFFSET 0x08
+#define CCU_PRONTO_LAST_ADDR0_OFFSET 0x0c
+#define CCU_PRONTO_LAST_ADDR1_OFFSET 0x10
+#define CCU_PRONTO_LAST_ADDR2_OFFSET 0x14
+
#define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
#define WCNSS_MAX_FRAME_SIZE 500
#define WCNSS_VERSION_LEN 30
@@ -173,6 +192,7 @@
void __iomem *msm_wcnss_base;
void __iomem *riva_ccu_base;
void __iomem *pronto_a2xb_base;
+ void __iomem *pronto_ccpu_base;
} *penv = NULL;
static ssize_t wcnss_serial_number_show(struct device *dev,
@@ -253,19 +273,19 @@
void __iomem *ccu_reg;
u32 reg = 0;
- ccu_reg = penv->riva_ccu_base + CCU_INVALID_ADDR_OFFSET;
+ ccu_reg = penv->riva_ccu_base + CCU_RIVA_INVALID_ADDR_OFFSET;
reg = readl_relaxed(ccu_reg);
pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
- ccu_reg = penv->riva_ccu_base + CCU_LAST_ADDR0_OFFSET;
+ ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR0_OFFSET;
reg = readl_relaxed(ccu_reg);
pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
- ccu_reg = penv->riva_ccu_base + CCU_LAST_ADDR1_OFFSET;
+ ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR1_OFFSET;
reg = readl_relaxed(ccu_reg);
pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
- ccu_reg = penv->riva_ccu_base + CCU_LAST_ADDR2_OFFSET;
+ ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR2_OFFSET;
reg = readl_relaxed(ccu_reg);
pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
@@ -275,7 +295,7 @@
/* Log pronto debug registers before sending reset interrupt */
void wcnss_pronto_log_debug_regs(void)
{
- void __iomem *reg_addr;
+ void __iomem *reg_addr, *tst_addr, *tst_ctrl_addr;
u32 reg = 0;
reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
@@ -290,6 +310,84 @@
reg = readl_relaxed(reg_addr);
pr_info_ratelimited("%s: A2XB_ERR_INFO_OFFSET %08x\n", __func__, reg);
+ reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
+
+ reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
+
+ reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
+
+ reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
+
+ tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
+ tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
+
+ /* read data FIFO */
+ reg = 0;
+ reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO;
+ writel_relaxed(reg, tst_ctrl_addr);
+ reg = readl_relaxed(tst_addr);
+ pr_info_ratelimited("%s: Read data FIFO testbus %08x\n",
+ __func__, reg);
+
+ /* command FIFO */
+ reg = 0;
+ reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO;
+ writel_relaxed(reg, tst_ctrl_addr);
+ reg = readl_relaxed(tst_addr);
+ pr_info_ratelimited("%s: Command FIFO testbus %08x\n",
+ __func__, reg);
+
+ /* write data FIFO */
+ reg = 0;
+ reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO;
+ writel_relaxed(reg, tst_ctrl_addr);
+ reg = readl_relaxed(tst_addr);
+ pr_info_ratelimited("%s: Rrite data FIFO testbus %08x\n",
+ __func__, reg);
+
+ /* AXIM SEL CFG0 */
+ reg = 0;
+ reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
+ WCNSS_TSTBUS_CTRL_AXIM_CFG0;
+ writel_relaxed(reg, tst_ctrl_addr);
+ reg = readl_relaxed(tst_addr);
+ pr_info_ratelimited("%s: AXIM SEL CFG0 testbus %08x\n",
+ __func__, reg);
+
+ /* AXIM SEL CFG1 */
+ reg = 0;
+ reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
+ WCNSS_TSTBUS_CTRL_AXIM_CFG1;
+ writel_relaxed(reg, tst_ctrl_addr);
+ reg = readl_relaxed(tst_addr);
+ pr_info_ratelimited("%s: AXIM SEL CFG1 testbus %08x\n",
+ __func__, reg);
+
+ /* CTRL SEL CFG0 */
+ reg = 0;
+ reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
+ WCNSS_TSTBUS_CTRL_CTRL_CFG0;
+ writel_relaxed(reg, tst_ctrl_addr);
+ reg = readl_relaxed(tst_addr);
+ pr_info_ratelimited("%s: CTRL SEL CFG0 testbus %08x\n",
+ __func__, reg);
+
+ /* CTRL SEL CFG1 */
+ reg = 0;
+ reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
+ WCNSS_TSTBUS_CTRL_CTRL_CFG1;
+ writel_relaxed(reg, tst_ctrl_addr);
+ reg = readl_relaxed(tst_addr);
+ pr_info_ratelimited("%s: CTRL SEL CFG1 testbus %08x\n", __func__, reg);
+
}
EXPORT_SYMBOL(wcnss_pronto_log_debug_regs);
@@ -1071,11 +1169,19 @@
pr_err("%s: ioremap wcnss physical failed\n", __func__);
goto fail_ioremap;
}
+ penv->pronto_ccpu_base = ioremap(MSM_PRONTO_CCPU_BASE, SZ_512);
+ if (!penv->pronto_ccpu_base) {
+ ret = -ENOMEM;
+ pr_err("%s: ioremap wcnss physical failed\n", __func__);
+ goto fail_ioremap2;
+ }
}
penv->cold_boot_done = 1;
return 0;
+fail_ioremap2:
+ iounmap(penv->pronto_a2xb_base);
fail_ioremap:
iounmap(penv->msm_wcnss_base);
fail_wake:
diff --git a/drivers/platform/msm/ipa/Makefile b/drivers/platform/msm/ipa/Makefile
index a25c799..b7eca61 100644
--- a/drivers/platform/msm/ipa/Makefile
+++ b/drivers/platform/msm/ipa/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_IPA) += ipat.o
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
- ipa_utils.o ipa_nat.o rmnet_bridge.o a2_service.o ipa_bridge.o ipa_intf.o \
+ ipa_utils.o ipa_nat.o rmnet_bridge.o a2_service.o ipa_bridge.o ipa_intf.o teth_bridge.o \
ipa_rm.o ipa_rm_dependency_graph.o ipa_rm_peers_list.o ipa_rm_resource.o ipa_rm_inactivity_timer.o
diff --git a/drivers/platform/msm/ipa/a2_service.c b/drivers/platform/msm/ipa/a2_service.c
index 2c5245c..4b5f0a2 100644
--- a/drivers/platform/msm/ipa/a2_service.c
+++ b/drivers/platform/msm/ipa/a2_service.c
@@ -53,6 +53,8 @@
spinlock_t lock;
int num_tx_pkts;
int use_wm;
+ u32 v4_hdr_hdl;
+ u32 v6_hdr_hdl;
};
struct tx_pkt_info {
struct sk_buff *skb;
@@ -70,6 +72,7 @@
u8 ch_id;
u16 pkt_len;
};
+
struct a2_mux_context_type {
u32 tethered_prod;
u32 tethered_cons;
@@ -515,6 +518,9 @@
goto bridge_tethered_dl_failed;
}
memset(&connect_params, 0, sizeof(struct ipa_sys_connect_params));
+ connect_params.ipa_ep_cfg.hdr.hdr_len = sizeof(struct bam_mux_hdr);
+ connect_params.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
+ connect_params.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 6;
connect_params.client = IPA_CLIENT_A2_EMBEDDED_CONS;
connect_params.notify = ipa_embedded_notify;
connect_params.desc_fifo_sz = 0x800;
@@ -527,6 +533,9 @@
goto bridge_embedded_ul_failed;
}
memset(&connect_params, 0, sizeof(struct ipa_sys_connect_params));
+ connect_params.ipa_ep_cfg.hdr.hdr_len = sizeof(struct bam_mux_hdr);
+ connect_params.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
+ connect_params.ipa_ep_cfg.hdr.hdr_ofst_metadata = 4;
connect_params.client = IPA_CLIENT_A2_EMBEDDED_PROD;
connect_params.notify = ipa_embedded_notify;
connect_params.desc_fifo_sz = 0x800;
@@ -1006,6 +1015,176 @@
}
/**
+ * a2_mux_add_hdr() - called when MUX header should
+ * be added
+ * @lcid: logical channel ID
+ *
+ * Returns: 0 on success, negative on failure
+ */
+static int a2_mux_add_hdr(enum a2_mux_logical_channel_id lcid)
+{
+ struct ipa_ioc_add_hdr *hdrs;
+ struct ipa_hdr_add *ipv4_hdr;
+ struct ipa_hdr_add *ipv6_hdr;
+ struct bam_mux_hdr *dmux_hdr;
+ int rc;
+
+ IPADBG("%s: ch %d\n", __func__, lcid);
+
+ if (lcid < A2_MUX_WWAN_0 || lcid > A2_MUX_WWAN_7) {
+ IPAERR("%s: non valid lcid passed: %d\n", __func__, lcid);
+ return -EINVAL;
+ }
+
+
+ hdrs = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
+ 2 * sizeof(struct ipa_hdr_add), GFP_KERNEL);
+ if (!hdrs) {
+ IPAERR("%s: hdr allocation fail for ch %d\n", __func__, lcid);
+ return -ENOMEM;
+ }
+
+ ipv4_hdr = &hdrs->hdr[0];
+ ipv6_hdr = &hdrs->hdr[1];
+
+ dmux_hdr = (struct bam_mux_hdr *)ipv4_hdr->hdr;
+ snprintf(ipv4_hdr->name, IPA_RESOURCE_NAME_MAX, "%s%d",
+ A2_MUX_HDR_NAME_V4_PREF, lcid);
+ dmux_hdr->magic_num = BAM_MUX_HDR_MAGIC_NO;
+ dmux_hdr->cmd = BAM_MUX_HDR_CMD_DATA;
+ dmux_hdr->reserved = 0;
+ dmux_hdr->ch_id = lcid;
+
+ /* Packet lenght is added by IPA */
+ dmux_hdr->pkt_len = 0;
+ dmux_hdr->pad_len = 0;
+
+ dmux_hdr->magic_num = htons(dmux_hdr->magic_num);
+ IPADBG("converted to network order magic_num=%d\n",
+ dmux_hdr->magic_num);
+
+ ipv4_hdr->hdr_len = sizeof(struct bam_mux_hdr);
+ ipv4_hdr->is_partial = 0;
+
+ dmux_hdr = (struct bam_mux_hdr *)ipv6_hdr->hdr;
+ snprintf(ipv6_hdr->name, IPA_RESOURCE_NAME_MAX, "%s%d",
+ A2_MUX_HDR_NAME_V6_PREF, lcid);
+ dmux_hdr->magic_num = BAM_MUX_HDR_MAGIC_NO;
+ dmux_hdr->cmd = BAM_MUX_HDR_CMD_DATA;
+ dmux_hdr->reserved = 0;
+ dmux_hdr->ch_id = lcid;
+
+ /* Packet lenght is added by IPA */
+ dmux_hdr->pkt_len = 0;
+ dmux_hdr->pad_len = 0;
+
+ dmux_hdr->magic_num = htons(dmux_hdr->magic_num);
+ IPADBG("converted to network order magic_num=%d\n",
+ dmux_hdr->magic_num);
+
+ ipv6_hdr->hdr_len = sizeof(struct bam_mux_hdr);
+ ipv6_hdr->is_partial = 0;
+
+ hdrs->commit = 1;
+ hdrs->num_hdrs = 2;
+
+ rc = ipa_add_hdr(hdrs);
+ if (rc) {
+ IPAERR("Fail on Header-Insertion(%d)\n", rc);
+ goto bail;
+ }
+
+ if (ipv4_hdr->status) {
+ IPAERR("Fail on Header-Insertion ipv4(%d)\n",
+ ipv4_hdr->status);
+ rc = ipv4_hdr->status;
+ goto bail;
+ }
+
+ if (ipv6_hdr->status) {
+ IPAERR("%s: Fail on Header-Insertion ipv4(%d)\n", __func__,
+ ipv6_hdr->status);
+ rc = ipv6_hdr->status;
+ goto bail;
+ }
+
+ a2_mux_ctx->bam_ch[lcid].v4_hdr_hdl = ipv4_hdr->hdr_hdl;
+ a2_mux_ctx->bam_ch[lcid].v6_hdr_hdl = ipv6_hdr->hdr_hdl;
+
+ rc = 0;
+bail:
+ kfree(hdrs);
+ return rc;
+}
+
+/**
+ * a2_mux_del_hdr() - called when MUX header should
+ * be removed
+ * @lcid: logical channel ID
+ *
+ * Returns: 0 on success, negative on failure
+ */
+static int a2_mux_del_hdr(enum a2_mux_logical_channel_id lcid)
+{
+ struct ipa_ioc_del_hdr *hdrs;
+ struct ipa_hdr_del *ipv4_hdl;
+ struct ipa_hdr_del *ipv6_hdl;
+ int rc;
+
+ IPADBG("%s: ch %d\n", __func__, lcid);
+
+ if (lcid < A2_MUX_WWAN_0 || lcid > A2_MUX_WWAN_7) {
+ IPAERR("invalid lcid passed: %d\n", lcid);
+ return -EINVAL;
+ }
+
+
+ hdrs = kzalloc(sizeof(struct ipa_ioc_del_hdr) +
+ 2 * sizeof(struct ipa_hdr_del), GFP_KERNEL);
+ if (!hdrs) {
+ IPAERR("hdr alloc fail for ch %d\n", lcid);
+ return -ENOMEM;
+ }
+
+ ipv4_hdl = &hdrs->hdl[0];
+ ipv6_hdl = &hdrs->hdl[1];
+
+ ipv4_hdl->hdl = a2_mux_ctx->bam_ch[lcid].v4_hdr_hdl;
+ ipv6_hdl->hdl = a2_mux_ctx->bam_ch[lcid].v6_hdr_hdl;
+
+ hdrs->commit = 1;
+ hdrs->num_hdls = 2;
+
+ rc = ipa_del_hdr(hdrs);
+ if (rc) {
+ IPAERR("Fail on Del Header-Insertion(%d)\n", rc);
+ goto bail;
+ }
+
+ if (ipv4_hdl->status) {
+ IPAERR("Fail on Del Header-Insertion ipv4(%d)\n",
+ ipv4_hdl->status);
+ rc = ipv4_hdl->status;
+ goto bail;
+ }
+ a2_mux_ctx->bam_ch[lcid].v4_hdr_hdl = 0;
+
+ if (ipv6_hdl->status) {
+ IPAERR("Fail on Del Header-Insertion ipv4(%d)\n",
+ ipv6_hdl->status);
+ rc = ipv6_hdl->status;
+ goto bail;
+ }
+ a2_mux_ctx->bam_ch[lcid].v6_hdr_hdl = 0;
+
+ rc = 0;
+bail:
+ kfree(hdrs);
+ return rc;
+
+}
+
+/**
* a2_mux_open_channel() - opens logical channel
* to A2
* @lcid: logical channel ID
@@ -1090,6 +1269,12 @@
kfree(hdr);
return rc;
}
+ rc = a2_mux_add_hdr(lcid);
+ if (rc) {
+ IPAERR("a2_mux_add_hdr failed %d; ch: %d\n",
+ rc, lcid);
+ return rc;
+ }
}
open_done:
@@ -1154,6 +1339,13 @@
kfree(hdr);
return rc;
}
+
+ rc = a2_mux_del_hdr(lcid);
+ if (rc) {
+ IPAERR("a2_mux_del_hdr failed %d; ch: %d\n",
+ rc, lcid);
+ return rc;
+ }
}
IPADBG("%s: closed ch %d\n", __func__, lcid);
return 0;
@@ -1342,6 +1534,13 @@
}
if (smsm_get_state(SMSM_MODEM_STATE) & SMSM_A2_POWER_CONTROL)
bam_dmux_smsm_cb(NULL, 0, smsm_get_state(SMSM_MODEM_STATE));
+
+ /*
+ * Set remote channel open for tethered channel since there is
+ * no actual remote tethered channel
+ */
+ a2_mux_ctx->bam_ch[A2_MUX_TETHERED_0].status |= BAM_CH_REMOTE_OPEN;
+
rc = 0;
goto bail;
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index b07c653..edf3a60 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -806,7 +806,7 @@
/* check all the system pipes for tx comp and rx avail */
if (ipa_ctx->sys[IPA_A5_LAN_WAN_IN].ep->valid)
- cnt |= ipa_handle_rx_core(false);
+ cnt |= ipa_handle_rx_core(false, true);
for (i = 0; i < num_tx_pipes; i++)
if (ipa_ctx->sys[tx_pipes[i]].ep->valid)
@@ -1578,6 +1578,10 @@
IPADBG("polling_mode=%u delay_ms=%u\n", polling_mode, polling_delay_ms);
ipa_ctx->polling_mode = polling_mode;
+ if (ipa_ctx->polling_mode)
+ atomic_set(&ipa_ctx->curr_polling_state, 1);
+ else
+ atomic_set(&ipa_ctx->curr_polling_state, 0);
IPADBG("hdr_lcl=%u ip4_rt=%u ip6_rt=%u ip4_flt=%u ip6_flt=%u\n",
hdr_tbl_lcl, ip4_rt_tbl_lcl, ip6_rt_tbl_lcl, ip4_flt_tbl_lcl,
ip6_flt_tbl_lcl);
@@ -1896,10 +1900,6 @@
ipa_ctx->aggregation_byte_limit = 1;
ipa_ctx->aggregation_time_limit = 0;
- /* gate IPA clocks */
- if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_NORMAL)
- ipa_disable_clks();
-
/* Initialize IPA RM (resource manager) */
result = ipa_rm_initialize();
if (result) {
@@ -1910,6 +1910,18 @@
a2_mux_init();
+ /* Initialize the tethering bridge driver */
+ result = teth_bridge_driver_init();
+ if (result) {
+ IPAERR(":teth_bridge_driver_init() failed\n");
+ result = -ENODEV;
+ goto fail_cdev_add;
+ }
+
+ /* gate IPA clocks */
+ if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_NORMAL)
+ ipa_disable_clks();
+
IPADBG(":IPA driver init OK.\n");
return 0;
diff --git a/drivers/platform/msm/ipa/ipa_bridge.c b/drivers/platform/msm/ipa/ipa_bridge.c
index 56e9b0d..0227ee4 100644
--- a/drivers/platform/msm/ipa/ipa_bridge.c
+++ b/drivers/platform/msm/ipa/ipa_bridge.c
@@ -42,9 +42,6 @@
struct sps_connect connection;
struct sps_mem_buffer desc_mem_buf;
struct sps_register_event register_event;
- spinlock_t spinlock;
- u32 len;
- u32 free_len;
struct list_head free_desc_list;
};
@@ -162,12 +159,10 @@
goto fail_dma;
}
- info->len = ~0;
-
list_add_tail(&info->link, &sys_rx->head_desc_list);
ret = sps_transfer_one(sys_rx->pipe, info->dma_address,
IPA_RX_SKB_SIZE, info,
- SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT);
+ SPS_IOVEC_FLAG_INT);
if (ret) {
list_del(&info->link);
dma_unmap_single(NULL, info->dma_address, IPA_RX_SKB_SIZE,
@@ -176,7 +171,6 @@
type, dir);
goto fail_dma;
}
- sys_rx->len++;
return 0;
fail_dma:
@@ -206,9 +200,6 @@
link);
list_move_tail(&tx_pkt->link,
&sys_tx->free_desc_list);
- sys_tx->len--;
- sys_tx->free_len++;
- tx_pkt->len = ~0;
cnt++;
}
} while (all);
@@ -245,7 +236,6 @@
struct ipa_pkt_info,
link);
list_del(&rx_pkt->link);
- sys_rx->len--;
rx_pkt->len = iov.size;
retry_alloc_tx:
@@ -285,15 +275,12 @@
list_add_tail(&tmp_pkt->link,
&sys_tx->free_desc_list);
- sys_tx->free_len++;
- tmp_pkt->len = ~0;
}
tx_pkt = list_first_entry(&sys_tx->free_desc_list,
struct ipa_pkt_info,
link);
list_del(&tx_pkt->link);
- sys_tx->free_len--;
retry_add_rx:
list_add_tail(&tx_pkt->link,
@@ -302,8 +289,7 @@
tx_pkt->dma_address,
IPA_RX_SKB_SIZE,
tx_pkt,
- SPS_IOVEC_FLAG_INT |
- SPS_IOVEC_FLAG_EOT);
+ SPS_IOVEC_FLAG_INT);
if (ret) {
list_del(&tx_pkt->link);
pr_debug_ratelimited("%s: sps_transfer_one failed %d type=%d dir=%d\n",
@@ -312,7 +298,6 @@
polling_max_sleep[dir]);
goto retry_add_rx;
}
- sys_rx->len++;
retry_add_tx:
list_add_tail(&rx_pkt->link,
@@ -332,7 +317,6 @@
polling_max_sleep[dir]);
goto retry_add_tx;
}
- sys_tx->len++;
IPA_STATS_INC_BRIDGE_CNT(ctx->type, dir,
ipa_ctx->stats.bridged_pkts);
}
@@ -444,7 +428,6 @@
INIT_LIST_HEAD(&sys->head_desc_list);
INIT_LIST_HEAD(&sys->free_desc_list);
- spin_lock_init(&sys->spinlock);
memset(&ipa_ctx->ep[ipa_ep_idx], 0,
sizeof(struct ipa_ep_context));
@@ -614,7 +597,6 @@
INIT_LIST_HEAD(&sys->head_desc_list);
INIT_LIST_HEAD(&sys->free_desc_list);
- spin_lock_init(&sys->spinlock);
if (dir == IPA_BRIDGE_DIR_DL) {
sys->register_event.options = SPS_O_EOT;
@@ -663,32 +645,32 @@
int ret;
int i;
- bridge[IPA_BRIDGE_TYPE_TETHERED].ul_wq = alloc_workqueue("ipa_ul_teth",
- WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
+ bridge[IPA_BRIDGE_TYPE_TETHERED].ul_wq =
+ create_singlethread_workqueue("ipa_ul_teth");
if (!bridge[IPA_BRIDGE_TYPE_TETHERED].ul_wq) {
IPAERR("ipa ul teth wq alloc failed\n");
ret = -ENOMEM;
goto fail_ul_teth;
}
- bridge[IPA_BRIDGE_TYPE_TETHERED].dl_wq = alloc_workqueue("ipa_dl_teth",
- WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
+ bridge[IPA_BRIDGE_TYPE_TETHERED].dl_wq =
+ create_singlethread_workqueue("ipa_dl_teth");
if (!bridge[IPA_BRIDGE_TYPE_TETHERED].dl_wq) {
IPAERR("ipa dl teth wq alloc failed\n");
ret = -ENOMEM;
goto fail_dl_teth;
}
- bridge[IPA_BRIDGE_TYPE_EMBEDDED].ul_wq = alloc_workqueue("ipa_ul_emb",
- WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
+ bridge[IPA_BRIDGE_TYPE_EMBEDDED].ul_wq =
+ create_singlethread_workqueue("ipa_ul_emb");
if (!bridge[IPA_BRIDGE_TYPE_EMBEDDED].ul_wq) {
IPAERR("ipa ul emb wq alloc failed\n");
ret = -ENOMEM;
goto fail_ul_emb;
}
- bridge[IPA_BRIDGE_TYPE_EMBEDDED].dl_wq = alloc_workqueue("ipa_dl_emb",
- WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
+ bridge[IPA_BRIDGE_TYPE_EMBEDDED].dl_wq =
+ create_singlethread_workqueue("ipa_dl_emb");
if (!bridge[IPA_BRIDGE_TYPE_EMBEDDED].dl_wq) {
IPAERR("ipa dl emb wq alloc failed\n");
ret = -ENOMEM;
diff --git a/drivers/platform/msm/ipa/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_debugfs.c
index ec83653..1605ed2 100644
--- a/drivers/platform/msm/ipa/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_debugfs.c
@@ -45,6 +45,37 @@
__stringify(IPA_CLIENT_MAX),
};
+const char *ipa_ic_name[] = {
+ __stringify_1(IPA_IP_CMD_INVALID),
+ __stringify_1(IPA_DECIPH_INIT),
+ __stringify_1(IPA_PPP_FRM_INIT),
+ __stringify_1(IPA_IP_V4_FILTER_INIT),
+ __stringify_1(IPA_IP_V6_FILTER_INIT),
+ __stringify_1(IPA_IP_V4_NAT_INIT),
+ __stringify_1(IPA_IP_V6_NAT_INIT),
+ __stringify_1(IPA_IP_V4_ROUTING_INIT),
+ __stringify_1(IPA_IP_V6_ROUTING_INIT),
+ __stringify_1(IPA_HDR_INIT_LOCAL),
+ __stringify_1(IPA_HDR_INIT_SYSTEM),
+ __stringify_1(IPA_DECIPH_SETUP),
+ __stringify_1(IPA_INSERT_NAT_RULE),
+ __stringify_1(IPA_DELETE_NAT_RULE),
+ __stringify_1(IPA_NAT_DMA),
+ __stringify_1(IPA_IP_PACKET_TAG),
+ __stringify_1(IPA_IP_PACKET_INIT),
+};
+
+const char *ipa_excp_name[] = {
+ __stringify_1(IPA_A5_MUX_HDR_EXCP_RSVD0),
+ __stringify_1(IPA_A5_MUX_HDR_EXCP_RSVD1),
+ __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_IHL),
+ __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_REPLICATED),
+ __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_TAG),
+ __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_SW_FLT),
+ __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_NAT),
+ __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_IP),
+};
+
static struct dentry *dent;
static struct dentry *dfile_gen_reg;
static struct dentry *dfile_ep_reg;
@@ -489,33 +520,39 @@
nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
"sw_tx=%u\n"
"hw_tx=%u\n"
- "rx=%u\n",
+ "rx=%u\n"
+ "rx_repl_repost=%u\n"
+ "x_intr_repost=%u\n"
+ "rx_q_len=%u\n",
ipa_ctx->stats.tx_sw_pkts,
ipa_ctx->stats.tx_hw_pkts,
- ipa_ctx->stats.rx_pkts);
+ ipa_ctx->stats.rx_pkts,
+ ipa_ctx->stats.rx_repl_repost,
+ ipa_ctx->stats.x_intr_repost,
+ ipa_ctx->stats.rx_q_len);
cnt += nbytes;
for (i = 0; i < MAX_NUM_EXCP; i++) {
nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
- "rx_excp[%u]=%u\n", i,
+ "rx_excp[%u:%35s]=%u\n", i, ipa_excp_name[i],
ipa_ctx->stats.rx_excp_pkts[i]);
cnt += nbytes;
}
for (i = 0; i < IPA_BRIDGE_TYPE_MAX; i++) {
nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
- "bridged_pkt[%u][dl]=%u\n"
- "bridged_pkt[%u][ul]=%u\n",
- i,
+ "brg_pkt[%u:%s][dl]=%u\n"
+ "brg_pkt[%u:%s][ul]=%u\n",
+ i, (i == 0) ? "teth" : "embd",
ipa_ctx->stats.bridged_pkts[i][0],
- i,
+ i, (i == 0) ? "teth" : "embd",
ipa_ctx->stats.bridged_pkts[i][1]);
cnt += nbytes;
}
for (i = 0; i < MAX_NUM_IMM_CMD; i++) {
nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
- "IC[%u]=%u\n", i,
+ "IC[%2u:%22s]=%u\n", i, ipa_ic_name[i],
ipa_ctx->stats.imm_cmds[i]);
cnt += nbytes;
}
diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c
index 52ed428..38690e9 100644
--- a/drivers/platform/msm/ipa/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_dp.c
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dmapool.h>
#include <linux/list.h>
@@ -19,6 +20,17 @@
#define list_next_entry(pos, member) \
list_entry(pos->member.next, typeof(*pos), member)
#define IPA_LAST_DESC_CNT 0xFFFF
+#define POLLING_INACTIVITY 40
+#define POLLING_MIN_SLEEP 950
+#define POLLING_MAX_SLEEP 1050
+
+static void replenish_rx_work_func(struct work_struct *work);
+static struct delayed_work replenish_rx_work;
+static void switch_to_intr_work_func(struct work_struct *work);
+static struct delayed_work switch_to_intr_work;
+static void ipa_wq_handle_rx(struct work_struct *work);
+static DECLARE_WORK(rx_work, ipa_wq_handle_rx);
+
/**
* ipa_write_done() - this function will be (eventually) called when a Tx
* operation is complete
@@ -40,7 +52,7 @@
unsigned long irq_flags;
struct ipa_mem_buffer mult = { 0 };
int i;
- u16 cnt;
+ u32 cnt;
tx_pkt = container_of(work, struct ipa_tx_pkt_wrapper, work);
cnt = tx_pkt->cnt;
@@ -66,9 +78,8 @@
}
next_pkt = list_next_entry(tx_pkt, link);
list_del(&tx_pkt->link);
- tx_pkt->sys->len--;
spin_unlock_irqrestore(&tx_pkt->sys->spinlock, irq_flags);
- if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
+ if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0)) {
dma_pool_free(ipa_ctx->one_kb_no_straddle_pool,
tx_pkt->bounce,
tx_pkt->mem.phys_base);
@@ -114,10 +125,10 @@
u16 sps_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_INT;
dma_addr_t dma_address;
u16 len;
- u32 mem_flag = GFP_KERNEL;
+ u32 mem_flag = GFP_ATOMIC;
- if (in_atomic)
- mem_flag = GFP_ATOMIC;
+ if (unlikely(!in_atomic))
+ mem_flag = GFP_KERNEL;
tx_pkt = kmem_cache_zalloc(ipa_ctx->tx_pkt_wrapper_cache, mem_flag);
if (!tx_pkt) {
@@ -125,7 +136,7 @@
goto fail_mem_alloc;
}
- if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
+ if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0)) {
WARN_ON(desc->len > 512);
/*
@@ -173,19 +184,15 @@
if (desc->type == IPA_IMM_CMD_DESC) {
sps_flags |= SPS_IOVEC_FLAG_IMME;
len = desc->opcode;
+ IPADBG("sending cmd=%d pyld_len=%d sps_flags=%x\n",
+ desc->opcode, desc->len, sps_flags);
+ IPA_DUMP_BUFF(desc->pyld, dma_address, desc->len);
} else {
len = desc->len;
}
- if (desc->type == IPA_IMM_CMD_DESC) {
- IPADBG("sending cmd=%d pyld_len=%d sps_flags=%x\n",
- desc->opcode, desc->len, sps_flags);
- IPA_DUMP_BUFF(desc->pyld, dma_address, desc->len);
- }
-
spin_lock_irqsave(&sys->spinlock, irq_flags);
list_add_tail(&tx_pkt->link, &sys->head_desc_list);
- sys->len++;
result = sps_transfer_one(sys->ep->ep_hdl, dma_address, len, tx_pkt,
sps_flags);
if (result) {
@@ -200,7 +207,7 @@
fail_sps_send:
list_del(&tx_pkt->link);
spin_unlock_irqrestore(&sys->spinlock, irq_flags);
- if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0)
+ if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0))
dma_pool_free(ipa_ctx->one_kb_no_straddle_pool, tx_pkt->bounce,
dma_address);
else
@@ -233,7 +240,7 @@
*
* Return codes: 0: success, -EFAULT: failure
*/
-int ipa_send(struct ipa_sys_context *sys, u16 num_desc, struct ipa_desc *desc,
+int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
bool in_atomic)
{
struct ipa_tx_pkt_wrapper *tx_pkt;
@@ -247,17 +254,18 @@
int result;
int fail_dma_wrap = 0;
uint size = num_desc * sizeof(struct sps_iovec);
- u32 mem_flag = GFP_KERNEL;
+ u32 mem_flag = GFP_ATOMIC;
- if (likely(in_atomic))
- mem_flag = GFP_ATOMIC;
+ if (unlikely(!in_atomic))
+ mem_flag = GFP_KERNEL;
transfer.iovec = dma_alloc_coherent(NULL, size, &dma_addr, 0);
transfer.iovec_phys = dma_addr;
transfer.iovec_count = num_desc;
+ spin_lock_irqsave(&sys->spinlock, irq_flags);
if (!transfer.iovec) {
IPAERR("fail to alloc DMA mem for sps xfr buff\n");
- goto failure;
+ goto failure_coherent;
}
for (i = 0; i < num_desc; i++) {
@@ -274,24 +282,23 @@
*/
if (i == 0) {
transfer.user = tx_pkt;
-
tx_pkt->mult.phys_base = dma_addr;
tx_pkt->mult.base = transfer.iovec;
tx_pkt->mult.size = size;
tx_pkt->cnt = num_desc;
+ INIT_WORK(&tx_pkt->work, ipa_wq_write_done);
}
iovec = &transfer.iovec[i];
iovec->flags = 0;
INIT_LIST_HEAD(&tx_pkt->link);
- INIT_WORK(&tx_pkt->work, ipa_wq_write_done);
tx_pkt->type = desc[i].type;
tx_pkt->mem.base = desc[i].pyld;
tx_pkt->mem.size = desc[i].len;
- if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
+ if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0)) {
WARN_ON(tx_pkt->mem.size > 512);
/*
@@ -334,10 +341,7 @@
* add this packet to system pipe context.
*/
iovec->addr = tx_pkt->mem.phys_base;
- spin_lock_irqsave(&sys->spinlock, irq_flags);
list_add_tail(&tx_pkt->link, &sys->head_desc_list);
- sys->len++;
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
/*
* Special treatment for immediate commands, where the structure
@@ -364,16 +368,15 @@
goto failure;
}
+ spin_unlock_irqrestore(&sys->spinlock, irq_flags);
return 0;
failure:
tx_pkt = transfer.user;
for (j = 0; j < i; j++) {
- spin_lock_irqsave(&sys->spinlock, irq_flags);
next_pkt = list_next_entry(tx_pkt, link);
list_del(&tx_pkt->link);
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
- if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0)
+ if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0))
dma_pool_free(ipa_ctx->one_kb_no_straddle_pool,
tx_pkt->bounce,
tx_pkt->mem.phys_base);
@@ -391,7 +394,8 @@
if (transfer.iovec_phys)
dma_free_coherent(NULL, size, transfer.iovec,
transfer.iovec_phys);
-
+failure_coherent:
+ spin_unlock_irqrestore(&sys->spinlock, irq_flags);
return -EFAULT;
}
@@ -512,15 +516,14 @@
* - Call the endpoints notify function, passing the skb in the parameters
* - Replenish the rx cache
*/
-int ipa_handle_rx_core(bool process_all)
+int ipa_handle_rx_core(bool process_all, bool in_poll_state)
{
struct ipa_a5_mux_hdr *mux_hdr;
struct ipa_rx_pkt_wrapper *rx_pkt;
struct sk_buff *rx_skb;
struct sps_iovec iov;
- unsigned long irq_flags;
- u16 pull_len;
- u16 padding;
+ unsigned int pull_len;
+ unsigned int padding;
int ret;
struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
struct ipa_ep_context *ep;
@@ -528,35 +531,35 @@
struct completion *compl;
struct ipa_tree_node *node;
- do {
+ while ((in_poll_state ? atomic_read(&ipa_ctx->curr_polling_state) :
+ !atomic_read(&ipa_ctx->curr_polling_state))) {
+ if (cnt && !process_all)
+ break;
+
ret = sps_get_iovec(sys->ep->ep_hdl, &iov);
if (ret) {
IPAERR("sps_get_iovec failed %d\n", ret);
break;
}
- /* Break the loop when there are no more packets to receive */
if (iov.addr == 0)
break;
- spin_lock_irqsave(&sys->spinlock, irq_flags);
- if (list_empty(&sys->head_desc_list))
- WARN_ON(1);
+ if (unlikely(list_empty(&sys->head_desc_list)))
+ continue;
+
rx_pkt = list_first_entry(&sys->head_desc_list,
struct ipa_rx_pkt_wrapper, link);
- if (!rx_pkt)
- WARN_ON(1);
+
rx_pkt->len = iov.size;
sys->len--;
list_del(&rx_pkt->link);
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
IPADBG("--curr_cnt=%d\n", sys->len);
rx_skb = rx_pkt->skb;
dma_unmap_single(NULL, rx_pkt->dma_address, IPA_RX_SKB_SIZE,
DMA_FROM_DEVICE);
- kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
/*
* make it look like a real skb, "data" was already set at
@@ -565,6 +568,7 @@
rx_skb->tail = rx_skb->data + rx_pkt->len;
rx_skb->len = rx_pkt->len;
rx_skb->truesize = rx_pkt->len + sizeof(struct sk_buff);
+ kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
mux_hdr = (struct ipa_a5_mux_hdr *)rx_skb->data;
@@ -601,20 +605,20 @@
}
mutex_unlock(&ipa_ctx->lock);
}
- dev_kfree_skb_any(rx_skb);
+ dev_kfree_skb(rx_skb);
ipa_replenish_rx_cache();
++cnt;
continue;
}
- if (mux_hdr->src_pipe_index >= IPA_NUM_PIPES ||
+ if (unlikely(mux_hdr->src_pipe_index >= IPA_NUM_PIPES ||
!ipa_ctx->ep[mux_hdr->src_pipe_index].valid ||
- !ipa_ctx->ep[mux_hdr->src_pipe_index].client_notify) {
+ !ipa_ctx->ep[mux_hdr->src_pipe_index].client_notify)) {
IPAERR("drop pipe=%d ep_valid=%d client_notify=%p\n",
mux_hdr->src_pipe_index,
ipa_ctx->ep[mux_hdr->src_pipe_index].valid,
ipa_ctx->ep[mux_hdr->src_pipe_index].client_notify);
- dev_kfree_skb_any(rx_skb);
+ dev_kfree_skb(rx_skb);
ipa_replenish_rx_cache();
++cnt;
continue;
@@ -634,11 +638,11 @@
IPADBG("pulling %d bytes from skb\n", pull_len);
skb_pull(rx_skb, pull_len);
+ ipa_replenish_rx_cache();
ep->client_notify(ep->priv, IPA_RECEIVE,
(unsigned long)(rx_skb));
- ipa_replenish_rx_cache();
cnt++;
- } while (process_all);
+ };
return cnt;
}
@@ -652,9 +656,9 @@
struct ipa_sys_context *sys;
IPADBG("Enter");
- if (!ipa_ctx->curr_polling_state) {
+ if (!atomic_read(&ipa_ctx->curr_polling_state)) {
IPAERR("already in intr mode\n");
- return;
+ goto fail;
}
sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
@@ -662,49 +666,28 @@
ret = sps_get_config(sys->ep->ep_hdl, &sys->ep->connect);
if (ret) {
IPAERR("sps_get_config() failed %d\n", ret);
- return;
+ goto fail;
}
sys->event.options = SPS_O_EOT;
ret = sps_register_event(sys->ep->ep_hdl, &sys->event);
if (ret) {
IPAERR("sps_register_event() failed %d\n", ret);
- return;
+ goto fail;
}
sys->ep->connect.options =
SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_EOT;
ret = sps_set_config(sys->ep->ep_hdl, &sys->ep->connect);
if (ret) {
IPAERR("sps_set_config() failed %d\n", ret);
- return;
+ goto fail;
}
- ipa_handle_rx_core(true);
- ipa_ctx->curr_polling_state = 0;
-}
+ atomic_set(&ipa_ctx->curr_polling_state, 0);
+ ipa_handle_rx_core(true, false);
+ return;
-/**
- * ipa_rx_switch_to_poll_mode() - Operate the Rx data path in polling mode
- */
-static void ipa_rx_switch_to_poll_mode(void)
-{
- int ret;
- struct ipa_ep_context *ep;
-
- IPADBG("Enter");
- ep = ipa_ctx->sys[IPA_A5_LAN_WAN_IN].ep;
-
- ret = sps_get_config(ep->ep_hdl, &ep->connect);
- if (ret) {
- IPAERR("sps_get_config() failed %d\n", ret);
- return;
- }
- ep->connect.options =
- SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_POLL;
- ret = sps_set_config(ep->ep_hdl, &ep->connect);
- if (ret) {
- IPAERR("sps_set_config() failed %d\n", ret);
- return;
- }
- ipa_ctx->curr_polling_state = 1;
+fail:
+ IPA_STATS_INC_CNT(ipa_ctx->stats.x_intr_repost);
+ schedule_delayed_work(&switch_to_intr_work, msecs_to_jiffies(1));
}
/**
@@ -722,16 +705,30 @@
*/
static void ipa_sps_irq_rx_notify(struct sps_event_notify *notify)
{
- struct ipa_rx_pkt_wrapper *rx_pkt;
+ struct ipa_ep_context *ep;
+ int ret;
IPADBG("event %d notified\n", notify->event_id);
switch (notify->event_id) {
case SPS_EVENT_EOT:
- if (!ipa_ctx->curr_polling_state) {
- ipa_rx_switch_to_poll_mode();
- rx_pkt = notify->data.transfer.user;
- queue_work(ipa_ctx->rx_wq, &rx_pkt->work);
+ if (!atomic_read(&ipa_ctx->curr_polling_state)) {
+ ep = ipa_ctx->sys[IPA_A5_LAN_WAN_IN].ep;
+
+ ret = sps_get_config(ep->ep_hdl, &ep->connect);
+ if (ret) {
+ IPAERR("sps_get_config() failed %d\n", ret);
+ break;
+ }
+ ep->connect.options = SPS_O_AUTO_ENABLE |
+ SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+ ret = sps_set_config(ep->ep_hdl, &ep->connect);
+ if (ret) {
+ IPAERR("sps_set_config() failed %d\n", ret);
+ break;
+ }
+ atomic_set(&ipa_ctx->curr_polling_state, 1);
+ queue_work(ipa_ctx->rx_wq, &rx_work);
}
break;
default:
@@ -861,6 +858,9 @@
/* fall through */
case 3:
sys_idx = ipa_ep_idx;
+ INIT_DELAYED_WORK(&replenish_rx_work, replenish_rx_work_func);
+ INIT_DELAYED_WORK(&switch_to_intr_work,
+ switch_to_intr_work_func);
break;
case WLAN_AMPDU_TX_EP:
sys_idx = IPA_A5_WLAN_AMPDU_OUT;
@@ -954,7 +954,7 @@
ipa_ctx->ep[ep_idx].client_notify(ipa_ctx->ep[ep_idx].priv,
IPA_WRITE_DONE, (unsigned long)skb);
else
- dev_kfree_skb_any(skb);
+ dev_kfree_skb(skb);
}
static void ipa_tx_cmd_comp(void *user1, void *user2)
@@ -1066,6 +1066,24 @@
}
EXPORT_SYMBOL(ipa_tx_dp);
+static void ipa_handle_rx(void)
+{
+ int inactive_cycles = 0;
+ int cnt;
+
+ do {
+ cnt = ipa_handle_rx_core(true, true);
+ if (cnt == 0) {
+ inactive_cycles++;
+ usleep_range(POLLING_MIN_SLEEP, POLLING_MAX_SLEEP);
+ } else {
+ inactive_cycles = 0;
+ }
+ } while (inactive_cycles <= POLLING_INACTIVITY);
+
+ ipa_rx_switch_to_intr_mode();
+}
+
/**
* ipa_handle_rx() - handle packet reception. This function is executed in the
* context of a work queue.
@@ -1074,10 +1092,9 @@
* ipa_handle_rx_core() is run in polling mode. After all packets has been
* received, the driver switches back to interrupt mode.
*/
-void ipa_wq_handle_rx(struct work_struct *work)
+static void ipa_wq_handle_rx(struct work_struct *work)
{
- ipa_handle_rx_core(true);
- ipa_rx_switch_to_intr_mode();
+ ipa_handle_rx();
}
/**
@@ -1099,26 +1116,23 @@
void *ptr;
struct ipa_rx_pkt_wrapper *rx_pkt;
int ret;
- int rx_len_cached;
- unsigned long irq_flags;
+ int rx_len_cached = 0;
struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
+ gfp_t flag = GFP_NOWAIT | __GFP_NOWARN;
- spin_lock_irqsave(&sys->spinlock, irq_flags);
rx_len_cached = sys->len;
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
while (rx_len_cached < IPA_RX_POOL_CEIL) {
rx_pkt = kmem_cache_zalloc(ipa_ctx->rx_pkt_wrapper_cache,
- GFP_KERNEL);
+ flag);
if (!rx_pkt) {
IPAERR("failed to alloc rx wrapper\n");
- return;
+ goto fail_kmem_cache_alloc;
}
INIT_LIST_HEAD(&rx_pkt->link);
- INIT_WORK(&rx_pkt->work, ipa_wq_handle_rx);
- rx_pkt->skb = __dev_alloc_skb(IPA_RX_SKB_SIZE, GFP_KERNEL);
+ rx_pkt->skb = __dev_alloc_skb(IPA_RX_SKB_SIZE, flag);
if (rx_pkt->skb == NULL) {
IPAERR("failed to alloc skb\n");
goto fail_skb_alloc;
@@ -1133,10 +1147,8 @@
goto fail_dma_mapping;
}
- spin_lock_irqsave(&sys->spinlock, irq_flags);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
ret = sps_transfer_one(sys->ep->ep_hdl, rx_pkt->dma_address,
IPA_RX_SKB_SIZE, rx_pkt,
@@ -1146,27 +1158,41 @@
IPAERR("sps_transfer_one failed %d\n", ret);
goto fail_sps_transfer;
}
-
- IPADBG("++curr_cnt=%d\n", sys->len);
}
+ ipa_ctx->stats.rx_q_len = sys->len;
+
return;
fail_sps_transfer:
- spin_lock_irqsave(&sys->spinlock, irq_flags);
list_del(&rx_pkt->link);
- --sys->len;
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+ rx_len_cached = --sys->len;
dma_unmap_single(NULL, rx_pkt->dma_address, IPA_RX_SKB_SIZE,
DMA_FROM_DEVICE);
fail_dma_mapping:
- dev_kfree_skb_any(rx_pkt->skb);
+ dev_kfree_skb(rx_pkt->skb);
fail_skb_alloc:
kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
-
+fail_kmem_cache_alloc:
+ if (rx_len_cached == 0) {
+ IPA_STATS_INC_CNT(ipa_ctx->stats.rx_repl_repost);
+ schedule_delayed_work(&replenish_rx_work,
+ msecs_to_jiffies(100));
+ }
+ ipa_ctx->stats.rx_q_len = sys->len;
return;
}
+static void replenish_rx_work_func(struct work_struct *work)
+{
+ ipa_replenish_rx_cache();
+}
+
+static void switch_to_intr_work_func(struct work_struct *work)
+{
+ ipa_handle_rx();
+}
+
/**
* ipa_cleanup_rx() - release RX queue resources
*
@@ -1175,18 +1201,15 @@
{
struct ipa_rx_pkt_wrapper *rx_pkt;
struct ipa_rx_pkt_wrapper *r;
- unsigned long irq_flags;
struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
- spin_lock_irqsave(&sys->spinlock, irq_flags);
list_for_each_entry_safe(rx_pkt, r,
&sys->head_desc_list, link) {
list_del(&rx_pkt->link);
dma_unmap_single(NULL, rx_pkt->dma_address, IPA_RX_SKB_SIZE,
DMA_FROM_DEVICE);
- dev_kfree_skb_any(rx_pkt->skb);
+ dev_kfree_skb(rx_pkt->skb);
kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
}
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
}
diff --git a/drivers/platform/msm/ipa/ipa_i.h b/drivers/platform/msm/ipa/ipa_i.h
index 14195d7..cb8c0f5 100644
--- a/drivers/platform/msm/ipa/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_i.h
@@ -110,7 +110,7 @@
#define IPA_EVENT_THRESHOLD 0x10
-#define IPA_RX_POOL_CEIL 24
+#define IPA_RX_POOL_CEIL 32
#define IPA_RX_SKB_SIZE 2048
#define IPA_DFLT_HDR_NAME "ipa_excp_hdr"
@@ -422,7 +422,7 @@
void *user2;
struct ipa_sys_context *sys;
struct ipa_mem_buffer mult;
- u16 cnt;
+ u32 cnt;
void *bounce;
};
@@ -453,16 +453,14 @@
* struct ipa_rx_pkt_wrapper - IPA Rx packet wrapper
* @skb: skb
* @dma_address: DMA address of this Rx packet
- * @work: work struct for current Rx packet
* @link: linked to the Rx packets on that pipe
* @len: how many bytes are copied into skb's flat buffer
*/
struct ipa_rx_pkt_wrapper {
struct sk_buff *skb;
dma_addr_t dma_address;
- struct work_struct work;
struct list_head link;
- u16 len;
+ u32 len;
};
/**
@@ -527,6 +525,9 @@
u32 rx_pkts;
u32 rx_excp_pkts[MAX_NUM_EXCP];
u32 bridged_pkts[IPA_BRIDGE_TYPE_MAX][IPA_BRIDGE_DIR_MAX];
+ u32 rx_repl_repost;
+ u32 x_intr_repost;
+ u32 rx_q_len;
};
/**
@@ -629,7 +630,7 @@
uint aggregation_type;
uint aggregation_byte_limit;
uint aggregation_time_limit;
- uint curr_polling_state;
+ atomic_t curr_polling_state;
struct delayed_work poll_work;
bool hdr_tbl_lcl;
struct ipa_mem_buffer hdr_mem;
@@ -742,7 +743,7 @@
u32 *consumer_handle);
int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc,
bool in_atomic);
-int ipa_send(struct ipa_sys_context *sys, u16 num_desc, struct ipa_desc *desc,
+int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
bool in_atomic);
int ipa_get_ep_mapping(enum ipa_operating_mode mode,
enum ipa_client_type client);
@@ -783,8 +784,7 @@
void ipa_cleanup_rx(void);
int ipa_cfg_filter(u32 disable);
void ipa_wq_write_done(struct work_struct *work);
-void ipa_wq_handle_rx(struct work_struct *work);
-int ipa_handle_rx_core(bool process_all);
+int ipa_handle_rx_core(bool process_all, bool in_poll_state);
int ipa_pipe_mem_init(u32 start_ofst, u32 size);
int ipa_pipe_mem_alloc(u32 *ofst, u32 size);
int ipa_pipe_mem_free(u32 ofst, u32 size);
@@ -823,4 +823,8 @@
int a2_mux_init(void);
int a2_mux_exit(void);
+void wwan_cleanup(void);
+
+int teth_bridge_driver_init(void);
+
#endif /* _IPA_I_H_ */
diff --git a/drivers/platform/msm/ipa/teth_bridge.c b/drivers/platform/msm/ipa/teth_bridge.c
new file mode 100644
index 0000000..76e2eee
--- /dev/null
+++ b/drivers/platform/msm/ipa/teth_bridge.c
@@ -0,0 +1,1483 @@
+/* Copyright (c) 2013, 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/completion.h>
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/if_ether.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msm_ipa.h>
+#include <linux/mutex.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <mach/bam_dmux.h>
+#include <mach/ipa.h>
+#include <mach/sps.h>
+#include "ipa_i.h"
+
+#define TETH_BRIDGE_DRV_NAME "ipa_tethering_bridge"
+
+#ifdef TETH_DEBUG
+#define TETH_DBG(fmt, args...) \
+ pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args)
+#define TETH_DBG_FUNC_ENTRY() \
+ pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d ENTRY\n", __func__, __LINE__)
+#define TETH_DBG_FUNC_EXIT() \
+ pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d EXIT\n", __func__, __LINE__)
+#else
+#define TETH_DBG(fmt, args...)
+#define TETH_DBG_FUNC_ENTRY()
+#define TETH_DBG_FUNC_EXIT()
+#endif
+
+#define TETH_ERR(fmt, args...) \
+ pr_err(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+
+#define USB_ETH_HDR_NAME_IPV4 "usb_bridge_ipv4"
+#define USB_ETH_HDR_NAME_IPV6 "usb_bridge_ipv6"
+#define A2_ETH_HDR_NAME_IPV4 "a2_bridge_ipv4"
+#define A2_ETH_HDR_NAME_IPV6 "a2_bridge_ipv6"
+
+#define USB_TO_A2_RT_TBL_NAME_IPV4 "usb_a2_rt_tbl_ipv4"
+#define A2_TO_USB_RT_TBL_NAME_IPV4 "a2_usb_rt_tbl_ipv4"
+#define USB_TO_A2_RT_TBL_NAME_IPV6 "usb_a2_rt_tbl_ipv6"
+#define A2_TO_USB_RT_TBL_NAME_IPV6 "a2_usb_rt_tbl_ipv6"
+
+#define MBIM_HEADER_NAME "mbim_header"
+#define TETH_DEFAULT_AGGR_TIME_LIMIT 1
+
+#define ETHERTYPE_IPV4 0x0800
+#define ETHERTYPE_IPV6 0x86DD
+
+struct mac_addresses_type {
+ u8 host_pc_mac_addr[ETH_ALEN];
+ bool host_pc_mac_addr_known;
+ u8 device_mac_addr[ETH_ALEN];
+ bool device_mac_addr_known;
+};
+
+struct teth_bridge_ctx {
+ struct class *class;
+ dev_t dev_num;
+ struct device *dev;
+ struct cdev cdev;
+ u32 usb_ipa_pipe_hdl;
+ u32 ipa_usb_pipe_hdl;
+ u32 a2_ipa_pipe_hdl;
+ u32 ipa_a2_pipe_hdl;
+ bool is_connected;
+ enum teth_link_protocol_type link_protocol;
+ struct mac_addresses_type mac_addresses;
+ bool is_hw_bridge_complete;
+ struct teth_aggr_params aggr_params;
+ bool aggr_params_known;
+ enum teth_tethering_mode tethering_mode;
+ struct completion is_bridge_prod_up;
+ struct completion is_bridge_prod_down;
+ struct work_struct comp_hw_bridge_work;
+ bool comp_hw_bridge_in_progress;
+ struct teth_aggr_capabilities *aggr_caps;
+};
+
+static struct teth_bridge_ctx *teth_ctx;
+
+#ifdef CONFIG_DEBUG_FS
+#define TETH_MAX_MSG_LEN 512
+static char dbg_buff[TETH_MAX_MSG_LEN];
+#endif
+
+static int add_eth_hdrs(char *hdr_name_ipv4, char *hdr_name_ipv6,
+ u8 *src_mac_addr, u8 *dst_mac_addr)
+{
+ int res;
+ struct ipa_ioc_add_hdr *hdrs;
+ struct ethhdr hdr_ipv4;
+ struct ethhdr hdr_ipv6;
+
+ TETH_DBG_FUNC_ENTRY();
+ memcpy(hdr_ipv4.h_source, src_mac_addr, ETH_ALEN);
+ memcpy(hdr_ipv4.h_dest, dst_mac_addr, ETH_ALEN);
+ hdr_ipv4.h_proto = htons(ETHERTYPE_IPV4);
+
+ memcpy(hdr_ipv6.h_source, src_mac_addr, ETH_ALEN);
+ memcpy(hdr_ipv6.h_dest, dst_mac_addr, ETH_ALEN);
+ hdr_ipv6.h_proto = htons(ETHERTYPE_IPV6);
+
+ /* Add headers to the header insertion tables */
+ hdrs = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
+ 2 * sizeof(struct ipa_hdr_add), GFP_KERNEL);
+ if (hdrs == NULL) {
+ TETH_ERR("Failed allocating memory for headers !\n");
+ return -ENOMEM;
+ }
+
+ hdrs->commit = 0;
+ hdrs->num_hdrs = 2;
+
+ /* Ethernet IPv4 header */
+ strlcpy(hdrs->hdr[0].name, hdr_name_ipv4, IPA_RESOURCE_NAME_MAX);
+ hdrs->hdr[0].hdr_len = ETH_HLEN;
+ memcpy(hdrs->hdr[0].hdr, &hdr_ipv4, ETH_HLEN);
+
+ /* Ethernet IPv6 header */
+ strlcpy(hdrs->hdr[1].name, hdr_name_ipv6, IPA_RESOURCE_NAME_MAX);
+ hdrs->hdr[1].hdr_len = ETH_HLEN;
+ memcpy(hdrs->hdr[1].hdr, &hdr_ipv6, ETH_HLEN);
+
+ res = ipa_add_hdr(hdrs);
+ if (res || hdrs->hdr[0].status || hdrs->hdr[1].status)
+ TETH_ERR("Header insertion failed\n");
+ kfree(hdrs);
+ TETH_DBG_FUNC_EXIT();
+
+ return res;
+}
+
+static int configure_ipa_header_block_internal(u32 usb_ipa_hdr_len,
+ u32 a2_ipa_hdr_len,
+ u32 ipa_usb_hdr_len,
+ u32 ipa_a2_hdr_len)
+{
+ struct ipa_ep_cfg_hdr hdr_cfg;
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Configure header removal for the USB->IPA pipe and A2->IPA pipe */
+ memset(&hdr_cfg, 0, sizeof(hdr_cfg));
+ hdr_cfg.hdr_len = usb_ipa_hdr_len;
+ res = ipa_cfg_ep_hdr(teth_ctx->usb_ipa_pipe_hdl, &hdr_cfg);
+ if (res) {
+ TETH_ERR("Header removal config for USB->IPA pipe failed\n");
+ goto bail;
+ }
+
+ hdr_cfg.hdr_len = a2_ipa_hdr_len;
+ res = ipa_cfg_ep_hdr(teth_ctx->a2_ipa_pipe_hdl, &hdr_cfg);
+ if (res) {
+ TETH_ERR("Header removal config for A2->IPA pipe failed\n");
+ goto bail;
+ }
+
+ /* Configure header insertion for the IPA->USB pipe and IPA->A2 pipe */
+ hdr_cfg.hdr_len = ipa_usb_hdr_len;
+ res = ipa_cfg_ep_hdr(teth_ctx->ipa_usb_pipe_hdl, &hdr_cfg);
+ if (res) {
+ TETH_ERR("Header insertion config for IPA->USB pipe failed\n");
+ goto bail;
+ }
+
+ hdr_cfg.hdr_len = ipa_a2_hdr_len;
+ res = ipa_cfg_ep_hdr(teth_ctx->ipa_a2_pipe_hdl, &hdr_cfg);
+ if (res) {
+ TETH_ERR("Header insertion config for IPA->A2 pipe failed\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int add_mbim_hdr(void)
+{
+ int res;
+ struct ipa_ioc_add_hdr *mbim_hdr;
+ u8 mbim_stream_id = 0;
+
+ TETH_DBG_FUNC_ENTRY();
+ mbim_hdr = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
+ sizeof(struct ipa_hdr_add),
+ GFP_KERNEL);
+ if (!mbim_hdr) {
+ TETH_ERR("Failed allocating memory for MBIM header\n");
+ return -ENOMEM;
+ }
+
+ mbim_hdr->commit = 0;
+ mbim_hdr->num_hdrs = 1;
+ strlcpy(mbim_hdr->hdr[0].name, MBIM_HEADER_NAME, IPA_RESOURCE_NAME_MAX);
+ memcpy(mbim_hdr->hdr[0].hdr, &mbim_stream_id, sizeof(u8));
+ mbim_hdr->hdr[0].hdr_len = sizeof(u8);
+ mbim_hdr->hdr[0].is_partial = false;
+ res = ipa_add_hdr(mbim_hdr);
+ if (res || mbim_hdr->hdr[0].status) {
+ TETH_ERR("Failed adding MBIM header\n");
+ res = -EFAULT;
+ } else {
+ TETH_DBG("Added MBIM stream ID header\n");
+ }
+ kfree(mbim_hdr);
+ TETH_DBG_FUNC_EXIT();
+
+ return res;
+}
+
+static int configure_ipa_header_block(void)
+{
+ int res;
+ u32 hdr_len = 0;
+ u32 ipa_usb_hdr_len = 0;
+
+ TETH_DBG_FUNC_ENTRY();
+ if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP) {
+ /*
+ * Create a new header for MBIM stream ID and associate it with
+ * the IPA->USB routing table
+ */
+ if (teth_ctx->aggr_params.dl.aggr_prot ==
+ TETH_AGGR_PROTOCOL_MBIM) {
+ ipa_usb_hdr_len = 1;
+ res = add_mbim_hdr();
+ if (res) {
+ TETH_ERR("Failed adding MBIM header\n");
+ goto bail;
+ }
+ }
+ } else if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
+ /* Add a header entry for USB */
+ res = add_eth_hdrs(USB_ETH_HDR_NAME_IPV4,
+ USB_ETH_HDR_NAME_IPV6,
+ teth_ctx->mac_addresses.host_pc_mac_addr,
+ teth_ctx->mac_addresses.device_mac_addr);
+ if (res) {
+ TETH_ERR("Failed adding USB Ethernet header\n");
+ goto bail;
+ }
+ TETH_DBG("Added USB Ethernet headers (IPv4 / IPv6)\n");
+
+ /* Add a header entry for A2 */
+ res = add_eth_hdrs(A2_ETH_HDR_NAME_IPV4,
+ A2_ETH_HDR_NAME_IPV6,
+ teth_ctx->mac_addresses.device_mac_addr,
+ teth_ctx->mac_addresses.host_pc_mac_addr);
+ if (res) {
+ TETH_ERR("Failed adding A2 Ethernet header\n");
+ goto bail;
+ }
+ TETH_DBG("Added A2 Ethernet headers (IPv4 / IPv6\n");
+
+ hdr_len = ETH_HLEN;
+ ipa_usb_hdr_len = ETH_HLEN;
+ }
+
+ res = configure_ipa_header_block_internal(hdr_len,
+ hdr_len,
+ ipa_usb_hdr_len,
+ hdr_len);
+ if (res) {
+ TETH_ERR("Configuration of header removal/insertion failed\n");
+ goto bail;
+ }
+
+ res = ipa_commit_hdr();
+ if (res) {
+ TETH_ERR("Failed committing headers\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_routing_by_ip(char *hdr_name,
+ char *rt_tbl_name,
+ enum ipa_client_type dst,
+ enum ipa_ip_type ip_address_family)
+{
+
+ struct ipa_ioc_add_rt_rule *rt_rule;
+ struct ipa_ioc_get_hdr hdr_info;
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Get the header handle */
+ memset(&hdr_info, 0, sizeof(hdr_info));
+ strlcpy(hdr_info.name, hdr_name, IPA_RESOURCE_NAME_MAX);
+ ipa_get_hdr(&hdr_info);
+
+ rt_rule = kzalloc(sizeof(struct ipa_ioc_add_rt_rule) +
+ 1 * sizeof(struct ipa_rt_rule_add),
+ GFP_KERNEL);
+ if (!rt_rule) {
+ TETH_ERR("Memory allocation failure");
+ return -ENOMEM;
+ }
+
+ /* Match all, do not commit to HW*/
+ rt_rule->commit = 0;
+ rt_rule->num_rules = 1;
+ rt_rule->ip = ip_address_family;
+ strlcpy(rt_rule->rt_tbl_name, rt_tbl_name, IPA_RESOURCE_NAME_MAX);
+ rt_rule->rules[0].rule.dst = dst;
+ rt_rule->rules[0].rule.hdr_hdl = hdr_info.hdl;
+ rt_rule->rules[0].rule.attrib.attrib_mask = 0; /* Match all */
+ res = ipa_add_rt_rule(rt_rule);
+ if (res || rt_rule->rules[0].status)
+ TETH_ERR("Failed adding routing rule\n");
+ kfree(rt_rule);
+ TETH_DBG_FUNC_EXIT();
+
+ return res;
+}
+
+static int configure_routing(char *hdr_name_ipv4,
+ char *rt_tbl_name_ipv4,
+ char *hdr_name_ipv6,
+ char *rt_tbl_name_ipv6,
+ enum ipa_client_type dst)
+{
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Configure IPv4 routing table */
+ res = configure_routing_by_ip(hdr_name_ipv4,
+ rt_tbl_name_ipv4,
+ dst,
+ IPA_IP_v4);
+ if (res) {
+ TETH_ERR("Failed adding IPv4 routing table\n");
+ goto bail;
+ }
+
+ /* Configure IPv6 routing table */
+ res = configure_routing_by_ip(hdr_name_ipv6,
+ rt_tbl_name_ipv6,
+ dst,
+ IPA_IP_v6);
+ if (res) {
+ TETH_ERR("Failed adding IPv6 routing table\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_ipa_routing_block(void)
+{
+ int res;
+ char hdr_name_ipv4[IPA_RESOURCE_NAME_MAX];
+ char hdr_name_ipv6[IPA_RESOURCE_NAME_MAX];
+
+ TETH_DBG_FUNC_ENTRY();
+ hdr_name_ipv4[0] = '\0';
+ hdr_name_ipv6[0] = '\0';
+
+ /* Configure USB -> A2 routing table */
+ if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
+ strlcpy(hdr_name_ipv4,
+ A2_ETH_HDR_NAME_IPV4,
+ IPA_RESOURCE_NAME_MAX);
+ strlcpy(hdr_name_ipv6,
+ A2_ETH_HDR_NAME_IPV6,
+ IPA_RESOURCE_NAME_MAX);
+ }
+ res = configure_routing(hdr_name_ipv4,
+ USB_TO_A2_RT_TBL_NAME_IPV4,
+ hdr_name_ipv6,
+ USB_TO_A2_RT_TBL_NAME_IPV6,
+ IPA_CLIENT_A2_TETHERED_CONS);
+ if (res) {
+ TETH_ERR("USB to A2 routing block configuration failed\n");
+ goto bail;
+ }
+
+ /* Configure A2 -> USB routing table */
+ if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
+ strlcpy(hdr_name_ipv4,
+ USB_ETH_HDR_NAME_IPV4,
+ IPA_RESOURCE_NAME_MAX);
+ strlcpy(hdr_name_ipv6,
+ USB_ETH_HDR_NAME_IPV6,
+ IPA_RESOURCE_NAME_MAX);
+ } else if (teth_ctx->aggr_params.dl.aggr_prot ==
+ TETH_AGGR_PROTOCOL_MBIM) {
+ strlcpy(hdr_name_ipv4,
+ MBIM_HEADER_NAME,
+ IPA_RESOURCE_NAME_MAX);
+ strlcpy(hdr_name_ipv6,
+ MBIM_HEADER_NAME,
+ IPA_RESOURCE_NAME_MAX);
+ }
+ res = configure_routing(hdr_name_ipv4,
+ A2_TO_USB_RT_TBL_NAME_IPV4,
+ hdr_name_ipv6,
+ A2_TO_USB_RT_TBL_NAME_IPV6,
+ IPA_CLIENT_USB_CONS);
+ if (res) {
+ TETH_ERR("A2 to USB routing block configuration failed\n");
+ goto bail;
+ }
+
+ /* Commit all the changes to HW in one shot */
+ res = ipa_commit_rt(IPA_IP_v4);
+ if (res) {
+ TETH_ERR("Failed commiting IPv4 routing tables\n");
+ goto bail;
+ }
+ res = ipa_commit_rt(IPA_IP_v6);
+ if (res) {
+ TETH_ERR("Failed commiting IPv6 routing tables\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_filtering_by_ip(char *rt_tbl_name,
+ enum ipa_client_type src,
+ enum ipa_ip_type ip_address_family)
+{
+ struct ipa_ioc_add_flt_rule *flt_tbl;
+ struct ipa_ioc_get_rt_tbl rt_tbl_info;
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Get the needed routing table handle */
+ rt_tbl_info.ip = ip_address_family;
+ strlcpy(rt_tbl_info.name, rt_tbl_name, IPA_RESOURCE_NAME_MAX);
+ res = ipa_get_rt_tbl(&rt_tbl_info);
+ if (res) {
+ TETH_ERR("Failed getting routing table handle\n");
+ goto bail;
+ }
+
+ flt_tbl = kzalloc(sizeof(struct ipa_ioc_add_flt_rule) +
+ 1 * sizeof(struct ipa_flt_rule_add), GFP_KERNEL);
+ if (!flt_tbl) {
+ TETH_ERR("Filtering table memory allocation failure\n");
+ return -ENOMEM;
+ }
+
+ flt_tbl->commit = 0;
+ flt_tbl->ep = src;
+ flt_tbl->global = 0;
+ flt_tbl->ip = ip_address_family;
+ flt_tbl->num_rules = 1;
+ flt_tbl->rules[0].rule.action = IPA_PASS_TO_ROUTING;
+ flt_tbl->rules[0].rule.rt_tbl_hdl = rt_tbl_info.hdl;
+ flt_tbl->rules[0].rule.attrib.attrib_mask = 0; /* Match all */
+
+ res = ipa_add_flt_rule(flt_tbl);
+ if (res || flt_tbl->rules[0].status)
+ TETH_ERR("Failed adding filtering table\n");
+ kfree(flt_tbl);
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_filtering(char *rt_tbl_name_ipv4,
+ char *rt_tbl_name_ipv6,
+ enum ipa_client_type src)
+{
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ res = configure_filtering_by_ip(rt_tbl_name_ipv4, src, IPA_IP_v4);
+ if (res) {
+ TETH_ERR("Failed adding IPv4 filtering table\n");
+ goto bail;
+ }
+
+ res = configure_filtering_by_ip(rt_tbl_name_ipv6, src, IPA_IP_v6);
+ if (res) {
+ TETH_ERR("Failed adding IPv4 filtering table\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_ipa_filtering_block(void)
+{
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Filter all traffic coming from USB to A2 */
+ res = configure_filtering(USB_TO_A2_RT_TBL_NAME_IPV4,
+ USB_TO_A2_RT_TBL_NAME_IPV6,
+ IPA_CLIENT_USB_PROD);
+ if (res) {
+ TETH_ERR("USB_PROD ep filtering configuration failed\n");
+ goto bail;
+ }
+
+ /* Filter all traffic coming from A2 to USB */
+ res = configure_filtering(A2_TO_USB_RT_TBL_NAME_IPV4,
+ A2_TO_USB_RT_TBL_NAME_IPV6,
+ IPA_CLIENT_A2_TETHERED_PROD);
+ if (res) {
+ TETH_ERR("A2_PROD filtering configuration failed\n");
+ goto bail;
+ }
+
+ /* Commit all the changes to HW in one shot */
+ res = ipa_commit_flt(IPA_IP_v4);
+ if (res) {
+ TETH_ERR("Failed commiting IPv4 filtering tables\n");
+ goto bail;
+ }
+ res = ipa_commit_flt(IPA_IP_v6);
+ if (res) {
+ TETH_ERR("Failed commiting IPv6 filtering tables\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int prepare_ipa_aggr_struct(
+ const struct teth_aggr_params_link *teth_aggr_params,
+ struct ipa_ep_cfg_aggr *ipa_aggr_params,
+ bool client_is_prod)
+{
+ TETH_DBG_FUNC_ENTRY();
+ memset(ipa_aggr_params, 0, sizeof(*ipa_aggr_params));
+
+ switch (teth_aggr_params->aggr_prot) {
+ case TETH_AGGR_PROTOCOL_NONE:
+ ipa_aggr_params->aggr_en = IPA_BYPASS_AGGR;
+ break;
+ case TETH_AGGR_PROTOCOL_MBIM:
+ ipa_aggr_params->aggr = IPA_MBIM_16;
+ ipa_aggr_params->aggr_en = (client_is_prod) ?
+ IPA_ENABLE_DEAGGR : IPA_ENABLE_AGGR;
+ break;
+ case TETH_AGGR_PROTOCOL_TLP:
+ ipa_aggr_params->aggr = IPA_TLP;
+ ipa_aggr_params->aggr_en = (client_is_prod) ?
+ IPA_ENABLE_DEAGGR : IPA_ENABLE_AGGR;
+ break;
+ default:
+ TETH_ERR("Unsupported aggregation protocol\n");
+ return -EFAULT;
+ }
+
+ ipa_aggr_params->aggr_byte_limit =
+ teth_aggr_params->max_transfer_size_byte / 1024;
+ ipa_aggr_params->aggr_time_limit = TETH_DEFAULT_AGGR_TIME_LIMIT;
+ TETH_DBG_FUNC_EXIT();
+
+ return 0;
+}
+
+static int teth_set_aggr_per_ep(
+ const struct teth_aggr_params_link *teth_aggr_params,
+ bool client_is_prod,
+ u32 pipe_hdl)
+{
+ struct ipa_ep_cfg_aggr agg_params;
+ struct ipa_ep_cfg_hdr hdr_params;
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ res = prepare_ipa_aggr_struct(teth_aggr_params,
+ &agg_params,
+ client_is_prod);
+ if (res) {
+ TETH_ERR("prepare_ipa_aggregation_struct() failed\n");
+ goto bail;
+ }
+
+ res = ipa_cfg_ep_aggr(pipe_hdl, &agg_params);
+ if (res) {
+ TETH_ERR("ipa_cfg_ep_aggr() failed\n");
+ goto bail;
+ }
+
+ if (!client_is_prod) {
+ memset(&hdr_params, 0, sizeof(hdr_params));
+ hdr_params.hdr_len = 1;
+ res = ipa_cfg_ep_hdr(pipe_hdl, &hdr_params);
+ if (res) {
+ TETH_ERR("ipa_cfg_ep_hdr() failed\n");
+ goto bail;
+ }
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static void aggr_prot_to_str(enum teth_aggr_protocol_type aggr_prot,
+ char *buff,
+ uint buff_size)
+{
+ switch (aggr_prot) {
+ case TETH_AGGR_PROTOCOL_NONE:
+ strlcpy(buff, "NONE", buff_size);
+ break;
+ case TETH_AGGR_PROTOCOL_MBIM:
+ strlcpy(buff, "MBIM", buff_size);
+ break;
+ case TETH_AGGR_PROTOCOL_TLP:
+ strlcpy(buff, "TLP", buff_size);
+ break;
+ default:
+ strlcpy(buff, "ERROR", buff_size);
+ break;
+ }
+}
+
+static int teth_set_aggregation(void)
+{
+ int res;
+ char aggr_prot_str[20];
+
+ TETH_DBG_FUNC_ENTRY();
+ if (teth_ctx->aggr_params.ul.aggr_prot == TETH_AGGR_PROTOCOL_MBIM ||
+ teth_ctx->aggr_params.dl.aggr_prot == TETH_AGGR_PROTOCOL_MBIM) {
+ res = ipa_set_aggr_mode(IPA_MBIM);
+ if (res) {
+ TETH_ERR("ipa_set_aggr_mode() failed\n");
+ goto bail;
+ }
+ res = ipa_set_single_ndp_per_mbim(false);
+ if (res) {
+ TETH_ERR("ipa_set_single_ndp_per_mbim() failed\n");
+ goto bail;
+ }
+ }
+
+ aggr_prot_to_str(teth_ctx->aggr_params.ul.aggr_prot,
+ aggr_prot_str,
+ sizeof(aggr_prot_str)-1);
+ TETH_DBG("Setting %s aggregation on UL\n", aggr_prot_str);
+ aggr_prot_to_str(teth_ctx->aggr_params.dl.aggr_prot,
+ aggr_prot_str,
+ sizeof(aggr_prot_str)-1);
+ TETH_DBG("Setting %s aggregation on DL\n", aggr_prot_str);
+
+ /* Configure aggregation on UL producer (USB->IPA) */
+ res = teth_set_aggr_per_ep(&teth_ctx->aggr_params.ul,
+ true,
+ teth_ctx->usb_ipa_pipe_hdl);
+ if (res) {
+ TETH_ERR("teth_set_aggregation_per_ep() failed\n");
+ goto bail;
+ }
+
+ /* Configure aggregation on DL consumer (IPA->USB) */
+ res = teth_set_aggr_per_ep(&teth_ctx->aggr_params.dl,
+ false,
+ teth_ctx->ipa_usb_pipe_hdl);
+ if (res) {
+ TETH_ERR("teth_set_aggregation_per_ep() failed\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+bail:
+ return res;
+}
+
+static void complete_hw_bridge(struct work_struct *work)
+{
+ int res;
+ static DEFINE_MUTEX(f_lock);
+
+ mutex_lock(&f_lock);
+
+ TETH_DBG_FUNC_ENTRY();
+ TETH_DBG("Completing HW bridge in %s mode\n",
+ (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) ?
+ "ETHERNET" :
+ "IP");
+
+ res = teth_set_aggregation();
+ if (res) {
+ TETH_ERR("Failed setting aggregation params\n");
+ goto bail;
+ }
+
+ /*
+ * Reset the Header, Routing and Filtering blocks.
+ * Resetting the Header block will also reset the other blocks.
+ * This reset is not comitted to HW.
+ */
+ res = ipa_reset_hdr();
+ if (res) {
+ TETH_ERR("Failed resetting IPA\n");
+ goto bail;
+ }
+
+ res = configure_ipa_header_block();
+ if (res) {
+ TETH_ERR("Configuration of IPA header block Failed\n");
+ goto bail;
+ }
+
+ res = configure_ipa_routing_block();
+ if (res) {
+ TETH_ERR("Configuration of IPA routing block Failed\n");
+ goto bail;
+ }
+
+ res = configure_ipa_filtering_block();
+ if (res) {
+ TETH_ERR("Configuration of IPA filtering block Failed\n");
+ goto bail;
+ }
+
+ teth_ctx->is_hw_bridge_complete = true;
+ teth_ctx->comp_hw_bridge_in_progress = false;
+bail:
+ mutex_unlock(&f_lock);
+ TETH_DBG_FUNC_EXIT();
+
+ return;
+}
+
+static void mac_addr_to_str(u8 mac_addr[ETH_ALEN],
+ char *buff,
+ uint buff_size)
+{
+ scnprintf(buff, buff_size, "%02x-%02x-%02x-%02x-%02x-%02x",
+ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
+ mac_addr[4], mac_addr[5]);
+}
+
+static void check_to_complete_hw_bridge(struct sk_buff *skb,
+ u8 *my_mac_addr,
+ bool *my_mac_addr_known,
+ bool *peer_mac_addr_known)
+{
+ bool both_mac_addresses_known;
+ char mac_addr_str[20];
+
+ if ((teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) &&
+ (!(*my_mac_addr_known))) {
+ memcpy(my_mac_addr, &skb->data[ETH_ALEN], ETH_ALEN);
+ mac_addr_to_str(my_mac_addr,
+ mac_addr_str,
+ sizeof(mac_addr_str)-1);
+ TETH_DBG("Extracted MAC addr: %s\n", mac_addr_str);
+ *my_mac_addr_known = true;
+ }
+
+ both_mac_addresses_known = *my_mac_addr_known && *peer_mac_addr_known;
+ if ((both_mac_addresses_known ||
+ (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP)) &&
+ (!teth_ctx->comp_hw_bridge_in_progress) &&
+ (teth_ctx->aggr_params_known)) {
+ INIT_WORK(&teth_ctx->comp_hw_bridge_work, complete_hw_bridge);
+ teth_ctx->comp_hw_bridge_in_progress = true;
+ schedule_work(&teth_ctx->comp_hw_bridge_work);
+ }
+}
+
+static void usb_notify_cb(void *priv,
+ enum ipa_dp_evt_type evt,
+ unsigned long data)
+{
+ struct sk_buff *skb = (struct sk_buff *)data;
+ int res;
+
+ switch (evt) {
+ case IPA_RECEIVE:
+ if (!teth_ctx->is_hw_bridge_complete)
+ check_to_complete_hw_bridge(
+ skb,
+ teth_ctx->mac_addresses.host_pc_mac_addr,
+ &teth_ctx->mac_addresses.host_pc_mac_addr_known,
+ &teth_ctx->mac_addresses.device_mac_addr_known);
+
+ /* Send the packet to A2, using a2_service driver API */
+ res = a2_mux_write(A2_MUX_TETHERED_0, skb);
+ if (res) {
+ TETH_ERR("Packet send failure, dropping packet !\n");
+ dev_kfree_skb(skb);
+ }
+ break;
+
+ case IPA_WRITE_DONE:
+ dev_kfree_skb(skb);
+ break;
+
+ default:
+ TETH_ERR("Unsupported IPA event !\n");
+ WARN_ON(1);
+ }
+
+ return;
+}
+
+static void a2_notify_cb(void *user_data,
+ enum a2_mux_event_type event,
+ unsigned long data)
+{
+ struct sk_buff *skb = (struct sk_buff *)data;
+ int res;
+
+ switch (event) {
+ case A2_MUX_RECEIVE:
+ if (!teth_ctx->is_hw_bridge_complete)
+ check_to_complete_hw_bridge(
+ skb,
+ teth_ctx->mac_addresses.device_mac_addr,
+ &teth_ctx->mac_addresses.device_mac_addr_known,
+ &teth_ctx->
+ mac_addresses.host_pc_mac_addr_known);
+
+ /* Send the packet to USB */
+ res = ipa_tx_dp(IPA_CLIENT_USB_CONS, skb, NULL);
+ if (res) {
+ TETH_ERR("Packet send failure, dropping packet !\n");
+ dev_kfree_skb(skb);
+ }
+ break;
+
+ case A2_MUX_WRITE_DONE:
+ dev_kfree_skb(skb);
+ break;
+
+ default:
+ TETH_ERR("Unsupported IPA event !\n");
+ WARN_ON(1);
+ }
+
+ return;
+}
+
+static void bridge_prod_notify_cb(void *notify_cb_data,
+ enum ipa_rm_event event,
+ unsigned long data)
+{
+ switch (event) {
+ case IPA_RM_RESOURCE_GRANTED:
+ complete(&teth_ctx->is_bridge_prod_up);
+ break;
+
+ case IPA_RM_RESOURCE_RELEASED:
+ complete(&teth_ctx->is_bridge_prod_down);
+ break;
+
+ default:
+ TETH_ERR("Unsupported notification!\n");
+ WARN_ON(1);
+ break;
+ }
+
+ return;
+}
+
+/**
+* teth_bridge_init() - Initialize the Tethering bridge driver
+* @usb_notify_cb_ptr: Callback function which should be used
+* by the caller. Output parameter.
+* @private_data_ptr: Data for the callback function. Should
+* be used by the caller. Output parameter.
+* Return codes: 0: success,
+* -EINVAL - Bad parameter
+* Other negative value - Failure
+*/
+int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr, void **private_data_ptr)
+{
+ int res = 0;
+ struct ipa_rm_create_params bridge_prod_params;
+
+ TETH_DBG_FUNC_ENTRY();
+ if (usb_notify_cb_ptr == NULL) {
+ TETH_ERR("Bad parameter\n");
+ res = -EINVAL;
+ goto bail;
+ }
+
+ *usb_notify_cb_ptr = usb_notify_cb;
+ *private_data_ptr = NULL;
+
+ /* Build IPA Resource manager dependency graph */
+ bridge_prod_params.name = IPA_RM_RESOURCE_BRIDGE_PROD;
+ bridge_prod_params.reg_params.user_data = NULL;
+ bridge_prod_params.reg_params.notify_cb = bridge_prod_notify_cb;
+ res = ipa_rm_create_resource(&bridge_prod_params);
+ if (res) {
+ TETH_ERR("ipa_rm_create_resource() failed\n");
+ goto bail;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
+ if (res) {
+ TETH_ERR("ipa_rm_add_dependency() failed\n");
+ goto bail;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+ if (res) {
+ TETH_ERR("ipa_rm_add_dependency() failed\n");
+ goto fail_add_dependency_1;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_USB_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+ if (res) {
+ TETH_ERR("ipa_rm_add_dependency() failed\n");
+ goto fail_add_dependency_2;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_A2_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
+ if (res) {
+ TETH_ERR("ipa_rm_add_dependency() failed\n");
+ goto fail_add_dependency_3;
+ }
+
+ init_completion(&teth_ctx->is_bridge_prod_up);
+ init_completion(&teth_ctx->is_bridge_prod_down);
+
+ /* The default link protocol is Ethernet */
+ teth_ctx->link_protocol = TETH_LINK_PROTOCOL_ETHERNET;
+ goto bail;
+
+fail_add_dependency_3:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+fail_add_dependency_2:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+fail_add_dependency_1:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
+bail:
+ TETH_DBG_FUNC_EXIT();
+ return res;
+}
+EXPORT_SYMBOL(teth_bridge_init);
+
+/**
+* teth_bridge_disconnect() - Disconnect tethering bridge module
+*
+* Return codes: 0: success
+* -EPERM: Operation not permitted as the bridge is already
+* disconnected
+*/
+int teth_bridge_disconnect(void)
+{
+ int res = -EPERM;
+
+ TETH_DBG_FUNC_ENTRY();
+ if (!teth_ctx->is_connected) {
+ TETH_ERR(
+ "Trying to disconnect an already disconnected bridge\n");
+ goto bail;
+ }
+
+ teth_ctx->is_connected = false;
+
+ res = ipa_rm_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ if (res == -EINPROGRESS)
+ wait_for_completion(&teth_ctx->is_bridge_prod_down);
+
+bail:
+ TETH_DBG_FUNC_EXIT();
+ return res;
+}
+EXPORT_SYMBOL(teth_bridge_disconnect);
+
+/**
+* teth_bridge_connect() - Connect bridge for a tethered Rmnet / MBIM call
+* @connect_params: Connection info
+*
+* Return codes: 0: success
+* -EINVAL: invalid parameters
+* -EPERM: Operation not permitted as the bridge is already
+* connected
+*/
+int teth_bridge_connect(struct teth_bridge_connect_params *connect_params)
+{
+ int res;
+ struct ipa_ep_cfg ipa_ep_cfg;
+
+ TETH_DBG_FUNC_ENTRY();
+ if (teth_ctx->is_connected) {
+ TETH_ERR("Trying to connect an already connected bridge !\n");
+ return -EPERM;
+ }
+ if (connect_params == NULL ||
+ connect_params->ipa_usb_pipe_hdl <= 0 ||
+ connect_params->usb_ipa_pipe_hdl <= 0 ||
+ connect_params->tethering_mode >= TETH_TETHERING_MODE_MAX ||
+ connect_params->tethering_mode < 0)
+ return -EINVAL;
+
+ teth_ctx->ipa_usb_pipe_hdl = connect_params->ipa_usb_pipe_hdl;
+ teth_ctx->usb_ipa_pipe_hdl = connect_params->usb_ipa_pipe_hdl;
+ teth_ctx->tethering_mode = connect_params->tethering_mode;
+
+ res = ipa_rm_request_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ if (res < 0) {
+ if (res == -EINPROGRESS)
+ wait_for_completion(&teth_ctx->is_bridge_prod_up);
+ else
+ goto bail;
+ }
+
+ res = a2_mux_open_channel(A2_MUX_TETHERED_0,
+ NULL,
+ a2_notify_cb);
+ if (res) {
+ TETH_ERR("a2_mux_open_channel() failed\n");
+ goto bail;
+ }
+
+ res = a2_mux_get_tethered_client_handles(A2_MUX_TETHERED_0,
+ &teth_ctx->ipa_a2_pipe_hdl,
+ &teth_ctx->a2_ipa_pipe_hdl);
+ if (res) {
+ TETH_ERR(
+ "a2_mux_get_tethered_client_handles() failed, res = %d\n", res);
+ goto bail;
+ }
+
+ /* Reset the various endpoints configuration */
+ memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
+ ipa_cfg_ep(teth_ctx->ipa_usb_pipe_hdl, &ipa_ep_cfg);
+ ipa_cfg_ep(teth_ctx->usb_ipa_pipe_hdl, &ipa_ep_cfg);
+ ipa_cfg_ep(teth_ctx->ipa_a2_pipe_hdl, &ipa_ep_cfg);
+ ipa_cfg_ep(teth_ctx->a2_ipa_pipe_hdl, &ipa_ep_cfg);
+
+ teth_ctx->is_connected = true;
+
+ if (teth_ctx->tethering_mode == TETH_TETHERING_MODE_MBIM)
+ teth_ctx->link_protocol = TETH_LINK_PROTOCOL_IP;
+ TETH_DBG_FUNC_EXIT();
+bail:
+ if (res)
+ ipa_rm_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ return res;
+}
+EXPORT_SYMBOL(teth_bridge_connect);
+
+static void set_aggr_default_params(struct teth_aggr_params_link *params)
+{
+ if (params->max_datagrams == 0)
+ params->max_datagrams = 16;
+ if (params->max_transfer_size_byte == 0)
+ params->max_transfer_size_byte = 16*1024;
+}
+
+static void teth_set_bridge_mode(enum teth_link_protocol_type link_protocol)
+{
+ teth_ctx->link_protocol = link_protocol;
+ teth_ctx->is_hw_bridge_complete = false;
+ memset(&teth_ctx->mac_addresses, 0, sizeof(teth_ctx->mac_addresses));
+}
+
+static long teth_bridge_ioctl(struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int res = 0;
+
+ TETH_DBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
+
+ if ((_IOC_TYPE(cmd) != TETH_BRIDGE_IOC_MAGIC) ||
+ (_IOC_NR(cmd) >= TETH_BRIDGE_IOCTL_MAX)) {
+ TETH_ERR("Invalid ioctl\n");
+ return -ENOIOCTLCMD;
+ }
+
+ switch (cmd) {
+ case TETH_BRIDGE_IOC_SET_BRIDGE_MODE:
+ TETH_DBG("TETH_BRIDGE_IOC_SET_BRIDGE_MODE ioctl called\n");
+ if (teth_ctx->link_protocol != arg)
+ teth_set_bridge_mode(arg);
+ break;
+
+ case TETH_BRIDGE_IOC_SET_AGGR_PARAMS:
+ TETH_DBG("TETH_BRIDGE_IOC_SET_AGGR_PARAMS ioctl called\n");
+ res = copy_from_user(&teth_ctx->aggr_params,
+ (struct teth_aggr_params *)arg,
+ sizeof(struct teth_aggr_params));
+ if (res) {
+ TETH_ERR("Error, res = %d\n", res);
+ res = -EFAULT;
+ break;
+ }
+ set_aggr_default_params(&teth_ctx->aggr_params.dl);
+ set_aggr_default_params(&teth_ctx->aggr_params.ul);
+ teth_ctx->aggr_params_known = true;
+ break;
+
+ case TETH_BRIDGE_IOC_GET_AGGR_PARAMS:
+ TETH_DBG("TETH_BRIDGE_IOC_GET_AGGR_PARAMS ioctl called\n");
+ if (copy_to_user((u8 *)arg, (u8 *)&teth_ctx->aggr_params,
+ sizeof(struct teth_aggr_params))) {
+ res = -EFAULT;
+ break;
+ }
+ break;
+
+ case TETH_BRIDGE_IOC_GET_AGGR_CAPABILITIES:
+ {
+ u16 sz;
+ u16 pyld_sz;
+ struct teth_aggr_capabilities caps;
+
+ TETH_DBG("GET_AGGR_CAPABILITIES ioctl called\n");
+ sz = sizeof(struct teth_aggr_capabilities);
+ if (copy_from_user(&caps,
+ (struct teth_aggr_capabilities *)arg,
+ sz)) {
+ res = -EFAULT;
+ break;
+ }
+
+ if (caps.num_protocols < teth_ctx->aggr_caps->num_protocols) {
+ caps.num_protocols = teth_ctx->aggr_caps->num_protocols;
+ if (copy_to_user((struct teth_aggr_capabilities *)arg,
+ &caps,
+ sz)) {
+ res = -EFAULT;
+ break;
+ }
+ TETH_DBG("Not enough space allocated.\n");
+ res = -EAGAIN;
+ break;
+ }
+
+ pyld_sz = sz + caps.num_protocols *
+ sizeof(struct teth_aggr_params_link);
+
+ if (copy_to_user((u8 *)arg,
+ (u8 *)(teth_ctx->aggr_caps),
+ pyld_sz)) {
+ res = -EFAULT;
+ break;
+ }
+ }
+ break;
+ }
+
+ return res;
+}
+
+static void set_aggr_capabilities(void)
+{
+ u16 NUM_PROTOCOLS = 2;
+
+ teth_ctx->aggr_caps = kzalloc(sizeof(struct teth_aggr_capabilities) +
+ NUM_PROTOCOLS *
+ sizeof(struct teth_aggr_params_link),
+ GFP_KERNEL);
+ if (teth_ctx->aggr_caps == NULL) {
+ TETH_ERR("Memory alloc failed for aggregation capabilities.\n");
+ return;
+ }
+
+ teth_ctx->aggr_caps->num_protocols = NUM_PROTOCOLS;
+
+ teth_ctx->aggr_caps->prot_caps[0].aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
+ teth_ctx->aggr_caps->prot_caps[0].max_datagrams = 16;
+ teth_ctx->aggr_caps->prot_caps[0].max_transfer_size_byte = 16*1024;
+
+ teth_ctx->aggr_caps->prot_caps[1].aggr_prot = TETH_AGGR_PROTOCOL_TLP;
+ teth_ctx->aggr_caps->prot_caps[1].max_datagrams = 16;
+ teth_ctx->aggr_caps->prot_caps[1].max_transfer_size_byte = 16*1024;
+}
+
+void teth_bridge_get_client_handles(u32 *producer_handle,
+ u32 *consumer_handle)
+{
+ if (producer_handle == NULL || consumer_handle == NULL)
+ return;
+
+ *producer_handle = teth_ctx->usb_ipa_pipe_hdl;
+ *consumer_handle = teth_ctx->ipa_usb_pipe_hdl;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *dent;
+static struct dentry *dfile_link_protocol;
+static struct dentry *dfile_get_aggr_params;
+static struct dentry *dfile_set_aggr_protocol;
+
+static ssize_t teth_debugfs_read_link_protocol(struct file *file,
+ char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ int nbytes;
+
+ nbytes = scnprintf(dbg_buff, TETH_MAX_MSG_LEN, "Link protocol = %s\n",
+ (teth_ctx->link_protocol ==
+ TETH_LINK_PROTOCOL_ETHERNET) ?
+ "ETHERNET" :
+ "IP");
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+static ssize_t teth_debugfs_write_link_protocol(struct file *file,
+ const char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ unsigned long missing;
+ enum teth_link_protocol_type link_protocol;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ missing = copy_from_user(dbg_buff, ubuf, count);
+ if (missing)
+ return -EFAULT;
+
+ if (count > 0)
+ dbg_buff[count-1] = '\0';
+
+ if (strcmp(dbg_buff, "ETHERNET") == 0) {
+ link_protocol = TETH_LINK_PROTOCOL_ETHERNET;
+ } else if (strcmp(dbg_buff, "IP") == 0) {
+ link_protocol = TETH_LINK_PROTOCOL_IP;
+ } else {
+ TETH_ERR("Bad link protocol, got %s,\n"
+ "Use <ETHERNET> or <IP>.\n", dbg_buff);
+ return count;
+ }
+
+ teth_set_bridge_mode(link_protocol);
+
+ return count;
+}
+
+static ssize_t teth_debugfs_read_aggr_params(struct file *file,
+ char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ int nbytes = 0;
+ char aggr_str[20];
+
+ aggr_prot_to_str(teth_ctx->aggr_params.ul.aggr_prot,
+ aggr_str,
+ sizeof(aggr_str)-1);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN,
+ "Aggregation parameters for uplink:\n");
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Aggregation protocol: %s\n",
+ aggr_str);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Max transfer size [byte]: %d\n",
+ teth_ctx->aggr_params.ul.max_transfer_size_byte);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Max datagrams: %d\n",
+ teth_ctx->aggr_params.ul.max_datagrams);
+
+ aggr_prot_to_str(teth_ctx->aggr_params.dl.aggr_prot,
+ aggr_str,
+ sizeof(aggr_str)-1);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN,
+ "Aggregation parameters for downlink:\n");
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Aggregation protocol: %s\n",
+ aggr_str);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Max transfer size [byte]: %d\n",
+ teth_ctx->aggr_params.dl.max_transfer_size_byte);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Max datagrams: %d\n",
+ teth_ctx->aggr_params.dl.max_datagrams);
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+static ssize_t teth_debugfs_set_aggr_protocol(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long missing;
+ enum teth_aggr_protocol_type aggr_prot;
+ int res;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ missing = copy_from_user(dbg_buff, ubuf, count);
+ if (missing)
+ return -EFAULT;
+
+ if (count > 0)
+ dbg_buff[count-1] = '\0';
+
+ set_aggr_default_params(&teth_ctx->aggr_params.dl);
+ set_aggr_default_params(&teth_ctx->aggr_params.ul);
+
+ if (strcmp(dbg_buff, "NONE") == 0) {
+ aggr_prot = TETH_AGGR_PROTOCOL_NONE;
+ } else if (strcmp(dbg_buff, "MBIM") == 0) {
+ aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
+ } else if (strcmp(dbg_buff, "TLP") == 0) {
+ aggr_prot = TETH_AGGR_PROTOCOL_TLP;
+ } else {
+ TETH_ERR("Bad aggregation protocol, got %s,\n"
+ "Use <NONE>, <MBIM> or <TLP>.\n", dbg_buff);
+ return count;
+ }
+
+ teth_ctx->aggr_params.dl.aggr_prot = aggr_prot;
+ teth_ctx->aggr_params.ul.aggr_prot = aggr_prot;
+ teth_ctx->aggr_params_known = true;
+
+ res = teth_set_aggregation();
+ if (res)
+ TETH_ERR("Failed setting aggregation params\n");
+
+ return count;
+}
+
+const struct file_operations teth_link_protocol_ops = {
+ .read = teth_debugfs_read_link_protocol,
+ .write = teth_debugfs_write_link_protocol,
+};
+
+const struct file_operations teth_get_aggr_params_ops = {
+ .read = teth_debugfs_read_aggr_params,
+};
+
+const struct file_operations teth_set_aggr_protocol_ops = {
+ .write = teth_debugfs_set_aggr_protocol,
+};
+
+void teth_debugfs_init(void)
+{
+ const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
+ const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
+ S_IWUSR | S_IWGRP | S_IWOTH;
+
+ dent = debugfs_create_dir("ipa_teth", 0);
+ if (IS_ERR(dent)) {
+ IPAERR("fail to create folder ipa_teth debug_fs.\n");
+ return;
+ }
+
+ dfile_link_protocol =
+ debugfs_create_file("link_protocol", read_write_mode, dent, 0,
+ &teth_link_protocol_ops);
+ if (!dfile_link_protocol || IS_ERR(dfile_link_protocol)) {
+ IPAERR("fail to create file link_protocol\n");
+ goto fail;
+ }
+
+ dfile_get_aggr_params =
+ debugfs_create_file("get_aggr_params", read_only_mode, dent, 0,
+ &teth_get_aggr_params_ops);
+ if (!dfile_get_aggr_params || IS_ERR(dfile_get_aggr_params)) {
+ IPAERR("fail to create file get_aggr_params\n");
+ goto fail;
+ }
+
+ dfile_set_aggr_protocol =
+ debugfs_create_file("set_aggr_protocol", read_only_mode, dent,
+ 0, &teth_set_aggr_protocol_ops);
+ if (!dfile_set_aggr_protocol || IS_ERR(dfile_set_aggr_protocol)) {
+ IPAERR("fail to create file set_aggr_protocol\n");
+ goto fail;
+ }
+
+ return;
+fail:
+ debugfs_remove_recursive(dent);
+}
+#else
+void teth_debugfs_init(void) {}
+#endif /* CONFIG_DEBUG_FS */
+
+
+static const struct file_operations teth_bridge_drv_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = teth_bridge_ioctl,
+};
+
+/**
+* teth_bridge_driver_init() - Initialize tethering bridge driver
+*
+*/
+int teth_bridge_driver_init(void)
+{
+ int res;
+
+ TETH_DBG("Tethering bridge driver init\n");
+ teth_ctx = kzalloc(sizeof(*teth_ctx), GFP_KERNEL);
+ if (!teth_ctx) {
+ TETH_ERR("kzalloc err.\n");
+ return -ENOMEM;
+ }
+
+ set_aggr_capabilities();
+
+ teth_ctx->class = class_create(THIS_MODULE, TETH_BRIDGE_DRV_NAME);
+
+ res = alloc_chrdev_region(&teth_ctx->dev_num, 0, 1,
+ TETH_BRIDGE_DRV_NAME);
+ if (res) {
+ TETH_ERR("alloc_chrdev_region err.\n");
+ res = -ENODEV;
+ goto fail_alloc_chrdev_region;
+ }
+
+ teth_ctx->dev = device_create(teth_ctx->class, NULL, teth_ctx->dev_num,
+ teth_ctx, TETH_BRIDGE_DRV_NAME);
+ if (IS_ERR(teth_ctx->dev)) {
+ TETH_ERR(":device_create err.\n");
+ res = -ENODEV;
+ goto fail_device_create;
+ }
+
+ cdev_init(&teth_ctx->cdev, &teth_bridge_drv_fops);
+ teth_ctx->cdev.owner = THIS_MODULE;
+ teth_ctx->cdev.ops = &teth_bridge_drv_fops;
+
+ res = cdev_add(&teth_ctx->cdev, teth_ctx->dev_num, 1);
+ if (res) {
+ TETH_ERR(":cdev_add err=%d\n", -res);
+ res = -ENODEV;
+ goto fail_cdev_add;
+ }
+
+ teth_ctx->comp_hw_bridge_in_progress = false;
+
+ teth_debugfs_init();
+ TETH_DBG("Tethering bridge driver init OK\n");
+
+ return 0;
+fail_cdev_add:
+ device_destroy(teth_ctx->class, teth_ctx->dev_num);
+fail_device_create:
+ unregister_chrdev_region(teth_ctx->dev_num, 1);
+fail_alloc_chrdev_region:
+ kfree(teth_ctx->aggr_caps);
+ kfree(teth_ctx);
+ teth_ctx = NULL;
+
+ return res;
+}
+EXPORT_SYMBOL(teth_bridge_driver_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Tethering bridge driver");
diff --git a/drivers/platform/msm/ssbi.c b/drivers/platform/msm/ssbi.c
index a08eb48..e0bbdd1 100644
--- a/drivers/platform/msm/ssbi.c
+++ b/drivers/platform/msm/ssbi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
* Copyright (c) 2010, Google Inc.
*
* Original authors: Code Aurora Forum
@@ -362,7 +362,7 @@
ssbi->base = ioremap(mem_res->start, resource_size(mem_res));
if (!ssbi->base) {
- pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start);
+ pr_err("ioremap failed: %pr\n", mem_res);
ret = -EINVAL;
goto err_ioremap;
}
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index f87a443..03b3e0d 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -255,12 +255,9 @@
struct dentry *dent;
struct bms_notify bms_notify;
int *usb_trim_table;
- struct regulator *vreg_xoadc;
bool ext_charging;
bool ext_charge_done;
bool iusb_fine_res;
- bool final_kickstart;
- bool lockup_lpm_wrkarnd;
DECLARE_BITMAP(enabled_irqs, PM_CHG_MAX_INTS);
struct work_struct battery_id_valid_work;
int64_t batt_id_min;
@@ -296,6 +293,7 @@
int stop_chg_upon_expiry;
bool disable_aicl;
int usb_type;
+ bool disable_chg_rmvl_wrkarnd;
};
/* user space parameter to limit usb current */
@@ -311,7 +309,6 @@
static struct pm8921_chg_chip *the_chip;
-static DEFINE_SPINLOCK(lpm_lock);
#define LPM_ENABLE_BIT BIT(2)
static int pm8921_chg_set_lpm(struct pm8921_chg_chip *chip, int enable)
{
@@ -340,66 +337,11 @@
static int pm_chg_write(struct pm8921_chg_chip *chip, u16 addr, u8 reg)
{
int rc;
- unsigned long flags = 0;
- u8 temp;
- /* Disable LPM */
- if (chip->lockup_lpm_wrkarnd) {
- spin_lock_irqsave(&lpm_lock, flags);
+ rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+ if (rc)
+ pr_err("failed: addr=%03X, rc=%d\n", addr, rc);
- /*
- * This delay is to prevent exit out of 32khz mode within
- * 200uS. It could be that chg was removed just few uS before
- * this gets called.
- */
- udelay(200);
- /* no clks */
- temp = 0xD1;
- rc = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (rc) {
- pr_err("Error %d writing %d to CHG_TEST\n", rc, temp);
- goto release_lpm_lock;
- }
-
- /* force 19.2Mhz before reading */
- temp = 0xD3;
- rc = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (rc) {
- pr_err("Error %d writing %d to CHG_TEST\n", rc, temp);
- goto release_lpm_lock;
- }
-
- rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
- if (rc) {
- pr_err("failed: addr=%03X, rc=%d\n", addr, rc);
- goto release_lpm_lock;
- }
-
- /* no clks */
- temp = 0xD1;
- rc = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (rc) {
- pr_err("Error %d writing %d to CHG_TEST\n", rc, temp);
- goto release_lpm_lock;
- }
-
- /* switch to hw clk selection */
- temp = 0xD0;
- rc = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (rc) {
- pr_err("Error %d writing %d to CHG_TEST\n", rc, temp);
- goto release_lpm_lock;
- }
-
- udelay(200);
-
-release_lpm_lock:
- spin_unlock_irqrestore(&lpm_lock, flags);
- } else {
- rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
- if (rc)
- pr_err("failed: addr=%03X, rc=%d\n", addr, rc);
- }
return rc;
}
@@ -430,23 +372,6 @@
chip->pmic_chg_irq[irq_id]);
}
-static int is_chg_on_bat(struct pm8921_chg_chip *chip)
-{
- return !(pm_chg_get_rt_status(chip, DCIN_VALID_IRQ)
- || pm_chg_get_rt_status(chip, USBIN_VALID_IRQ));
-}
-
-static void pm8921_chg_bypass_bat_gone_debounce(struct pm8921_chg_chip *chip,
- int bypass)
-{
- int rc;
-
- rc = pm_chg_write(chip, COMPARATOR_OVERRIDE, bypass ? 0x89 : 0x88);
- if (rc) {
- pr_err("Failed to set bypass bit to %d rc=%d\n", bypass, rc);
- }
-}
-
/* Treat OverVoltage/UnderVoltage as source missing */
static int is_usb_chg_plugged_in(struct pm8921_chg_chip *chip)
{
@@ -469,35 +394,8 @@
static int pm_chg_get_fsm_state(struct pm8921_chg_chip *chip)
{
u8 temp;
- unsigned long flags = 0;
int err = 0, ret = 0;
- if (chip->lockup_lpm_wrkarnd) {
- spin_lock_irqsave(&lpm_lock, flags);
-
- /*
- * This delay is to prevent exit out of 32khz mode within
- * 200uS. It could be that chg was removed just few uS before
- * this gets called.
- */
- udelay(200);
- /* no clks */
- temp = 0xD1;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to CHG_TEST\n", err, temp);
- goto err_out;
- }
-
- /* force 19.2Mhz before reading */
- temp = 0xD3;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to CHG_TEST\n", err, temp);
- goto err_out;
- }
- }
-
temp = CAPTURE_FSM_STATE_CMD;
err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
if (err) {
@@ -535,29 +433,7 @@
/* get the upper 1 bit */
ret |= (temp & 0x1) << 4;
- if (chip->lockup_lpm_wrkarnd) {
- /* no clks */
- temp = 0xD1;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to CHG_TEST\n", err, temp);
- goto err_out;
- }
-
- /* switch to hw clk selection */
- temp = 0xD0;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to CHG_TEST\n", err, temp);
- goto err_out;
- }
-
- udelay(200);
- }
-
err_out:
- if (chip->lockup_lpm_wrkarnd)
- spin_unlock_irqrestore(&lpm_lock, flags);
if (err)
return err;
@@ -568,35 +444,8 @@
static int pm_chg_get_regulation_loop(struct pm8921_chg_chip *chip)
{
u8 temp, data;
- unsigned long flags = 0;
int err = 0;
- if (chip->lockup_lpm_wrkarnd) {
- spin_lock_irqsave(&lpm_lock, flags);
-
- /*
- * This delay is to prevent exit out of 32khz mode within
- * 200uS. It could be that chg was removed just few uS before
- * this gets called.
- */
- udelay(200);
- /* no clks */
- temp = 0xD1;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to CHG_TEST\n", err, temp);
- goto err_out;
- }
-
- /* force 19.2Mhz before reading */
- temp = 0xD3;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to CHG_TEST\n", err, temp);
- goto err_out;
- }
- }
-
temp = READ_BANK_6;
err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
if (err) {
@@ -610,29 +459,7 @@
goto err_out;
}
- if (chip->lockup_lpm_wrkarnd) {
- /* no clks */
- temp = 0xD1;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to CHG_TEST\n", err, temp);
- goto err_out;
- }
-
- /* switch to hw clk selection */
- temp = 0xD0;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to CHG_TEST\n", err, temp);
- goto err_out;
- }
-
- udelay(200);
- }
-
err_out:
- if (chip->lockup_lpm_wrkarnd)
- spin_unlock_irqrestore(&lpm_lock, flags);
if (err)
return err;
@@ -2099,10 +1926,10 @@
* This would also apply when the battery has been
* removed from the running system.
*/
- if (the_chip && !get_prop_batt_present(the_chip)
+ if (mA == 0 && the_chip && !get_prop_batt_present(the_chip)
&& !is_dc_chg_plugged_in(the_chip)) {
if (!the_chip->has_dc_supply) {
- pr_err("rejected: no other power source connected\n");
+ pr_err("rejected: no other power source mA = %d\n", mA);
return;
}
}
@@ -2377,96 +2204,9 @@
return get_prop_batt_temp(the_chip);
}
-static int __pm8921_apply_19p2mhz_kickstart(struct pm8921_chg_chip *chip)
-{
- int err;
- u8 temp;
-
-
- temp = 0xD1;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
- return err;
- }
-
- temp = 0xD3;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
- return err;
- }
-
- temp = 0xD1;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
- return err;
- }
-
- temp = 0xD5;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
- return err;
- }
-
- /* Wait a few clock cycles before re-enabling hw clock switching */
- udelay(183);
-
- temp = 0xD1;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
- return err;
- }
-
- temp = 0xD0;
- err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
- if (err) {
- pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
- return err;
- }
-
- /* Wait for few clock cycles before re-enabling LPM */
- udelay(32);
-
- return 0;
-}
-
-static int pm8921_apply_19p2mhz_kickstart(struct pm8921_chg_chip *chip)
-{
- int err;
- unsigned long flags = 0;
-
- spin_lock_irqsave(&lpm_lock, flags);
- err = pm8921_chg_set_lpm(chip, 0);
- if (err) {
- pr_err("Error settig LPM rc=%d\n", err);
- goto kick_err;
- }
-
- __pm8921_apply_19p2mhz_kickstart(chip);
-
-kick_err:
- err = pm8921_chg_set_lpm(chip, 1);
- if (err)
- pr_err("Error settig LPM rc=%d\n", err);
-
- spin_unlock_irqrestore(&lpm_lock, flags);
-
- return err;
-}
-
static void handle_usb_insertion_removal(struct pm8921_chg_chip *chip)
{
- int usb_present, rc = 0;
-
- if (chip->lockup_lpm_wrkarnd) {
- rc = pm8921_apply_19p2mhz_kickstart(chip);
- if (rc)
- pr_err("Failed to apply kickstart rc=%d\n", rc);
- }
+ int usb_present;
pm_chg_failed_clear(chip, 1);
usb_present = is_usb_chg_plugged_in(chip);
@@ -2476,11 +2216,6 @@
power_supply_changed(&chip->usb_psy);
power_supply_changed(&chip->batt_psy);
pm8921_bms_calibrate_hkadc();
-
- /* Enable/disable bypass if charger is on battery */
- if (chip->lockup_lpm_wrkarnd)
- pm8921_chg_bypass_bat_gone_debounce(chip,
- is_chg_on_bat(chip));
}
if (usb_present) {
schedule_delayed_work(&chip->unplug_check_work,
@@ -2496,10 +2231,6 @@
static void handle_stop_ext_chg(struct pm8921_chg_chip *chip)
{
- if (chip->lockup_lpm_wrkarnd)
- /* Enable bypass if charger is on battery */
- pm8921_chg_bypass_bat_gone_debounce(chip, is_chg_on_bat(chip));
-
if (!chip->ext_psy) {
pr_debug("external charger not registered.\n");
return;
@@ -2529,10 +2260,6 @@
unsigned long delay =
round_jiffies_relative(msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
- /* Disable bypass if charger connected and not running on bat */
- if (chip->lockup_lpm_wrkarnd)
- pm8921_chg_bypass_bat_gone_debounce(chip, is_chg_on_bat(chip));
-
if (!chip->ext_psy) {
pr_debug("external charger not registered.\n");
return;
@@ -3014,28 +2741,12 @@
pm_chg_get_fsm_state(chip),
get_prop_batt_current(chip)
);
- if (chip->lockup_lpm_wrkarnd) {
- rc = pm8921_apply_19p2mhz_kickstart(chip);
- if (rc)
- pr_err("Failed kickstart rc=%d\n", rc);
-
- /*
- * Make sure kickstart happens at least 200 ms
- * after charger has been removed.
- */
- if (chip->final_kickstart) {
- chip->final_kickstart = false;
- goto check_again_later;
- }
- }
return;
} else {
goto check_again_later;
}
}
- chip->final_kickstart = true;
-
/* AICL only for usb wall charger */
if ((active_path & USB_ACTIVE_BIT) && usb_target_ma > 0 &&
!chip->disable_aicl) {
@@ -3057,7 +2768,7 @@
pr_debug("reg_loop=0x%x usb_ma = %d\n", reg_loop, usb_ma);
ibat = get_prop_batt_current(chip);
- if (reg_loop & VIN_ACTIVE_BIT) {
+ if ((reg_loop & VIN_ACTIVE_BIT) && !chip->disable_chg_rmvl_wrkarnd) {
if (ibat > 0) {
pr_debug("revboost ibat = %d fsm = %d loop = 0x%x\n",
ibat, pm_chg_get_fsm_state(chip), reg_loop);
@@ -3077,7 +2788,8 @@
active_path, active_chg_plugged_in);
chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
- if (chg_gone == 1 && active_chg_plugged_in == 1) {
+ if (chg_gone == 1 && active_chg_plugged_in == 1 &&
+ !chip->disable_chg_rmvl_wrkarnd) {
pr_debug("chg_gone=%d, active_chg_plugged_in = %d\n",
chg_gone, active_chg_plugged_in);
unplug_ovp_fet_open(chip);
@@ -3328,11 +3040,6 @@
else
handle_stop_ext_chg(chip);
} else {
- if (chip->lockup_lpm_wrkarnd)
- /* if no external supply call bypass debounce here */
- pm8921_chg_bypass_bat_gone_debounce(chip,
- is_chg_on_bat(chip));
-
if (dc_present)
schedule_delayed_work(&chip->unplug_check_work,
msecs_to_jiffies(UNPLUG_CHECK_WAIT_PERIOD_MS));
@@ -4164,6 +3871,91 @@
return -EINVAL;
}
+static void pm8921_chg_force_19p2mhz_clk(struct pm8921_chg_chip *chip)
+{
+ int err;
+ u8 temp;
+
+ temp = 0xD1;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+
+ temp = 0xD3;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+
+ temp = 0xD1;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+
+ temp = 0xD5;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+
+ udelay(183);
+
+ temp = 0xD1;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+
+ temp = 0xD0;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+ udelay(32);
+
+ temp = 0xD1;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+
+ temp = 0xD3;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+}
+
+static void pm8921_chg_set_hw_clk_switching(struct pm8921_chg_chip *chip)
+{
+ int err;
+ u8 temp;
+
+ temp = 0xD1;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+
+ temp = 0xD0;
+ err = pm_chg_write(chip, CHG_TEST, temp);
+ if (err) {
+ pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+ return;
+ }
+}
+
#define VREF_BATT_THERM_FORCE_ON BIT(7)
static void detect_battery_removal(struct pm8921_chg_chip *chip)
{
@@ -4195,15 +3987,8 @@
u8 subrev;
int rc, vdd_safe, fcc_uah, safety_time = DEFAULT_SAFETY_MINUTES;
- spin_lock_init(&lpm_lock);
-
- if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921) {
- rc = __pm8921_apply_19p2mhz_kickstart(chip);
- if (rc) {
- pr_err("Failed to apply kickstart rc=%d\n", rc);
- return rc;
- }
- }
+ /* forcing 19p2mhz before accessing any charger registers */
+ pm8921_chg_force_19p2mhz_clk(chip);
detect_battery_removal(chip);
@@ -4451,45 +4236,6 @@
return rc;
}
- if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921) {
- /* Clear kickstart */
- rc = pm8xxx_writeb(chip->dev->parent, CHG_TEST, 0xD0);
- if (rc) {
- pr_err("Failed to clear kickstart rc=%d\n", rc);
- return rc;
- }
-
- /* From here the lpm_workaround will be active */
- chip->lockup_lpm_wrkarnd = true;
-
- /* Enable LPM */
- pm8921_chg_set_lpm(chip, 1);
- }
-
- if (chip->lockup_lpm_wrkarnd) {
- chip->vreg_xoadc = regulator_get(chip->dev, "vreg_xoadc");
- if (IS_ERR(chip->vreg_xoadc))
- return -ENODEV;
-
- rc = regulator_set_optimum_mode(chip->vreg_xoadc, 10000);
- if (rc < 0) {
- pr_err("Failed to set configure HPM rc=%d\n", rc);
- return rc;
- }
-
- rc = regulator_set_voltage(chip->vreg_xoadc, 1800000, 1800000);
- if (rc) {
- pr_err("Failed to set L14 voltage rc=%d\n", rc);
- return rc;
- }
-
- rc = regulator_enable(chip->vreg_xoadc);
- if (rc) {
- pr_err("Failed to enable L14 rc=%d\n", rc);
- return rc;
- }
- }
-
return 0;
}
@@ -4740,19 +4486,16 @@
int rc;
struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
- if (chip->lockup_lpm_wrkarnd) {
- rc = regulator_disable(chip->vreg_xoadc);
- if (rc)
- pr_err("Failed to disable L14 rc=%d\n", rc);
-
- rc = pm8921_apply_19p2mhz_kickstart(chip);
- if (rc)
- pr_err("Failed to apply kickstart rc=%d\n", rc);
- }
-
rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON, 0);
if (rc)
pr_err("Failed to Force Vref therm off rc=%d\n", rc);
+
+ rc = pm8921_chg_set_lpm(chip, 1);
+ if (rc)
+ pr_err("Failed to set lpm rc=%d\n", rc);
+
+ pm8921_chg_set_hw_clk_switching(chip);
+
return 0;
}
@@ -4761,15 +4504,11 @@
int rc;
struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
- if (chip->lockup_lpm_wrkarnd) {
- rc = regulator_enable(chip->vreg_xoadc);
- if (rc)
- pr_err("Failed to enable L14 rc=%d\n", rc);
+ pm8921_chg_force_19p2mhz_clk(chip);
- rc = pm8921_apply_19p2mhz_kickstart(chip);
- if (rc)
- pr_err("Failed to apply kickstart rc=%d\n", rc);
- }
+ rc = pm8921_chg_set_lpm(chip, 0);
+ if (rc)
+ pr_err("Failed to set lpm rc=%d\n", rc);
rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON,
VREF_BATT_THERM_FORCE_ON);
@@ -4869,6 +4608,7 @@
chip->vin_min = pdata->vin_min;
chip->thermal_mitigation = pdata->thermal_mitigation;
chip->thermal_levels = pdata->thermal_levels;
+ chip->disable_chg_rmvl_wrkarnd = pdata->disable_chg_rmvl_wrkarnd;
chip->cold_thr = pdata->cold_thr;
chip->hot_thr = pdata->hot_thr;
@@ -5000,7 +4740,6 @@
{
struct pm8921_chg_chip *chip = platform_get_drvdata(pdev);
- regulator_put(chip->vreg_xoadc);
free_irqs(chip);
platform_set_drvdata(pdev, NULL);
the_chip = NULL;
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index eb75475..ec0b0e7 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -1095,60 +1095,6 @@
return 1;
}
-#define BMS_OVERRIDE_MODE_EN_BIT BIT(7)
-#define EN_VBAT_BIT BIT(0)
-#define OVERRIDE_MODE_DELAY_MS 20
-static int override_mode_batt_v_and_i(
- struct qpnp_bms_chip *chip, int *ibat_ua, int *vbat_uv)
-{
- int16_t vsense_raw, vbat_raw;
- int vsense_uv, rc;
- u8 delay;
-
- mutex_lock(&chip->bms_output_lock);
-
- delay = 0x00;
- rc = qpnp_write_wrapper(chip, &delay,
- chip->base + BMS1_S1_DELAY_CTL, 1);
- if (rc)
- pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc);
-
- rc = qpnp_masked_write(chip, BMS1_MODE_CTL,
- BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT,
- BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT);
- if (rc)
- pr_err("unable to write into BMS1_MODE_CTL, rc: %d\n", rc);
-
- msleep(OVERRIDE_MODE_DELAY_MS);
-
- lock_output_data(chip);
- qpnp_read_wrapper(chip, (u8 *)&vsense_raw,
- chip->base + BMS1_VSENSE_AVG_DATA0, 2);
- qpnp_read_wrapper(chip, (u8 *)&vbat_raw,
- chip->base + BMS1_VBAT_AVG_DATA0, 2);
- unlock_output_data(chip);
-
- rc = qpnp_masked_write(chip, BMS1_MODE_CTL,
- BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT, 0);
-
- delay = 0x0B;
- rc = qpnp_write_wrapper(chip, &delay,
- chip->base + BMS1_S1_DELAY_CTL, 1);
- if (rc)
- pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc);
-
- mutex_unlock(&chip->bms_output_lock);
-
- *vbat_uv = convert_vbatt_raw_to_uv(chip, vbat_raw);
- vsense_uv = convert_vsense_to_uv(chip, vsense_raw);
- *ibat_ua = div_s64(vsense_uv * 1000000LL, (int)chip->r_sense_uohm);
-
- pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x ibat_ua = %d vbat_uv = %d\n",
- (uint16_t)vsense_raw, (uint16_t)vbat_raw,
- *ibat_ua, *vbat_uv);
- return 0;
-}
-
static bool is_battery_charging(struct qpnp_bms_chip *chip)
{
union power_supply_propval ret = {0,};
@@ -1188,23 +1134,21 @@
static int get_simultaneous_batt_v_and_i(struct qpnp_bms_chip *chip,
int *ibat_ua, int *vbat_uv)
{
+ struct qpnp_iadc_result i_result;
+ struct qpnp_vadc_result v_result;
+ enum qpnp_iadc_channels iadc_channel;
int rc;
- if (is_batfet_open(chip)) {
- pr_debug("batfet is open using separate vbat and ibat meas\n");
- rc = get_battery_voltage(vbat_uv);
- if (rc < 0) {
- pr_err("adc vbat failed err = %d\n", rc);
- return rc;
- }
- rc = get_battery_current(chip, ibat_ua);
- if (rc < 0) {
- pr_err("bms ibat failed err = %d\n", rc);
- return rc;
- }
- } else {
- return override_mode_batt_v_and_i(chip, ibat_ua, vbat_uv);
+ iadc_channel = chip->use_external_rsense ?
+ EXTERNAL_RSENSE : INTERNAL_RSENSE;
+ rc = qpnp_iadc_vadc_sync_read(iadc_channel, &i_result,
+ VBAT_SNS, &v_result);
+ if (rc) {
+ pr_err("vadc read failed with rc: %d\n", rc);
+ return rc;
}
+ *ibat_ua = (int)i_result.result_ua;
+ *vbat_uv = (int)v_result.physical;
return 0;
}
@@ -1231,7 +1175,7 @@
static int reset_bms_for_test(struct qpnp_bms_chip *chip)
{
- int ibat_ua, vbat_uv, rc;
+ int ibat_ua = 0, vbat_uv = 0, rc;
int ocv_est_uv;
if (!chip) {
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index e2ba042..7833afa 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -86,8 +86,8 @@
#define USB_OVP_CTL 0x42
#define SEC_ACCESS 0xD0
-/* SMBB peripheral subtype values */
#define REG_OFFSET_PERP_SUBTYPE 0x05
+/* SMBB peripheral subtype values */
#define SMBB_CHGR_SUBTYPE 0x01
#define SMBB_BUCK_SUBTYPE 0x02
#define SMBB_BAT_IF_SUBTYPE 0x03
@@ -96,6 +96,14 @@
#define SMBB_BOOST_SUBTYPE 0x06
#define SMBB_MISC_SUBTYPE 0x07
+/* SMBB peripheral subtype values */
+#define SMBBP_CHGR_SUBTYPE 0x31
+#define SMBBP_BUCK_SUBTYPE 0x32
+#define SMBBP_BAT_IF_SUBTYPE 0x33
+#define SMBBP_USB_CHGPTH_SUBTYPE 0x34
+#define SMBBP_BOOST_SUBTYPE 0x36
+#define SMBBP_MISC_SUBTYPE 0x37
+
#define QPNP_CHARGER_DEV_NAME "qcom,qpnp-charger"
/* Status bits and masks */
@@ -341,6 +349,9 @@
u8 dcin_valid_rt_sts;
int rc;
+ if (!chip->dc_chgpth_base)
+ return 0;
+
rc = qpnp_chg_read(chip, &dcin_valid_rt_sts,
INT_RT_STS(chip->dc_chgpth_base), 1);
if (rc) {
@@ -1212,6 +1223,7 @@
switch (subtype) {
case SMBB_CHGR_SUBTYPE:
+ case SMBBP_CHGR_SUBTYPE:
chip->chg_done_irq = spmi_get_irq_byname(chip->spmi,
spmi_resource, "chg-done");
if (chip->chg_done_irq < 0) {
@@ -1289,6 +1301,7 @@
enable_irq_wake(chip->chg_done_irq);
break;
case SMBB_BUCK_SUBTYPE:
+ case SMBBP_BUCK_SUBTYPE:
rc = qpnp_chg_masked_write(chip,
chip->chgr_base + CHGR_BUCK_BCK_VBAT_REG_MODE,
BUCK_VBAT_REG_NODE_SEL_BIT,
@@ -1299,8 +1312,10 @@
}
break;
case SMBB_BAT_IF_SUBTYPE:
+ case SMBBP_BAT_IF_SUBTYPE:
break;
case SMBB_USB_CHGPTH_SUBTYPE:
+ case SMBBP_USB_CHGPTH_SUBTYPE:
chip->usbin_valid_irq = spmi_get_irq_byname(chip->spmi,
spmi_resource, "usbin-valid");
if (chip->usbin_valid_irq < 0) {
@@ -1361,8 +1376,10 @@
enable_irq_wake(chip->dcin_valid_irq);
break;
case SMBB_BOOST_SUBTYPE:
+ case SMBBP_BOOST_SUBTYPE:
break;
case SMBB_MISC_SUBTYPE:
+ case SMBBP_MISC_SUBTYPE:
pr_debug("Setting BOOT_DONE\n");
rc = qpnp_chg_masked_write(chip,
chip->misc_base + CHGR_MISC_BOOT_DONE,
@@ -1397,10 +1414,6 @@
return -ENOMEM;
}
- rc = qpnp_vadc_is_ready();
- if (rc)
- goto fail_chg_enable;
-
chip->dev = &(spmi->dev);
chip->spmi = spmi;
@@ -1557,6 +1570,7 @@
switch (subtype) {
case SMBB_CHGR_SUBTYPE:
+ case SMBBP_CHGR_SUBTYPE:
chip->chgr_base = resource->start;
rc = qpnp_chg_hwinit(chip, subtype, spmi_resource);
if (rc) {
@@ -1566,6 +1580,7 @@
}
break;
case SMBB_BUCK_SUBTYPE:
+ case SMBBP_BUCK_SUBTYPE:
chip->buck_base = resource->start;
rc = qpnp_chg_hwinit(chip, subtype, spmi_resource);
if (rc) {
@@ -1575,6 +1590,7 @@
}
break;
case SMBB_BAT_IF_SUBTYPE:
+ case SMBBP_BAT_IF_SUBTYPE:
chip->bat_if_base = resource->start;
rc = qpnp_chg_hwinit(chip, subtype, spmi_resource);
if (rc) {
@@ -1584,6 +1600,7 @@
}
break;
case SMBB_USB_CHGPTH_SUBTYPE:
+ case SMBBP_USB_CHGPTH_SUBTYPE:
chip->usb_chgpth_base = resource->start;
rc = qpnp_chg_hwinit(chip, subtype, spmi_resource);
if (rc) {
@@ -1602,6 +1619,7 @@
}
break;
case SMBB_BOOST_SUBTYPE:
+ case SMBBP_BOOST_SUBTYPE:
chip->boost_base = resource->start;
rc = qpnp_chg_hwinit(chip, subtype, spmi_resource);
if (rc) {
@@ -1611,6 +1629,7 @@
}
break;
case SMBB_MISC_SUBTYPE:
+ case SMBBP_MISC_SUBTYPE:
chip->misc_base = resource->start;
rc = qpnp_chg_hwinit(chip, subtype, spmi_resource);
if (rc) {
@@ -1628,34 +1647,44 @@
dev_set_drvdata(&spmi->dev, chip);
device_init_wakeup(&spmi->dev, 1);
- chip->dc_psy.name = "qpnp-dc";
- chip->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
- chip->dc_psy.supplied_to = pm_power_supplied_to;
- chip->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
- chip->dc_psy.properties = pm_power_props_mains;
- chip->dc_psy.num_properties = ARRAY_SIZE(pm_power_props_mains);
- chip->dc_psy.get_property = qpnp_power_get_property_mains;
+ if (chip->bat_if_base) {
+ rc = qpnp_vadc_is_ready();
+ if (rc)
+ goto fail_chg_enable;
- chip->batt_psy.name = "battery";
- chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
- chip->batt_psy.properties = msm_batt_power_props;
- chip->batt_psy.num_properties = ARRAY_SIZE(msm_batt_power_props);
- chip->batt_psy.get_property = qpnp_batt_power_get_property;
- chip->batt_psy.set_property = qpnp_batt_power_set_property;
- chip->batt_psy.property_is_writeable = qpnp_batt_property_is_writeable;
- chip->batt_psy.external_power_changed =
+ chip->batt_psy.name = "battery";
+ chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ chip->batt_psy.properties = msm_batt_power_props;
+ chip->batt_psy.num_properties =
+ ARRAY_SIZE(msm_batt_power_props);
+ chip->batt_psy.get_property = qpnp_batt_power_get_property;
+ chip->batt_psy.set_property = qpnp_batt_power_set_property;
+ chip->batt_psy.property_is_writeable =
+ qpnp_batt_property_is_writeable;
+ chip->batt_psy.external_power_changed =
qpnp_batt_external_power_changed;
- rc = power_supply_register(chip->dev, &chip->batt_psy);
- if (rc < 0) {
- pr_err("power_supply_register batt failed rc = %d\n", rc);
- goto fail_chg_enable;
+ rc = power_supply_register(chip->dev, &chip->batt_psy);
+ if (rc < 0) {
+ pr_err("batt failed to register rc = %d\n", rc);
+ goto fail_chg_enable;
+ }
}
- rc = power_supply_register(chip->dev, &chip->dc_psy);
- if (rc < 0) {
- pr_err("power_supply_register usb failed rc = %d\n", rc);
- goto unregister_batt;
+ if (chip->dc_chgpth_base) {
+ chip->dc_psy.name = "qpnp-dc";
+ chip->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+ chip->dc_psy.supplied_to = pm_power_supplied_to;
+ chip->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+ chip->dc_psy.properties = pm_power_props_mains;
+ chip->dc_psy.num_properties = ARRAY_SIZE(pm_power_props_mains);
+ chip->dc_psy.get_property = qpnp_power_get_property_mains;
+
+ rc = power_supply_register(chip->dev, &chip->dc_psy);
+ if (rc < 0) {
+ pr_err("power_supply_register dc failed rc=%d\n", rc);
+ goto unregister_batt;
+ }
}
/* Turn on appropriate workaround flags */
@@ -1664,11 +1693,11 @@
power_supply_set_present(chip->usb_psy,
qpnp_chg_is_usb_chg_plugged_in(chip));
- if (chip->maxinput_dc_ma) {
+ if (chip->maxinput_dc_ma && chip->dc_chgpth_base) {
rc = qpnp_chg_idcmax_set(chip, chip->maxinput_dc_ma);
if (rc) {
pr_err("Error setting idcmax property %d\n", rc);
- goto fail_chg_enable;
+ goto unregister_batt;
}
}
@@ -1684,7 +1713,8 @@
return 0;
unregister_batt:
- power_supply_unregister(&chip->batt_psy);
+ if (chip->bat_if_base)
+ power_supply_unregister(&chip->batt_psy);
fail_chg_enable:
kfree(chip->thermal_mitigation);
kfree(chip);
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index 9b0b8b4..9a864aa 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -263,6 +263,17 @@
*/
mb();
complete(&dev->rx_msgq_notify);
+ } else if (mt == SLIM_MSG_MT_CORE &&
+ mc == SLIM_MSG_MC_REPORT_ABSENT) {
+ writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+ MGR_INT_CLR);
+ /*
+ * Guarantee that CLR bit write goes through
+ * before signalling completion
+ */
+ mb();
+ complete(&dev->rx_msgq_notify);
+
} else if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
mc == SLIM_MSG_MC_REPLY_VALUE) {
msm_slim_rx_enqueue(dev, rx_buf, len);
@@ -975,6 +986,10 @@
txn.wbuf = wbuf;
gen_ack = true;
ret = msm_xfer_msg(&dev->ctrl, &txn);
+ break;
+ case SLIM_MSG_MC_REPORT_ABSENT:
+ dev_info(dev->dev, "Received Report Absent Message\n");
+ break;
default:
break;
}
@@ -1087,7 +1102,8 @@
laddr = (u8)((buffer[0] >> 16) & 0xff);
sat = addr_to_sat(dev, laddr);
}
- } else if ((index * 4) >= msg_len) {
+ }
+ if ((index * 4) >= msg_len) {
index = 0;
if (sat) {
msm_sat_enqueue(sat, buffer, msg_len);
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 482d383..b04213c 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -616,7 +616,8 @@
calib_data[5] = readl_relaxed(
(TSENS_EEPROM_8X26_2(tmdev->tsens_calib_addr)) + 0x8);
- tsens_calibration_mode = calib_data[5] & TSENS_8X26_TSENS_CAL_SEL;
+ tsens_calibration_mode = (calib_data[5] & TSENS_8X26_TSENS_CAL_SEL)
+ >> TSENS_8X26_CAL_SEL_SHIFT;
if ((tsens_calibration_mode == TSENS_TWO_POINT_CALIB) ||
(tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2)) {
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index f38de0c..65b2199 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1963,7 +1963,7 @@
(mdwc->ext_xceiv.otg_capability || !init)) {
mdwc->ext_xceiv.bsv = val->intval;
queue_delayed_work(system_nrt_wq,
- &mdwc->resume_work, 0);
+ &mdwc->resume_work, 20);
if (!init)
init = true;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 5694999..f060718 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1567,6 +1567,8 @@
return ret;
}
+static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc);
+
static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
{
struct dwc3 *dwc = gadget_to_dwc(_gadget);
@@ -1597,17 +1599,18 @@
} else {
ret = dwc3_gadget_run_stop(dwc, 0);
}
- } else if (dwc->gadget_driver && !dwc->softconnect &&
- !dwc->vbus_active) {
- if (dwc->gadget_driver->disconnect) {
- spin_unlock_irqrestore(&dwc->lock, flags);
- dwc->gadget_driver->disconnect(&dwc->gadget);
- return 0;
- }
+ }
+
+ /*
+ * Clearing run/stop bit might occur before disconnect event is seen.
+ * Make sure to let gadget driver know in that case.
+ */
+ if (!dwc->vbus_active && dwc->start_config_issued) {
+ dev_dbg(dwc->dev, "calling disconnect from %s\n", __func__);
+ dwc3_gadget_disconnect_interrupt(dwc);
}
spin_unlock_irqrestore(&dwc->lock, flags);
-
return ret;
}
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index a2997e9..5a6faf2 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -667,7 +667,7 @@
if (ret)
pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
__func__, ret);
- rmnet_bridge_disconnect();
+ teth_bridge_disconnect();
}
}
@@ -707,8 +707,11 @@
static void gbam2bam_connect_work(struct work_struct *w)
{
struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+ struct teth_bridge_connect_params connect_params;
struct bam_ch_info *d = &port->data_ch;
u32 sps_params;
+ ipa_notify_cb usb_notify_cb;
+ void *priv;
int ret;
if (d->trans == USB_GADGET_XPORT_BAM2BAM) {
@@ -720,6 +723,15 @@
return;
}
} else if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ ret = teth_bridge_init(&usb_notify_cb, &priv);
+ if (ret) {
+ pr_err("%s:teth_bridge_init() failed\n", __func__);
+ return;
+ }
+ d->ipa_params.notify = usb_notify_cb;
+ d->ipa_params.priv = priv;
+ d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
+
d->ipa_params.client = IPA_CLIENT_USB_CONS;
d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
ret = usb_bam_connect_ipa(&d->ipa_params);
@@ -731,18 +743,21 @@
d->ipa_params.client = IPA_CLIENT_USB_PROD;
d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
- /* Currently only DMA mode is supported */
- d->ipa_params.ipa_ep_cfg.mode.mode = IPA_DMA;
- d->ipa_params.ipa_ep_cfg.mode.dst =
- IPA_CLIENT_A2_TETHERED_CONS;
ret = usb_bam_connect_ipa(&d->ipa_params);
if (ret) {
pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
__func__, ret);
return;
}
- rmnet_bridge_connect(d->ipa_params.prod_clnt_hdl,
- d->ipa_params.cons_clnt_hdl, 0);
+
+ connect_params.ipa_usb_pipe_hdl = d->ipa_params.prod_clnt_hdl;
+ connect_params.usb_ipa_pipe_hdl = d->ipa_params.cons_clnt_hdl;
+ connect_params.tethering_mode = TETH_TETHERING_MODE_RMNET;
+ ret = teth_bridge_connect(&connect_params);
+ if (ret) {
+ pr_err("%s:teth_bridge_connect() failed\n", __func__);
+ return;
+ }
}
d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 8e32aa9..f1f6962 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -1059,6 +1059,7 @@
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
u32 __iomem *status_reg = &ehci->regs->port_status[0];
+ u32 cmd;
unsigned long flags;
int retries = 0, ret, cnt = RESET_SIGNAL_TIME_USEC;
@@ -1067,6 +1068,19 @@
pdata->swfi_latency + 1);
mehci->bus_reset = 1;
+
+ /* Halt the controller */
+ cmd = ehci_readl(ehci, &ehci->regs->command);
+ cmd &= ~CMD_RUN;
+ ehci_writel(ehci, cmd, &ehci->regs->command);
+ ret = handshake(ehci, &ehci->regs->status, STS_HALT,
+ STS_HALT, 16 * 125);
+ if (ret) {
+ pr_err("halt handshake fatal error\n");
+ dbg_log_event(NULL, "HALT: fatal", 0);
+ goto fail;
+ }
+
retry:
retries++;
dbg_log_event(NULL, "RESET: start", retries);
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index 40e1eea..faa5625 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -45,6 +45,7 @@
struct ehci_hcd ehci;
spinlock_t wakeup_lock;
struct device *dev;
+ struct clk *xo_clk;
struct clk *iface_clk;
struct clk *core_clk;
struct clk *alt_core_clk;
@@ -659,10 +660,14 @@
clk_disable_unprepare(mhcd->core_clk);
/* usb phy does not require TCXO clock, hence vote for TCXO disable */
- ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
- if (ret)
- dev_err(mhcd->dev, "%s failed to devote for "
- "TCXO D0 buffer%d\n", __func__, ret);
+ if (!IS_ERR(mhcd->xo_clk)) {
+ clk_disable_unprepare(mhcd->xo_clk);
+ } else {
+ ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
+ if (ret)
+ dev_err(mhcd->dev, "%s failed to devote for TCXO %d\n",
+ __func__, ret);
+ }
msm_ehci_config_vddcx(mhcd, 0);
@@ -714,10 +719,14 @@
wake_lock(&mhcd->wlock);
/* Vote for TCXO when waking up the phy */
- ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
- if (ret)
- dev_err(mhcd->dev, "%s failed to vote for "
- "TCXO D0 buffer%d\n", __func__, ret);
+ if (!IS_ERR(mhcd->xo_clk)) {
+ clk_prepare_enable(mhcd->xo_clk);
+ } else {
+ ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
+ if (ret)
+ dev_err(mhcd->dev, "%s failed to vote for TCXO D0 %d\n",
+ __func__, ret);
+ }
clk_prepare_enable(mhcd->core_clk);
clk_prepare_enable(mhcd->iface_clk);
@@ -1091,18 +1100,23 @@
}
snprintf(pdev_name, PDEV_NAME_LEN, "%s.%d", pdev->name, pdev->id);
- mhcd->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, pdev_name);
- if (IS_ERR(mhcd->xo_handle)) {
- dev_err(&pdev->dev, "%s not able to get the handle "
- "to vote for TCXO D0 buffer\n", __func__);
- ret = PTR_ERR(mhcd->xo_handle);
- goto free_async_irq;
+ mhcd->xo_clk = clk_get(&pdev->dev, "xo");
+ if (!IS_ERR(mhcd->xo_clk)) {
+ ret = clk_prepare_enable(mhcd->xo_clk);
+ } else {
+ mhcd->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, pdev_name);
+ if (IS_ERR(mhcd->xo_handle)) {
+ dev_err(&pdev->dev, "%s fail to get handle for X0 D0\n",
+ __func__);
+ ret = PTR_ERR(mhcd->xo_handle);
+ goto free_async_irq;
+ } else {
+ ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
+ }
}
-
- ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
if (ret) {
- dev_err(&pdev->dev, "%s failed to vote for TCXO "
- "D0 buffer%d\n", __func__, ret);
+ dev_err(&pdev->dev, "%s failed to vote for TCXO %d\n",
+ __func__, ret);
goto free_xo_handle;
}
@@ -1202,9 +1216,15 @@
deinit_clocks:
msm_ehci_init_clocks(mhcd, 0);
devote_xo_handle:
- msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
+ if (!IS_ERR(mhcd->xo_clk))
+ clk_disable_unprepare(mhcd->xo_clk);
+ else
+ msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
free_xo_handle:
- msm_xo_put(mhcd->xo_handle);
+ if (!IS_ERR(mhcd->xo_clk))
+ clk_put(mhcd->xo_clk);
+ else
+ msm_xo_put(mhcd->xo_handle);
free_async_irq:
if (mhcd->async_irq)
free_irq(mhcd->async_irq, mhcd);
@@ -1236,7 +1256,12 @@
usb_remove_hcd(hcd);
- msm_xo_put(mhcd->xo_handle);
+ if (!IS_ERR(mhcd->xo_clk)) {
+ clk_disable_unprepare(mhcd->xo_clk);
+ clk_put(mhcd->xo_clk);
+ } else {
+ msm_xo_put(mhcd->xo_handle);
+ }
msm_ehci_vbus_power(mhcd, 0);
msm_ehci_init_vbus(mhcd, 0);
msm_ehci_ldo_enable(mhcd, 0);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index bd1423d..b35d904 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -95,7 +95,7 @@
static struct regulator *hsusb_3p3;
static struct regulator *hsusb_1p8;
-static struct regulator *hsusb_vddcx;
+static struct regulator *hsusb_vdd;
static struct regulator *vbus_otg;
static struct regulator *mhl_usb_hs_switch;
static struct power_supply *psy;
@@ -111,7 +111,7 @@
#endif
}
-static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
+static int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
{ /* VDD_CX CORNER Voting */
[VDD_NONE] = RPM_VREG_CORNER_NONE,
[VDD_MIN] = RPM_VREG_CORNER_NOMINAL,
@@ -175,7 +175,7 @@
int ret;
min_vol = vdd_val[vdd_type][!!high];
- ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol);
+ ret = regulator_set_voltage(hsusb_vdd, min_vol, max_vol);
if (ret) {
pr_err("%s: unable to set the voltage for regulator "
"HSUSB_VDDCX\n", __func__);
@@ -3038,7 +3038,7 @@
set_bit(A_SRP_DET, &motg->inputs);
set_bit(A_BUS_REQ, &motg->inputs);
work = 1;
- } else if (otgsc & OTGSC_BSVIS) {
+ } else if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
writel_relaxed(otgsc, USB_OTGSC);
/*
* BSV interrupt comes when operating as an A-device
@@ -3157,7 +3157,15 @@
static bool init;
struct msm_otg *motg = the_msm_otg;
- /* Ignore received BSV interrupts, if ID pin is GND */
+ if (online) {
+ pr_debug("PMIC: BSV set\n");
+ set_bit(B_SESS_VLD, &motg->inputs);
+ } else {
+ pr_debug("PMIC: BSV clear\n");
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ }
+
+ /* do not queue state m/c work if id is grounded */
if (!test_bit(ID, &motg->inputs)) {
/*
* state machine work waits for initial VBUS
@@ -3166,17 +3174,8 @@
*/
if (init)
return;
- goto complete;
}
- if (online) {
- pr_debug("PMIC: BSV set\n");
- set_bit(B_SESS_VLD, &motg->inputs);
- } else {
- pr_debug("PMIC: BSV clear\n");
- clear_bit(B_SESS_VLD, &motg->inputs);
- }
-complete:
if (!init) {
init = true;
complete(&pmic_vbus_init);
@@ -3795,6 +3794,8 @@
"qcom,hsusb-otg-lpm-on-dev-suspend");
pdata->core_clk_always_on_workaround = of_property_read_bool(node,
"qcom,hsusb-otg-clk-always-on-workaround");
+ pdata->delay_lpm_on_disconnect = of_property_read_bool(node,
+ "qcom,hsusb-otg-delay-lpm");
return pdata;
}
@@ -3802,6 +3803,8 @@
static int __init msm_otg_probe(struct platform_device *pdev)
{
int ret = 0;
+ int len = 0;
+ u32 tmp[3];
struct resource *res;
struct msm_otg *motg;
struct usb_phy *phy;
@@ -3958,24 +3961,40 @@
clk_prepare_enable(motg->pclk);
motg->vdd_type = VDDCX_CORNER;
- hsusb_vddcx = devm_regulator_get(motg->phy.dev, "hsusb_vdd_dig");
- if (IS_ERR(hsusb_vddcx)) {
- hsusb_vddcx = devm_regulator_get(motg->phy.dev, "HSUSB_VDDCX");
- if (IS_ERR(hsusb_vddcx)) {
+ hsusb_vdd = devm_regulator_get(motg->phy.dev, "hsusb_vdd_dig");
+ if (IS_ERR(hsusb_vdd)) {
+ hsusb_vdd = devm_regulator_get(motg->phy.dev, "HSUSB_VDDCX");
+ if (IS_ERR(hsusb_vdd)) {
dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
- ret = PTR_ERR(hsusb_vddcx);
+ ret = PTR_ERR(hsusb_vdd);
goto devote_xo_handle;
}
motg->vdd_type = VDDCX;
}
+ if (pdev->dev.of_node) {
+ of_get_property(pdev->dev.of_node,
+ "qcom,vdd-voltage-level",
+ &len);
+ if (len == sizeof(tmp)) {
+ of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,vdd-voltage-level",
+ tmp, len/sizeof(*tmp));
+ vdd_val[motg->vdd_type][0] = tmp[0];
+ vdd_val[motg->vdd_type][1] = tmp[1];
+ vdd_val[motg->vdd_type][2] = tmp[2];
+ } else {
+ dev_dbg(&pdev->dev, "Using default hsusb vdd config.\n");
+ }
+ }
+
ret = msm_hsusb_config_vddcx(1);
if (ret) {
dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
goto devote_xo_handle;
}
- ret = regulator_enable(hsusb_vddcx);
+ ret = regulator_enable(hsusb_vdd);
if (ret) {
dev_err(&pdev->dev, "unable to enable the hsusb vddcx\n");
goto free_config_vddcx;
@@ -3984,7 +4003,7 @@
ret = msm_hsusb_ldo_init(motg, 1);
if (ret) {
dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
- goto free_hsusb_vddcx;
+ goto free_hsusb_vdd;
}
if (pdata->mhl_enable) {
@@ -4167,10 +4186,10 @@
msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
free_ldo_init:
msm_hsusb_ldo_init(motg, 0);
-free_hsusb_vddcx:
- regulator_disable(hsusb_vddcx);
+free_hsusb_vdd:
+ regulator_disable(hsusb_vdd);
free_config_vddcx:
- regulator_set_voltage(hsusb_vddcx,
+ regulator_set_voltage(hsusb_vdd,
vdd_val[motg->vdd_type][VDD_NONE],
vdd_val[motg->vdd_type][VDD_MAX]);
devote_xo_handle:
@@ -4264,8 +4283,8 @@
}
msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
msm_hsusb_ldo_init(motg, 0);
- regulator_disable(hsusb_vddcx);
- regulator_set_voltage(hsusb_vddcx,
+ regulator_disable(hsusb_vdd);
+ regulator_set_voltage(hsusb_vdd,
vdd_val[motg->vdd_type][VDD_NONE],
vdd_val[motg->vdd_type][VDD_MAX]);
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index ed0a385..a3d8d7e 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2013, 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
@@ -276,6 +276,7 @@
struct mdp4_overlay_pipe *solidfill_pipe;
};
+
struct mdp4_overlay_pipe {
uint32 pipe_used;
uint32 pipe_type; /* rgb, video/graphic */
@@ -983,6 +984,8 @@
void mdp4_overlay_mdp_perf_upd(struct msm_fb_data_type *mfd, int flag);
int mdp4_update_base_blend(struct msm_fb_data_type *mfd,
struct mdp_blend_cfg *mdp_blend_cfg);
+int mdp4_update_writeback_format(struct msm_fb_data_type *mfd,
+ struct mdp_mixer_cfg *mdp_mixer_cfg);
u32 mdp4_get_mixer_num(u32 panel_type);
#ifndef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index fbae011..bfd8238 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -49,6 +49,7 @@
struct mdp4_overlay_pipe *baselayer[MDP4_MIXER_MAX];
struct blend_cfg blend[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX];
struct mdp4_overlay_pipe sf_plist[MDP4_MIXER_MAX][OVERLAY_PIPE_MAX];
+ struct mdp_mixer_cfg mdp_mixer_cfg[MDP4_MIXER_MAX];
uint32 mixer_cfg[MDP4_MIXER_MAX];
uint32 flush[MDP4_MIXER_MAX];
struct iommu_free_list iommu_free[MDP4_MIXER_MAX];
@@ -1456,6 +1457,87 @@
(pipe->element1 << 8) | pipe->element0;
}
+static uint32 mdp4_overlayproc_cfg_wb_panel(struct mdp4_overlay_pipe *pipe,
+ char *overlay_base, uint32 curr)
+{
+ int off, bpp;
+ uint32 flag;
+ bool is_rgb = false;
+ struct mdp_mixer_cfg *mixer_cfg;
+
+ off = 0;
+ mixer_cfg = &ctrl->mdp_mixer_cfg[MDP4_MIXER2];
+
+ switch (mixer_cfg->writeback_format) {
+ case WB_FORMAT_RGB_888:
+ bpp = 3; /* RGB888 */
+ flag = 0x0;
+ is_rgb = true;
+ break;
+ case WB_FORMAT_RGB_565:
+ bpp = 2; /* RGB565 */
+ flag = 0x1;
+ is_rgb = true;
+ break;
+ case WB_FORMAT_xRGB_8888:
+ bpp = 4; /* xRGB8888 */
+ flag = 0x3;
+ is_rgb = true;
+ break;
+ case WB_FORMAT_ARGB_8888:
+ bpp = 4; /* ARGB8888 */
+ flag = 0x80000003;
+ is_rgb = true;
+ break;
+ case WB_FORMAT_ARGB_8888_INPUT_ALPHA:
+ pr_warn("currently not supported ARGB_8888_INPUT_ALPHA\n");
+ default:
+ bpp = 1; /* NV12 */
+ is_rgb = false;
+ break;
+ }
+
+ if (is_rgb == true) {
+ if (pipe->ov_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+
+ outpdw(overlay_base + 0x000c, pipe->ov_blt_addr + off);
+ /* overlay ouput is RGB888 */
+ outpdw(overlay_base + 0x0010, pipe->src_width * bpp);
+ outpdw(overlay_base + 0x001c, pipe->ov_blt_addr + off);
+ /* MDDI - BLT + on demand */
+ outpdw(overlay_base + 0x0004, 0x08);
+
+ curr = inpdw(overlay_base + 0x0014);
+ curr &= 0x4;
+
+ outpdw(overlay_base + 0x0014, curr | flag);
+ } else {
+ if (pipe->ov_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+
+ outpdw(overlay_base + 0x000c, pipe->ov_blt_addr + off);
+ /* overlay ouput is RGB888 */
+ outpdw(overlay_base + 0x0010, ((pipe->src_width << 16) |
+ pipe->src_width));
+ outpdw(overlay_base + 0x001c, pipe->ov_blt_addr + off);
+ off = pipe->src_height * pipe->src_width;
+ /* align chroma to 2k address */
+ off = (off + 2047) & ~2047;
+ /* UV plane adress */
+ outpdw(overlay_base + 0x0020, pipe->ov_blt_addr + off);
+ /* MDDI - BLT + on demand */
+ outpdw(overlay_base + 0x0004, 0x08);
+ /* pseudo planar + writeback */
+ curr = inpdw(overlay_base + 0x0014);
+ curr &= 0x4;
+ outpdw(overlay_base + 0x0014, curr | 0x012);
+ /* rgb->yuv */
+ outpdw(overlay_base + 0x0200, 0x05);
+ }
+ return curr;
+}
+
/*
* mdp4_overlayproc_cfg: only be called from base layer
*/
@@ -1515,34 +1597,8 @@
#endif
} else if (pipe->mixer_num == MDP4_MIXER2) {
if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) {
- off = 0;
- bpp = 1;
- if (pipe->ov_cnt & 0x01)
- off = pipe->src_height *
- pipe->src_width * bpp;
-
- outpdw(overlay_base + 0x000c,
- pipe->ov_blt_addr + off);
- /* overlay ouput is RGB888 */
- outpdw(overlay_base + 0x0010,
- ((pipe->src_width << 16) |
- pipe->src_width));
- outpdw(overlay_base + 0x001c,
- pipe->ov_blt_addr + off);
- off = pipe->src_height * pipe->src_width;
- /* align chroma to 2k address */
- off = (off + 2047) & ~2047;
- /* UV plane adress */
- outpdw(overlay_base + 0x0020,
- pipe->ov_blt_addr + off);
- /* MDDI - BLT + on demand */
- outpdw(overlay_base + 0x0004, 0x08);
- /* pseudo planar + writeback */
- curr = inpdw(overlay_base + 0x0014);
- curr &= 0x4;
- outpdw(overlay_base + 0x0014, curr | 0x012);
- /* rgb->yuv */
- outpdw(overlay_base + 0x0200, 0x05);
+ curr = mdp4_overlayproc_cfg_wb_panel(pipe,
+ overlay_base, curr);
}
}
} else {
@@ -3865,6 +3921,42 @@
mutex_unlock(&mfd->dma->ov_mutex);
return err;
}
+
+int mdp4_update_writeback_format(struct msm_fb_data_type *mfd,
+ struct mdp_mixer_cfg *mdp_mixer_cfg)
+{
+ int ret = 0;
+ u32 mixer_num;
+ struct mdp_mixer_cfg *mixer;
+
+ mixer_num = mdp4_get_mixer_num(mfd->panel_info.type);
+ if (!ctrl) {
+ pr_warn("mdp4_overlay_ctrl is NULL\n");
+ return -EPERM;
+ }
+ mixer = &ctrl->mdp_mixer_cfg[mixer_num];
+
+ switch (mdp_mixer_cfg->writeback_format) {
+ case WB_FORMAT_RGB_888:
+ case WB_FORMAT_RGB_565:
+ case WB_FORMAT_NV12:
+ case WB_FORMAT_xRGB_8888:
+ case WB_FORMAT_ARGB_8888:
+ mixer->writeback_format = mdp_mixer_cfg->writeback_format;
+ break;
+ case WB_FORMAT_ARGB_8888_INPUT_ALPHA:
+ mixer->writeback_format = mdp_mixer_cfg->writeback_format;
+ mixer->alpha = mdp_mixer_cfg->alpha;
+ break;
+ default:
+ mixer->writeback_format = WB_FORMAT_NV12;
+ pr_warn("Unsupported format request, setting to NV12\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
int mdp4_update_base_blend(struct msm_fb_data_type *mfd,
struct mdp_blend_cfg *mdp_blend_cfg)
{
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index ea0eb7b..1e0de89 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -1069,7 +1069,15 @@
int i, ret = 0;
/* buf sync */
for (i = 0; i < mfd->acq_fen_cnt; i++) {
- ret = sync_fence_wait(mfd->acq_fen[i], WAIT_FENCE_TIMEOUT);
+ ret = sync_fence_wait(mfd->acq_fen[i],
+ WAIT_FENCE_FIRST_TIMEOUT);
+ if (ret == -ETIME) {
+ pr_warn("sync_fence_wait timed out! ");
+ pr_cont("Waiting %ld more seconds\n",
+ WAIT_FENCE_FINAL_TIMEOUT/MSEC_PER_SEC);
+ ret = sync_fence_wait(mfd->acq_fen[i],
+ WAIT_FENCE_FINAL_TIMEOUT);
+ }
if (ret < 0) {
pr_err("%s: sync_fence_wait failed! ret = %x\n",
__func__, ret);
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index c4e837e..db2e305 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -27,10 +27,11 @@
#define MSM_FB_MAX_DEV_LIST 32
#define MSM_FB_ENABLE_DBGFS
-/* 900 ms for fence time out */
-#define WAIT_FENCE_TIMEOUT 900
-/* 950 ms for display operation time out */
-#define WAIT_DISP_OP_TIMEOUT 950
+#define WAIT_FENCE_FIRST_TIMEOUT MSEC_PER_SEC
+#define WAIT_FENCE_FINAL_TIMEOUT (10 * MSEC_PER_SEC)
+/* Display op timeout should be greater than total timeout */
+#define WAIT_DISP_OP_TIMEOUT ((WAIT_FENCE_FIRST_TIMEOUT + \
+ WAIT_FENCE_FINAL_TIMEOUT) * MDP_MAX_FENCE_FD)
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 5404000..4f95aee 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -378,8 +378,9 @@
} else if (1 == hpd && !hdmi_ctrl->hpd_feature_on) {
rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, true);
} else {
- rc = -EPERM;
- ret = rc;
+ DEV_DBG("%s: hpd is already '%s'. return\n", __func__,
+ hdmi_ctrl->hpd_feature_on ? "enabled" : "disabled");
+ return ret;
}
if (!rc) {
@@ -387,8 +388,9 @@
(~hdmi_ctrl->hpd_feature_on) & BIT(0);
DEV_DBG("%s: '%d'\n", __func__, hdmi_ctrl->hpd_feature_on);
} else {
- DEV_DBG("%s: '%d' (unchanged)\n", __func__,
- hdmi_ctrl->hpd_feature_on);
+ DEV_ERR("%s: failed to '%s' hpd. rc = %d\n", __func__,
+ hpd ? "enable" : "disable", rc);
+ ret = rc;
}
return ret;
@@ -426,9 +428,6 @@
hdmi_ctrl->kobj = &fbi->dev->kobj;
DEV_DBG("%s: sysfs group %p\n", __func__, hdmi_ctrl->kobj);
- kobject_uevent(hdmi_ctrl->kobj, KOBJ_ADD);
- DEV_DBG("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
-
return 0;
} /* hdmi_tx_sysfs_create */
@@ -695,17 +694,13 @@
if (hdmi_ctrl->hpd_state) {
hdmi_tx_read_sink_info(hdmi_ctrl);
- DEV_INFO("HDMI HPD: sense CONNECTED: send ONLINE\n");
- kobject_uevent(hdmi_ctrl->kobj, KOBJ_ONLINE);
switch_set_state(&hdmi_ctrl->sdev, 1);
- DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
- hdmi_ctrl->sdev.state);
+ DEV_INFO("%s: sense cable CONNECTED: state switch to %d\n",
+ __func__, hdmi_ctrl->sdev.state);
} else {
- DEV_INFO("HDMI HPD: sense DISCONNECTED: send OFFLINE\n");
- kobject_uevent(hdmi_ctrl->kobj, KOBJ_OFFLINE);
switch_set_state(&hdmi_ctrl->sdev, 0);
- DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
- hdmi_ctrl->sdev.state);
+ DEV_INFO("%s: sense cable DISCONNECTED: state switch to %d\n",
+ __func__, hdmi_ctrl->sdev.state);
}
if (!completion_done(&hdmi_ctrl->hpd_done))
@@ -2265,8 +2260,6 @@
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);
}
}
@@ -2486,10 +2479,7 @@
if (!timeout & !hdmi_ctrl->hpd_state) {
DEV_INFO("%s: cable removed during suspend\n",
__func__);
-
- kobject_uevent(hdmi_ctrl->kobj, KOBJ_OFFLINE);
switch_set_state(&hdmi_ctrl->sdev, 0);
-
rc = -EPERM;
} else {
DEV_DBG("%s: cable present after resume\n",
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index e4099ad..977fc63 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -124,6 +124,7 @@
char *prop_name, u32 *offsets, int len);
static int mdss_mdp_parse_dt_prop_len(struct platform_device *pdev,
char *prop_name);
+static int mdss_mdp_parse_dt_smp(struct platform_device *pdev);
static inline int mdss_irq_dispatch(u32 hw_ndx, int irq, void *ptr)
{
@@ -808,10 +809,6 @@
mdata->clk_ctrl_wq = create_singlethread_workqueue("mdp_clk_wq");
INIT_WORK(&mdata->clk_ctrl_worker, mdss_mdp_clk_ctrl_workqueue_handler);
- mdata->smp_mb_cnt = MDSS_MDP_SMP_MMB_BLOCKS;
- mdata->smp_mb_size = MDSS_MDP_SMP_MMB_SIZE;
-
-
mdata->iclient = msm_ion_client_create(-1, mdata->pdev->name);
if (IS_ERR_OR_NULL(mdata->iclient)) {
pr_err("msm_ion_client_create() return error (%p)\n",
@@ -1045,6 +1042,12 @@
return rc;
}
+ rc = mdss_mdp_parse_dt_smp(pdev);
+ if (rc) {
+ pr_err("Error in device tree : smp\n");
+ return rc;
+ }
+
return 0;
}
@@ -1305,6 +1308,30 @@
return rc;
}
+static int mdss_mdp_parse_dt_smp(struct platform_device *pdev)
+{
+ struct mdss_data_type *mdata = platform_get_drvdata(pdev);
+ u32 num;
+ u32 data[2];
+ int rc;
+
+ num = mdss_mdp_parse_dt_prop_len(pdev, "qcom,mdss-smp-data");
+
+ if (num != 2)
+ return -EINVAL;
+
+ rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-smp-data", data, num);
+ if (rc)
+ return rc;
+
+ rc = mdss_mdp_smp_setup(mdata, data[0], data[1]);
+
+ if (rc)
+ pr_err("unable to setup smp data\n");
+
+ return rc;
+}
+
static int mdss_mdp_parse_dt_handler(struct platform_device *pdev,
char *prop_name, u32 *offsets, int len)
{
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 4c70770..efd93c0 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -323,6 +323,7 @@
struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata,
struct msm_fb_data_type *mfd);
+int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl,
struct mdss_panel_data *pdata);
int mdss_mdp_ctl_destroy(struct mdss_mdp_ctl *ctl);
@@ -354,6 +355,7 @@
int mdss_mdp_pipe_pp_setup(struct mdss_mdp_pipe *pipe, u32 *op);
int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op);
void mdss_mdp_pipe_sspp_term(struct mdss_mdp_pipe *pipe);
+int mdss_mdp_smp_setup(struct mdss_data_type *mdata, u32 cnt, u32 size);
int mdss_hw_init(struct mdss_data_type *mdata);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index c640c73..8515782 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -39,6 +39,8 @@
static DEFINE_MUTEX(mdss_mdp_ctl_lock);
+static int mdss_mdp_mixer_free(struct mdss_mdp_mixer *mixer);
+
static inline void mdp_mixer_write(struct mdss_mdp_mixer *mixer,
u32 reg, u32 val)
{
@@ -265,8 +267,14 @@
mutex_lock(&mdss_mdp_ctl_lock);
ctl->ref_cnt--;
- ctl->mixer_left = NULL;
- ctl->mixer_right = NULL;
+ if (ctl->mixer_left) {
+ mdss_mdp_mixer_free(ctl->mixer_left);
+ ctl->mixer_left = NULL;
+ }
+ if (ctl->mixer_right) {
+ mdss_mdp_mixer_free(ctl->mixer_right);
+ ctl->mixer_right = NULL;
+ }
ctl->power_on = false;
ctl->start_fnc = NULL;
ctl->stop_fnc = NULL;
@@ -443,7 +451,7 @@
return NULL;
}
-static int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
+int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_ctl *split_ctl;
u32 width, height;
@@ -517,6 +525,50 @@
return 0;
}
+static int mdss_mdp_ctl_setup_wfd(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_data_type *mdata = ctl->mdata;
+ struct mdss_mdp_mixer *mixer;
+ int mixer_type;
+
+ /* if WB2 is supported, try to allocate it first */
+ if (mdata->nmixers_intf >= MDSS_MDP_INTF_LAYERMIXER2)
+ mixer_type = MDSS_MDP_MIXER_TYPE_INTF;
+ else
+ mixer_type = MDSS_MDP_MIXER_TYPE_WRITEBACK;
+
+ mixer = mdss_mdp_mixer_alloc(ctl, mixer_type, false);
+ if (!mixer && mixer_type == MDSS_MDP_MIXER_TYPE_INTF)
+ mixer = mdss_mdp_mixer_alloc(ctl, MDSS_MDP_MIXER_TYPE_WRITEBACK,
+ false);
+
+ if (!mixer) {
+ pr_err("Unable to allocate writeback mixer\n");
+ return -ENOMEM;
+ }
+
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ ctl->opmode = MDSS_MDP_CTL_OP_WFD_MODE;
+ } else {
+ switch (mixer->num) {
+ case MDSS_MDP_WB_LAYERMIXER0:
+ ctl->opmode = MDSS_MDP_CTL_OP_WB0_MODE;
+ break;
+ case MDSS_MDP_WB_LAYERMIXER1:
+ ctl->opmode = MDSS_MDP_CTL_OP_WB1_MODE;
+ break;
+ default:
+ pr_err("Incorrect writeback config num=%d\n",
+ mixer->num);
+ mdss_mdp_mixer_free(mixer);
+ return -EINVAL;
+ }
+ }
+ ctl->mixer_left = mixer;
+
+ return 0;
+}
+
struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata,
struct msm_fb_data_type *mfd)
{
@@ -555,8 +607,10 @@
break;
case WRITEBACK_PANEL:
ctl->intf_num = MDSS_MDP_NO_INTF;
- ctl->opmode = MDSS_MDP_CTL_OP_WFD_MODE;
ctl->start_fnc = mdss_mdp_writeback_start;
+ ret = mdss_mdp_ctl_setup_wfd(ctl);
+ if (ret)
+ goto ctl_init_fail;
break;
default:
pr_err("unsupported panel type (%d)\n", pdata->panel_info.type);
@@ -566,12 +620,6 @@
ctl->opmode |= (ctl->intf_num << 4);
- ret = mdss_mdp_ctl_setup(ctl);
- if (ret) {
- pr_err("unable to setup control path %d\n", ctl->num);
- goto ctl_init_fail;
- }
-
if (ctl->intf_num == MDSS_MDP_NO_INTF) {
ctl->dst_format = pdata->panel_info.out_format;
} else {
@@ -972,9 +1020,13 @@
ctl->flush_bits |= BIT(6) << mixer->num; /* LAYER_MIXER */
- off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
mdp_mixer_write(mixer, MDSS_MDP_REG_LM_OP_MODE, blend_color_out);
- mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(mixer->num), mixercfg);
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF)
+ off = MDSS_MDP_REG_CTL_LAYER(mixer->num);
+ else
+ off = MDSS_MDP_REG_CTL_LAYER(mixer->num +
+ MDSS_MDP_INTF_MAX_LAYERMIXER);
+ mdss_mdp_ctl_write(ctl, off, mixercfg);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index d4ffaff..f8cd0ce 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -447,8 +447,7 @@
#define MDSS_MDP_REG_SMP_ALLOC_W0 0x00180
#define MDSS_MDP_REG_SMP_ALLOC_R0 0x00230
-#define MDSS_MDP_SMP_MMB_SIZE 4096
-#define MDSS_MDP_SMP_MMB_BLOCKS 22
+#define MDSS_MDP_SMP_MMB_BLOCKS 22
enum mdss_mdp_smp_client_index {
MDSS_MDP_SMP_CLIENT_UNUSED,
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index c1dcc18..dcefc09 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -654,10 +654,8 @@
ret = mdss_mdp_display_commit(ctl, NULL);
mutex_unlock(&mfd->lock);
- if (IS_ERR_VALUE(ret)) {
- mutex_unlock(&mfd->ov_lock);
- return ret;
- }
+ if (IS_ERR_VALUE(ret))
+ goto commit_fail;
ret = mdss_mdp_display_wait4comp(ctl);
@@ -670,6 +668,7 @@
add_timer(&mfd->no_update.timer);
mutex_unlock(&mfd->no_update.lock);
+commit_fail:
ret = mdss_mdp_overlay_cleanup(mfd);
mutex_unlock(&mfd->ov_lock);
@@ -1017,6 +1016,12 @@
return;
}
+ ret = mdss_mdp_overlay_start(mfd);
+ if (ret) {
+ pr_err("unable to start overlay %d (%d)\n", mfd->index, ret);
+ return;
+ }
+
if (is_mdss_iommu_attached())
data.p[0].addr = mfd->iova;
else
@@ -1454,6 +1459,10 @@
rc = mdss_mdp_overlay_start(mfd);
if (!IS_ERR_VALUE(rc))
rc = mdss_mdp_overlay_kickoff(mfd->ctl);
+ } else {
+ rc = mdss_mdp_ctl_setup(mfd->ctl);
+ if (rc)
+ return rc;
}
if (!IS_ERR_VALUE(rc) && mfd->vsync_pending) {
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index 8c88646..d51b144 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -130,6 +130,17 @@
return 0;
}
+int mdss_mdp_smp_setup(struct mdss_data_type *mdata, u32 cnt, u32 size)
+{
+ if (!mdata)
+ return -EINVAL;
+
+ mdata->smp_mb_cnt = cnt;
+ mdata->smp_mb_size = size;
+
+ return 0;
+}
+
void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe)
{
int tmp;
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 59d760b..aa223a1 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -193,17 +193,24 @@
struct mdp_csc_cfg *data)
{
int i, ret = 0;
- u32 *off, base, val = 0;
+ char __iomem *base, *off;
+ u32 val = 0;
+ struct mdss_data_type *mdata;
+ struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_ctl *ctl;
+
if (data == NULL) {
pr_err("no csc matrix specified\n");
return -EINVAL;
}
+ mdata = mdss_mdp_get_mdata();
switch (block) {
case MDSS_MDP_BLOCK_SSPP:
- if (blk_idx < MDSS_MDP_SSPP_RGB0) {
- base = MDSS_MDP_REG_SSPP_OFFSET(blk_idx);
+ if (blk_idx < mdata->nvig_pipes) {
+ pipe = mdata->vig_pipes + blk_idx;
+ base = pipe->base;
if (tbl_idx == 1)
base += MDSS_MDP_REG_VIG_CSC_1_BASE;
else
@@ -213,9 +220,9 @@
}
break;
case MDSS_MDP_BLOCK_WB:
- if (blk_idx < MDSS_MDP_MAX_WRITEBACK) {
- base = MDSS_MDP_REG_WB_OFFSET(blk_idx) +
- MDSS_MDP_REG_WB_CSC_BASE;
+ if (blk_idx < mdata->nctl) {
+ ctl = mdata->ctl_off + blk_idx;
+ base = ctl->wb_base + MDSS_MDP_REG_WB_CSC_BASE;
} else {
ret = -EINVAL;
}
@@ -229,34 +236,33 @@
return ret;
}
- off = (u32 *) (base + CSC_MV_OFF);
+ off = base + CSC_MV_OFF;
for (i = 0; i < 9; i++) {
if (i & 0x1) {
val |= data->csc_mv[i] << 16;
- MDSS_MDP_REG_WRITE(off, val);
- off++;
+ writel_relaxed(val, off);
+ off += sizeof(u32 *);
} else {
val = data->csc_mv[i];
}
}
- MDSS_MDP_REG_WRITE(off, val); /* COEFF_33 */
+ writel_relaxed(val, off); /* COEFF_33 */
- off = (u32 *) (base + CSC_BV_OFF);
+ off = base + CSC_BV_OFF;
for (i = 0; i < 3; i++) {
- MDSS_MDP_REG_WRITE(off, data->csc_pre_bv[i]);
- MDSS_MDP_REG_WRITE((u32 *)(((u32)off) + CSC_POST_OFF),
- data->csc_post_bv[i]);
- off++;
+ writel_relaxed(data->csc_pre_bv[i], off);
+ writel_relaxed(data->csc_post_bv[i], off + CSC_POST_OFF);
+ off += sizeof(u32 *);
}
- off = (u32 *) (base + CSC_LV_OFF);
+ off = base + CSC_LV_OFF;
for (i = 0; i < 6; i += 2) {
val = (data->csc_pre_lv[i] << 8) | data->csc_pre_lv[i+1];
- MDSS_MDP_REG_WRITE(off, val);
+ writel_relaxed(val, off);
val = (data->csc_post_lv[i] << 8) | data->csc_post_lv[i+1];
- MDSS_MDP_REG_WRITE((u32 *)(((u32)off) + CSC_POST_OFF), val);
- off++;
+ writel_relaxed(val, off + CSC_POST_OFF);
+ off += sizeof(u32 *);
}
return ret;
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 5f994a0..b96e093 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -3301,6 +3301,10 @@
ret = mdp4_update_base_blend(mfd,
&metadata_ptr->data.blend_cfg);
break;
+ case metadata_op_wb_format:
+ ret = mdp4_update_writeback_format(mfd,
+ &metadata_ptr->data.mixer_cfg);
+ break;
#endif
default:
pr_warn("Unsupported request to MDP META IOCTL.\n");
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index b8231ed..f6ca334 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -451,3 +451,4 @@
header-y += ci-bridge-spi.h
header-y += msm_audio_amrwbplus.h
header-y += avtimer.h
+header-y += msm_ipa.h
diff --git a/include/linux/bif/consumer.h b/include/linux/bif/consumer.h
new file mode 100644
index 0000000..e4c190e
--- /dev/null
+++ b/include/linux/bif/consumer.h
@@ -0,0 +1,613 @@
+/* Copyright (c) 2013, 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 _LINUX_BIF_CONSUMER_H_
+#define _LINUX_BIF_CONSUMER_H_
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+
+#define BIF_DEVICE_ID_BYTE_LENGTH 8
+#define BIF_UNIQUE_ID_BYTE_LENGTH 10
+#define BIF_UNIQUE_ID_BIT_LENGTH 80
+
+#define BIF_PRIMARY_SLAVE_DEV_ADR 0x01
+
+/**
+ * enum bif_transaction - BIF master bus transaction types
+ * %BIF_TRANS_WD: Write data
+ * %BIF_TRANS_ERA: Extended register address
+ * %BIF_TRANS_WRA: Write register address
+ * %BIF_TRANS_RRA: Read register address
+ * %BIF_TRANS_BC: Bus command
+ * %BIF_TRANS_EDA: Extended device address
+ * %BIF_TRANS_SDA: Slave device address
+ *
+ * These values correspond to BIF word bits: BCF, bit 9, bit 8.
+ * BCF_n bit is inserted automatically.
+ */
+enum bif_transaction {
+ BIF_TRANS_WD = 0x00,
+ BIF_TRANS_ERA = 0x01,
+ BIF_TRANS_WRA = 0x02,
+ BIF_TRANS_RRA = 0x03,
+ BIF_TRANS_BC = 0x04,
+ BIF_TRANS_EDA = 0x05,
+ BIF_TRANS_SDA = 0x06,
+};
+
+/* BIF slave response components */
+#define BIF_SLAVE_RD_ACK 0x200
+#define BIF_SLAVE_RD_EOT 0x100
+#define BIF_SLAVE_RD_DATA 0x0FF
+#define BIF_SLAVE_RD_ERR 0x0FF
+#define BIF_SLAVE_TACK_ACK 0x200
+#define BIF_SLAVE_TACK_WCNT 0x0FF
+#define BIF_SLAVE_TACK_ERR 0x0FF
+
+/**
+ * enum bif_bus_command - MIPI defined bus commands to use in BC transaction
+ * %BIF_CMD_BRES: Bus reset of all slaves
+ * %BIF_CMD_PDWN: Put all slaves into power down mode
+ * %BIF_CMD_STBY: Put all slaves into standby mode
+ * %BIF_CMD_EINT: Enable interrupts for all slaves
+ * %BIF_CMD_ISTS: Poll interrupt status for all slaves. Expects BQ
+ * response if any slave has a pending interrupt.
+ * %BIF_CMD_RBL: Specify the burst read length for the next read
+ * transaction. Bits 3 to 0 should also be ORed on in
+ * order to specify the number of bytes to read.
+ * %BIF_CMD_RBE: Specify the extended burst read length for the next read
+ * transaction. Bits 3 to 0 should also be ORed on in
+ * order to specify the number of bytes to read. The burst
+ * read length for RBEy and RBLx = 16 * y + x.
+ * %BIF_CMD_DASM: Device activation stick mode. This keeps a slave
+ * selected if it would otherwise become unselected by the
+ * next transaction.
+ * %BIF_CMD_DISS: UID search start
+ * %BIF_CMD_DILC: UID length check. Expects BQ response if all 80 UID
+ * bits for a given slave have been entered.
+ * %BIF_CMD_DIE0: UID search enter 0
+ * %BIF_CMD_DIE1: UID search enter 1
+ * %BIF_CMD_DIP0: UID search probe 0
+ * %BIF_CMD_DIP1: UID search probe 1
+ * %BIF_CMD_DRES: Device reset of selected slaves
+ * %BIF_CMD_TQ: Transaction query; expects TACK response
+ * %BIF_CMD_AIO: Address increment off for the next transaction
+ *
+ * These values correspond to BIF word bits 7 to 0.
+ */
+enum bif_bus_command {
+ BIF_CMD_BRES = 0x00,
+ BIF_CMD_PDWN = 0x02,
+ BIF_CMD_STBY = 0x03,
+ BIF_CMD_EINT = 0x10,
+ BIF_CMD_ISTS = 0x11,
+ BIF_CMD_RBL = 0x20,
+ BIF_CMD_RBE = 0x30,
+ BIF_CMD_DASM = 0x40,
+ BIF_CMD_DISS = 0x80,
+ BIF_CMD_DILC = 0x81,
+ BIF_CMD_DIE0 = 0x84,
+ BIF_CMD_DIE1 = 0x85,
+ BIF_CMD_DIP0 = 0x86,
+ BIF_CMD_DIP1 = 0x87,
+ BIF_CMD_DRES = 0xC0,
+ BIF_CMD_TQ = 0xC2,
+ BIF_CMD_AIO = 0xC4,
+};
+
+/**
+ * struct bif_ddb_l1_data - MIPI defined L1 DDB data structure
+ * @revision: DDB version; should be 0x10 for DDB v1.0
+ * @level: DDB level support; should be 0x03 for DDB L1 and L2
+ * @device_class: MIPI device class; should be 0x0800
+ * @manufacturer_id: Manufacturer ID number allocated by MIPI
+ * @product_id: Manufacturer specified product ID number
+ * @length: Size of L2 function directory in bytes
+ */
+struct bif_ddb_l1_data {
+ u8 revision;
+ u8 level;
+ u16 device_class;
+ u16 manufacturer_id;
+ u16 product_id;
+ u16 length;
+};
+
+/**
+ * struct bif_ddb_l2_data - MIPI defined L2 DDB function data structure
+ * @function_type: Defines the type of the function. The type may be
+ * either MIPI or manufacturer defined.
+ * @function_version: Defines the version of the function. The version may
+ * be either MIPI or manufacturer defined.
+ * @function_pointer: Address in BIF slave memory where the register map for
+ * the function begins.
+ */
+struct bif_ddb_l2_data {
+ u8 function_type;
+ u8 function_version;
+ u16 function_pointer;
+};
+
+/**
+ * enum bif_mipi_function_type - MIPI defined DDB L2 function types
+ * %BIF_FUNC_PROTOCOL: Protocol function which provides access to core
+ * BIF communication features.
+ * %BIF_FUNC_SLAVE_CONTROL: Slave control function which provides control
+ * for BIF slave interrupts and tasks.
+ * %BIF_FUNC_TEMPERATURE: Temperature sensor function which provides a
+ * means to accurately read the battery temperature
+ * in a single-shot or periodic fashion.
+ * %BIF_FUNC_NVM: Non-volatile memory function which provides a
+ * means to store data onto a BIF slave that is
+ * non-volatile. Secondary slave objects are also
+ * found through the NVM function.
+ * %BIF_FUNC_AUTHENTICATION: Authentication function which provides a means
+ * to authenticate batteries. This function does
+ * not have a MIPI defined implimentation. Instead
+ * all aspects of the authentication function are
+ * left to the discretion of the manufacturer.
+ */
+enum bif_mipi_function_type {
+ BIF_FUNC_PROTOCOL = 0x01,
+ BIF_FUNC_SLAVE_CONTROL = 0x02,
+ BIF_FUNC_TEMPERATURE = 0x03,
+ BIF_FUNC_NVM = 0x04,
+ BIF_FUNC_AUTHENTICATION = 0x05,
+};
+
+#define BIF_DDB_L1_BASE_ADDR 0x0000
+#define BIF_DDB_L2_BASE_ADDR 0x000A
+
+/**
+ * enum bif_slave_error_code - MIPI defined BIF slave error codes
+ * %BIF_ERR_NONE: No error occurred
+ * %BIF_ERR_GENERAL: An unenumerated error occurred
+ * %BIF_ERR_PARITY: A Hamming-15 parity check failed for a word
+ * sent on the bus
+ * %BIF_ERR_INVERSION: More than 8 bits in a word were 1
+ * %BIF_ERR_BAD_LENGTH: Word had more or less than 17 bits
+ * %BIF_ERR_TIMING: Bit timing was violated in a word
+ * %BIF_ERR_UNKNOWN_CMD: Bus command was unknown to the slave
+ * %BIF_ERR_CMD_SEQ: Commands with ordering dependency were not
+ * sent in the right order
+ * %BIF_ERR_BUS_COLLISION: BCL was already low at the beginning of a new
+ * transaction
+ * %BIF_ERR_SLAVE_BUSY: Slave is busy and cannot respond
+ * %BIF_ERR_FATAL: Slave is in an unrecoverable error state and
+ * must be reset
+ *
+ * These values are present in the ERR portion of an RD or TACK slave response
+ * word. These values can also be found in the ERR_CODE register of the
+ * protocol function.
+ */
+enum bif_slave_error_code {
+ BIF_ERR_NONE = 0x00,
+ BIF_ERR_GENERAL = 0x10,
+ BIF_ERR_PARITY = 0x11,
+ BIF_ERR_INVERSION = 0x12,
+ BIF_ERR_BAD_LENGTH = 0x13,
+ BIF_ERR_TIMING = 0x14,
+ BIF_ERR_UNKNOWN_CMD = 0x15,
+ BIF_ERR_CMD_SEQ = 0x16,
+ BIF_ERR_BUS_COLLISION = 0x1F,
+ BIF_ERR_SLAVE_BUSY = 0x20,
+ BIF_ERR_FATAL = 0x7F,
+};
+
+/**
+ * struct bif_protocol_function - constant data present in protocol function
+ * @l2_entry: Pointer to protocol function L2 DDB data struct
+ * @protocol_pointer: BIF slave address where protocol registers begin
+ * @device_id_pointer: BIF slave address where device ID begins
+ * @device_id: The 8-byte unique device ID in MSB to LSB order
+ */
+struct bif_protocol_function {
+ struct bif_ddb_l2_data *l2_entry;
+ u16 protocol_pointer;
+ u16 device_id_pointer;
+ u8 device_id[BIF_DEVICE_ID_BYTE_LENGTH]; /* Unique ID */
+};
+
+#define PROTOCOL_FUNC_DEV_ADR_ADDR(protocol_pointer) ((protocol_pointer) + 0)
+#define PROTOCOL_FUNC_ERR_CODE_ADDR(protocol_pointer) ((protocol_pointer) + 2)
+#define PROTOCOL_FUNC_ERR_CNT_ADDR(protocol_pointer) ((protocol_pointer) + 3)
+#define PROTOCOL_FUNC_WORD_CNT_ADDR(protocol_pointer) ((protocol_pointer) + 4)
+
+/**
+ * struct bif_slave_control_function - constant data present in slave control
+ * function as well internal software state parameters
+ * @l2_entry: Pointer to slave control function L2 DDB data struct
+ * @slave_ctrl_pointer: BIF slave address where slave control registers begin
+ * @task_count: Number of tasks supported by the slave
+ * @irq_notifier_list: List of notifiers for consumers drivers that wish to be
+ * notified when any given interrupt triggers. This list
+ * is dynamically allocated with length task_count.
+ */
+struct bif_slave_control_function {
+ struct bif_ddb_l2_data *l2_entry;
+ u16 slave_ctrl_pointer;
+ unsigned int task_count;
+ struct blocking_notifier_head *irq_notifier_list;
+};
+
+#define SLAVE_CTRL_TASKS_PER_SET 8
+
+/**
+ * bif_slave_control_task_is_valid() - returns true if the specified task
+ * is supported by the slave or false if it isn't
+ * @func: Pointer to slave's slave control function structure
+ * @task: Slave task number to check
+ */
+static inline bool
+bif_slave_control_task_is_valid(struct bif_slave_control_function *func,
+ unsigned int task)
+{
+ return func ? task < func->task_count : false;
+}
+
+#define SLAVE_CTRL_FUNC_IRQ_EN_ADDR(slave_ctrl_pointer, task) \
+ ((slave_ctrl_pointer) + 4 * ((task) / SLAVE_CTRL_TASKS_PER_SET) + 0)
+
+#define SLAVE_CTRL_FUNC_IRQ_STATUS_ADDR(slave_ctrl_pointer, task) \
+ ((slave_ctrl_pointer) + 4 * ((task) / SLAVE_CTRL_TASKS_PER_SET) + 1)
+#define SLAVE_CTRL_FUNC_IRQ_CLEAR_ADDR(slave_ctrl_pointer, task) \
+ SLAVE_CTRL_FUNC_IRQ_STATUS_ADDR(slave_ctrl_pointer, task)
+
+#define SLAVE_CTRL_FUNC_TASK_TRIGGER_ADDR(slave_ctrl_pointer, task) \
+ ((slave_ctrl_pointer) + 4 * ((task) / SLAVE_CTRL_TASKS_PER_SET) + 2)
+#define SLAVE_CTRL_FUNC_TASK_BUSY_ADDR(slave_ctrl_pointer, task) \
+ SLAVE_CTRL_FUNC_TASK_TRIGGER_ADDR(slave_ctrl_pointer, task)
+
+#define SLAVE_CTRL_FUNC_TASK_AUTO_TRIGGER_ADDR(slave_ctrl_pointer, task) \
+ ((slave_ctrl_pointer) + 4 * ((task) / SLAVE_CTRL_TASKS_PER_SET) + 3)
+
+/**
+ * struct bif_temperature_function - constant data present in temperature
+ * sensor function
+ * @temperatuer_pointer: BIF slave address where temperature sensor
+ * control registers begin
+ * @slave_control_channel: Slave control channel associated with the
+ * temperature sensor function. This channel is
+ * also the task number.
+ * @accuracy_pointer: BIF slave address where temperature accuracy
+ * registers begin
+ */
+struct bif_temperature_function {
+ u16 temperature_pointer;
+ u8 slave_control_channel;
+ u16 accuracy_pointer;
+};
+
+/**
+ * enum bif_mipi_object_type - MIPI defined BIF object types
+ * %BIF_OBJ_END_OF_LIST: Indicates that the end of the object list in
+ * NVM has been reached
+ * %BIF_OBJ_SEC_SLAVE: Specifies the UIDs of secondary slaves found
+ * inside of the battery pack
+ * %BIF_OBJ_BATT_PARAM: Specifies some variety of battery parameter.
+ * There is no MIPI defined format for this object
+ * type so parsing is manufacturer specific.
+ */
+enum bif_mipi_object_type {
+ BIF_OBJ_END_OF_LIST = 0x00,
+ BIF_OBJ_SEC_SLAVE = 0x01,
+ BIF_OBJ_BATT_PARAM = 0x02,
+};
+
+/**
+ * struct bif_object - contains all header and data information for a slave
+ * data object
+ * @type: Object type
+ * @version: Object version
+ * @manufacturer_id: Manufacturer ID number allocated by MIPI
+ * @length: Length of the entire object including header and CRC
+ * @data: Raw byte data found in the object
+ * @crc: CRC of the object calculated using CRC-CCITT
+ * @list: Linked-list connection parameter
+ * @addr: BIF slave address correspond to the start of the object
+ *
+ * manufacturer_id == 0x0000 if MIPI type and version.
+ */
+struct bif_object {
+ u8 type;
+ u8 version;
+ u16 manufacturer_id;
+ u16 length;
+ u8 *data;
+ u16 crc;
+ struct list_head list;
+ u16 addr;
+};
+
+/**
+ * struct bif_nvm_function - constant data present in non-volatile memory
+ * function as well internal software state
+ * parameters
+ * @nvm_pointer: BIF slave address where NVM registers begin
+ * @slave_control_channel: Slave control channel associated with the
+ * NVM function. This channel is also the task
+ * number.
+ * @write_buffer_size: Size in bytes of the NVM write buffer. 0x00
+ * is used to denote a 256 byte buffer.
+ * @nvm_base_address: BIF slave address where NVM begins
+ * @nvm_size: NVM size in bytes
+ * @object_count: Number of BIF objects read from NVM
+ * @object_list: List of BIF objects read from NVM
+ */
+struct bif_nvm_function {
+ u16 nvm_pointer;
+ u8 slave_control_channel;
+ u8 write_buffer_size;
+ u16 nvm_base_address;
+ u16 nvm_size;
+ int object_count;
+ struct list_head object_list;
+};
+
+/**
+ * struct bif_ctrl - Opaque handle for a BIF controller to be used in bus
+ * oriented BIF function calls.
+ */
+struct bif_ctrl;
+
+/**
+ * struct bif_slave - Opaque handle for a BIF slave to be used in slave oriented
+ * BIF function calls.
+ */
+struct bif_slave;
+
+/**
+ * enum bif_bus_state - indicates the current or desired state of the BIF bus
+ * %BIF_BUS_STATE_MASTER_DISABLED: BIF host hardware is disabled
+ * %BIF_BUS_STATE_POWER_DOWN: BIF bus is in power down state and
+ * BCL is not being pulled high
+ * %BIF_BUS_STATE_STANDBY: BIF slaves are in standby state in which
+ * less power is drawn
+ * %BIF_BUS_STATE_ACTIVE: BIF slaves are ready for immediate
+ * communications
+ * %BIF_BUS_STATE_INTERRUPT: BIF bus is active, but no communication
+ * is possible. Instead, either one of the
+ * slaves or the master must transition to
+ * active state by pulling BCL low for 1
+ * tau bif period.
+ */
+enum bif_bus_state {
+ BIF_BUS_STATE_MASTER_DISABLED,
+ BIF_BUS_STATE_POWER_DOWN,
+ BIF_BUS_STATE_STANDBY,
+ BIF_BUS_STATE_ACTIVE,
+ BIF_BUS_STATE_INTERRUPT,
+};
+
+/**
+ * enum bif_bus_event - events that the BIF framework may send to BIF consumers
+ * %BIF_BUS_EVENT_BATTERY_INSERTED: Indicates that a battery was just
+ * inserted physically or that the BIF
+ * host controller for the battery just
+ * probed and a battery was already
+ * present.
+ * %BIF_BUS_EVENT_BATTERY_REMOVED: Indicates that a battery was just
+ * removed and thus its slaves are no
+ * longer accessible.
+ */
+enum bif_bus_event {
+ BIF_BUS_EVENT_BATTERY_INSERTED,
+ BIF_BUS_EVENT_BATTERY_REMOVED,
+};
+
+/* Mask values to be ORed together for use in bif_match_criteria.match_mask. */
+#define BIF_MATCH_MANUFACTURER_ID BIT(0)
+#define BIF_MATCH_PRODUCT_ID BIT(1)
+#define BIF_MATCH_FUNCTION_TYPE BIT(2)
+#define BIF_MATCH_FUNCTION_VERSION BIT(3)
+#define BIF_MATCH_IGNORE_PRESENCE BIT(4)
+
+/**
+ * struct bif_match_criteria - specifies the matching criteria that a BIF
+ * consumer uses to find an appropriate BIF slave
+ * @match_mask: Mask value specifying which parameters to match upon.
+ * This value should be some ORed combination of
+ * BIF_MATCH_* specified above.
+ * @manufacturer_id: Manufacturer ID number allocated by MIPI
+ * @product_id: Manufacturer specified product ID number
+ * @function_type: Defines the type of the function. The type may be
+ * either MIPI or manufacturer defined.
+ * @function_version: Defines the version of the function. The version may
+ * be either MIPI or manufacturer defined.
+ * @ignore_presence: If true, then slaves that are currently not present
+ * will be successfully matched against. By default, only
+ * present slaves can be matched.
+ */
+struct bif_match_criteria {
+ u32 match_mask;
+ u16 manufacturer_id;
+ u16 product_id;
+ u8 function_type;
+ u8 function_version;
+ bool ignore_presence;
+};
+
+/**
+ * bif_battery_rid_ranges - MIPI-BIF defined Rid battery pack resistance ranges
+ * %BIF_BATT_RID_SPECIAL1_MIN: Minimum Rid for special case 1
+ * %BIF_BATT_RID_SPECIAL1_MAX: Maximum Rid for special case 1
+ * %BIF_BATT_RID_SPECIAL2_MIN: Minimum Rid for special case 2
+ * %BIF_BATT_RID_SPECIAL2_MAX: Maximum Rid for special case 2
+ * %BIF_BATT_RID_SPECIAL3_MIN: Minimum Rid for special case 3
+ * %BIF_BATT_RID_SPECIAL3_MAX: Maximum Rid for special case 3
+ * %BIF_BATT_RID_LOW_COST_MIN: Minimum Rid for a low cost battery pack
+ * %BIF_BATT_RID_LOW_COST_MAX: Maximum Rid for a low cost battery pack
+ * %BIF_BATT_RID_SMART_MIN: Minimum Rid for a smart battery pack
+ * %BIF_BATT_RID_SMART_MAX: Maximum Rid for a smart battery pack
+ */
+enum bif_battery_rid_ranges {
+ BIF_BATT_RID_SPECIAL1_MIN = 0,
+ BIF_BATT_RID_SPECIAL1_MAX = 1,
+ BIF_BATT_RID_SPECIAL2_MIN = 7350,
+ BIF_BATT_RID_SPECIAL2_MAX = 7650,
+ BIF_BATT_RID_SPECIAL3_MIN = 12740,
+ BIF_BATT_RID_SPECIAL3_MAX = 13260,
+ BIF_BATT_RID_LOW_COST_MIN = 19600,
+ BIF_BATT_RID_LOW_COST_MAX = 140000,
+ BIF_BATT_RID_SMART_MIN = 240000,
+ BIF_BATT_RID_SMART_MAX = 450000,
+};
+
+#ifdef CONFIG_BIF
+
+int bif_request_irq(struct bif_slave *slave, unsigned int task,
+ struct notifier_block *nb);
+int bif_free_irq(struct bif_slave *slave, unsigned int task,
+ struct notifier_block *nb);
+
+int bif_trigger_task(struct bif_slave *slave, unsigned int task);
+int bif_task_is_busy(struct bif_slave *slave, unsigned int task);
+
+int bif_ctrl_count(void);
+struct bif_ctrl *bif_ctrl_get_by_id(unsigned int id);
+struct bif_ctrl *bif_ctrl_get(struct device *consumer_dev);
+void bif_ctrl_put(struct bif_ctrl *ctrl);
+
+int bif_ctrl_signal_battery_changed(struct bif_ctrl *ctrl);
+
+int bif_slave_match_count(const struct bif_ctrl *ctrl,
+ const struct bif_match_criteria *match_criteria);
+
+struct bif_slave *bif_slave_match_get(const struct bif_ctrl *ctrl,
+ unsigned int id, const struct bif_match_criteria *match_criteria);
+
+void bif_slave_put(struct bif_slave *slave);
+
+int bif_ctrl_notifier_register(struct bif_ctrl *ctrl,
+ struct notifier_block *nb);
+
+int bif_ctrl_notifier_unregister(struct bif_ctrl *ctrl,
+ struct notifier_block *nb);
+
+struct bif_ctrl *bif_get_ctrl_handle(struct bif_slave *slave);
+
+int bif_slave_find_function(struct bif_slave *slave, u8 function, u8 *version,
+ u16 *function_pointer);
+
+int bif_slave_read(struct bif_slave *slave, u16 addr, u8 *buf, int len);
+int bif_slave_write(struct bif_slave *slave, u16 addr, u8 *buf, int len);
+
+int bif_slave_is_present(struct bif_slave *slave);
+
+int bif_slave_is_selected(struct bif_slave *slave);
+int bif_slave_select(struct bif_slave *slave);
+
+int bif_ctrl_raw_transaction(struct bif_ctrl *ctrl, int transaction, u8 data);
+int bif_ctrl_raw_transaction_read(struct bif_ctrl *ctrl, int transaction,
+ u8 data, int *response);
+int bif_ctrl_raw_transaction_query(struct bif_ctrl *ctrl, int transaction,
+ u8 data, bool *query_response);
+
+void bif_ctrl_bus_lock(struct bif_ctrl *ctrl);
+void bif_ctrl_bus_unlock(struct bif_ctrl *ctrl);
+
+u16 bif_crc_ccitt(const u8 *buffer, unsigned int len);
+
+int bif_ctrl_measure_rid(struct bif_ctrl *ctrl);
+int bif_ctrl_get_bus_period(struct bif_ctrl *ctrl);
+int bif_ctrl_set_bus_period(struct bif_ctrl *ctrl, int period_ns);
+int bif_ctrl_get_bus_state(struct bif_ctrl *ctrl);
+int bif_ctrl_set_bus_state(struct bif_ctrl *ctrl, enum bif_bus_state state);
+
+#else
+
+static inline int bif_request_irq(struct bif_slave *slave, unsigned int task,
+ struct notifier_block *nb) { return -EPERM; }
+static inline int bif_free_irq(struct bif_slave *slave, unsigned int task,
+ struct notifier_block *nb) { return -EPERM; }
+
+static inline int bif_trigger_task(struct bif_slave *slave, unsigned int task)
+{ return -EPERM; }
+static inline int bif_task_is_busy(struct bif_slave *slave, unsigned int task)
+{ return -EPERM; }
+
+static inline int bif_ctrl_count(void) { return -EPERM; }
+static inline struct bif_ctrl *bif_ctrl_get_by_id(unsigned int id)
+{ return ERR_PTR(-EPERM); }
+struct bif_ctrl *bif_ctrl_get(struct device *consumer_dev)
+{ return ERR_PTR(-EPERM); }
+static inline void bif_ctrl_put(struct bif_ctrl *ctrl) { return; }
+
+int bif_ctrl_signal_battery_changed(struct bif_ctrl *ctrl) { return -EPERM; }
+
+static inline int bif_slave_match_count(const struct bif_ctrl *ctrl,
+ const struct bif_match_criteria *match_criteria)
+{ return -EPERM; }
+
+static inline struct bif_slave *bif_slave_match_get(const struct bif_ctrl *ctrl,
+ unsigned int id, const struct bif_match_criteria *match_criteria)
+{ return ERR_PTR(-EPERM); }
+
+static inline void bif_slave_put(struct bif_slave *slave) { return; }
+
+static inline int bif_ctrl_notifier_register(struct bif_ctrl *ctrl,
+ struct notifier_block *nb)
+{ return -EPERM; }
+
+static inline int bif_ctrl_notifier_unregister(struct bif_ctrl *ctrl,
+ struct notifier_block *nb)
+{ return -EPERM; }
+
+static inline struct bif_ctrl *bif_get_ctrl_handle(struct bif_slave *slave)
+{ return ERR_PTR(-EPERM); }
+
+static inline int bif_slave_find_function(struct bif_slave *slave, u8 function,
+ u8 *version, u16 *function_pointer)
+{ return -EPERM; }
+
+static inline int bif_slave_read(struct bif_slave *slave, u16 addr, u8 *buf,
+ int len)
+{ return -EPERM; }
+static inline int bif_slave_write(struct bif_slave *slave, u16 addr, u8 *buf,
+ int len)
+{ return -EPERM; }
+
+int bif_slave_is_present(struct bif_slave *slave) { return -EPERM; }
+
+int bif_slave_is_selected(struct bif_slave *slave) { return -EPERM; }
+int bif_slave_select(struct bif_slave *slave) { return -EPERM; }
+
+int bif_ctrl_raw_transaction(struct bif_ctrl *ctrl, int transaction, u8 data)
+{ return -EPERM; }
+int bif_ctrl_raw_transaction_read(struct bif_ctrl *ctrl, int transaction,
+ u8 data, int *response)
+{ return -EPERM; }
+int bif_ctrl_raw_transaction_query(struct bif_ctrl *ctrl, int transaction,
+ u8 data, bool *query_response)
+{ return -EPERM; }
+
+static inline void bif_ctrl_bus_lock(struct bif_ctrl *ctrl)
+{ return -EPERM; }
+static inline void bif_ctrl_bus_unlock(struct bif_ctrl *ctrl)
+{ return -EPERM; }
+
+static inline u16 bif_crc_ccitt(const u8 *buffer, unsigned int len)
+{ return 0; }
+
+static inline int bif_ctrl_measure_rid(struct bif_ctrl *ctrl) { return -EPERM; }
+int bif_ctrl_get_bus_period(struct bif_ctrl *ctrl) { return -EPERM; }
+int bif_ctrl_set_bus_period(struct bif_ctrl *ctrl, int period_ns)
+{ return -EPERM; }
+int bif_ctrl_get_bus_state(struct bif_ctrl *ctrl) { return -EPERM; }
+int bif_ctrl_set_bus_state(struct bif_ctrl *ctrl, enum bif_bus_state state)
+{ return -EPERM; }
+
+#endif
+
+#endif
diff --git a/include/linux/bif/driver.h b/include/linux/bif/driver.h
new file mode 100644
index 0000000..184d46f
--- /dev/null
+++ b/include/linux/bif/driver.h
@@ -0,0 +1,161 @@
+/* Copyright (c) 2013, 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 _LINUX_BIF_DRIVER_H_
+#define _LINUX_BIF_DRIVER_H_
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/bif/consumer.h>
+
+/**
+ * struct bif_ctrl_dev - opaque handle used to identify a given BIF controller
+ * device
+ */
+struct bif_ctrl_dev;
+
+/**
+ * struct bif_ctrl_ops - BIF operations which may be implemented by BIF
+ * controller drivers
+ * @bus_transaction: Perform the specified BIF transaction which does
+ * not result in any slave response.
+ * @bus_transaction_query: Perform the specified BIF transaction which
+ * expects a BQ response in the case of slave
+ * positive acknowledgement.
+ * @bus_transaction_read: Perform the specified BIF transaction which
+ * expects an RD or TACK response from the selected
+ * slave.
+ * @read_slave_registers: Perform all BIF transactions necessary to read
+ * the specified set of contiguous registers from
+ * the previously selected slave. This operation
+ * is used to optimize the common case of slave
+ * register reads since the a BIF controller driver
+ * can take advantage of BIF burst reads while the
+ * BIF core driver cannot due to the inherient
+ * tight timing requirements.
+ * @write_slave_registers: Perform all BIF transactions necessary to write
+ * the specified set of contiguous registers to
+ * the previously selected slave. This operation
+ * is used to optimize the common case of slave
+ * register writes since the a BIF controller
+ * driver can remove redundant steps when
+ * performing several WD commands in a row.
+ * @get_bus_period: Return the tau_bif BIF bus clock period in
+ * nanoseconds.
+ * @set_bus_period: Set the tau_bif BIF bus clock period in
+ * nanoseconds. If the exact period is not
+ * supported by the BIF controller hardware, then
+ * the next larger supported period should be used.
+ * @get_battery_presence: Return the current state of the battery pack.
+ * If a battery pack is present, then return >= 1.
+ * If a battery pack is not present, then return 0.
+ * If an error occurs during presence detection,
+ * then return errno.
+ * @get_battery_rid: Return the measured value of the Rid battery
+ * pack pull-down resistor in ohms.
+ * @get_bus_state: Return the current bus state as defined by one
+ * of the enum bif_bus_state values.
+ * @set_bus_state: Set the BIF bus state to the specified enum
+ * bif_bus_state value.
+ *
+ * The following operations must be defined by every BIF controller driver in
+ * order to ensure baseline functionality:
+ * bus_transaction, bus_transaction_query, get_bus_state, and set_bus_state.
+ *
+ * The BIF core driver is unaware of BIF transaction timing constraints. A
+ * given BIF controller driver must ensure that all timing constraints in the
+ * MIPI-BIF specification are met as transactions are carried out.
+ *
+ * Conversion between 11-bit and 17-bit BIF words (i.e. the insertion of BCF_n,
+ * parity bits, and the inversion bit) must be handled inside of the BIF
+ * controller driver (either in software or hardware). This guarantees maximum
+ * performance if hardware support is available.
+ *
+ * The bus_transaction_read operation must return -ETIMEDOUT in the case of no
+ * RD or TACK word received. This allows the transaction query, TQ, command
+ * to be used for slave selection verification.
+ *
+ * It is acceptable for the BIF bus state to be changed autonomously by a BIF
+ * controller driver in response to low level bus actions without a call to
+ * set_bus_state. One example is the case of receiving a slave interrupt
+ * while in interrupt state as this intrinsically causes the bus to enter the
+ * active communication state.
+ */
+struct bif_ctrl_ops {
+ int (*bus_transaction) (struct bif_ctrl_dev *bdev, int transaction,
+ u8 data);
+ int (*bus_transaction_query) (struct bif_ctrl_dev *bdev,
+ int transaction, u8 data,
+ bool *query_response);
+ int (*bus_transaction_read) (struct bif_ctrl_dev *bdev,
+ int transaction, u8 data,
+ int *response);
+ int (*read_slave_registers) (struct bif_ctrl_dev *bdev, u16 addr,
+ u8 *data, int len);
+ int (*write_slave_registers) (struct bif_ctrl_dev *bdev, u16 addr,
+ const u8 *data, int len);
+ int (*get_bus_period) (struct bif_ctrl_dev *bdev);
+ int (*set_bus_period) (struct bif_ctrl_dev *bdev, int period_ns);
+ int (*get_battery_presence) (struct bif_ctrl_dev *bdev);
+ int (*get_battery_rid) (struct bif_ctrl_dev *bdev);
+ int (*get_bus_state) (struct bif_ctrl_dev *bdev);
+ int (*set_bus_state) (struct bif_ctrl_dev *bdev, int state);
+};
+
+/**
+ * struct bif_ctrl_desc - BIF bus controller descriptor
+ * @name: Name used to identify the BIF controller
+ * @ops: BIF operations supported by the BIF controller
+ * @bus_clock_min_ns: Minimum tau_bif BIF bus clock period supported by the
+ * BIF controller
+ * @bus_clock_max_ns: Maximum tau_bif BIF bus clock period supported by the
+ * BIF controller
+ *
+ * Each BIF controller registered with the BIF core is described with a
+ * structure of this type.
+ */
+struct bif_ctrl_desc {
+ const char *name;
+ struct bif_ctrl_ops *ops;
+ int bus_clock_min_ns;
+ int bus_clock_max_ns;
+};
+
+#ifdef CONFIG_BIF
+
+struct bif_ctrl_dev *bif_ctrl_register(struct bif_ctrl_desc *bif_desc,
+ struct device *dev, void *driver_data, struct device_node *of_node);
+
+void bif_ctrl_unregister(struct bif_ctrl_dev *bdev);
+
+void *bdev_get_drvdata(struct bif_ctrl_dev *bdev);
+
+int bif_ctrl_notify_battery_changed(struct bif_ctrl_dev *bdev);
+int bif_ctrl_notify_slave_irq(struct bif_ctrl_dev *bdev);
+
+#else
+
+static inline struct bif_ctrl_dev *bif_ctrl_register(
+ struct bif_ctrl_desc *bif_desc, struct device *dev, void *driver_data,
+ struct device_node *of_node)
+{ return ERR_PTR(-EINVAL); }
+
+static inline void bif_ctrl_unregister(struct bif_ctrl_dev *bdev) { }
+
+static inline void *bdev_get_drvdata(struct bif_ctrl_dev *bdev) { return NULL; }
+
+int bif_ctrl_notify_slave_irq(struct bif_ctrl_dev *bdev) { return -EINVAL; }
+
+#endif
+
+#endif
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index d3ee879..7a5ab0d 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -112,10 +112,10 @@
/* This needs to be modified manually now, when we add
a new RANGE of SSIDs to the msg_mask_tbl */
#define MSG_MASK_TBL_CNT 24
-#define EVENT_LAST_ID 0x099F
+#define EVENT_LAST_ID 0x09AB
#define MSG_SSID_0 0
-#define MSG_SSID_0_LAST 93
+#define MSG_SSID_0_LAST 94
#define MSG_SSID_1 500
#define MSG_SSID_1_LAST 506
#define MSG_SSID_2 1000
@@ -278,6 +278,9 @@
MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_HIGH,
MSG_LVL_LOW
};
@@ -713,7 +716,7 @@
/* LOG CODES */
#define LOG_0 0x0
-#define LOG_1 0x1750
+#define LOG_1 0x1755
#define LOG_2 0x0
#define LOG_3 0x0
#define LOG_4 0x4910
diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h
index 285b593..8a1b3a1 100644
--- a/include/linux/dma-contiguous.h
+++ b/include/linux/dma-contiguous.h
@@ -70,7 +70,7 @@
void dma_contiguous_reserve(phys_addr_t addr_limit);
int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base,
- phys_addr_t limit);
+ phys_addr_t limit, const char *name);
int dma_contiguous_add_device(struct device *dev, phys_addr_t base);
@@ -91,7 +91,7 @@
phys_addr_t base, phys_addr_t limit)
{
int ret;
- ret = dma_contiguous_reserve_area(size, &base, limit);
+ ret = dma_contiguous_reserve_area(size, &base, limit, NULL);
if (ret == 0)
ret = dma_contiguous_add_device(dev, base);
return ret;
diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h
index 785a33a..1c67b1e 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -188,6 +188,7 @@
int btc_delay_ms;
int btc_panic_if_cant_stop_chg;
int stop_chg_upon_expiry;
+ bool disable_chg_rmvl_wrkarnd;
};
enum pm8921_charger_source {
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index e9051e1..565d6b5 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -91,6 +91,20 @@
unsigned int quirks2; /* More deviations from spec. */
#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
+/*
+ * Read Transfer Active/ Write Transfer Active may be not
+ * de-asserted after end of transaction. Issue reset for DAT line.
+ */
+#define SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT (1<<1)
+/*
+ * Slow interrupt clearance at 400KHz may cause
+ * host controller driver interrupt handler to
+ * be called twice.
+ */
+#define SDHCI_QUIRK2_SLOW_INT_CLR (1<<2)
+/* Ignore CMD CRC errors for tuning commands */
+#define SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING (1<<3)
+
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
diff --git a/include/linux/msm_ipa.h b/include/linux/msm_ipa.h
index 1b869b1..30bf4f2 100644
--- a/include/linux/msm_ipa.h
+++ b/include/linux/msm_ipa.h
@@ -155,6 +155,10 @@
* wlan client normal: wlan client moved out of power save
* sw routing enable: ipa routing is disabled
* sw routing disable: ipa routing is enabled
+ * wlan ap connect: wlan AP(access point) is up
+ * wlan ap disconnect: wlan AP(access point) is down
+ * wlan sta connect: wlan STA(station) is up
+ * wlan sta disconnect: wlan STA(station) is down
*/
enum ipa_wlan_event {
WLAN_CLIENT_CONNECT,
@@ -163,6 +167,10 @@
WLAN_CLIENT_NORMAL_MODE,
SW_ROUTING_ENABLE,
SW_ROUTING_DISABLE,
+ WLAN_AP_CONNECT,
+ WLAN_AP_DISCONNECT,
+ WLAN_STA_CONNECT,
+ WLAN_STA_DISCONNECT,
};
@@ -761,4 +769,87 @@
IPA_IOCTL_PULL_MSG, \
struct ipa_msg_meta *)
+/*
+ * unique magic number of the Tethering bridge ioctls
+ */
+#define TETH_BRIDGE_IOC_MAGIC 0xCE
+
+/*
+ * Ioctls supported by Tethering bridge driver
+ */
+#define TETH_BRIDGE_IOCTL_SET_BRIDGE_MODE 0
+#define TETH_BRIDGE_IOCTL_SET_AGGR_PARAMS 1
+#define TETH_BRIDGE_IOCTL_GET_AGGR_PARAMS 2
+#define TETH_BRIDGE_IOCTL_GET_AGGR_CAPABILITIES 3
+#define TETH_BRIDGE_IOCTL_MAX 4
+
+
+/**
+ * enum teth_link_protocol_type - link protocol (IP / Ethernet)
+ */
+enum teth_link_protocol_type {
+ TETH_LINK_PROTOCOL_IP,
+ TETH_LINK_PROTOCOL_ETHERNET,
+ TETH_LINK_PROTOCOL_MAX,
+};
+
+/**
+ * enum teth_aggr_protocol_type - Aggregation protocol (MBIM / TLP)
+ */
+enum teth_aggr_protocol_type {
+ TETH_AGGR_PROTOCOL_NONE,
+ TETH_AGGR_PROTOCOL_MBIM,
+ TETH_AGGR_PROTOCOL_TLP,
+ TETH_AGGR_PROTOCOL_MAX,
+};
+
+/**
+ * struct teth_aggr_params_link - Aggregation parameters for uplink/downlink
+ * @aggr_prot: Aggregation protocol (MBIM / TLP)
+ * @max_transfer_size_byte: Maximal size of aggregated packet in bytes.
+ * Default value is 16*1024.
+ * @max_datagrams: Maximal number of IP packets in an aggregated
+ * packet. Default value is 16
+ */
+struct teth_aggr_params_link {
+ enum teth_aggr_protocol_type aggr_prot;
+ uint32_t max_transfer_size_byte;
+ uint32_t max_datagrams;
+};
+
+
+/**
+ * struct teth_aggr_params - Aggregation parmeters
+ * @ul: Uplink parameters
+ * @dl: Downlink parmaeters
+ */
+struct teth_aggr_params {
+ struct teth_aggr_params_link ul;
+ struct teth_aggr_params_link dl;
+};
+
+/**
+ * struct teth_aggr_capabilities - Aggregation capabilities
+ * @num_protocols: Number of protocols described in the array
+ * @prot_caps[]: Array of aggregation capabilities per protocol
+ */
+struct teth_aggr_capabilities {
+ uint16_t num_protocols;
+ struct teth_aggr_params_link prot_caps[0];
+};
+
+
+#define TETH_BRIDGE_IOC_SET_BRIDGE_MODE _IOW(TETH_BRIDGE_IOC_MAGIC, \
+ TETH_BRIDGE_IOCTL_SET_BRIDGE_MODE, \
+ enum teth_link_protocol_type)
+#define TETH_BRIDGE_IOC_SET_AGGR_PARAMS _IOW(TETH_BRIDGE_IOC_MAGIC, \
+ TETH_BRIDGE_IOCTL_SET_AGGR_PARAMS, \
+ struct teth_aggr_params *)
+#define TETH_BRIDGE_IOC_GET_AGGR_PARAMS _IOR(TETH_BRIDGE_IOC_MAGIC, \
+ TETH_BRIDGE_IOCTL_GET_AGGR_PARAMS, \
+ struct teth_aggr_params *)
+#define TETH_BRIDGE_IOC_GET_AGGR_CAPABILITIES _IOWR(TETH_BRIDGE_IOC_MAGIC, \
+ TETH_BRIDGE_IOCTL_GET_AGGR_CAPABILITIES, \
+ struct teth_aggr_capabilities *)
+
#endif /* _MSM_IPA_H_ */
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 404ea52..45bc0ea 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -564,6 +564,15 @@
mdp_op_max,
};
+enum {
+ WB_FORMAT_NV12,
+ WB_FORMAT_RGB_565,
+ WB_FORMAT_RGB_888,
+ WB_FORMAT_xRGB_8888,
+ WB_FORMAT_ARGB_8888,
+ WB_FORMAT_ARGB_8888_INPUT_ALPHA /* Need to support */
+};
+
struct msmfb_mdp_pp {
uint32_t op;
union {
@@ -585,6 +594,7 @@
metadata_op_base_blend,
metadata_op_frame_rate,
metadata_op_vic,
+ metadata_op_wb_format,
metadata_op_max
};
@@ -592,11 +602,17 @@
uint32_t is_premultiplied;
};
+struct mdp_mixer_cfg {
+ uint32_t writeback_format;
+ uint32_t alpha;
+};
+
struct msmfb_metadata {
uint32_t op;
uint32_t flags;
union {
struct mdp_blend_cfg blend_cfg;
+ struct mdp_mixer_cfg mixer_cfg;
uint32_t panel_frame_rate;
uint32_t video_info_code;
} data;
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index c6ee4f0..6e4b7a6 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -365,8 +365,8 @@
* requests to connect to a specified network but without separating
* auth and assoc steps. For this, you need to specify the SSID in a
* %NL80211_ATTR_SSID attribute, and can optionally specify the association
- * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
- * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
+ * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
* %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
* %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
* Background scan period can optionally be
@@ -554,14 +554,6 @@
* @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
* No Acknowledgement Policy should be applied.
*
- * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
- * Information Element to the WLAN driver
- *
- * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
- * to the supplicant. This will carry the target AP's MAC address along
- * with the relevant Information Elements. This event to report received
- * FT IEs( MDIE, FTIE,RSN IE, TIE, RICIE).
- *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -703,9 +695,6 @@
NL80211_CMD_SET_NOACK_MAP,
- NL80211_CMD_UPDATE_FT_IES,
- NL80211_CMD_FT_EVENT,
-
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -906,7 +895,7 @@
* @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
* used for the association (&enum nl80211_mfp, represented as a u32);
* this attribute can be used
- * with %NL80211_CMD_ASSOCIATE request
+ * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
*
* @NL80211_ATTR_STA_FLAGS2: Attribute containing a
* &struct nl80211_sta_flag_update.
@@ -1281,10 +1270,6 @@
* @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
* or 0 to disable background scan.
*
- * @NL80211_ATTR_MDID: Mobility Domain Identifier
- *
- * @NL80211_ATTR_IE_RIC: Resource Information Container Information Element
- *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1536,9 +1521,6 @@
NL80211_ATTR_BG_SCAN_PERIOD,
- NL80211_ATTR_MDID,
- NL80211_ATTR_IE_RIC,
-
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/linux/regulator/krait-regulator.h b/include/linux/regulator/krait-regulator.h
index 836f9d6..b784531 100644
--- a/include/linux/regulator/krait-regulator.h
+++ b/include/linux/regulator/krait-regulator.h
@@ -13,7 +13,8 @@
#ifndef __KRAIT_REGULATOR_H__
#define __KRAIT_REGULATOR_H__
-#define KRAIT_REGULATOR_DRIVER_NAME "krait-power-regulator"
+#define KRAIT_REGULATOR_DRIVER_NAME "krait-power-regulator"
+#define KRAIT_PDN_DRIVER_NAME "krait-pdn"
/**
* krait_power_init - driver initialization function
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index fae1efa..c53d604 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -17,20 +17,6 @@
MSM_VIDC_MAX_DEVICES,
};
-struct msm_vidc_iommu_info {
- u32 addr_range[2];
- char name[64];
- char ctx[64];
- int domain;
- int partition;
-};
-
-enum msm_vidc_io_maps {
- CP_MAP,
- NS_MAP,
- MAX_MAP
-};
-
void *msm_vidc_open(int core_id, int session_type);
int msm_vidc_close(void *instance);
int msm_vidc_querycap(void *instance, struct v4l2_capability *cap);
@@ -50,8 +36,8 @@
int msm_vidc_encoder_cmd(void *instance, struct v4l2_encoder_cmd *enc);
int msm_vidc_poll(void *instance, struct file *filp,
struct poll_table_struct *pt);
-int msm_vidc_get_iommu_maps(void *instance,
- struct msm_vidc_iommu_info maps[MAX_MAP]);
+int msm_vidc_get_iommu_domain_partition(void *instance, u32 flags,
+ enum v4l2_buf_type, int *domain, int *partition);
int msm_vidc_subscribe_event(void *instance,
struct v4l2_event_subscription *sub);
int msm_vidc_unsubscribe_event(void *instance,
diff --git a/include/media/msmb_pproc.h b/include/media/msmb_pproc.h
index 6bac1d6..56c257d 100644
--- a/include/media/msmb_pproc.h
+++ b/include/media/msmb_pproc.h
@@ -89,10 +89,9 @@
struct ion_handle *dest_ion_handle;
};
-struct msm_ver_num_info {
- uint32_t main;
- uint32_t minor;
- uint32_t rev;
+struct cpp_hw_info {
+ uint32_t cpp_hw_version;
+ uint32_t cpp_hw_caps;
};
#define VIDIOC_MSM_CPP_CFG \
@@ -107,6 +106,9 @@
#define VIDIOC_MSM_CPP_LOAD_FIRMWARE \
_IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct msm_camera_v4l2_ioctl_t)
+#define VIDIOC_MSM_CPP_GET_HW_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct msm_camera_v4l2_ioctl_t)
+
#define V4L2_EVENT_CPP_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 0)
struct msm_camera_v4l2_ioctl_t {
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5e32ff7..3a7edf3 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1199,6 +1199,7 @@
* @ie: IEs for association request
* @ie_len: Length of assoc_ie in octets
* @privacy: indicates whether privacy-enabled APs should be used
+ * @mfp: indicate whether management frame protection is used
* @crypto: crypto settings
* @key_len: length of WEP key for shared key authentication
* @key_idx: index of WEP key for shared key authentication
@@ -1219,6 +1220,7 @@
u8 *ie;
size_t ie_len;
bool privacy;
+ enum nl80211_mfp mfp;
struct cfg80211_crypto_settings crypto;
const u8 *key;
u8 key_len, key_idx;
@@ -1318,21 +1320,6 @@
};
/**
- * struct cfg80211_update_ft_ies_params - FT IE Information
- *
- * This structure provides information needed to update the fast transition IE
- *
- * @md: The Mobility Domain ID, 2 Octet value
- * @ie: Fast Transition IEs
- * @ie_len: Length of ft_ie in octets
- */
-struct cfg80211_update_ft_ies_params {
- u16 md;
- u8 *ie;
- size_t ie_len;
-};
-
-/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -1712,8 +1699,6 @@
u16 noack_map);
struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy);
- int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_update_ft_ies_params *ftie);
};
/*
@@ -3384,32 +3369,6 @@
*/
u16 cfg80211_calculate_bitrate(struct rate_info *rate);
-/**
- * struct cfg80211_ft_event - FT Information Elements
- * @dev: network device
- * @ies: FT IEs
- * @ies_len: length of the FT IE in bytes
- * @target_ap: target AP's MAC address
- * @ric_ies: RIC IE
- * @ric_ies_len: length of the RIC IE in bytes
- */
-struct cfg80211_ft_event_params {
- u8 *ies;
- size_t ies_len;
- u8 target_ap[ETH_ALEN];
- u8 *ric_ies;
- size_t ric_ies_len;
-};
-
-/**
- * cfg80211_ft_event - notify userspace about FT IE and RIC IE
- * @dev: network device
- * @cfg80211_ft_event_params: IE information
- */
-int cfg80211_ft_event(struct net_device *dev,
- struct cfg80211_ft_event_params ft_event);
-
-
/* Logging, debugging and troubleshooting/diagnostic helpers. */
/* wiphy_printk helpers, similar to dev_printk */
diff --git a/kernel/sys.c b/kernel/sys.c
index e7006eb..39791be 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1179,15 +1179,16 @@
* Work around broken programs that cannot handle "Linux 3.0".
* Instead we map 3.x to 2.6.40+x, so e.g. 3.0 would be 2.6.40
*/
-static int override_release(char __user *release, int len)
+static int override_release(char __user *release, size_t len)
{
int ret = 0;
- char buf[65];
if (current->personality & UNAME26) {
- char *rest = UTS_RELEASE;
+ const char *rest = UTS_RELEASE;
+ char buf[65] = { 0 };
int ndots = 0;
unsigned v;
+ size_t copy;
while (*rest) {
if (*rest == '.' && ++ndots >= 3)
@@ -1197,8 +1198,9 @@
rest++;
}
v = ((LINUX_VERSION_CODE >> 8) & 0xff) + 40;
- snprintf(buf, len, "2.6.%u%s", v, rest);
- ret = copy_to_user(release, buf, len);
+ copy = min(sizeof(buf), max_t(size_t, 1, len));
+ copy = scnprintf(buf, copy, "2.6.%u%s", v, rest);
+ ret = copy_to_user(release, buf, copy + 1);
}
return ret;
}
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index dd99041..f5a7ac3 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -954,16 +954,3 @@
return nl80211_unexpected_4addr_frame(dev, addr, gfp);
}
EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
-
-int cfg80211_ft_event(struct net_device *dev,
- struct cfg80211_ft_event_params ft_event)
-{
- int err = 0;
- struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- nl80211_ft_event(rdev, dev, ft_event);
-
- return err;
-}
-EXPORT_SYMBOL(cfg80211_ft_event);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5c2e805..68a6b17 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -206,9 +206,6 @@
[NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
[NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
- [NL80211_ATTR_MDID] = { .type = NLA_U16 },
- [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
- .len = IEEE80211_MAX_DATA_LEN },
};
/* policy for the key attributes */
@@ -5173,6 +5170,15 @@
connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
+ if (info->attrs[NL80211_ATTR_USE_MFP]) {
+ connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
+ if (connect.mfp != NL80211_MFP_REQUIRED &&
+ connect.mfp != NL80211_MFP_NO)
+ return -EINVAL;
+ } else {
+ connect.mfp = NL80211_MFP_NO;
+ }
+
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
connect.channel =
ieee80211_get_channel(wiphy,
@@ -6302,26 +6308,6 @@
return 0;
}
-static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
-{
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
- struct cfg80211_update_ft_ies_params ft_params;
- struct net_device *dev = info->user_ptr[1];
-
- if (!info->attrs[NL80211_ATTR_MDID])
- return -EINVAL;
-
- ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
-
- if (!info->attrs[NL80211_ATTR_IE])
- return -EINVAL;
-
- ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
- ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
-
- return rdev->ops->update_ft_ies(&rdev->wiphy, dev, &ft_params);
-}
-
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -6910,14 +6896,6 @@
.internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL,
},
- {
- .cmd = NL80211_CMD_UPDATE_FT_IES,
- .doit = nl80211_update_ft_ies,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
- NL80211_FLAG_NEED_RTNL,
- },
};
@@ -8155,47 +8133,6 @@
.notifier_call = nl80211_netlink_notify,
};
-void nl80211_ft_event(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, struct cfg80211_ft_event_params ft_event)
-{
- struct sk_buff *msg;
- void *hdr;
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return;
-
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
- if (!hdr) {
- nlmsg_free(msg);
- return;
- }
-
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
- if (ft_event.target_ap)
- NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event.target_ap);
- if (ft_event.ies)
- NLA_PUT(msg, NL80211_ATTR_IE, ft_event.ies_len, ft_event.ies);
- if (ft_event.ric_ies)
- NLA_PUT(msg, NL80211_ATTR_IE_RIC, ft_event.ric_ies_len,
- ft_event.ric_ies);
-
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
-
- genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
- nl80211_mlme_mcgrp.id, GFP_KERNEL);
- return;
-
- nla_put_failure:
- genlmsg_cancel(msg, hdr);
- nlmsg_free(msg);
-}
-
-
/* initialisation/exit functions */
int nl80211_init(void)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index ffd4c8a..4ffe50d 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -123,8 +123,4 @@
bool nl80211_unexpected_4addr_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp);
-void nl80211_ft_event(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- struct cfg80211_ft_event_params ft_event);
-
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index bbbed73..ab91446 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -190,7 +190,8 @@
prev_bssid,
params->ssid, params->ssid_len,
params->ie, params->ie_len,
- false, ¶ms->crypto,
+ params->mfp != NL80211_MFP_NO,
+ ¶ms->crypto,
params->flags, ¶ms->ht_capa,
¶ms->ht_capa_mask);
if (err)
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index 0b26a56..25d3f56 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -487,16 +487,16 @@
SOC_ENUM_SINGLE(TAPAN_A_CDC_TX4_MUX_CTL, 4, 3, cf_text);
static const struct soc_enum cf_rxmix1_enum =
- SOC_ENUM_SINGLE(TAPAN_A_CDC_RX1_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAPAN_A_CDC_RX1_B4_CTL, 0, 3, cf_text);
static const struct soc_enum cf_rxmix2_enum =
- SOC_ENUM_SINGLE(TAPAN_A_CDC_RX2_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAPAN_A_CDC_RX2_B4_CTL, 0, 3, cf_text);
static const struct soc_enum cf_rxmix3_enum =
- SOC_ENUM_SINGLE(TAPAN_A_CDC_RX3_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAPAN_A_CDC_RX3_B4_CTL, 0, 3, cf_text);
static const struct soc_enum cf_rxmix4_enum =
- SOC_ENUM_SINGLE(TAPAN_A_CDC_RX4_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAPAN_A_CDC_RX4_B4_CTL, 0, 3, cf_text);
static const struct snd_kcontrol_new tapan_snd_controls[] = {
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index f48dbf1..af268bd 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -829,25 +829,25 @@
SOC_ENUM_SINGLE(TAIKO_A_CDC_TX10_MUX_CTL, 4, 3, cf_text);
static const struct soc_enum cf_rxmix1_enum =
- SOC_ENUM_SINGLE(TAIKO_A_CDC_RX1_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_RX1_B4_CTL, 0, 3, cf_text);
static const struct soc_enum cf_rxmix2_enum =
- SOC_ENUM_SINGLE(TAIKO_A_CDC_RX2_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_RX2_B4_CTL, 0, 3, cf_text);
static const struct soc_enum cf_rxmix3_enum =
- SOC_ENUM_SINGLE(TAIKO_A_CDC_RX3_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_RX3_B4_CTL, 0, 3, cf_text);
static const struct soc_enum cf_rxmix4_enum =
- SOC_ENUM_SINGLE(TAIKO_A_CDC_RX4_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_RX4_B4_CTL, 0, 3, cf_text);
static const struct soc_enum cf_rxmix5_enum =
- SOC_ENUM_SINGLE(TAIKO_A_CDC_RX5_B4_CTL, 1, 3, cf_text)
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_RX5_B4_CTL, 0, 3, cf_text)
;
static const struct soc_enum cf_rxmix6_enum =
- SOC_ENUM_SINGLE(TAIKO_A_CDC_RX6_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_RX6_B4_CTL, 0, 3, cf_text);
static const struct soc_enum cf_rxmix7_enum =
- SOC_ENUM_SINGLE(TAIKO_A_CDC_RX7_B4_CTL, 1, 3, cf_text);
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_RX7_B4_CTL, 0, 3, cf_text);
static const char * const class_h_dsm_text[] = {
"ZERO", "DSM_HPHL_RX1", "DSM_SPKR_RX7"
@@ -932,12 +932,6 @@
SOC_SINGLE_TLV("ADC5 Volume", TAIKO_A_TX_5_6_EN, 5, 3, 0, analog_gain),
SOC_SINGLE_TLV("ADC6 Volume", TAIKO_A_TX_5_6_EN, 1, 3, 0, analog_gain),
-
- SOC_SINGLE("MICBIAS1 CAPLESS Switch", TAIKO_A_MICB_1_CTL, 4, 1, 1),
- SOC_SINGLE("MICBIAS2 CAPLESS Switch", TAIKO_A_MICB_2_CTL, 4, 1, 1),
- SOC_SINGLE("MICBIAS3 CAPLESS Switch", TAIKO_A_MICB_3_CTL, 4, 1, 1),
- SOC_SINGLE("MICBIAS4 CAPLESS Switch", TAIKO_A_MICB_4_CTL, 4, 1, 1),
-
SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 0, 100, taiko_get_anc_slot,
taiko_put_anc_slot),
SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
@@ -1813,28 +1807,61 @@
struct snd_soc_codec *codec = w->codec;
u16 adc_reg;
u8 init_bit_shift;
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
pr_debug("%s %d\n", __func__, event);
- if (w->reg == TAIKO_A_TX_1_2_EN)
- adc_reg = TAIKO_A_TX_1_2_TEST_CTL;
- else if (w->reg == TAIKO_A_TX_3_4_EN)
- adc_reg = TAIKO_A_TX_3_4_TEST_CTL;
- else if (w->reg == TAIKO_A_TX_5_6_EN)
- adc_reg = TAIKO_A_TX_5_6_TEST_CTL;
- else {
- pr_err("%s: Error, invalid adc register\n", __func__);
- return -EINVAL;
- }
+ if (TAIKO_IS_1_0(core->version)) {
+ if (w->reg == TAIKO_A_TX_1_2_EN) {
+ adc_reg = TAIKO_A_TX_1_2_TEST_CTL;
+ } else if (w->reg == TAIKO_A_TX_3_4_EN) {
+ adc_reg = TAIKO_A_TX_3_4_TEST_CTL;
+ } else if (w->reg == TAIKO_A_TX_5_6_EN) {
+ adc_reg = TAIKO_A_TX_5_6_TEST_CTL;
+ } else {
+ pr_err("%s: Error, invalid adc register\n", __func__);
+ return -EINVAL;
+ }
- if (w->shift == 3)
- init_bit_shift = 6;
- else if (w->shift == 7)
- init_bit_shift = 7;
- else {
- pr_err("%s: Error, invalid init bit postion adc register\n",
- __func__);
- return -EINVAL;
+ if (w->shift == 3) {
+ init_bit_shift = 6;
+ } else if (w->shift == 7) {
+ init_bit_shift = 7;
+ } else {
+ pr_err("%s: Error, invalid init bit postion adc register\n",
+ __func__);
+ return -EINVAL;
+ }
+ } else {
+ switch (w->reg) {
+ case TAIKO_A_CDC_TX_1_GAIN:
+ adc_reg = TAIKO_A_TX_1_2_TEST_CTL;
+ init_bit_shift = 7;
+ break;
+ case TAIKO_A_CDC_TX_2_GAIN:
+ adc_reg = TAIKO_A_TX_1_2_TEST_CTL;
+ init_bit_shift = 6;
+ break;
+ case TAIKO_A_CDC_TX_3_GAIN:
+ adc_reg = TAIKO_A_TX_3_4_TEST_CTL;
+ init_bit_shift = 7;
+ break;
+ case TAIKO_A_CDC_TX_4_GAIN:
+ adc_reg = TAIKO_A_TX_3_4_TEST_CTL;
+ init_bit_shift = 6;
+ break;
+ case TAIKO_A_CDC_TX_5_GAIN:
+ adc_reg = TAIKO_A_TX_5_6_TEST_CTL;
+ init_bit_shift = 7;
+ break;
+ case TAIKO_A_CDC_TX_6_GAIN:
+ adc_reg = TAIKO_A_TX_5_6_TEST_CTL;
+ init_bit_shift = 6;
+ break;
+ default:
+ pr_err("%s: Error, invalid adc register\n", __func__);
+ return -EINVAL;
+ }
}
switch (event) {
@@ -1844,9 +1871,7 @@
1 << init_bit_shift);
break;
case SND_SOC_DAPM_POST_PMU:
-
snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00);
-
break;
case SND_SOC_DAPM_POST_PMD:
taiko_codec_enable_adc_block(codec, 0);
@@ -2157,14 +2182,14 @@
e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON;
e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF;
} else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) {
- micb_ctl_reg = TAIKO_A_MICB_2_CTL;
+ micb_ctl_reg = TAIKO_A_MICB_3_CTL;
micb_int_reg = TAIKO_A_MICB_3_INT_RBIAS;
cfilt_sel_val = taiko->resmgr.pdata->micbias.bias3_cfilt_sel;
e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON;
e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON;
e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF;
} else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) {
- micb_ctl_reg = TAIKO_A_MICB_2_CTL;
+ micb_ctl_reg = TAIKO_A_MICB_4_CTL;
micb_int_reg = taiko->resmgr.reg_addr->micb_4_int_rbias;
cfilt_sel_val = taiko->resmgr.pdata->micbias.bias4_cfilt_sel;
e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON;
@@ -2190,14 +2215,12 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x3, 0x3);
- if (micb_ctl_reg == TAIKO_A_MICB_2_CTL) {
- WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ if (micb_ctl_reg == TAIKO_A_MICB_2_CTL)
wcd9xxx_resmgr_add_cond_update_bits(&taiko->resmgr,
WCD9XXX_COND_HPH_MIC,
micb_ctl_reg, w->shift,
false);
- WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
- } else
+ else
snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift,
1 << w->shift);
break;
@@ -2207,13 +2230,11 @@
wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_on);
break;
case SND_SOC_DAPM_POST_PMD:
- if (micb_ctl_reg == TAIKO_A_MICB_2_CTL) {
- WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ if (micb_ctl_reg == TAIKO_A_MICB_2_CTL)
wcd9xxx_resmgr_rm_cond_update_bits(&taiko->resmgr,
WCD9XXX_COND_HPH_MIC,
micb_ctl_reg, 7, false);
- WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
- } else
+ else
snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift,
0);
@@ -4305,27 +4326,14 @@
taiko_codec_enable_micbias,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_ADC_E("ADC1", NULL, TAIKO_A_TX_1_2_EN, 7, 0,
- taiko_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_INPUT("AMIC3"),
- SND_SOC_DAPM_ADC_E("ADC3", NULL, TAIKO_A_TX_3_4_EN, 7, 0,
- taiko_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_INPUT("AMIC4"),
- SND_SOC_DAPM_ADC_E("ADC4", NULL, TAIKO_A_TX_3_4_EN, 3, 0,
- taiko_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_INPUT("AMIC5"),
- SND_SOC_DAPM_ADC_E("ADC5", NULL, TAIKO_A_TX_5_6_EN, 7, 0,
- taiko_codec_enable_adc, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_INPUT("AMIC6"),
- SND_SOC_DAPM_ADC_E("ADC6", NULL, TAIKO_A_TX_5_6_EN, 3, 0,
- taiko_codec_enable_adc, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MUX_E("DEC1 MUX", TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0,
&dec1_mux, taiko_codec_enable_dec,
@@ -4420,10 +4428,6 @@
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_ADC_E("ADC2", NULL, TAIKO_A_TX_1_2_EN, 3, 0,
- taiko_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
AIF1_CAP, 0, taiko_codec_enable_slimtx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
@@ -5073,6 +5077,56 @@
return buck_volt;
}
+static const struct snd_soc_dapm_widget taiko_1_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, TAIKO_A_TX_1_2_EN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, TAIKO_A_TX_1_2_EN, 3, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, TAIKO_A_TX_3_4_EN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, TAIKO_A_TX_3_4_EN, 3, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC5", NULL, TAIKO_A_TX_5_6_EN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC_E("ADC6", NULL, TAIKO_A_TX_5_6_EN, 3, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_widget taiko_2_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, TAIKO_A_CDC_TX_1_GAIN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, TAIKO_A_CDC_TX_2_GAIN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, TAIKO_A_CDC_TX_3_GAIN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, TAIKO_A_CDC_TX_4_GAIN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC5", NULL, TAIKO_A_CDC_TX_5_GAIN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC_E("ADC6", NULL, TAIKO_A_CDC_TX_6_GAIN, 7, 0,
+ taiko_codec_enable_adc,
+ SND_SOC_DAPM_POST_PMU),
+};
+
static int taiko_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
@@ -5119,7 +5173,7 @@
}
taiko->clsh_d.buck_mv = taiko_codec_get_buck_mv(codec);
- wcd9xxx_clsh_init(&taiko->clsh_d);
+ wcd9xxx_clsh_init(&taiko->clsh_d, &taiko->resmgr);
/* init and start mbhc */
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
@@ -5185,6 +5239,13 @@
}
}
+ if (TAIKO_IS_1_0(control->version))
+ snd_soc_dapm_new_controls(dapm, taiko_1_dapm_widgets,
+ ARRAY_SIZE(taiko_1_dapm_widgets));
+ else
+ snd_soc_dapm_new_controls(dapm, taiko_2_dapm_widgets,
+ ARRAY_SIZE(taiko_2_dapm_widgets));
+
control->num_rx_port = TAIKO_RX_MAX;
control->rx_chs = ptr;
memcpy(control->rx_chs, taiko_rx_chs, sizeof(taiko_rx_chs));
diff --git a/sound/soc/codecs/wcd9xxx-common.c b/sound/soc/codecs/wcd9xxx-common.c
index dbf2e39..f1b6203 100644
--- a/sound/soc/codecs/wcd9xxx-common.c
+++ b/sound/soc/codecs/wcd9xxx-common.c
@@ -165,33 +165,38 @@
}
}
-static inline void wcd9xxx_clsh_computation_request(
- struct snd_soc_codec *codec, int compute_pa, bool on)
+static void wcd9xxx_clsh_comp_req(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ int compute_pa, bool on)
{
- u8 reg_val, reg_mask;
+ u8 shift;
- switch (compute_pa) {
- case CLSH_COMPUTE_EAR:
- reg_mask = 0x10;
- reg_val = (on ? 0x10 : 0x00);
- break;
- case CLSH_COMPUTE_HPH_L:
- reg_mask = 0x08;
- reg_val = (on ? 0x08 : 0x00);
- break;
- case CLSH_COMPUTE_HPH_R:
- reg_mask = 0x04;
- reg_val = (on ? 0x04 : 0x00);
- break;
- default:
- dev_dbg(codec->dev, "%s: class h computation PA request incorrect\n",
- __func__);
- return;
+ if (compute_pa == CLSH_COMPUTE_EAR) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL, 0x10,
+ (on ? 0x10 : 0));
+ } else {
+ if (compute_pa == CLSH_COMPUTE_HPH_L) {
+ shift = 3;
+ } else if (compute_pa == CLSH_COMPUTE_HPH_R) {
+ shift = 2;
+ } else {
+ dev_dbg(codec->dev,
+ "%s: classh computation request is incorrect\n",
+ __func__);
+ return;
+ }
+
+ if (on)
+ wcd9xxx_resmgr_add_cond_update_bits(clsh_d->resmgr,
+ WCD9XXX_COND_HPH,
+ WCD9XXX_A_CDC_CLSH_B1_CTL,
+ shift, false);
+ else
+ wcd9xxx_resmgr_rm_cond_update_bits(clsh_d->resmgr,
+ WCD9XXX_COND_HPH,
+ WCD9XXX_A_CDC_CLSH_B1_CTL,
+ shift, false);
}
-
- snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
- reg_mask, reg_val);
-
}
static void wcd9xxx_enable_buck_mode(struct snd_soc_codec *codec,
@@ -355,12 +360,12 @@
if (req_state == WCD9XXX_CLSH_STATE_EAR) {
wcd9xxx_clsh_turnoff_postpa(codec);
} else if (req_state == WCD9XXX_CLSH_STATE_HPHL) {
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_HPH_L, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L,
+ false);
wcd9xxx_clsh_turnoff_postpa(codec);
} else if (req_state == WCD9XXX_CLSH_STATE_HPHR) {
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_HPH_R, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R,
+ false);
wcd9xxx_clsh_turnoff_postpa(codec);
} else if (req_state == WCD9XXX_CLSH_STATE_LO) {
wcd9xxx_enable_ncp(codec, false);
@@ -380,8 +385,7 @@
wcd9xxx_enable_clsh_block(codec, true);
wcd9xxx_chargepump_request(codec, true);
wcd9xxx_enable_anc_delay(codec, true);
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_EAR, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, true);
wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
@@ -402,16 +406,15 @@
wcd9xxx_enable_clsh_block(codec, true);
wcd9xxx_chargepump_request(codec, true);
wcd9xxx_enable_anc_delay(codec, true);
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_HPH_L, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true);
wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
dev_dbg(codec->dev, "%s: Done\n", __func__);
} else {
if (req_state == WCD9XXX_CLSH_STATE_HPHR) {
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_HPH_R, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R,
+ false);
} else {
dev_dbg(codec->dev, "%s: stub fallback to hph_l\n",
__func__);
@@ -431,16 +434,15 @@
wcd9xxx_enable_clsh_block(codec, true);
wcd9xxx_chargepump_request(codec, true);
wcd9xxx_enable_anc_delay(codec, true);
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_HPH_R, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true);
wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
dev_dbg(codec->dev, "%s: Done\n", __func__);
} else {
if (req_state == WCD9XXX_CLSH_STATE_HPHL) {
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_HPH_L, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L,
+ false);
} else {
dev_dbg(codec->dev, "%s: stub fallback to hph_r\n",
__func__);
@@ -453,10 +455,8 @@
u8 req_state, bool is_enable)
{
if (is_enable) {
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_HPH_L, true);
- wcd9xxx_clsh_computation_request(codec,
- CLSH_COMPUTE_HPH_R, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true);
} else {
dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
}
@@ -566,10 +566,12 @@
}
EXPORT_SYMBOL_GPL(wcd9xxx_clsh_fsm);
-void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh)
+void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh,
+ struct wcd9xxx_resmgr *resmgr)
{
int i;
clsh->state = WCD9XXX_CLSH_STATE_IDLE;
+ clsh->resmgr = resmgr;
for (i = 0; i < NUM_CLSH_STATES; i++)
clsh_state_fp[i] = wcd9xxx_clsh_state_err;
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
index 743ab0c..dc00ec6 100644
--- a/sound/soc/codecs/wcd9xxx-common.h
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -14,6 +14,8 @@
#define WCD9XXX_CODEC_COMMON
+#include "wcd9xxx-resmgr.h"
+
#define WCD9XXX_CLSH_REQ_ENABLE true
#define WCD9XXX_CLSH_REQ_DISABLE false
@@ -50,6 +52,7 @@
struct wcd9xxx_clsh_cdc_data {
u8 state;
int buck_mv;
+ struct wcd9xxx_resmgr *resmgr;
};
@@ -63,6 +66,7 @@
struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
u8 req_state, bool req_type, u8 clsh_event);
-extern void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh);
+extern void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh,
+ struct wcd9xxx_resmgr *resmgr);
#endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index b3549cc..88f0567 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -85,6 +85,8 @@
#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
#define WCD9XXX_MUX_SWITCH_READY_WAIT_US 100
#define WCD9XXX_MEAS_DELTA_MAX_MV 50
+#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
+#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
#define WCD9XXX_GM_SWAP_THRES_MAX_MV 500
@@ -377,10 +379,14 @@
static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
struct snd_soc_jack *jack, int status, int mask)
{
- if (jack == &mbhc->headset_jack)
+ if (jack == &mbhc->headset_jack) {
wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
WCD9XXX_COND_HPH_MIC,
status & SND_JACK_MICROPHONE);
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH,
+ status & SND_JACK_HEADPHONE);
+ }
snd_soc_jack_report_no_dapm(jack, status, mask);
}
@@ -1012,6 +1018,20 @@
__func__, i, d->dce, vdce, d->_vdces,
d->swap_gnd, d->vddio, d->hphl_status & 0x01,
d->_type);
+
+
+ /*
+ * If GND and MIC prongs are aligned to HPHR and GND of
+ * headphone, codec measures the voltage based on
+ * impedance between HPHR and GND which results in ~80mv.
+ * Avoid this.
+ */
+ if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
+ d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
+ pr_debug("%s: within invalid range\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ }
}
if (ch != size && ch > 0) {
pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
@@ -1616,9 +1636,15 @@
* While we don't know whether MIC is there or not, let the resmgr know
* so micbias can be disabled temporarily
*/
- if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+ if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
WCD9XXX_COND_HPH_MIC, false);
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH, false);
+ } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH, false);
+ }
vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
mbhc->mbhc_micbias_switched);
@@ -1639,9 +1665,15 @@
if (vddio && (mbhc->current_plug == PLUG_TYPE_HEADSET))
__wcd9xxx_switch_micbias(mbhc, 1, true, true);
- if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+ if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH, true);
wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
WCD9XXX_COND_HPH_MIC, true);
+ } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH, true);
+ }
WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
return IRQ_HANDLED;
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index 2011346..18614d8 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -688,7 +688,6 @@
{
struct wcd9xxx_resmgr_cond_entry *entry;
- WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
@@ -697,9 +696,12 @@
entry->reg = reg;
entry->shift = shift;
entry->invert = invert;
+
+ WCD9XXX_BCL_LOCK(resmgr);
list_add_tail(&entry->list, &resmgr->update_bit_cond_h);
wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+ WCD9XXX_BCL_UNLOCK(resmgr);
return 0;
}
@@ -717,7 +719,7 @@
struct wcd9xxx_resmgr_cond_entry *e = NULL;
pr_debug("%s: enter\n", __func__);
- WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ WCD9XXX_BCL_LOCK(resmgr);
list_for_each_safe(l, next, &resmgr->update_bit_cond_h) {
e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list);
if (e->reg == reg && e->shift == shift && e->invert == invert) {
@@ -725,10 +727,12 @@
1 << e->shift,
e->invert << e->shift);
list_del(&e->list);
+ WCD9XXX_BCL_UNLOCK(resmgr);
kfree(e);
return 0;
}
}
+ WCD9XXX_BCL_UNLOCK(resmgr);
pr_err("%s: Cannot find update bit entry reg 0x%x, shift %d\n",
__func__, e ? e->reg : 0, e ? e->shift : 0);
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
index 53c48f6..8acc816 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.h
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -193,7 +193,8 @@
const enum wcd9xxx_notify_event e);
enum wcd9xxx_resmgr_cond {
- WCD9XXX_COND_HPH_MIC = 1,
+ WCD9XXX_COND_HPH = 0x01, /* Headphone */
+ WCD9XXX_COND_HPH_MIC = 0x02, /* Microphone on the headset */
};
int wcd9xxx_resmgr_rm_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
enum wcd9xxx_resmgr_cond cond,
diff --git a/sound/soc/msm/mdm9625.c b/sound/soc/msm/mdm9625.c
index eb7366c..2bef1b7 100644
--- a/sound/soc/msm/mdm9625.c
+++ b/sound/soc/msm/mdm9625.c
@@ -749,7 +749,7 @@
.name = "MDM9625 Media1",
.stream_name = "MultiMedia1",
.cpu_dai_name = "MultiMedia1",
- .platform_name = "msm-pcm-dsp",
+ .platform_name = "msm-pcm-dsp.0",
.dynamic = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index c5cfa11..4462213 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -133,6 +133,7 @@
struct msm8974_asoc_mach_data {
int mclk_gpio;
u32 mclk_freq;
+ int us_euro_gpio;
struct msm_auxpcm_ctrl *pri_auxpcm_ctrl;
};
@@ -1040,6 +1041,16 @@
slim0_rx_sample_rate_get, slim0_rx_sample_rate_put),
};
+static bool msm8974_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = codec->card;
+ struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ int value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+ pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+ gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+ return true;
+}
+
static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
{
int err;
@@ -1984,6 +1995,25 @@
return 0;
}
+static int msm8974_prepare_us_euro(struct snd_soc_card *card)
+{
+ struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ int ret;
+ if (pdata->us_euro_gpio) {
+ dev_dbg(card->dev, "%s : us_euro gpio request %d", __func__,
+ pdata->us_euro_gpio);
+ ret = gpio_request(pdata->us_euro_gpio, "TAIKO_CODEC_US_EURO");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: Failed to request taiko US/EURO gpio %d error %d\n",
+ __func__, pdata->us_euro_gpio, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static __devinit int msm8974_asoc_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &snd_soc_card_msm8974;
@@ -2073,6 +2103,23 @@
card->num_links = ARRAY_SIZE(msm8974_common_dai_links);
}
+ pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,us-euro-gpios", 0);
+ if (pdata->us_euro_gpio < 0) {
+ dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,us-euro-gpios",
+ pdev->dev.of_node->full_name);
+ } else {
+ dev_dbg(&pdev->dev, "%s detected %d",
+ "qcom,us-euro-gpios", pdata->us_euro_gpio);
+ mbhc_cfg.swap_gnd_mic = msm8974_swap_gnd_mic;
+ }
+
+ ret = msm8974_prepare_us_euro(card);
+ if (ret)
+ dev_err(&pdev->dev, "msm8974_prepare_us_euro failed (%d)\n",
+ ret);
+
mutex_init(&cdc_mclk_mutex);
atomic_set(&auxpcm_rsc_ref, 0);
spdev = pdev;
@@ -2094,6 +2141,18 @@
}
return 0;
err:
+ if (pdata->mclk_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free gpio %d\n",
+ __func__, pdata->mclk_gpio);
+ gpio_free(pdata->mclk_gpio);
+ pdata->mclk_gpio = 0;
+ }
+ if (pdata->us_euro_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n",
+ __func__, pdata->us_euro_gpio);
+ gpio_free(pdata->us_euro_gpio);
+ pdata->us_euro_gpio = 0;
+ }
devm_kfree(&pdev->dev, pdata);
return ret;
}
@@ -2107,6 +2166,7 @@
regulator_put(ext_spk_amp_regulator);
gpio_free(pdata->mclk_gpio);
+ gpio_free(pdata->us_euro_gpio);
if (ext_spk_amp_gpio >= 0)
gpio_free(ext_spk_amp_gpio);
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index 4c50b53..f030d3c 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -1354,10 +1354,17 @@
for (i = 0; i < num_copps; i++)
send_adm_cal(port_id[i], path);
- for (i = 0; i < num_copps; i++)
- rtac_add_adm_device(port_id[i], atomic_read(&this_adm.copp_id
- [afe_get_port_index(port_id[i])]),
- path, session_id);
+ for (i = 0; i < num_copps; i++) {
+ int tmp;
+ tmp = afe_get_port_index(port_id[i]);
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS)
+ rtac_add_adm_device(port_id[i],
+ atomic_read(&this_adm.copp_id[tmp]),
+ path, session_id);
+ else
+ pr_debug("%s: Invalid port index %d",
+ __func__, tmp);
+ }
return 0;
fail_cmd:
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index 1969fe8..c14cb74 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -503,6 +503,7 @@
rc = -EINVAL;
}
+ kfree(voice_ocm_work);
return;
}
/**
@@ -614,6 +615,7 @@
rc = -EINVAL;
}
+ kfree(audio_ocm_work);
return;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 2d2fe31..d0b5500 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -148,7 +148,6 @@
*/
snd_pcm_stream_lock_irq(substream);
if (snd_pcm_playback_empty(substream)) {
- atomic_set(&prtd->pending_buffer, 1);
runtime->render_flag |= SNDRV_RENDER_STOPPED;
stop_playback = 1;
}
@@ -1038,7 +1037,6 @@
(prtd->out_head + 1) & (runtime->periods - 1);
runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
- atomic_set(&prtd->pending_buffer, 0);
return 0;
}
return 0;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index ae7e76c..3a4a674 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -536,8 +536,8 @@
memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
rc = q6asm_get_session_time(prtd->audio_client, ×tamp);
if (rc < 0) {
- pr_err("%s: Get Session Time return value =%lld\n",
- __func__, timestamp);
+ pr_err("%s: Fail to get session time stamp, rc:%d\n",
+ __func__, rc);
return -EAGAIN;
}
temp = (timestamp * 2 * runtime->channels);
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 913dded..b32daaa 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -223,6 +223,12 @@
adm_params->hdr.dest_svc = APR_SVC_ADM;
adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
index = afe_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: invalid port idx %d portid %#x\n",
+ __func__, index, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
adm_params->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
adm_params->hdr.token = port_id;
adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
@@ -1011,10 +1017,17 @@
for (i = 0; i < num_copps; i++)
send_adm_cal(port_id[i], path);
- for (i = 0; i < num_copps; i++)
- rtac_add_adm_device(port_id[i], atomic_read(&this_adm.copp_id
- [afe_get_port_index(port_id[i])]),
- path, session_id);
+ for (i = 0; i < num_copps; i++) {
+ int tmp;
+ tmp = afe_get_port_index(port_id[i]);
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS)
+ rtac_add_adm_device(port_id[i],
+ atomic_read(&this_adm.copp_id[tmp]),
+ path, session_id);
+ else
+ pr_debug("%s: Invalid port index %d",
+ __func__, tmp);
+ }
fail_cmd:
kfree(matrix_map);