Merge "ASoc: msm: Synchronize access to compressed stream"
diff --git a/Documentation/arm/msm/msm_ipc_logging.txt b/Documentation/arm/msm/msm_ipc_logging.txt
new file mode 100644
index 0000000..cb330b0
--- /dev/null
+++ b/Documentation/arm/msm/msm_ipc_logging.txt
@@ -0,0 +1,323 @@
+Introduction
+============
+
+This module will be used to log the events by any module/driver which
+enables Inter Processor Communication (IPC). Some of the IPC drivers such
+as Message Routers, Multiplexers etc. which act as a passive pipe need
+some mechanism to log their events. Since all such IPC drivers handle a
+large amount of traffic/events, using kernel logs renders kernel logs
+unusable by other drivers and also degrades the performance of IPC
+drivers. This new module will help in logging such high frequency IPC
+driver events while keeping the standard kernel logging mechanism
+intact.
+
+Hardware description
+====================
+
+This module does not drive any hardware resource and will only use the
+kernel memory-space to log the events.
+
+Software description
+====================
+
+Design Goals
+------------
+This module is designed to
+ * support logging for drivers handling large amount of
+ traffic/events
+ * define & differentiate events/logs from different drivers
+ * support both id-based and stream-based logging
+ * support extracting the logs from both live target & memory dump
+
+IPC Log Context
+----------------
+
+This module will support logging by multiple drivers. To differentiate
+between the multiple drivers that are using this logging mechanism, each
+driver will be assigned a unique context by this module. Associated with
+each context is the logging space, dynamically allocated from the kernel
+memory-space, specific to that context so that the events logged using that
+context will not interfere with other contexts.
+
+Event Logging
+--------------
+
+Every event will be logged as a <Type: Size: Value> combination. Type
+field identifies the type of the event that is logged. Size field represents
+the size of the log information. Value field represents the actual
+information being logged. This approach will support both id-based logging
+and stream-based logging. This approach will also support logging sub-events
+of an event. This module will provide helper routines to encode/decode the
+logs to/from this format.
+
+Encode Context
+---------------
+
+Encode context is a temporary storage space that will be used by the client
+drivers to log the events in <Type: Size: Value> format. The client drivers
+will perform an encode start operation to initialize the encode context
+data structure. Then the client drivers will log their events into the
+encode context. Upon completion of event logging, the client drivers will
+perform an encode end operation to finalize the encode context data
+structure to be logged. Then this updated encode context data structure
+will be written into the client driver's IPC Log Context. The maximum
+event log size will be defined as 256 bytes.
+
+Log Space
+----------
+
+Each context will have an associated log space, which is dynamically
+allocated from the kernel memory-space. The log space is organized as a
+list of kernel memory pages. Each page contains header information which
+is used to differentiate the log kernel page from the other kernel pages.
+
+ 0 ---------------------------------
+ | magic_no = 0x52784425 |
+ ---------------------------------
+ | nmagic_no = 0xAD87BBDA |
+ ---------------------------------
+ | log_id |
+ ---------------------------------
+ | page_num |
+ ---------------------------------
+ | read_offset | write_offset |
+ ---------------------------------
+ | next/prev page |
+ ---------------------------------
+ | |
+ | Data |
+ | . |
+ | . |
+ | . |
+ | |
+ --------------------------------- PAGE_SIZE - 1
+
+This approach will support extracting the logs either from the memory dumps
+or from the live targets using DEBUGFS.
+
+Design
+======
+
+Alternate solutions discussed include using kernel & SMEM logs which are
+limited in size and hence using them render them unusable by other drivers.
+Also kernel logging into serial console is slowing down the performance of
+the drivers by multiple times and sometimes lead to APPs watchdog bite.
+
+Power Management
+================
+
+Not-Applicable
+
+SMP/multi-core
+==============
+
+This module uses spinlocks & mutexes to handle multi-core safety.
+
+Security
+========
+
+Not-Applicable
+
+Performance
+===========
+
+This logging mechanism, based on experimental data, is not expected to
+cause a significant performance degradation. Under worst case, it can
+cause 1 - 2 percent degradation in the throughput of the IPC Drivers.
+
+Interface
+=========
+
+Exported Data Structures
+------------------------
+struct encode_context {
+ struct tsv_header hdr;
+ char buff[MAX_MSG_SIZE];
+ int offset;
+};
+
+struct decode_context {
+ int output_format;
+ char *buff;
+ int size;
+};
+
+Kernel-Space Interface APIs
+----------------------------
+/*
+ * ipc_log_context_create: Create a ipc log context
+ *
+ * @max_num_pages: Number of pages of logging space required (max. 10)
+ * @mod_name : Name of the directory entry under DEBUGFS
+ *
+ * returns reference to context on success, NULL on failure
+ */
+void * ipc_log_context_create(int max_num_pages,
+ const char *mod_name);
+
+/*
+ * msg_encode_start: Start encoding a log message
+ *
+ * @ectxt: Temporary storage to hold the encoded message
+ * @type: Root event type defined by the module which is logging
+ */
+void msg_encode_start(struct encode_context *ectxt, uint32_t type);
+
+/*
+ * msg_encode_end: Complete the message encode process
+ *
+ * @ectxt: Temporary storage which holds the encoded message
+ */
+void msg_encode_end(struct encode_context *ectxt);
+
+/*
+ * tsv_timestamp_write: Writes the current timestamp count
+ *
+ * @ectxt: Context initialized by calling msg_encode_start()
+ *
+ * Returns 0 on success, -ve error code on failure
+ */
+int tsv_timestamp_write(struct encode_context *ectxt);
+
+/*
+ * tsv_pointer_write: Writes a data pointer
+ *
+ * @ectxt: Context initialized by calling msg_encode_start()
+ * @pointer: Pointer value to write
+ *
+ * Returns 0 on success, -ve error code on failure
+ */
+int tsv_pointer_write(struct encode_context *ectxt, void *pointer);
+
+/*
+ * tsv_int32_write: Writes a 32-bit integer value
+ *
+ * @ectxt: Context initialized by calling msg_encode_start()
+ * @n: Integer to write
+ *
+ * Returns 0 on success, -ve error code on failure
+ */
+int tsv_int32_write(struct encode_context *ectxt, int32_t n);
+
+/*
+ * tsv_byte_array_write: Writes a byte array
+ *
+ * @ectxt: Context initialized by calling msg_encode_start()
+ * @data: Location of data
+ * @data_size: Size of data to be written
+ *
+ * Returns 0 on success, -ve error code on failure
+ */
+int tsv_byte_array_write(struct encode_context *ectxt,
+ void *data, int data_size);
+
+/*
+ * ipc_log_write: Write the encoded message into the log space
+ *
+ * @ctxt: IPC log context where the message has to be logged into
+ * @ectxt: Temporary storage containing the encoded message
+ */
+void ipc_log_write(unsigned long ctxt, struct encode_context *ectxt);
+
+/*
+ * ipc_log_string: Helper function to log a string
+ *
+ * @dlctxt: IPC Log Context created using ipc_log_context_create()
+ * @fmt: Data specified using format specifiers
+ */
+int ipc_log_string(unsigned long dlctxt, const char *fmt, ...);
+
+/*
+ * tsv_timestamp_read: Reads a timestamp
+ *
+ * @ectxt: Context retrieved by reading from log space
+ * @dctxt: Temporary storage to hold the decoded message
+ * @format: Output format while dumping through DEBUGFS
+ */
+void tsv_timestamp_read(struct encode_context *ectxt,
+ struct decode_context *dctxt, const char *format);
+
+/*
+ * tsv_pointer_read: Reads a data pointer
+ *
+ * @ectxt: Context retrieved by reading from log space
+ * @dctxt: Temporary storage to hold the decoded message
+ * @format: Output format while dumping through DEBUGFS
+ */
+void tsv_pointer_read(struct encode_context *ectxt,
+ struct decode_context *dctxt, const char *format);
+
+/*
+ * tsv_int32_read: Reads a 32-bit integer value
+ *
+ * @ectxt: Context retrieved by reading from log space
+ * @dctxt: Temporary storage to hold the decoded message
+ * @format: Output format while dumping through DEBUGFS
+ */
+void tsv_int32_read(struct encode_context *ectxt,
+ struct decode_context *dctxt, const char *format);
+
+/*
+ * tsv_byte_array_read: Reads a byte array/string
+ *
+ * @ectxt: Context retrieved by reading from log space
+ * @dctxt: Temporary storage to hold the decoded message
+ * @format: Output format while dumping through DEBUGFS
+ */
+void tsv_byte_array_read(struct encode_context *ectxt,
+ struct decode_context *dctxt, const char *format);
+
+/*
+ * add_deserialization_func: Register a deserialization function to
+ * to unpack the subevents of a main event
+ *
+ * @ctxt: IPC log context to which the deserialization function has
+ * to be registered
+ * @type: Main/Root event, defined by the module which is logging, to
+ * which this deserialization function has to be registered.
+ * @dfune: Deserialization function to be registered
+ *
+ * return 0 on success, -ve value on FAILURE
+ */
+int add_deserialization_func(unsigned long ctxt, int type,
+ void (*dfunc)(struct encode_context *,
+ struct decode_context *));
+
+Driver parameters
+=================
+
+Not-Applicable
+
+Config options
+==============
+
+Not-Applicable
+
+Dependencies
+============
+
+This module will partially depend on CONFIG_DEBUGFS, in order to dump the
+logs through debugfs. If CONFIG_DEBUGFS is disabled, the above mentioned
+helper functions will perform no operation and return appropriate error
+code if the return value is non void. Under such circumstances the logs can
+only be extracted through the memory dump.
+
+User space utilities
+====================
+
+DEBUGFS
+
+Other
+=====
+
+Not-Applicable
+
+Known issues
+============
+
+None
+
+To do
+=====
+
+None
diff --git a/Documentation/devicetree/bindings/arm/msm/board-id.txt b/Documentation/devicetree/bindings/arm/msm/board-id.txt
new file mode 100644
index 0000000..c29a2d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/board-id.txt
@@ -0,0 +1,18 @@
+* BOARD-ID
+
+The qcom,board-id entry specifies the MSM platform and subtype revision.
+It can optionally be an array of these to indicate multiple hardware that use
+the same device tree. It is expected that the bootloader will use this
+information at boot-up to decide which device tree to use when given multiple
+device trees, some of which may not be compatible with the actual hardware. It
+is the bootloader's responsibility to pass the correct device tree to the kernel.
+
+Format:
+
+It is expected that the qcom,board-id entry be at the top level of the device
+tree structure. The format of the entry is:
+
+ qcom,board-id = <platform_id, subtype_id> [, <p2, s2> ...]
+
+Example:
+ qcom,board-id = <15 0>;
diff --git a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
index 04310ec..b489f7a 100644
--- a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
+++ b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
@@ -13,21 +13,17 @@
Required properties:
- compatible: Must be "qcom,cpr-regulator"
- reg: Register addresses for RBCPR, RBCPR clock
- select, PVS eFuse and CPR eFuse
+ select, PVS and CPR eFuse address
- reg-names: Register names. Must be "rbcpr", "rbcpr_clk",
- "pvs_efuse" and "cpr_efuse"
+ "efuse_addr"
- regulator-name: A string used to describe the regulator
- interrupts: Interrupt line from RBCPR to interrupt controller.
- regulator-min-microvolt: Minimum corner value as min constraint, which
should be 1 for SVS corner
- regulator-max-microvolt: Maximum corner value as max constraint, which
should be 4 for SUPER_TURBO or 3 for TURBO
-- qcom,num-efuse-bits: The number of bits used in efuse memory to
- represent total number of PVS bins. It should
- not exceed a maximum of 5 for total number of
- 32 bins.
- qcom,pvs-bin-process: A list of integers whose length is equal to 2 to
- the power of qcom,num-efuse-bits. The location or
+ the power of qcom,pvs-fuse[num-of-bits]. The location or
0-based index of an element in the list corresponds
to the bin number. The value of each integer
corresponds to the PVS process speed of the APC
@@ -54,7 +50,7 @@
- qcom,cpr-timer-cons-down: Consecutive number of timer interval (qcom,cpr-timer-delay)
occurred before issuing DOWN interrupt.
- qcom,cpr-irq-line: Internal interrupt route signal of RBCPR, one of 0, 1 or 2.
-- qcom,cpr-step-quotient: Number of CPR quotient (RO count) per vdd-apc-supply step
+- qcom,cpr-step-quotient: Number of CPR quotient (Ring Oscillator(RO) count) per vdd-apc-supply step
to issue error_steps.
- qcom,cpr-up-threshold: The threshold for CPR to issue interrupt when
error_steps is greater than it when stepping up.
@@ -64,6 +60,62 @@
- qcom,cpr-gcnt-time: The time for gate count in microseconds.
- qcom,cpr-apc-volt-step: The voltage in microvolt per CPR step, such as 5000uV.
+- qcom,pvs-fuse-redun-sel: Array of 4 elements to indicate where to read the bits and what value to
+ compare with in order to decide if the redundant PVS fuse bits would be
+ used instead of the original bits. The 4 elements with index [0..3] are:
+ [0] => the fuse row number of the selector
+ [1] => LSB bit position of the bits
+ [2] => number of bits
+ [3] => the value to indicate redundant selection
+ When the value of the fuse bits specified by first 3 elements equals to
+ the value in 4th element, redundant PVS fuse bits should be selected.
+ Otherwise, the original PVS bits should be selected.
+- qcom,pvs-fuse: Array of three elements to indicate the bits for PVS fuse. The array
+ should have index and value like this:
+ [0] => the PVS fuse row number
+ [1] => LSB bit position of the bits
+ [2] => number of bits
+- qcom,pvs-fuse-redun: Array of three elements to indicate the bits for redundant PVS fuse.
+ The array should have index and value like this:
+ [0] => the redundant PVS fuse row number
+ [1] => LSB bit position of the bits
+ [2] => number of bits
+
+- qcom,cpr-fuse-redun-sel: Array of 4 elements to indicate where to read the bits and what value to
+ compare with in order to decide if the redundant CPR fuse bits would be
+ used instead of the original bits. The 4 elements with index [0..3] are:
+ [0] => the fuse row number of the selector
+ [1] => LSB bit position of the bits
+ [2] => number of bits
+ [3] => the value to indicate redundant selection
+ When the value of the fuse bits specified by first 3 elements equals to
+ the value in 4th element, redundant CPR fuse bits should be selected.
+ Otherwise, the original CPR bits should be selected.
+- qcom,cpr-fuse-row: Row number of CPR fuse
+- qcom,cpr-fuse-bp-cpr-disable: Bit position of the bit to indicate if CPR should be disable
+- qcom,cpr-fuse-bp-scheme: Bit position of the bit to indicate if it's a global/local scheme
+- qcom,cpr-fuse-target-quot: Array of bit positions in fuse for Target Quotient of all corners.
+ It should have index and value like this:
+ [0] => bit position of the LSB bit for SVS target quotient
+ [1] => bit position of the LSB bit for NOMINAL target quotient
+ [2] => bit position of the LSB bit for TURBO target quotient
+- qcom,cpr-fuse-ro-sel: Array of bit positions in fuse for RO select of all corners.
+ It should have index and value like this:
+ [0] => bit position of the LSB bit for SVS RO select bits
+ [1] => bit position of the LSB bit for NOMINAL RO select bits
+ [2] => bit position of the LSB bit for TURBO RO select bits
+- qcom,cpr-fuse-redun-row: Row number of the redundant CPR fuse
+- qcom,cpr-fuse-redun-target-quot: Array of bit positions in fuse for redundant Target Quotient of all corners.
+ It should have index and value like this:
+ [0] => bit position of the LSB bit for redundant SVS target quotient
+ [1] => bit position of the LSB bit for redundant NOMINAL target quotient
+ [2] => bit position of the LSB bit for redundant TURBO target quotient
+- qcom,cpr-fuse-redun-ro-sel: Array of bit positions in eFuse for redundant RO select.
+ It should have index and value like this:
+ [0] => bit position of the LSB bit for redundant SVS RO select bits
+ [1] => bit position of the LSB bit for redundant NOMINAL RO select bits
+ [2] => bit position of the LSB bit for redundant TURBO RO select bits
+
Optional properties:
- vdd-mx-supply: Regulator to supply memory power as dependency
@@ -78,6 +130,10 @@
2 => equal to slow speed corner ceiling
3 => equal to qcom,vdd-mx-vmax
This is required when vdd-mx-supply is present.
+- qcom,cpr-fuse-redun-bp-cpr-disable: Redundant bit position of the bit to indicate if CPR should be disable
+- qcom,cpr-fuse-redun-bp-scheme: Redundant bit position of the bit to indicate if it's a global/local scheme
+ This property is required if cpr-fuse-redun-bp-cpr-disable
+ is present, and vise versa.
- qcom,cpr-enable: Present: CPR enabled by default.
Not Present: CPR disable by default.
@@ -86,13 +142,17 @@
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>, <0xfc4b80b0 8>, <0xfc4bc450 16>;
- reg-names = "rbcpr", "pvs_efuse", "cpr_efuse";
+ reg = <0xf9018000 0x1000>, <0xfc4b8000 0x1000>;
+ reg-names = "rbcpr", "efuse_addr";
interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
regulator-max-microvolt = <3>;
- qcom,num-efuse-bits = <5>;
+
+ qcom,pvs-fuse = <22 6 5>;
+ qcom,pvs-fuse-redun-sel = <22 24 3 2>;
+ qcom,pvs-fuse-redun = <22 27 5>;
+
qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
qcom,pvs-corner-ceiling-slow = <1050000 1160000 1275000>;
@@ -115,5 +175,15 @@
qcom,cpr-idle-clocks = <5>;
qcom,cpr-gcnt-time = <1>;
qcom,cpr-apc-volt-step = <5000>;
+
+ qcom,cpr-fuse-row = <138>;
+ qcom,cpr-fuse-bp-cpr-disable = <36>;
+ qcom,cpr-fuse-bp-scheme = <37>;
+ qcom,cpr-fuse-target-quot = <24 12 0>;
+ qcom,cpr-fuse-ro-sel = <54 38 41>;
+ qcom,cpr-fuse-redun-sel = <138 57 1 1>;
+ qcom,cpr-fuse-redun-row = <139>;
+ qcom,cpr-fuse-redun-target-quot = <24 12 0>;
+ qcom,cpr-fuse-redun-ro-sel = <46 36 39>;
};
diff --git a/Documentation/devicetree/bindings/arm/msm/jtag-fuse.txt b/Documentation/devicetree/bindings/arm/msm/jtag-fuse.txt
new file mode 100644
index 0000000..617d2ba
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/jtag-fuse.txt
@@ -0,0 +1,18 @@
+* JTAG-FUSE
+
+The jtag-fuse entry specifies the memory mapped addresses for the fuse
+registers. The jtag-fuse driver uses these to provide api(s) that can be used
+by jtag save and restore driver(s) to query whether the Hardware they manage
+is functionally disabled or not and take corresponding steps.
+
+Required Properties:
+compatible: component name used for driver matching, should be "qcom,jtag-fuse"
+reg: physical base address and length of the register set
+reg-names: should be "fuse-base"
+
+Example:
+ jtag_fuse: jtagfuse@fc4be024 {
+ compatible = "qcom,jtag-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/msm-id.txt b/Documentation/devicetree/bindings/arm/msm/msm-id.txt
index b8c71d76..b2da59f 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm-id.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm-id.txt
@@ -10,9 +10,16 @@
Format:
It is expected that the qcom,msm-id entry be at the top level of the device
-tree structure. The format of the entry is:
+tree structure. The format can take one of the two forms below:
qcom,msm-id = <chipset_id, platform_id, rev_id> [, <c2, p2, r2> ...]
+ qcom,msm-id = <chipset_id, rev_id> [, <c2, r2> ...]
+
+If the second format is used one must also define the board-id.
Example:
qcom,msm-id = <126 15 0>;
+
+ qcom,board-id= <15 2>;
+ qcom,msm-id = <126 0>;
+
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_thermal.txt b/Documentation/devicetree/bindings/arm/msm/msm_thermal.txt
index 6ef2b77..d07eba6 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_thermal.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_thermal.txt
@@ -85,19 +85,19 @@
qcom,core-control-mask = <7>;
qcom,pmic-sw-mode-temp = <90>;
qcom,pmic-sw-mode-temp-hysteresis = <80>;
- qcom,pmic-sw-mode-regs = "vdd_dig";
+ qcom,pmic-sw-mode-regs = "vdd-dig";
qcom,vdd-restriction-temp = <5>;
qcom,vdd-restriction-temp-hysteresis = <10>;
- vdd_dig-supply=<&pm8841_s2_floor_corner>
+ vdd-dig-supply=<&pm8841_s2_floor_corner>
qcom,vdd-dig-rstr{
- qcom,vdd-rstr-reg = "vdd_dig";
+ qcom,vdd-rstr-reg = "vdd-dig";
qcom,levels = <5 7 7>; /* Nominal, Super Turbo, Super Turbo */
qcom,min-level = <1>; /* No Request */
};
qcom,vdd-apps-rstr{
- qcom,vdd-rstr-reg = "vdd_apps";
+ qcom,vdd-rstr-reg = "vdd-apps";
qcom,levels = <1881600 1958400 2265600>;
qcom,freq-req;
};
diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt
index ce797d3..d24e671 100644
--- a/Documentation/devicetree/bindings/coresight/coresight.txt
+++ b/Documentation/devicetree/bindings/coresight/coresight.txt
@@ -21,6 +21,7 @@
"qcom,coresight-csr" for coresight csr device,
"arm,coresight-cti" for coresight cti devices,
"qcom,coresight-hwevent" for coresight hardware event devices
+ "arm,coresight-fuse" for coresight fuse device,
- reg : physical base address and length of the register set(s) of the component
- 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
@@ -68,6 +69,10 @@
"<ss-mux>" - physical base address of hardware event mux
control registers where <ss-mux> is subsystem mux it
represents
+ - for coresight fuse device:
+ compatible : should be "arm,coresight-fuse"
+ reg-names : should be:
+ "fuse-base" - physical base address of fuse 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
@@ -226,8 +231,9 @@
compatible = "qcom,coresight-hwevent";
reg = <0xfdf30018 0x80>,
<0xf9011080 0x80>,
- <0xfd4ab160 0x80>;
- reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <29>;
coresight-name = "coresight-hwevent";
@@ -235,3 +241,13 @@
qcom,hwevent-clks = "core_mmss_clk";
};
+
+ fuse: fuse@fc4be024 {
+ compatible = "arm,coresight-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+
+ coresight-id = <30>;
+ coresight-name = "coresight-fuse";
+ coresight-nr-inports = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
index 9bc949e..7093fb8 100644
--- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
@@ -123,6 +123,13 @@
offsets as well (i.e. the first mixer offset
corresponds to the same pathway as the first
AD offset).
+- qcom,mdss-has-wfd-blk: Boolean property to indicate the presence of dedicated
+ writeback wfd block in MDSS as opposed to writeback
+ block that is shared between rotator and wfd.
+- qcom,mdss-smp-mb-per-pipe: Maximum number of shared memory pool blocks
+ restricted for a source surface pipe. If this
+ property is not specified, no such restriction
+ would be applied.
Optional subnodes:
Child nodes representing the frame buffer virtual devices.
@@ -159,8 +166,10 @@
qcom,mdss-pipe-dma-fetch-id = <10 13>;
qcom,mdss-smp-data = <22 4096>;
qcom,mdss-rot-block-size = <64>;
+ qcom,mdss-smp-mb-per-pipe = <2>;
qcom,mdss-has-bwc;
qcom,mdss-has-decimation;
+ qcom,mdss-has-wfd-blk;
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
0x00000900 0x0000A00>;
diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index 4e16ba4..a336287 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -75,4 +75,40 @@
gpio-controller;
};
+2.1) gpio-controller and pinctrl subsystem
+------------------------------------------
+gpio-controller on a SOC might be tightly coupled with the pinctrl
+subsystem, in the sense that the pins can be used by other functions
+together with optional gpio feature.
+
+While the pin allocation is totally managed by the pin ctrl subsystem,
+gpio (under gpiolib) is still maintained by gpio drivers. It may happen
+that different pin ranges in a SoC is managed by different gpio drivers.
+
+This makes it logical to let gpio drivers announce their pin ranges to
+the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to
+request the corresponding pin before any gpio usage.
+
+For this, the gpio controller can use a pinctrl phandle and pins to
+announce the pinrange to the pin ctrl subsystem. For example,
+
+ qe_pio_e: gpio-controller@1460 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1460 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 20 10>, <&pinctrl2 50 20>;
+
+ }
+
+where,
+ &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
+
+ Next values specify the base pin and number of pins for the range
+ handled by 'qe_pio_e' gpio. In the given example from base pin 20 to
+ pin 29 under pinctrl1 and pin 50 to pin 69 under pinctrl2 is handled
+ by this gpio controller.
+
+The pinctrl node must have "#gpio-range-cells" property to show number of
+arguments to pass with phandle from gpio controllers node.
diff --git a/Documentation/devicetree/bindings/input/misc/bmp180.txt b/Documentation/devicetree/bindings/input/misc/bmp180.txt
new file mode 100644
index 0000000..13e0839
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/misc/bmp180.txt
@@ -0,0 +1,35 @@
+Bosch BMP18x-series altimeter driver
+
+Required properties:
+
+ - compatible : Should be "bosch,bmp180".
+ - reg : i2c slave address of the device.
+ - vdd-supply : Analog power supply needed to power device.
+ - vddio-supply : Digital IO power supply needed for IO and I2C.
+ - bosch,chip-id : Chip id for the bmp18x altimeter sensor.
+ - bosch,oversample : Sensor default oversampling value.
+ Default oversampling value to be used at startup.
+ Value range is 0-3 with rising sensitivity.
+ - bosch,period : Temperature measurement period (milliseconds).
+ Set to zero if unsure.
+
+Optional properties:
+
+ - bosch,sw-oversample : Boolean to enable software oversampling if
+ this property is defined. Only take effect when
+ default_oversampling is 3.
+
+Example:
+ i2c@f9925000 {
+ bmp180@77 {
+ status = "okay";
+ reg = <0x77>;
+ compatible = "bosch,bmp180";
+ vdd-supply = <&pm8110_l19>;
+ vddio-supply = <&pm8110_l14>;
+ bosch,chip-id = <0x55>;
+ bosch,oversample = <3>;
+ bosch,period = <1000>;
+ bosch,sw-oversample;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/input/misc/mma8x5x.txt b/Documentation/devicetree/bindings/input/misc/mma8x5x.txt
new file mode 100644
index 0000000..854939c
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/misc/mma8x5x.txt
@@ -0,0 +1,39 @@
+Freescale MMA8x5x 3-Axis Orientation/Motion Detection Sensor series
+(MMA8451/MMA8452/MMA8453/MMA8652/MMA8653)
+
+The Freescale 3-Axis Orientation/Motion Detection Sensor is
+connected to host processor via i2c.
+The sensor can be polling for acceleration data or configure to
+generates interrupts when a motion is detected.
+
+Required properties:
+
+ - compatible : should be "fsl,mma8x5x"
+ - reg : i2c slave address of the device
+ - vdd-supply : power supply needed to power up the device.
+ - vio-supply : power supply needed for device IO and to pullup i2c bus.
+ - fsl,sensors-position : select from 0 to 7 depends on how the sensor is
+ mounting on the board, this will decide how the 3-axis reading
+ of data will be translated to X/Y/Z axis of acceleration data.
+
+Optional properties:
+ Sensor can work on polling mode or interrupt mode, following interrupt
+ is required if the sensor need to work on interrupt mode.
+ - interrupt-parent : parent of interrupt.
+ - interrupts : sensor signal interrupt to indicate new data ready or
+ a motion is detected.
+ - fsl,irq-gpio : First irq gpio which is to provide interrupts to host, same
+ as "interrupts" node. It will also contain active low or active
+ high information.
+
+Example:
+ fsl@1c {
+ compatible = "fsl,mma8x5x";
+ reg = <0x1c>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <81 0x2>;
+ vdd-supply = <&pm8110_l19>;
+ vio-supply = <&pm8110_l14>;
+ fsl,sensors-position = <5>;
+ fsl,irq-gpio = <&msmgpio 0x81 0x02>;
+ };
\ No newline at end of file
diff --git a/Documentation/devicetree/bindings/input/misc/mpu3050.txt b/Documentation/devicetree/bindings/input/misc/mpu3050.txt
new file mode 100644
index 0000000..edbe147
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/misc/mpu3050.txt
@@ -0,0 +1,31 @@
+InvenSense MPU30X0-series gyrometer driver
+
+Required properties:
+
+ - compatible : Should be "invn,mpu3050".
+ - reg : i2c slave address of the device.
+ - interrupt-parent : Parent of interrupt.
+ - interrupts : Gyrometer sample interrupt to indicate new data ready.
+ - vdd-supply : Analog power supply needed to power device.
+ - vlogic-supply : Digital IO power supply needed for IO and I2C.
+ - invn,gpio-int : GPIO used for interrupt.
+ - invn,gpio-en : GPIO used for power enabling.
+ - invn,poll-interval : Initial data polling interval in milliseconds.
+
+Example:
+ i2c@f9925000 {
+ mpu3050@68 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+ reg = <0x68>;
+ compatible = "invn,mpu3050";
+ interrupt-parent = <&msmgpio>;
+ interrupts = <84 0x2>;
+ vlogic-supply = <&pm8110_l14>;
+ vdd-supply = <&pm8110_l19>;
+ invn,gpio-int = <&msmgpio 84 0x2>;
+ invn,gpio-en = <&pm8110_gpios 2 0x2>;
+ invn,poll-interval = <200>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt
index 6fe88a9..4c6ae93 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt
@@ -35,6 +35,8 @@
needed during wakeup.
- atmel,no-force-update : flag that signifies whether force configuration
update is applicable or not
+ - atmel,no-lpm-support : flag that signifies whether low power mode is
+ supported or not on this platform
Example:
i2c@f9966000 {
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
index 26a119c..9d3ee39 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
@@ -54,6 +54,10 @@
qcom,iommu-bfb-regs property. If this property is present, the
qcom,iommu-bfb-regs property shall also be present, and the lengths of both
properties shall be the same.
+- qcom,iommu-lpae-bfb-regs : See description for qcom,iommu-bfb-regs. This is
+ the same property except this is for IOMMU with LPAE support.
+- qcom,iommu-lpae-bfb-data : See description for qcom,iommu-bfb-data. This is
+ the same property except this is for IOMMU with LPAE support.
Example:
diff --git a/Documentation/devicetree/bindings/leds/leds-msm-gpio-flash.txt b/Documentation/devicetree/bindings/leds/leds-msm-gpio-flash.txt
new file mode 100644
index 0000000..ddbfd5a
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-msm-gpio-flash.txt
@@ -0,0 +1,30 @@
+Qualcomm GPIO Flash Leds
+
+Qualcomm GPIO flash LEDs driver is used for
+controlling flash LEDs that are connected with 2 GPIOs. The 2 GPIOs are name
+flash_en and flash now.
+
+
+The flash LEDs has torch mode and flash mode. The flash mode is used for camera.
+The torch mode is used for torch.
+
+
+Required properties for each node:
+- compatible : should be "qcom,leds-gpio-flash"
+- qcom,flash-en : the GPIO pin number of flash-en function
+- qcom,flash-now : the GPIO pin number of flash-now function
+- linux,name : name of the led that is used in led framework
+- linux,default-trigger : name of the led trigger event
+
+No other optional properties for it.
+
+Example:
+
+ flashlight {
+ compatible = "leds-gpio-flash";
+ status = "okay";
+ qcom,flash-en = <&msmgpio 18 0>;
+ qcom,flash-now = <&msmgpio 19 0>;
+ linux,name = "flashlight";
+ linux,default-trigger = "flashlight-trigger";
+ };
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
index ff95d43..e9c5061 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
@@ -81,7 +81,9 @@
- qcom,source-sel: select power source, default 1 (enabled)
- qcom,mode-ctrl: select operation mode, default 0x60 = Mode Sink
- qcom,mode: mode the led should operate in, options "pwm", "lpg" and "manual"
+- qcom,vin-ctrl: select input source, supported values are 0 to 3
- qcom,use-blink: Use blink sysfs entry for switching into lpg mode. For optimal use, set default mode to pwm. All required lpg parameters must be supplied.
+- qcom,min-brightness - Lowest possible brightness supported on this LED other than 0.
Required properties for PWM mode only:
- qcom,pwm-channel: pwm channel the led will operate on
@@ -136,6 +138,8 @@
qcom,id = <6>;
qcom,source-sel = <1>;
qcom,mode-ctrl = <0x10>;
+ qcom,vin-ctrl = <0x03>;
+ qcom,min-brightness = <20>;
};
};
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index a2503cd..8e988be 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -37,8 +37,11 @@
MSM sensor node contains properties of camera sensor
Required properties:
-- compatible : should be "qcom" followed by sensor name
+- compatible : should be manufacturer name followed by sensor name
- "qcom,s5k3l1yx"
+ - "shinetech,gc0339"
+ - "shinetech,hi256"
+ - "shinetech,s5k4e1"
- reg : should contain i2c slave address of the device
- qcom,slave-id : should contain i2c slave address, device id address
and expected id read value
@@ -92,6 +95,11 @@
#gpio-cells specifying specific gpio (controller specific)
- qcom,gpio-reset : should contain index to gpio used by sensors reset_n
- qcom,gpio-standby : should contain index to gpio used by sensors standby_n
+- qcom,gpio-vio : should contain index to gpio used by sensors io vreg enable
+- qcom,gpio-vana : should contain index to gpio used by sensors analog vreg enable
+- qcom,gpio-vdig : should contain index to gpio used by sensors digital vreg enable
+- qcom,gpio-vaf : should contain index to gpio used by sensors af vreg enable
+- qcom,gpio-af-pwdm : should contain index to gpio used by sensors af pwdm_n
- qcom,gpio-req-tbl-num : should contain index to gpios specific to this sensor
- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
qcom,gpio-req-tbl-num property (in the same order)
diff --git a/Documentation/devicetree/bindings/nfc/nfc-nci.txt b/Documentation/devicetree/bindings/nfc/nfc-nci.txt
new file mode 100644
index 0000000..f70d90f
--- /dev/null
+++ b/Documentation/devicetree/bindings/nfc/nfc-nci.txt
@@ -0,0 +1,28 @@
+Qualcomm QCA199x NFC NCI device
+
+Near Field Communication (NFC) device is based on NFC Controller Interface (NCI)
+
+Required properties:
+
+- compatible: "qcom,nfc-nci"
+- reg: NCI i2c slave address.
+- qcom,dis-gpio: specific gpio for hardware reset.
+- qcom,irq-gpio: specific gpio for read interrupt.
+- interrupt-parent: Should be phandle for the interrupt controller
+ that services interrupts for this device.
+- interrupts: should contain the NFC interrupt. NFC has one read interrupt.
+- qcom,clk-gpio: pmic gpio on which bbclk2 signal is coming.
+
+Example:
+
+ i2c@f9925000 { /* BLSP1 QUP3 */
+ nfc-nci@0e {
+ compatible = "qcom,nfc-nci";
+ reg = <0x0e>;
+ qcom,irq-gpio = <&msmgpio 21 0x00>;
+ qcom,dis-gpio = <&msmgpio 20 0x00>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <21 0>;
+ qcom,clk-gpio = <&pm8226_gpios 3 0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
new file mode 100644
index 0000000..2d730e3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
@@ -0,0 +1,186 @@
+== Introduction ==
+
+Hardware modules that control pin multiplexing or configuration parameters
+such as pull-up/down, tri-state, drive-strength etc are designated as pin
+controllers. Each pin controller must be represented as a node in device tree,
+just like any other hardware module.
+
+Hardware modules whose signals are affected by pin configuration are
+designated client devices. Again, each client device must be represented as a
+node in device tree, just like any other hardware module.
+
+For a client device to operate correctly, certain pin controllers must
+set up certain specific pin configurations. Some client devices need a
+single static pin configuration, e.g. set up during initialization. Others
+need to reconfigure pins at run-time, for example to tri-state pins when the
+device is inactive. Hence, each client device can define a set of named
+states. The number and names of those states is defined by the client device's
+own binding.
+
+The common pinctrl bindings defined in this file provide an infrastructure
+for client device device tree nodes to map those state names to the pin
+configuration used by those states.
+
+Note that pin controllers themselves may also be client devices of themselves.
+For example, a pin controller may set up its own "active" state when the
+driver loads. This would allow representing a board's static pin configuration
+in a single place, rather than splitting it across multiple client device
+nodes. The decision to do this or not somewhat rests with the author of
+individual board device tree files, and any requirements imposed by the
+bindings for the individual client devices in use by that board, i.e. whether
+they require certain specific named states for dynamic pin configuration.
+
+== Pinctrl client devices ==
+
+For each client device individually, every pin state is assigned an integer
+ID. These numbers start at 0, and are contiguous. For each state ID, a unique
+property exists to define the pin configuration. Each state may also be
+assigned a name. When names are used, another property exists to map from
+those names to the integer IDs.
+
+Each client device's own binding determines the set of states the must be
+defined in its device tree node, and whether to define the set of state
+IDs that must be provided, or whether to define the set of state names that
+must be provided.
+
+Required properties:
+pinctrl-0: List of phandles, each pointing at a pin configuration
+ node. These referenced pin configuration nodes must be child
+ nodes of the pin controller that they configure. Multiple
+ entries may exist in this list so that multiple pin
+ controllers may be configured, or so that a state may be built
+ from multiple nodes for a single pin controller, each
+ contributing part of the overall configuration. See the next
+ section of this document for details of the format of these
+ pin configuration nodes.
+
+ In some cases, it may be useful to define a state, but for it
+ to be empty. This may be required when a common IP block is
+ used in an SoC either without a pin controller, or where the
+ pin controller does not affect the HW module in question. If
+ the binding for that IP block requires certain pin states to
+ exist, they must still be defined, but may be left empty.
+
+Optional properties:
+pinctrl-1: List of phandles, each pointing at a pin configuration
+ node within a pin controller.
+...
+pinctrl-n: List of phandles, each pointing at a pin configuration
+ node within a pin controller.
+pinctrl-names: The list of names to assign states. List entry 0 defines the
+ name for integer state ID 0, list entry 1 for state ID 1, and
+ so on.
+
+For example:
+
+ /* For a client device requiring named states */
+ device {
+ pinctrl-names = "active", "idle";
+ pinctrl-0 = <&state_0_node_a>;
+ pinctrl-1 = <&state_1_node_a &state_1_node_b>;
+ };
+
+ /* For the same device if using state IDs */
+ device {
+ pinctrl-0 = <&state_0_node_a>;
+ pinctrl-1 = <&state_1_node_a &state_1_node_b>;
+ };
+
+ /*
+ * For an IP block whose binding supports pin configuration,
+ * but in use on an SoC that doesn't have any pin control hardware
+ */
+ device {
+ pinctrl-names = "active", "idle";
+ pinctrl-0 = <>;
+ pinctrl-1 = <>;
+ };
+
+== Pin controller devices ==
+
+Pin controller devices should contain the pin configuration nodes that client
+devices reference.
+
+For example:
+
+ pincontroller {
+ ... /* Standard DT properties for the device itself elided */
+
+ state_0_node_a {
+ ...
+ };
+ state_1_node_a {
+ ...
+ };
+ state_1_node_b {
+ ...
+ };
+ }
+
+The contents of each of those pin configuration child nodes is defined
+entirely by the binding for the individual pin controller device. There
+exists no common standard for this content.
+
+The pin configuration nodes need not be direct children of the pin controller
+device; they may be grandchildren, for example. Whether this is legal, and
+whether there is any interaction between the child and intermediate parent
+nodes, is again defined entirely by the binding for the individual pin
+controller device.
+
+== Using generic pinconfig options ==
+
+Generic pinconfig parameters can be used by defining a separate node containing
+the applicable parameters (and optional values), like:
+
+pcfg_pull_up: pcfg_pull_up {
+ bias-pull-up;
+ drive-strength = <20>;
+};
+
+This node should then be referenced in the appropriate pinctrl node as a phandle
+and parsed in the driver using the pinconf_generic_parse_dt_config function.
+
+Supported configuration parameters are:
+
+bias-disable - disable any pin bias
+bias-high-impedance - high impedance mode ("third-state", "floating")
+bias-bus-hold - latch weakly
+bias-pull-up - pull up the pin
+bias-pull-down - pull down the pin
+bias-pull-pin-default - use pin-default pull state
+drive-push-pull - drive actively high and low
+drive-open-drain - drive with open drain
+drive-open-source - drive with open source
+drive-strength - sink or source at most X mA
+input-schmitt-enable - enable schmitt-trigger mode
+input-schmitt-disable - disable schmitt-trigger mode
+input-schmitt - run in schmitt-trigger mode with hysteresis X
+input-debounce - debounce mode with debound time X
+power-source - select power source X
+slew-rate - use slew-rate X
+low-power-enable - enable low power mode
+low-power-disable - disable low power mode
+output-low - set the pin to output mode with low level
+output-high - set the pin to output mode with high level
+
+Arguments for parameters:
+
+- bias-pull-up, -down and -pin-default take as optional argument 0 to disable
+ the pull, on hardware supporting it the pull strength in Ohm. bias-disable
+ will also disable any active pull.
+
+- drive-strength takes as argument the target strength in mA.
+
+- input-schmitt takes as argument the adjustable hysteresis in a
+ driver-specific format
+
+- input-debounce takes the debounce time as argument or 0 to disable debouncing
+
+- power-source argument is the custom value describing the source to select
+
+- slew-rate takes as argument the target rate in a driver-specific format
+
+All parameters not listed here, do not take an argument.
+
+More in-depth documentation on these parameters can be found in
+<include/linux/pinctrl/pinconfig-generic.h>
diff --git a/Documentation/devicetree/bindings/power/qpnp-bms.txt b/Documentation/devicetree/bindings/power/qpnp-bms.txt
index 3cac311..3e7a578 100644
--- a/Documentation/devicetree/bindings/power/qpnp-bms.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-bms.txt
@@ -77,6 +77,10 @@
number of the FCC measurement cycles required to
generate an FCC update. This is applicable only
if the FCC learning is enabled.
+- qcom,fcc-resolution: An integer which defines the fcc resolution used
+ for storing the FCC(mAh) in the 8-bit BMS register.
+ For example - A value of 10 indicates:
+ FCC value (in mAh) = (8-bit register value) * 10.
Parent node optional properties:
- qcom,ignore-shutdown-soc: A boolean that controls whether BMS will
diff --git a/Documentation/devicetree/bindings/power/qpnp-charger.txt b/Documentation/devicetree/bindings/power/qpnp-charger.txt
index 6e125f2..e3c3555 100644
--- a/Documentation/devicetree/bindings/power/qpnp-charger.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-charger.txt
@@ -65,10 +65,21 @@
detection, "bpd_thm_id" selects both.
If the property is not set, the temperatue pin will
be used.
+- qcom,btc-disabled: If flag is set battery hot and cold monitoring is
+ disabled in hardware. This monitoring is turned on
+ by default.
+- qcom,batt-hot-percent: Specify a supported hot threshold percentage.
+ Supported thresholds: 25% and 35%. If none is specified
+ hardware defaults will be used.
+- qcom,batt-cold-percent: Specify a supported cold threshold percentage.
+ Supported thresholds: 70% and 80%. If none is specified
+ hardware defaults will be used.
- otg-parent-supply Specify a phandle to a parent supply regulator
for the OTG regulator.
- boost-parent-supply Specify a phandle to a parent supply regulator
for the boost regulator.
+- qcom,resume-soc Capacity in percent at which charging should resume
+ when a fully charged battery drops below this level.
Sub node required structure:
- A qcom,chg node must be a child of an SPMI node that has specified
@@ -182,6 +193,9 @@
qcom,warm-bat-mv = <4100>;
qcom,ibatmax-cool-ma = <350>;
qcom,vbatdet-delta-mv = <60>;
+ qcom,batt-hot-percent = <25>;
+ qcom,batt-cold-percent = <85>;
+ qcom,btc-disabled = <0>;
qcom,chgr@1000 {
reg = <0x1000 0x100>;
diff --git a/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
index 6b090fa..64f2ddd 100644
--- a/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
+++ b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
@@ -44,6 +44,17 @@
- qcom,rxreg-access: This boolean indicates that slimbus RX should use direct
register access to receive data. This flag is only needed if
BAM pipe is not available to receive data from slimbus
+ - qcom,apps-ch-pipes: This value represents BAM pipe-mask used by application
+ processor for data channels. If this property is not defined,
+ default mask of 0x3F000000 is used indicating apps can use 6
+ pipes from 24-29.
+ - qcom,ea-pc: This value represents product code (PC) field of enumeration
+ address (EA) for the Qualcomm slimbus controller hardware.
+ This value is needed if data-channels originating from apps
+ are to be used, so that application processor can query
+ logical address of the ported generic device to be used.
+ Other than PC, fields of EA are same across platforms.
+
Example:
slim@fe12f000 {
cell-index = <1>;
@@ -55,4 +66,6 @@
interrupt-names = "slimbus_irq", "slimbus_bam_irq";
qcom,min-clk-gear = <10>;
qcom,rxreg-access;
+ qcom,apps-ch-pipes = <0x60000000>;
+ qcom,ea-pc = <0x30>;
};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 4c6569e..b618597 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -131,15 +131,9 @@
* msm-auxpcm
-[First Level Nodes]
-
Required properties:
- - compatible : "qcom,msm-auxpcm-resource"
-
- - qcom,msm-cpudai-auxpcm-clk: clock for auxpcm. The first value is
- for 8khz mode, the second is for
- 16khz
+ - compatible : "qcom,msm-auxpcm-dev"
- qcom,msm-cpudai-auxpcm-mode: mode information. The first value is
for 8khz mode, the second is for
@@ -174,16 +168,10 @@
first value is for 8khz mode, the
second is for auxpcm
-[Second Level Nodes]
-
-Required Properties:
-
- - qcom,msm-auxpcm-dev-id: This property specifies the device
- port id.
- For Rx device, the port id is 4106
- and for Tx device, the port id is 4107
-
- - compatible: "qcom,msm-auxpcm-dev"
+ - qcom,msm-auxpcm-interface: name of AUXPCM interface "primary"
+ indicates primary AUXPCM interface
+ "secondary" indicates secondary
+ AUXPCM interface
* msm-pcm-hostless
@@ -378,37 +366,17 @@
};
};
- qcom,msm-auxpcm {
- compatible = "qcom,msm-auxpcm-resource";
- qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
- qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
- qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
- qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
- qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
- qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
- qcom,msm-cpudai-auxpcm-data = <0>, <0>;
- qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
-
- qcom,msm-prim-auxpcm-rx {
- qcom,msm-auxpcm-dev-id = <4106>;
- compatible = "qcom,msm-auxpcm-dev";
- };
-
- qcom,msm-prim-auxpcm-tx {
- qcom,msm-auxpcm-dev-id = <4107>;
- compatible = "qcom,msm-auxpcm-dev";
- };
-
- qcom,msm-sec-auxpcm-rx {
- qcom,msm-auxpcm-dev-id = <4108>;
- compatible = "qcom,msm-auxpcm-dev";
- };
-
- qcom,msm-sec-auxpcm-tx {
- qcom,msm-auxpcm-dev-id = <4109>;
- compatible = "qcom,msm-auxpcm-dev";
- };
- };
+ qcom,msm-pri-auxpcm {
+ qcom,msm-cpudai-auxpcm-mode = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+ qcom,msm-auxpcm-interface = "primary";
+ compatible = "qcom,msm-auxpcm-dev";
+ };
qcom,msm-pcm-hostless {
compatible = "qcom,msm-pcm-hostless";
@@ -693,7 +661,9 @@
prim-gpio-tert : Primary AUXPCM shares GPIOs with Tertiary MI2S
Optional Properties:
-- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- qcom,cdc-lineout-spkr-gpios : GPIO which controls external PAs to enable Lineout1/2 speaker
+- qcom,cdc-vdd-spkr-gpios : GPIO which controls PA for VDD speaker
Example:
@@ -707,4 +677,6 @@
qcom,prim-auxpcm-gpio-dout = <&msmgpio 66 0>;
qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
qcom,cdc-us-euro-gpios = <&msmgpio 69 0>;
+ qcom,cdc-lineout-spkr-gpios = <&pm8226_gpios 2 0>;
+ qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 3 0>;
};
diff --git a/Documentation/devicetree/bindings/spi/spi_qsd.txt b/Documentation/devicetree/bindings/spi/spi_qsd.txt
index f16bcbc..1504dc0 100644
--- a/Documentation/devicetree/bindings/spi/spi_qsd.txt
+++ b/Documentation/devicetree/bindings/spi/spi_qsd.txt
@@ -17,9 +17,13 @@
'spi{n}' where n is the bus number.
Optional properties:
-- gpios : Specifies the gpio pins to be used for SPI CLK, MISO, MOSI in
- that order.
-- cs-gpios : Specifies the gpio pins to be used for chipselects.
+- qcom,gpio-mosi : GPIO pin number of the MOSI bus line.
+- qcom,gpio-miso : GPIO pin number of the MISO bus line.
+- qcom,gpio-clk : GPIO pin number of the CLK bus line.
+- qcom,gpio-cs0 : GPIO pin number of the chipselect0 bus line.
+- qcom,gpio-cs1 : GPIO pin number of the chipselect1 bus line.
+- qcom,gpio-cs2 : GPIO pin number of the chipselect2 bus line.
+- qcom,gpio-cs3 : GPIO pin number of the chipselect3 bus line.
- qcom,infinite-mode: When missing or set to zero, QUP uses infinite-mode. When
value is non-zero, the value is the number of words in maximum transfer
length.
@@ -77,10 +81,10 @@
spi-max-frequency = <19200000>;
#address-cells = <1>;
#size-cells = <0>;
- gpios = <&msmgpio 3 0>, /* CLK */
- <&msmgpio 1 0>, /* MISO */
- <&msmgpio 0 0>; /* MOSI */
- cs-gpios = <&msmgpio 9 0>;
+ qcom,gpio-mosi = <&msmgpio 0 0>;
+ qcom,gpio-miso = <&msmgpio 1 0>;
+ qcom,gpio-clk = <&msmgpio 3 0>;
+ qcom,gpio-cs2 = <&msmgpio 9 0>;
qcom,infinite-mode = <0>;
qcom,use-bam;
diff --git a/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
index 96c9486..762d40f 100644
--- a/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
+++ b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
@@ -12,6 +12,11 @@
- interrupt-names :
- "core_irq" to be used as uart irq
- "bam irq" to be used as bam irq
+- #interrupt-cells: Specifies the number of cells needed to encode an interrupt
+ source. The type shall be a <u32> and the value shall be 1
+- #address-cells: Specifies the number of cells needed to encode an address.
+ The type shall be <u32> and the value shall be 0
+- interrupt-parent = It is needed for interrupt mapping
- bam-tx-ep-pipe-index : BAM TX Endpoint Pipe Index for HSUART
- bam-rx-ep-pipe-index : BAM RX Endpoint Pipe Index for HSUART
@@ -35,8 +40,13 @@
reg = <0xf995d000 0x1000>,
<0xf9944000 0x5000>;
reg-names = "core_mem", "bam_mem";
- interrupts = <0 113 0>, <0 239 0>;
interrupt-names = "core_irq", "bam_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&uart7>;
+ interrupts = <0 1>;
+ #interrupt-cells = <1>;
+ interrupt-map = <0 &intc 0 113 0
+ 1 &intc 0 239 0>
qcom,bam-tx-ep-pipe-index = <0>;
qcom,bam-rx-ep-pipe-index = <1>;
};
@@ -50,10 +60,10 @@
case where there is nothing connected and we want to use internal loopback mode
for uart.
- qcom, wakeup_irq : UART RX GPIO IRQ line to be configured as wakeup source.
-- qcom,inject_rx_on_wakeup : inject_rx_on_wakeup enables feature where on
+- qcom,inject-rx-on-wakeup : inject_rx_on_wakeup enables feature where on
receiving interrupt with UART RX GPIO IRQ line (i.e. above wakeup_irq property),
HSUART driver injects provided character with property rx_to_inject.
-- qcom, rx_to_inject : The character to be inserted on wakeup.
+- qcom, rx-char-to-inject : The character to be inserted on wakeup.
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
below optional properties:
- qcom,msm_bus,name
@@ -80,13 +90,21 @@
reg = <0x19c40000 0x1000">,
<0xf9944000 0x5000>;
reg-names = "core_mem", "bam_mem";
- interrupts = <0 113 0>, <0 239 0>;
- interrupt-names = "core_irq", "bam_irq";
-
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&uart7>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 113 0
+ 1 &intc 0 239 0
+ 2 &msmgpio 42 0>;
qcom,tx-gpio = <&msmgpio 41 0x00>;
qcom,rx-gpio = <&msmgpio 42 0x00>;
qcom,cts-gpio = <&msmgpio 43 0x00>;
qcom,rfr-gpio = <&msmgpio 44 0x00>;
+ qcom,inject-rx-on-wakeup = <1>;
+ qcom,rx-char-to-inject = <0xFD>;
qcom,bam-tx-ep-pipe-index = <0>;
qcom,bam-rx-ep-pipe-index = <1>;
diff --git a/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt b/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
index 70bf993..e724c62 100644
--- a/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
@@ -65,6 +65,14 @@
5 (2^5 = 32) 32 micro frame interrupt threshold aka 4ms interrupt threshold
6 (2^6 = 64) 64 micro frame interrupt threshold aka 8ms interrupt threshold
+- hsic,disable-cerr: CERR is 2bit down error counter that keeps track of number
+ of consecutive errors detected on single usb transaction. When set to non
+ zero value, hw decrements the count and updates qTD when transaction fails.
+ If counter reaches zero, hw marks the qTD inactive and triggers the interrupt.
+ When CERR is programmed to zero, hw ignores transaction failures. ECHI stack
+ programs the CERR to 3 by default. When this flag is true, CERR is set to
+ zero and transaction errors are ignored.
+
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
below optional properties:
- qcom,msm_bus,name
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index ebb3134..ad1b5ad 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -50,7 +50,10 @@
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
below optional properties:
- qcom,msm_bus,name
- - qcom,msm_bus,num_cases
+ - qcom,msm_bus,num_cases - There are three valid cases for this: NONE, MAX
+ and MIN bandwidth votes. Minimum two cases must be defined for
+ both NONE and MAX votes. If MIN vote is different from NONE VOTE
+ then specify third case for MIN VOTE.
- qcom,msm_bus,active_only
- qcom,msm_bus,num_paths
- qcom,msm_bus,vectors
@@ -76,6 +79,13 @@
HSPHY.
- qcom,hsusb-log2-itc: value of 2^(log2_itc-1) will be used as the
interrupt threshold (ITC), when log2_itc is between 1 to 7.
+- qcom,hsusb-l1-supported: If present, the device supports l1 (Link power
+ management).
+- qcom,no-selective-suspend: If present selective suspend is disabled on hub ports.
+- qcom,hsusb-otg-dpsehv-int: If present, indicates mpm interrupt to be configured
+ for detection of dp line transition during VDD minimization.
+- qcom,hsusb-otg-dmsehv-int: If present, indicates mpm interrupt to be configured
+ for detection of dm line transition during VDD minimization.
Example HSUSB OTG controller device node :
usb@f9690000 {
@@ -100,6 +110,8 @@
HSUSB_3p3-supply = <&pm8226_l20>;
qcom,vdd-voltage-level = <1 5 7>;
qcom,dp-manual-pullup;
+ qcom,hsusb-otg-dpsehv-int = <49>;
+ qcom,hsusb-otg-dmsehv-int = <58>;
qcom,msm_bus,name = "usb2";
qcom,msm_bus,num_cases = <2>;
qcom,msm_bus,active_only = <0>;
@@ -123,6 +135,9 @@
Optional properties :
- qcom,usb2-enable-hsphy2: If present, select second PHY for USB operation.
+- qcom,resume-gpio: if present then peripheral connected to usb controller
+ cannot wakeup from XO shutdown using in-band usb bus resume. Use resume
+ gpio to wakeup peripheral.
Example MSM HSUSB EHCI controller device node :
ehci: qcom,ehci-host@f9a55000 {
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 282257c..ff7b03d 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -47,6 +47,8 @@
bits 20-25 PARAMETER_OVERRIDE_D
- qcom,skip-charger-detection: If present then charger detection using BC1.2
is not supported and attached host should always be assumed as SDP.
+- USB3_GDSC-supply : phandle to the globally distributed switch controller
+ regulator node to the USB controller.
Sub nodes:
- Sub node for "DWC3- USB3 controller".
@@ -66,6 +68,7 @@
HSUSB_1p8-supply = <&pm8941_l6>;
HSUSB_3p3-supply = <&pm8941_l24>;
vbus_dwc3-supply = <&pm8941_mvs1>;
+ USB3_GDSC-supply = <&gdsc_usb30>;
qcom,dwc-usb3-msm-dbm-eps = <4>
qcom,vdd-voltage-level = <1 5 7>;
qcom,dwc-hsphy-init = <0x00D195A4>;
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 8fa9f4a..a3a3807 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -8,6 +8,7 @@
apm Applied Micro Circuits Corporation (APM)
arm ARM Ltd.
atmel Atmel Corporation
+bosch Bosch Sensortec GmbH
cavium Cavium, Inc.
chrp Common Hardware Reference Platform
cortina Cortina Systems, Inc.
@@ -23,6 +24,7 @@
ibm International Business Machines (IBM)
idt Integrated Device Technologies, Inc.
intercontrol Inter Control Group
+invn InvenSense Inc.
linux Linux-specific binding
kionix Kionix Inc.
marvell Marvell Technology Group Ltd.
@@ -40,6 +42,7 @@
samsung Samsung Semiconductor
sbs Smart Battery System
schindler Schindler
+shinetech Shine Tech Corporation, Ltd.
sil Silicon Image
simtek
sirf SiRF Technology, Inc.
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index 2a596a4..ef4fa7b 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -276,3 +276,7 @@
devm_regulator_get()
devm_regulator_put()
devm_regulator_bulk_get()
+
+PINCTRL
+ devm_pinctrl_get()
+ devm_pinctrl_put()
diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt
index 620a078..1d5b334 100644
--- a/Documentation/gpio.txt
+++ b/Documentation/gpio.txt
@@ -436,6 +436,48 @@
signaling rate accordingly.
+GPIO controllers and the pinctrl subsystem
+------------------------------------------
+
+A GPIO controller on a SOC might be tightly coupled with the pinctrl
+subsystem, in the sense that the pins can be used by other functions
+together with an optional gpio feature. We have already covered the
+case where e.g. a GPIO controller need to reserve a pin or set the
+direction of a pin by calling any of:
+
+pinctrl_request_gpio()
+pinctrl_free_gpio()
+pinctrl_gpio_direction_input()
+pinctrl_gpio_direction_output()
+
+But how does the pin control subsystem cross-correlate the GPIO
+numbers (which are a global business) to a certain pin on a certain
+pin controller?
+
+This is done by registering "ranges" of pins, which are essentially
+cross-reference tables. These are described in
+Documentation/pinctrl.txt
+
+While the pin allocation is totally managed by the pinctrl subsystem,
+gpio (under gpiolib) is still maintained by gpio drivers. It may happen
+that different pin ranges in a SoC is managed by different gpio drivers.
+
+This makes it logical to let gpio drivers announce their pin ranges to
+the pin ctrl subsystem before it will call 'pinctrl_request_gpio' in order
+to request the corresponding pin to be prepared by the pinctrl subsystem
+before any gpio usage.
+
+For this, the gpio controller can register its pin range with pinctrl
+subsystem. There are two ways of doing it currently: with or without DT.
+
+For with DT support refer to Documentation/devicetree/bindings/gpio/gpio.txt.
+
+For non-DT support, user can call gpiochip_add_pin_range() with appropriate
+parameters to register a range of gpio pins with a pinctrl driver. For this
+exact name string of pinctrl device has to be passed as one of the
+argument to this routine.
+
+
What do these conventions omit?
===============================
One of the biggest things these conventions omit is pin multiplexing, since
diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt
index d97bccf..e454a6a 100644
--- a/Documentation/pinctrl.txt
+++ b/Documentation/pinctrl.txt
@@ -152,11 +152,9 @@
};
-static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_groups);
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
@@ -175,7 +173,7 @@
}
static struct pinctrl_ops foo_pctrl_ops = {
- .list_groups = foo_list_groups,
+ .get_groups_count = foo_get_groups_count,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
@@ -186,13 +184,12 @@
.pctlops = &foo_pctrl_ops,
};
-The pin control subsystem will call the .list_groups() function repeatedly
-beginning on 0 until it returns non-zero to determine legal selectors, then
-it will call the other functions to retrieve the name and pins of the group.
-Maintaining the data structure of the groups is up to the driver, this is
-just a simple example - in practice you may need more entries in your group
-structure, for example specific register ranges associated with each group
-and so on.
+The pin control subsystem will call the .get_groups_count() function to
+determine total number of legal selectors, then it will call the other functions
+to retrieve the name and pins of the group. Maintaining the data structure of
+the groups is up to the driver, this is just a simple example - in practice you
+may need more entries in your group structure, for example specific register
+ranges associated with each group and so on.
Pin configuration
@@ -362,6 +359,10 @@
the range ID value, so that the pin controller knows which range it should
deal with.
+Calling pinctrl_add_gpio_range from pinctrl driver is DEPRECATED. Please see
+section 2.1 of Documentation/devicetree/bindings/gpio/gpio.txt on how to bind
+pinctrl and gpio drivers.
+
PINMUX interfaces
=================
@@ -606,11 +607,9 @@
};
-static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_groups);
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
@@ -629,7 +628,7 @@
}
static struct pinctrl_ops foo_pctrl_ops = {
- .list_groups = foo_list_groups,
+ .get_groups_count = foo_get_groups_count,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
@@ -663,11 +662,9 @@
},
};
-int foo_list_funcs(struct pinctrl_dev *pctldev, unsigned selector)
+int foo_get_functions_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_functions);
}
const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector)
@@ -703,7 +700,7 @@
}
struct pinmux_ops foo_pmxops = {
- .list_functions = foo_list_funcs,
+ .get_functions_count = foo_get_functions_count,
.get_function_name = foo_get_fname,
.get_function_groups = foo_get_groups,
.enable = foo_enable,
@@ -952,13 +949,13 @@
The result of grabbing this mapping from the device with something like
this (see next paragraph):
- p = pinctrl_get(dev);
+ p = devm_pinctrl_get(dev);
s = pinctrl_lookup_state(p, "8bit");
ret = pinctrl_select_state(p, s);
or more simply:
- p = pinctrl_get_select(dev, "8bit");
+ p = devm_pinctrl_get_select(dev, "8bit");
Will be that you activate all the three bottom records in the mapping at
once. Since they share the same name, pin controller device, function and
@@ -970,6 +967,18 @@
Pinmux requests from drivers
============================
+When a device driver is about to probe the device core will automatically
+attempt to issue pinctrl_get_select_default() on these devices.
+This way driver writers do not need to add any of the boilerplate code
+of the type found below. However when doing fine-grained state selection
+and not using the "default" state, you may have to do some device driver
+handling of the pinctrl handles and states.
+
+So if you just want to put the pins for a certain device into the default
+state and be done with it, there is nothing you need to do besides
+providing the proper mapping table. The device core will take care of
+the rest.
+
Generally it is discouraged to let individual drivers get and enable pin
control. So if possible, handle the pin control in platform code or some other
place where you have access to all the affected struct device * pointers. In
@@ -992,7 +1001,7 @@
/* Allocate a state holder named "foo" etc */
struct foo_state *foo = ...;
- foo->p = pinctrl_get(&device);
+ foo->p = devm_pinctrl_get(&device);
if (IS_ERR(foo->p)) {
/* FIXME: clean up "foo" here */
return PTR_ERR(foo->p);
@@ -1000,24 +1009,17 @@
foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT);
if (IS_ERR(foo->s)) {
- pinctrl_put(foo->p);
/* FIXME: clean up "foo" here */
return PTR_ERR(s);
}
ret = pinctrl_select_state(foo->s);
if (ret < 0) {
- pinctrl_put(foo->p);
/* FIXME: clean up "foo" here */
return ret;
}
}
-foo_remove()
-{
- pinctrl_put(state->p);
-}
-
This get/lookup/select/put sequence can just as well be handled by bus drivers
if you don't want each and every driver to handle it and you know the
arrangement on your bus.
@@ -1029,6 +1031,11 @@
kernel memory to hold the pinmux state. All mapping table parsing or similar
slow operations take place within this API.
+- devm_pinctrl_get() is a variant of pinctrl_get() that causes pinctrl_put()
+ to be called automatically on the retrieved pointer when the associated
+ device is removed. It is recommended to use this function over plain
+ pinctrl_get().
+
- pinctrl_lookup_state() is called in process context to obtain a handle to a
specific state for a the client device. This operation may be slow too.
@@ -1041,14 +1048,75 @@
- pinctrl_put() frees all information associated with a pinctrl handle.
+- devm_pinctrl_put() is a variant of pinctrl_put() that may be used to
+ explicitly destroy a pinctrl object returned by devm_pinctrl_get().
+ However, use of this function will be rare, due to the automatic cleanup
+ that will occur even without calling it.
+
+ pinctrl_get() must be paired with a plain pinctrl_put().
+ pinctrl_get() may not be paired with devm_pinctrl_put().
+ devm_pinctrl_get() can optionally be paired with devm_pinctrl_put().
+ devm_pinctrl_get() may not be paired with plain pinctrl_put().
+
Usually the pin control core handled the get/put pair and call out to the
device drivers bookkeeping operations, like checking available functions and
the associated pins, whereas the enable/disable pass on to the pin controller
driver which takes care of activating and/or deactivating the mux setting by
quickly poking some registers.
-The pins are allocated for your device when you issue the pinctrl_get() call,
-after this you should be able to see this in the debugfs listing of all pins.
+The pins are allocated for your device when you issue the devm_pinctrl_get()
+call, after this you should be able to see this in the debugfs listing of all
+pins.
+
+NOTE: the pinctrl system will return -EPROBE_DEFER if it cannot find the
+requested pinctrl handles, for example if the pinctrl driver has not yet
+registered. Thus make sure that the error path in your driver gracefully
+cleans up and is ready to retry the probing later in the startup process.
+
+
+Drivers needing both pin control and GPIOs
+==========================================
+
+Again, it is discouraged to let drivers lookup and select pin control states
+themselves, but again sometimes this is unavoidable.
+
+So say that your driver is fetching its resources like this:
+
+#include <linux/pinctrl/consumer.h>
+#include <linux/gpio.h>
+
+struct pinctrl *pinctrl;
+int gpio;
+
+pinctrl = devm_pinctrl_get_select_default(&dev);
+gpio = devm_gpio_request(&dev, 14, "foo");
+
+Here we first request a certain pin state and then request GPIO 14 to be
+used. If you're using the subsystems orthogonally like this, you should
+nominally always get your pinctrl handle and select the desired pinctrl
+state BEFORE requesting the GPIO. This is a semantic convention to avoid
+situations that can be electrically unpleasant, you will certainly want to
+mux in and bias pins in a certain way before the GPIO subsystems starts to
+deal with them.
+
+The above can be hidden: using the device core, the pinctrl core may be
+setting up the config and muxing for the pins right before the device is
+probing, nevertheless orthogonal to the GPIO subsystem.
+
+But there are also situations where it makes sense for the GPIO subsystem
+to communicate directly with with the pinctrl subsystem, using the latter
+as a back-end. This is when the GPIO driver may call out to the functions
+described in the section "Pin control interaction with the GPIO subsystem"
+above. This only involves per-pin multiplexing, and will be completely
+hidden behind the gpio_*() function namespace. In this case, the driver
+need not interact with the pin control subsystem at all.
+
+If a pin control driver and a GPIO driver is dealing with the same pins
+and the use cases involve multiplexing, you MUST implement the pin controller
+as a back-end for the GPIO driver like this, unless your hardware design
+is such that the GPIO controller can override the pin controller's
+multiplexing state through hardware without the need to interact with the
+pin control system.
System pin control hogging
@@ -1094,13 +1162,13 @@
#include <linux/pinctrl/consumer.h>
-foo_switch()
-{
- struct pinctrl *p;
- struct pinctrl_state *s1, *s2;
+struct pinctrl *p;
+struct pinctrl_state *s1, *s2;
+foo_probe()
+{
/* Setup */
- p = pinctrl_get(&device);
+ p = devm_pinctrl_get(&device);
if (IS_ERR(p))
...
@@ -1111,7 +1179,10 @@
s2 = pinctrl_lookup_state(foo->p, "pos-B");
if (IS_ERR(s2))
...
+}
+foo_switch()
+{
/* Enable on position A */
ret = pinctrl_select_state(s1);
if (ret < 0)
@@ -1125,8 +1196,8 @@
...
...
-
- pinctrl_put(p);
}
-The above has to be done from process context.
+The above has to be done from process context. The reservation of the pins
+will be done when the state is activated, so in effect one specific pin
+can be used by different functions at different times on a running system.
diff --git a/arch/arm/boot/dts/apq8026-mtp.dts b/arch/arm/boot/dts/apq8026-v1-mtp.dts
similarity index 95%
rename from arch/arm/boot/dts/apq8026-mtp.dts
rename to arch/arm/boot/dts/apq8026-v1-mtp.dts
index e14a6856..b89c676 100644
--- a/arch/arm/boot/dts/apq8026-mtp.dts
+++ b/arch/arm/boot/dts/apq8026-v1-mtp.dts
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "apq8026.dtsi"
+/include/ "apq8026-v1.dtsi"
/include/ "msm8226-mtp.dtsi"
/ {
diff --git a/arch/arm/boot/dts/apq8026-xpm.dts b/arch/arm/boot/dts/apq8026-v1-xpm.dts
similarity index 95%
rename from arch/arm/boot/dts/apq8026-xpm.dts
rename to arch/arm/boot/dts/apq8026-v1-xpm.dts
index 67152af..8c97823 100644
--- a/arch/arm/boot/dts/apq8026-xpm.dts
+++ b/arch/arm/boot/dts/apq8026-v1-xpm.dts
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "apq8026.dtsi"
+/include/ "apq8026-v1.dtsi"
/include/ "msm8226-cdp.dtsi"
/ {
diff --git a/arch/arm/boot/dts/apq8026.dtsi b/arch/arm/boot/dts/apq8026-v1.dtsi
similarity index 88%
rename from arch/arm/boot/dts/apq8026.dtsi
rename to arch/arm/boot/dts/apq8026-v1.dtsi
index db6576a..7fbfcb5 100644
--- a/arch/arm/boot/dts/apq8026.dtsi
+++ b/arch/arm/boot/dts/apq8026-v1.dtsi
@@ -16,9 +16,14 @@
* file.
*/
-/include/ "msm8226.dtsi"
+/include/ "msm8226-v1.dtsi"
/ {
model = "Qualcomm APQ 8026";
compatible = "qcom,apq8026";
};
+
+&tsens {
+ qcom,sensors = <4>;
+ qcom,slope = <2901 2846 3038 2955>;
+};
diff --git a/arch/arm/boot/dts/apq8026-mtp.dts b/arch/arm/boot/dts/apq8026-v2-mtp.dts
similarity index 86%
copy from arch/arm/boot/dts/apq8026-mtp.dts
copy to arch/arm/boot/dts/apq8026-v2-mtp.dts
index e14a6856..c6b7f2a 100644
--- a/arch/arm/boot/dts/apq8026-mtp.dts
+++ b/arch/arm/boot/dts/apq8026-v2-mtp.dts
@@ -12,11 +12,11 @@
/dts-v1/;
-/include/ "apq8026.dtsi"
+/include/ "apq8026-v2.dtsi"
/include/ "msm8226-mtp.dtsi"
/ {
- model = "Qualcomm APQ 8026 MTP";
+ model = "Qualcomm APQ 8026v2 MTP";
compatible = "qcom,apq8026-mtp", "qcom,apq8026", "qcom,mtp";
- qcom,msm-id = <199 8 0>;
+ qcom,msm-id = <199 8 0x20000>;
};
diff --git a/arch/arm/boot/dts/apq8026-xpm.dts b/arch/arm/boot/dts/apq8026-v2-xpm.dts
similarity index 86%
copy from arch/arm/boot/dts/apq8026-xpm.dts
copy to arch/arm/boot/dts/apq8026-v2-xpm.dts
index 67152af..42acd98 100644
--- a/arch/arm/boot/dts/apq8026-xpm.dts
+++ b/arch/arm/boot/dts/apq8026-v2-xpm.dts
@@ -12,11 +12,11 @@
/dts-v1/;
-/include/ "apq8026.dtsi"
+/include/ "apq8026-v2.dtsi"
/include/ "msm8226-cdp.dtsi"
/ {
- model = "Qualcomm APQ 8026 XPM";
+ model = "Qualcomm APQ 8026v2 XPM";
compatible = "qcom,apq8026-xpm", "qcom,apq8026", "qcom,xpm";
- qcom,msm-id = <199 14 0>;
+ qcom,msm-id = <199 14 0x20000>;
};
diff --git a/arch/arm/boot/dts/apq8026.dtsi b/arch/arm/boot/dts/apq8026-v2.dtsi
similarity index 87%
copy from arch/arm/boot/dts/apq8026.dtsi
copy to arch/arm/boot/dts/apq8026-v2.dtsi
index db6576a..4c0d3ce 100644
--- a/arch/arm/boot/dts/apq8026.dtsi
+++ b/arch/arm/boot/dts/apq8026-v2.dtsi
@@ -16,9 +16,14 @@
* file.
*/
-/include/ "msm8226.dtsi"
+/include/ "msm8226-v2.dtsi"
/ {
model = "Qualcomm APQ 8026";
compatible = "qcom,apq8026";
};
+
+&tsens {
+ qcom,sensors = <6>;
+ qcom,slope = <2901 2846 3038 2955 2901 2846>;
+};
diff --git a/arch/arm/boot/dts/apq8074-dragonboard.dtsi b/arch/arm/boot/dts/apq8074-dragonboard.dtsi
index 4b20e9f..53e9b3b 100644
--- a/arch/arm/boot/dts/apq8074-dragonboard.dtsi
+++ b/arch/arm/boot/dts/apq8074-dragonboard.dtsi
@@ -14,6 +14,10 @@
/include/ "msm8974-camera-sensor-dragonboard.dtsi"
/include/ "msm8974-leds.dtsi"
+&vph_pwr_vreg {
+ status = "ok";
+};
+
&soc {
serial@f991e000 {
status = "ok";
@@ -62,6 +66,10 @@
#size-cells = <1>;
ranges;
smsc,reset-gpio = <&pm8941_gpios 8 0x00>;
+ /* Dragonboard has an always-on VBUS supply for HSIC hub,
+ * providing a dummy regulator for the hub driver
+ */
+ hub_vbus-supply = <&vph_pwr_vreg>;
hsic_host: hsic@f9a00000 {
compatible = "qcom,hsic-host";
@@ -183,10 +191,10 @@
"MIC BIAS2 External", "ANCRight Headset Mic",
"AMIC4", "MIC BIAS2 External",
"MIC BIAS2 External", "ANCLeft Headset Mic",
- "AMIC5", "MIC BIAS1 External",
- "MIC BIAS1 External", "Analog Mic6",
- "AMIC6", "MIC BIAS1 External",
- "MIC BIAS1 External", "Analog Mic7",
+ "AMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Analog Mic6",
+ "AMIC6", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Analog Mic7",
"DMIC1", "MIC BIAS3 External",
"MIC BIAS3 External", "Digital Mic1",
"DMIC2", "MIC BIAS3 External",
@@ -199,6 +207,15 @@
"MIC BIAS4 External", "Digital Mic5",
"DMIC6", "MIC BIAS4 External",
"MIC BIAS4 External", "Digital Mic6";
+
+
+ qcom,prim-auxpcm-gpio-clk = <&msmgpio 74 0>;
+ qcom,prim-auxpcm-gpio-sync = <&msmgpio 75 0>;
+ qcom,prim-auxpcm-gpio-din = <&msmgpio 76 0>;
+ qcom,prim-auxpcm-gpio-dout = <&msmgpio 77 0>;
+ qcom,prim-auxpcm-gpio-set = "prim-gpio-tert";
+
+ qcom,cdc-micbias2-headset-only;
};
qcom,pronto@fb21b000 {
@@ -273,6 +290,7 @@
};
&usb3 {
+ interrupts = <0>; /* remove pmic_id_irq; used by &usb_otg */
qcom,charging-disabled;
vbus_dwc3-supply = <0>;
dwc3@f9200000 {
@@ -282,8 +300,17 @@
&slim_msm {
taiko_codec {
+ qcom,cdc-micbias1-ext-cap;
qcom,cdc-micbias2-ext-cap;
qcom,cdc-micbias3-ext-cap;
+ qcom,cdc-micbias4-ext-cap;
+
+ /* If boot isn't available, vph_pwr_vreg can be used instead */
+ cdc-vdd-spkdrv-supply = <&pm8941_boost>;
+ qcom,cdc-vdd-spkdrv-voltage = <5000000 5000000>;
+ qcom,cdc-vdd-spkdrv-current = <1250000>;
+
+ qcom,cdc-on-demand-supplies = "cdc-vdd-spkdrv";
};
};
@@ -484,6 +511,7 @@
&pm8941_mpps {
mpp@a000 { /* MPP 1 */
+ status = "disabled";
};
mpp@a100 { /* MPP 2 */
@@ -524,52 +552,35 @@
};
&spi_epm {
- epm-adc@0 {
- compatible = "cy,epm-adc-cy8c5568lti-114";
- reg = <0>;
- interrupt-parent = <&msmgpio>;
- spi-max-frequency = <960000>;
- qcom,channels = <31>;
- qcom,gain = <50 50 50 50 50 100 50 50 50 50
- 50 50 50 50 100 50 50 50 50 100
- 50 50 50 100 50 50 50 1 1 1
- 1>;
- qcom,rsense = <40 10 10 25 10 1000 75 25 10 25
- 33 500 200 10 500 100 33 200 25 100
- 75 500 50 200 5 5 3 1 1 1
- 1>;
- qcom,channel-type = <0xf0000000>;
- };
+ status = "disabled";
};
-&spmi_bus {
- qcom,pm8941@1 {
- qcom,leds@d000 {
- qcom,rgb_2 {
- status = "ok";
- qcom,default-state = "on";
- qcom,turn-off-delay-ms = <1000>;
- };
+&pm8941_lsid1 {
+ qcom,leds@d000 {
+ qcom,rgb_2 {
+ status = "ok";
+ qcom,default-state = "on";
+ qcom,turn-off-delay-ms = <1000>;
};
+ };
- 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 = <1>;
- qcom,default-state = "on";
- qcom,max-current = <20>;
- qcom,ctrl-delay-us = <0>;
- qcom,boost-curr-lim = <3>;
- qcom,cp-sel = <0>;
- qcom,switch-freq = <2>;
- qcom,ovp-val = <1>;
- qcom,num-strings = <1>;
- qcom,id = <0>;
- };
+ 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 = <1>;
+ qcom,default-state = "on";
+ qcom,max-current = <20>;
+ qcom,ctrl-delay-us = <0>;
+ qcom,boost-curr-lim = <3>;
+ qcom,cp-sel = <0>;
+ qcom,switch-freq = <2>;
+ qcom,ovp-val = <1>;
+ qcom,num-strings = <1>;
+ qcom,id = <0>;
};
};
};
diff --git a/arch/arm/boot/dts/apq8074-v2-cdp.dts b/arch/arm/boot/dts/apq8074-v2-cdp.dts
new file mode 100644
index 0000000..1dc0912
--- /dev/null
+++ b/arch/arm/boot/dts/apq8074-v2-cdp.dts
@@ -0,0 +1,34 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+/include/ "apq8074-v2.dtsi"
+/include/ "msm8974-cdp.dtsi"
+
+/ {
+ model = "Qualcomm APQ 8074v2 CDP";
+ compatible = "qcom,apq8074-cdp", "qcom,apq8074", "qcom,cdp";
+ qcom,msm-id = <184 1 0x20000>;
+};
+
+&usb3 {
+ interrupt-parent = <&usb3>;
+ interrupts = <0 1>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0x0 0xffffffff>;
+ interrupt-map = <0x0 0 &intc 0 133 0
+ 0x0 1 &spmi_bus 0x0 0x0 0x9 0x0>;
+ interrupt-names = "hs_phy_irq", "pmic_id_irq";
+
+ qcom,misc-ref = <&pm8941_misc>;
+};
diff --git a/arch/arm/boot/dts/apq8074-v2-liquid.dts b/arch/arm/boot/dts/apq8074-v2-liquid.dts
index 4ec1cdd..a0ecb50 100644
--- a/arch/arm/boot/dts/apq8074-v2-liquid.dts
+++ b/arch/arm/boot/dts/apq8074-v2-liquid.dts
@@ -20,15 +20,3 @@
compatible = "qcom,apq8074-liquid", "qcom,apq8074", "qcom,liquid";
qcom,msm-id = <184 9 0x20000>;
};
-
-&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 133 0
- 0x0 1 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "pmic_id_irq";
-
- qcom,misc-ref = <&pm8941_misc>;
-};
diff --git a/arch/arm/boot/dts/apq8084-coresight.dtsi b/arch/arm/boot/dts/apq8084-coresight.dtsi
new file mode 100644
index 0000000..610d80b
--- /dev/null
+++ b/arch/arm/boot/dts/apq8084-coresight.dtsi
@@ -0,0 +1,145 @@
+/* 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.
+ */
+
+&soc {
+ tmc_etr: tmc@fc326000 {
+ compatible = "arm,coresight-tmc";
+ reg = <0xfc326000 0x1000>,
+ <0xfc37c000 0x3000>;
+ reg-names = "tmc-base", "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>;
+ };
+
+ replicator: replicator@fc324000 {
+ compatible = "qcom,coresight-replicator";
+ reg = <0xfc324000 0x1000>;
+ reg-names = "replicator-base";
+
+ coresight-id = <2>;
+ coresight-name = "coresight-replicator";
+ coresight-nr-inports = <1>;
+ coresight-outports = <0>;
+ coresight-child-list = <&tmc_etr>;
+ coresight-child-ports = <0>;
+ };
+
+ tmc_etf: tmc@fc325000 {
+ compatible = "arm,coresight-tmc";
+ reg = <0xfc325000 0x1000>;
+ reg-names = "tmc-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@fc323000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc323000 0x1000>;
+ reg-names = "funnel-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@fc321000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc321000 0x1000>;
+ reg-names = "funnel-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@fc322000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc322000 0x1000>;
+ reg-names = "funnel-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_kpss: funnel@fc355000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc355000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-id = <7>;
+ coresight-name = "coresight-funnel-kpss";
+ coresight-nr-inports = <4>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in1>;
+ coresight-child-ports = <5>;
+ };
+
+ funnel_mmss: funnel@fc36c000 {
+ compatible = "arm,coresight-funnel";
+ reg = <0xfc36c000 0x1000>;
+ reg-names = "funnel-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@fc302000 {
+ compatible = "arm,coresight-stm";
+ reg = <0xfc302000 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@fc301000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0xfc301000 0x1000>;
+ reg-names = "csr-base";
+
+ coresight-id = <14>;
+ coresight-name = "coresight-csr";
+ coresight-nr-inports = <0>;
+
+ qcom,blk-size = <3>;
+ };
+};
diff --git a/arch/arm/boot/dts/apq8084-gpu.dtsi b/arch/arm/boot/dts/apq8084-gpu.dtsi
new file mode 100644
index 0000000..8570b4f
--- /dev/null
+++ b/arch/arm/boot/dts/apq8084-gpu.dtsi
@@ -0,0 +1,95 @@
+/* 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.
+ */
+
+&soc {
+ msm_gpu: qcom,kgsl-3d0@fdb00000 {
+ label = "kgsl-3d0";
+ compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
+ reg = <0xfdb00000 0x10000
+ 0xfdb20000 0x20000>;
+ reg-names = "kgsl_3d0_reg_memory" , "kgsl_3d0_shader_memory";
+ interrupts = <0 33 0>;
+ interrupt-names = "kgsl_3d0_irq";
+ qcom,id = <0>;
+
+ qcom,chipid = <0x04020000>;
+
+ qcom,initial-pwrlevel = <2>;
+
+ qcom,idle-timeout = <8>; //<HZ/12>
+ qcom,strtstp-sleepwake;
+ qcom,clk-map = <0x00000016>; //KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "grp3d";
+ qcom,msm-bus,num-cases = <5>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ <26 512 0 0>, <89 604 0 0>,
+ <26 512 0 3144000>, <89 604 0 3200000>,
+ <26 512 0 4800000>, <89 604 0 4800000>,
+ <26 512 0 6400000>, <89 604 0 6400000>,
+ <26 512 0 8528000>, <89 604 0 9600000>;
+
+ /* GDSC oxili regulators */
+ vddcx-supply = <&gdsc_oxili_cx>;
+ vdd-supply = <&gdsc_oxili_gx>;
+
+ /* IOMMU Data */
+ iommu = <&kgsl_iommu>;
+
+ /* Power levels */
+ qcom,gpu-pwrlevels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "qcom,gpu-pwrlevels";
+
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <600000000>;
+ qcom,bus-freq = <4>;
+ qcom,io-fraction = <33>;
+ };
+
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <400000000>;
+ qcom,bus-freq = <3>;
+ qcom,io-fraction = <66>;
+ };
+
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <300000000>;
+ qcom,bus-freq = <2>;
+ qcom,io-fraction = <100>;
+ };
+
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <200000000>;
+ qcom,bus-freq = <1>;
+ qcom,io-fraction = <100>;
+ };
+
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <27000000>;
+ qcom,bus-freq = <0>;
+ qcom,io-fraction = <0>;
+ };
+ };
+
+ };
+};
+
diff --git a/arch/arm/boot/dts/apq8084-iommu.dtsi b/arch/arm/boot/dts/apq8084-iommu.dtsi
new file mode 100644
index 0000000..72d2748
--- /dev/null
+++ b/arch/arm/boot/dts/apq8084-iommu.dtsi
@@ -0,0 +1,477 @@
+/* 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-iommu-v1.dtsi"
+
+&venus_iommu {
+ status = "ok";
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c>;
+
+ qcom,iommu-bfb-data = <0x7ffffff
+ 0x4
+ 0x8
+ 0x0
+ 0x13607
+ 0x4201
+ 0x14221
+ 0x0
+ 0x0
+ 0x94
+ 0x114
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c>;
+
+ qcom,iommu-lpae-bfb-data = <0x7ffffff
+ 0x0
+ 0x4
+ 0x8
+ 0x13607
+ 0x0
+ 0x4201
+ 0x14221
+ 0x0
+ 0x0
+ 0x94
+ 0x114
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+
+ venus_ns: qcom,iommu-ctx@fdc8c000 {
+ qcom,iommu-ctx-sids = <0 1 2 3 4 5 7>;
+ };
+
+ venus_sec_bitstream: qcom,iommu-ctx@fdc8d000 {
+ qcom,iommu-ctx-sids = <0x80 0x81 0x82 0x83 0x84>;
+ label = "venus_sec_bitstream";
+ };
+
+ venus_sec_pixel: qcom,iommu-ctx@fdc8f000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfdc8f000 0x1000>;
+ interrupts = <0 42 0>;
+ qcom,iommu-ctx-sids = <0x85>;
+ label = "venus_sec_pixel";
+ qcom,secure-context;
+ };
+
+ venus_sec_non_pixel: qcom,iommu-ctx@fdc90000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfdc90000 0x1000>;
+ interrupts = <0 42 0>;
+ qcom,iommu-ctx-sids = <0x87 0xA0>;
+ label = "venus_sec_non_pixel";
+ qcom,secure-context;
+ };
+};
+
+&jpeg_iommu {
+ status = "ok";
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2008
+ 0x200c
+ 0x2010>;
+
+ qcom,iommu-bfb-data = <0x3fff
+ 0x4
+ 0x4
+ 0x0
+ 0x0
+ 0x10
+ 0x50
+ 0x0
+ 0x2000
+ 0x2804
+ 0x9614
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2008
+ 0x200c
+ 0x2010>;
+
+ qcom,iommu-lpae-bfb-data = <0x3fff
+ 0x0
+ 0x4
+ 0x4
+ 0x0
+ 0x0
+ 0x10
+ 0x50
+ 0x2000
+ 0x964b
+ 0x2804
+ 0x9614
+ 0x0
+ 0x0
+ 0x0>;
+
+};
+
+&mdp_iommu {
+ status = "ok";
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018>;
+
+ qcom,iommu-bfb-data = <0x7fffff
+ 0x4
+ 0x10
+ 0x0
+ 0x5000
+ 0x5a1d
+ 0x1822d
+ 0x0
+ 0x0
+ 0x28
+ 0x68
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018>;
+
+ qcom,iommu-lpae-bfb-data = <0x7fffff
+ 0x0
+ 0x4
+ 0x10
+ 0x5000
+ 0x182c1
+ 0x5a1d
+ 0x1822d
+ 0x0
+ 0x0
+ 0x28
+ 0x68
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+};
+
+&kgsl_iommu {
+ status = "ok";
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x2600
+ 0x2604
+ 0x2608
+ 0x260c
+ 0x2610
+ 0x2614
+ 0x2618
+ 0x261c
+ 0x2620
+ 0x2624
+ 0x2628
+ 0x262c>;
+
+ qcom,iommu-bfb-data = <0x3
+ 0x8
+ 0x10
+ 0x0
+ 0x0
+ 0x0
+ 0x20
+ 0x0
+ 0x0
+ 0x1
+ 0x101
+ 0x0
+ 0x0
+ 0x7
+ 0x4
+ 0x8
+ 0x14
+ 0x0
+ 0x0
+ 0xc
+ 0x6c
+ 0x0
+ 0x8
+ 0x10
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x2600
+ 0x2604
+ 0x2608
+ 0x260c
+ 0x2610
+ 0x2614
+ 0x2618
+ 0x261c
+ 0x2620
+ 0x2624
+ 0x2628
+ 0x262c>;
+
+ qcom,iommu-lpae-bfb-data = <0x3
+ 0x0
+ 0x8
+ 0x10
+ 0x0
+ 0x120
+ 0x0
+ 0x20
+ 0x0
+ 0x0
+ 0x1
+ 0x101
+ 0x0
+ 0x7
+ 0x4
+ 0x20
+ 0x8
+ 0x14
+ 0x0
+ 0x0
+ 0xc
+ 0x6c
+ 0x0
+ 0x8
+ 0x10>;
+
+};
+
+&vfe_iommu {
+ status = "ok";
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0x4
+ 0x8
+ 0x0
+ 0x0
+ 0x20
+ 0x78
+ 0x0
+ 0x4000
+ 0x3c08
+ 0xb41e
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020>;
+
+ qcom,iommu-lpae-bfb-data = <0xffffffff
+ 0x0
+ 0x4
+ 0x8
+ 0x0
+ 0x0
+ 0x20
+ 0x78
+ 0x4000
+ 0xb45a
+ 0x3c08
+ 0xb41e
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+};
+
+&copss_iommu {
+ status = "ok";
+};
+
+&vpu_iommu {
+ status = "ok";
+};
+
+&lpass_qdsp_iommu {
+ status = "ok";
+};
+
+&lpass_core_iommu {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/apq8084-mdss.dtsi b/arch/arm/boot/dts/apq8084-mdss.dtsi
new file mode 100644
index 0000000..b9609cc
--- /dev/null
+++ b/arch/arm/boot/dts/apq8084-mdss.dtsi
@@ -0,0 +1,77 @@
+/* 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.
+ */
+
+&soc {
+ mdss_mdp: qcom,mdss_mdp@fd900000 {
+ compatible = "qcom,mdss_mdp";
+ reg = <0xfd900000 0x22100>,
+ <0xfd924000 0x1000>;
+ reg-names = "mdp_phys", "vbif_phys";
+ interrupts = <0 72 0>;
+
+ qcom,max-clk-rate = <320000000>;
+
+ qcom,mdss-pipe-vig-off = <0x00001200 0x00001600
+ 0x00001A00 0x00001E00>;
+ qcom,mdss-pipe-rgb-off = <0x00002200 0x00002600
+ 0x00002A00 0x00002E00>;
+ qcom,mdss-pipe-dma-off = <0x00003200 0x00003600>;
+
+ qcom,mdss-pipe-vig-fetch-id = <1 4 7 19>;
+ qcom,mdss-pipe-rgb-fetch-id = <16 17 18 22>;
+ qcom,mdss-pipe-dma-fetch-id = <10 13>;
+
+ qcom,mdss-smp-data = <44 8192>;
+
+ qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
+ 0x00000900 0x0000A00>;
+ qcom,mdss-mixer-intf-off = <0x00003A00 0x00003E00
+ 0x00004200 0x00004E00>;
+ qcom,mdss-mixer-wb-off = <0x00004600 0x00004A00>;
+ qcom,mdss-dspp-off = <0x00005200 0x00005600 0x00005A00
+ 0x00005E00>;
+ qcom,mdss-wb-off = <0x00011100 0x00011500 0x00011900
+ 0x00011D00 0x00012100>;
+ qcom,mdss-intf-off = <0x00012500 0x00012700
+ 0x00012900 0x00012b00>;
+ qcom,mdss-pingpong-off = <0x00012F00 0x00013000 0x00013100
+ 0x00013200>;
+ qcom,mdss-ad-off = <0x0013500 0x00013700 0x00013900>;
+
+ qcom,mdss-has-bwc;
+ qcom,mdss-has-decimation;
+
+ mdss_fb0: qcom,mdss_fb_primary {
+ cell-index = <0>;
+ compatible = "qcom,mdss-fb";
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x1000000>;
+ };
+
+ mdss_fb1: qcom,mdss_fb_external {
+ cell-index = <1>;
+ compatible = "qcom,mdss-fb";
+ };
+
+ mdss_fb2: qcom,mdss_fb_wfd {
+ cell-index = <2>;
+ compatible = "qcom,mdss-fb";
+ };
+ };
+
+ qcom,mdss_wb_panel {
+ compatible = "qcom,mdss_wb";
+ qcom,mdss_pan_res = <1920 1080>;
+ qcom,mdss_pan_bpp = <24>;
+ qcom,mdss-fb-map = <&mdss_fb2>;
+ };
+};
diff --git a/arch/arm/boot/dts/apq8084-sim.dts b/arch/arm/boot/dts/apq8084-sim.dts
index e206d4d..dccdbc4 100644
--- a/arch/arm/boot/dts/apq8084-sim.dts
+++ b/arch/arm/boot/dts/apq8084-sim.dts
@@ -171,3 +171,7 @@
&usb3 {
qcom,skip-charger-detection;
};
+
+&ufs1 {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/apq8084.dtsi b/arch/arm/boot/dts/apq8084.dtsi
index e5f083a..3014813 100644
--- a/arch/arm/boot/dts/apq8084.dtsi
+++ b/arch/arm/boot/dts/apq8084.dtsi
@@ -19,8 +19,13 @@
soc: soc { };
};
+/include/ "msm-gdsc.dtsi"
/include/ "apq8084-ion.dtsi"
+/include/ "apq8084-iommu.dtsi"
/include/ "apq8084-smp2p.dtsi"
+/include/ "apq8084-coresight.dtsi"
+/include/ "apq8084-mdss.dtsi"
+/include/ "apq8084-gpu.dtsi"
&soc {
#address-cells = <1>;
@@ -193,7 +198,7 @@
tsens: tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b8000 0x1000>;
+ <0xfc4bc000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
qcom,sensors = <11>;
@@ -240,7 +245,7 @@
memory_hole: qcom,msm-mem-hole {
compatible = "qcom,msm-mem-hole";
- qcom,memblock-remove = <0x0dc00000 0x2000000>; /* Address and Size of Hole */
+ qcom,memblock-remove = <0x0d200000 0x02c00000>; /* Address and Size of Hole */
};
qcom,ipc-spinlock@fd484000 {
@@ -282,6 +287,79 @@
qcom,irq-no-suspend;
};
};
+
+ qcom,venus@fdce0000 {
+ compatible = "qcom,pil-venus";
+ reg = <0xfdce0000 0x4000>,
+ <0xfdc80000 0x400>;
+ reg-names = "wrapper_base", "vbif_base";
+ vdd-supply = <&gdsc_venus>;
+
+ qcom,firmware-name = "venus";
+ };
+
+ ufs1: ufshc@0xfc598000 {
+ compatible = "jedec,ufs-1.1";
+ reg = <0xfc598000 0x800>;
+ interrupts = <0 28 0>;
+ status = "disabled";
+ };
+};
+
+&gdsc_venus {
+ status = "ok";
+};
+
+&gdsc_venus_core0 {
+ status = "ok";
+};
+
+&gdsc_venus_core1 {
+ status = "ok";
+};
+
+&gdsc_vpu {
+ status = "ok";
+};
+
+&gdsc_mdss {
+ status = "ok";
+};
+
+&gdsc_jpeg {
+ status = "ok";
+};
+
+&gdsc_vfe {
+ status = "ok";
+};
+
+&gdsc_oxili_gx {
+ status = "ok";
+};
+
+&gdsc_oxili_cx {
+ status = "ok";
+};
+
+&gdsc_usb_hsic {
+ status = "ok";
+};
+
+&gdsc_pcie_0{
+ status = "ok";
+};
+
+&gdsc_pcie_1{
+ status = "ok";
+};
+
+&gdsc_usb30{
+ status = "ok";
+};
+
+&gdsc_usb30_sec{
+ status = "ok";
};
/include/ "msm-pma8084.dtsi"
diff --git a/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi
new file mode 100644
index 0000000..25c1851
--- /dev/null
+++ b/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi
@@ -0,0 +1,107 @@
+/* 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.
+ */
+
+&soc {
+ qcom,mdss_dsi_hx8394a_720p_video {
+ compatible = "qcom,mdss-dsi-panel";
+ label = "hx8394a 720p video mode dsi panel";
+ status = "disable";
+ qcom,dsi-ctrl-phandle = <&mdss_dsi0>;
+ qcom,rst-gpio = <&msmgpio 25 0>;
+ qcom,mdss-pan-res = <720 1280>;
+ qcom,mdss-pan-bpp = <24>;
+ qcom,mdss-pan-dest = "display_1";
+ qcom,mdss-pan-porch-values = <59 60 79 10 2 7>;
+ qcom,mdss-pan-underflow-clr = <0xff>;
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
+ qcom,mdss-pan-bl-levels = <1 4095>;
+ qcom,mdss-pan-dsi-mode = <0>;
+ qcom,mdss-pan-dsi-h-pulse-mode = <1>;
+ qcom,mdss-pan-dsi-h-power-stop = <0 0 0>;
+ qcom,mdss-pan-dsi-bllp-power-stop = <1 1>;
+ qcom,mdss-pan-dsi-traffic-mode = <2>;
+ qcom,mdss-pan-dsi-dst-format = <3>;
+ qcom,mdss-pan-dsi-vc = <0>;
+ qcom,mdss-pan-dsi-rgb-swap = <0>;
+ qcom,mdss-pan-dsi-data-lanes = <1 1 1 1>; /* 4 lanes */
+ qcom,mdss-pan-dsi-dlane-swap = <0>;
+ qcom,mdss-pan-dsi-t-clk = <0x2d 0x1f>;
+ qcom,mdss-pan-dsi-stream = <0>;
+ qcom,mdss-pan-dsi-mdp-tr = <0x0>;
+ qcom,mdss-pan-dsi-dma-tr = <0x04>;
+ qcom,mdss-pan-dsi-frame-rate = <60>;
+ qcom,panel-phy-regulatorSettings = [07 09 03 00 /* Regualotor settings */
+ 20 00 01];
+ qcom,panel-phy-timingSettings = [8d 24 19 00 34 34
+ 1d 26 2a 03 04 00];
+ qcom,panel-phy-strengthCtrl = [ff 06];
+ qcom,panel-phy-bistCtrl = [00 00 b1 ff /* BIST Ctrl settings */
+ 00 00];
+ qcom,panel-phy-laneConfig = [00 00 00 00 00 00 00 01 97 /* lane0 config */
+ 00 00 00 00 05 00 00 01 97 /* lane1 config */
+ 00 00 00 00 0a 00 00 01 97 /* lane2 config */
+ 00 00 00 00 0f 00 00 01 97 /* lane3 config */
+ 00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
+ qcom,panel-on-cmds = [39 01 00 00 00 00 04
+ b9 ff 83 94
+ 39 01 00 00 00 00 05
+ c7 00 10 00 10
+ 39 01 00 00 00 00 02
+ bc 07
+ 39 01 00 00 00 00 02
+ ba 13
+ 39 01 00 00 00 00 10
+ b1 01 00 07 83 01
+ 12 0f 32 38 29 29
+ 50 02 00 00
+ 39 01 00 00 00 00 07
+ b2 00 c8 09 05 00
+ 71
+ 39 01 00 00 00 00 02
+ cc 05
+ 05 01 00 00 00 00 02 00 00
+ 39 01 00 00 00 00 35
+ d5 00 00 00 00 0a
+ 00 01 00 00 00 33
+ 00 23 45 67 01 01
+ 23 88 88 88 88 88
+ 88 88 99 99 99 88
+ 88 99 88 54 32 10
+ 76 32 10 88 88 88
+ 88 88 88 88 99 99
+ 99 88 88 88 99
+ 39 01 00 00 00 00 17
+ b4 80 08 32 10 00
+ 32 15 08 32 12 20
+ 33 05 4c 05 37 05
+ 3f 1e 5f 5f 06
+ 39 01 00 00 00 00 02
+ b6 00
+ 39 01 00 00 00 00 23
+ e0 01 05 07 25 35
+ 3f 0b 32 04 09 0e
+ 10 13 10 14 16 1b
+ 01 05 07 25 35 3f
+ 0b 32 04 09 0e 10
+ 13 10 14 16 1b
+ 05 01 00 00 00 00 02 00 00
+ 39 01 00 00 00 00 04
+ bf 06 00 10
+ 05 01 00 00 c8 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
+
+ qcom,on-cmds-dsi-state = "DSI_LP_MODE";
+ qcom,panel-off-cmds = [05 01 00 00 0a 00 02 28 00
+ 05 01 00 00 96 00 02 10 00];
+ qcom,off-cmds-dsi-state = "DSI_HS_MODE";
+ };
+};
diff --git a/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi b/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
index 7942567..2a6bbf9 100644
--- a/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_dsi_nt35590_720p_cmd {
compatible = "qcom,mdss-dsi-panel";
label = "nt35590 720p command mode dsi panel";
@@ -58,473 +58,474 @@
00 00 00 00 0a 00 00 01 97 /* lane2 config */
00 00 00 00 0f 00 00 01 97 /* lane3 config */
00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
- qcom,panel-on-cmds = [29 01 00 00 00 02 FF EE
- 29 01 00 00 00 02 26 08
- 29 01 00 00 00 02 26 00
- 29 01 00 00 10 02 FF 00
- 29 01 00 00 00 02 BA 03
- 29 01 00 00 00 02 C2 08
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 00 4A
- 29 01 00 00 00 02 01 33
- 29 01 00 00 00 02 02 53
- 29 01 00 00 00 02 03 55
- 29 01 00 00 00 02 04 55
- 29 01 00 00 00 02 05 33
- 29 01 00 00 00 02 06 22
- 29 01 00 00 00 02 08 56
- 29 01 00 00 00 02 09 8F
- 29 01 00 00 00 02 36 73
- 29 01 00 00 00 02 0B 9F
- 29 01 00 00 00 02 0C 9F
- 29 01 00 00 00 02 0D 2F
- 29 01 00 00 00 02 0E 24
- 29 01 00 00 00 02 11 83
- 29 01 00 00 00 02 12 03
- 29 01 00 00 00 02 71 2C
- 29 01 00 00 00 02 6F 03
- 29 01 00 00 00 02 0F 0A
- 29 01 00 00 00 02 FF 05
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 01 00
- 29 01 00 00 00 02 02 8B
- 29 01 00 00 00 02 03 82
- 29 01 00 00 00 02 04 82
- 29 01 00 00 00 02 05 30
- 29 01 00 00 00 02 06 33
- 29 01 00 00 00 02 07 01
- 29 01 00 00 00 02 08 00
- 29 01 00 00 00 02 09 46
- 29 01 00 00 00 02 0A 46
- 29 01 00 00 00 02 0D 0B
- 29 01 00 00 00 02 0E 1D
- 29 01 00 00 00 02 0F 08
- 29 01 00 00 00 02 10 53
- 29 01 00 00 00 02 11 00
- 29 01 00 00 00 02 12 00
- 29 01 00 00 00 02 14 01
- 29 01 00 00 00 02 15 00
- 29 01 00 00 00 02 16 05
- 29 01 00 00 00 02 17 00
- 29 01 00 00 00 02 19 7F
- 29 01 00 00 00 02 1A FF
- 29 01 00 00 00 02 1B 0F
- 29 01 00 00 00 02 1C 00
- 29 01 00 00 00 02 1D 00
- 29 01 00 00 00 02 1E 00
- 29 01 00 00 00 02 1F 07
- 29 01 00 00 00 02 20 00
- 29 01 00 00 00 02 21 06
- 29 01 00 00 00 02 22 55
- 29 01 00 00 00 02 23 4D
- 29 01 00 00 00 02 2D 02
- 29 01 00 00 00 02 28 01
- 29 01 00 00 00 02 2F 02
- 29 01 00 00 00 02 83 01
- 29 01 00 00 00 02 9E 58
- 29 01 00 00 00 02 9F 6A
- 29 01 00 00 00 02 A0 01
- 29 01 00 00 00 02 A2 10
- 29 01 00 00 00 02 BB 0A
- 29 01 00 00 00 02 BC 0A
- 29 01 00 00 00 02 32 08
- 29 01 00 00 00 02 33 B8
- 29 01 00 00 00 02 36 01
- 29 01 00 00 00 02 37 00
- 29 01 00 00 00 02 43 00
- 29 01 00 00 00 02 4B 21
- 29 01 00 00 00 02 4C 03
- 29 01 00 00 00 02 50 21
- 29 01 00 00 00 02 51 03
- 29 01 00 00 00 02 58 21
- 29 01 00 00 00 02 59 03
- 29 01 00 00 00 02 5D 21
- 29 01 00 00 00 02 5E 03
- 29 01 00 00 00 02 6C 00
- 29 01 00 00 00 02 6D 00
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 75 00
- 29 01 00 00 00 02 76 7D
- 29 01 00 00 00 02 77 00
- 29 01 00 00 00 02 78 8A
- 29 01 00 00 00 02 79 00
- 29 01 00 00 00 02 7A 9C
- 29 01 00 00 00 02 7B 00
- 29 01 00 00 00 02 7C B1
- 29 01 00 00 00 02 7D 00
- 29 01 00 00 00 02 7E BF
- 29 01 00 00 00 02 7F 00
- 29 01 00 00 00 02 80 CF
- 29 01 00 00 00 02 81 00
- 29 01 00 00 00 02 82 DD
- 29 01 00 00 00 02 83 00
- 29 01 00 00 00 02 84 E8
- 29 01 00 00 00 02 85 00
- 29 01 00 00 00 02 86 F2
- 29 01 00 00 00 02 87 01
- 29 01 00 00 00 02 88 1F
- 29 01 00 00 00 02 89 01
- 29 01 00 00 00 02 8A 41
- 29 01 00 00 00 02 8B 01
- 29 01 00 00 00 02 8C 78
- 29 01 00 00 00 02 8D 01
- 29 01 00 00 00 02 8E A5
- 29 01 00 00 00 02 8F 01
- 29 01 00 00 00 02 90 EE
- 29 01 00 00 00 02 91 02
- 29 01 00 00 00 02 92 29
- 29 01 00 00 00 02 93 02
- 29 01 00 00 00 02 94 2A
- 29 01 00 00 00 02 95 02
- 29 01 00 00 00 02 96 5D
- 29 01 00 00 00 02 97 02
- 29 01 00 00 00 02 98 93
- 29 01 00 00 00 02 99 02
- 29 01 00 00 00 02 9A B8
- 29 01 00 00 00 02 9B 02
- 29 01 00 00 00 02 9C E7
- 29 01 00 00 00 02 9D 03
- 29 01 00 00 00 02 9E 07
- 29 01 00 00 00 02 9F 03
- 29 01 00 00 00 02 A0 37
- 29 01 00 00 00 02 A2 03
- 29 01 00 00 00 02 A3 46
- 29 01 00 00 00 02 A4 03
- 29 01 00 00 00 02 A5 56
- 29 01 00 00 00 02 A6 03
- 29 01 00 00 00 02 A7 66
- 29 01 00 00 00 02 A9 03
- 29 01 00 00 00 02 AA 7A
- 29 01 00 00 00 02 AB 03
- 29 01 00 00 00 02 AC 93
- 29 01 00 00 00 02 AD 03
- 29 01 00 00 00 02 AE A3
- 29 01 00 00 00 02 AF 03
- 29 01 00 00 00 02 B0 B4
- 29 01 00 00 00 02 B1 03
- 29 01 00 00 00 02 B2 CB
- 29 01 00 00 00 02 B3 00
- 29 01 00 00 00 02 B4 7D
- 29 01 00 00 00 02 B5 00
- 29 01 00 00 00 02 B6 8A
- 29 01 00 00 00 02 B7 00
- 29 01 00 00 00 02 B8 9C
- 29 01 00 00 00 02 B9 00
- 29 01 00 00 00 02 BA B1
- 29 01 00 00 00 02 BB 00
- 29 01 00 00 00 02 BC BF
- 29 01 00 00 00 02 BD 00
- 29 01 00 00 00 02 BE CF
- 29 01 00 00 00 02 BF 00
- 29 01 00 00 00 02 C0 DD
- 29 01 00 00 00 02 C1 00
- 29 01 00 00 00 02 C2 E8
- 29 01 00 00 00 02 C3 00
- 29 01 00 00 00 02 C4 F2
- 29 01 00 00 00 02 C5 01
- 29 01 00 00 00 02 C6 1F
- 29 01 00 00 00 02 C7 01
- 29 01 00 00 00 02 C8 41
- 29 01 00 00 00 02 C9 01
- 29 01 00 00 00 02 CA 78
- 29 01 00 00 00 02 CB 01
- 29 01 00 00 00 02 CC A5
- 29 01 00 00 00 02 CD 01
- 29 01 00 00 00 02 CE EE
- 29 01 00 00 00 02 CF 02
- 29 01 00 00 00 02 D0 29
- 29 01 00 00 00 02 D1 02
- 29 01 00 00 00 02 D2 2A
- 29 01 00 00 00 02 D3 02
- 29 01 00 00 00 02 D4 5D
- 29 01 00 00 00 02 D5 02
- 29 01 00 00 00 02 D6 93
- 29 01 00 00 00 02 D7 02
- 29 01 00 00 00 02 D8 B8
- 29 01 00 00 00 02 D9 02
- 29 01 00 00 00 02 DA E7
- 29 01 00 00 00 02 DB 03
- 29 01 00 00 00 02 DC 07
- 29 01 00 00 00 02 DD 03
- 29 01 00 00 00 02 DE 37
- 29 01 00 00 00 02 DF 03
- 29 01 00 00 00 02 E0 46
- 29 01 00 00 00 02 E1 03
- 29 01 00 00 00 02 E2 56
- 29 01 00 00 00 02 E3 03
- 29 01 00 00 00 02 E4 66
- 29 01 00 00 00 02 E5 03
- 29 01 00 00 00 02 E6 7A
- 29 01 00 00 00 02 E7 03
- 29 01 00 00 00 02 E8 93
- 29 01 00 00 00 02 E9 03
- 29 01 00 00 00 02 EA A3
- 29 01 00 00 00 02 EB 03
- 29 01 00 00 00 02 EC B4
- 29 01 00 00 00 02 ED 03
- 29 01 00 00 00 02 EE CB
- 29 01 00 00 00 02 EF 00
- 29 01 00 00 00 02 F0 ED
- 29 01 00 00 00 02 F1 00
- 29 01 00 00 00 02 F2 F3
- 29 01 00 00 00 02 F3 00
- 29 01 00 00 00 02 F4 FE
- 29 01 00 00 00 02 F5 01
- 29 01 00 00 00 02 F6 09
- 29 01 00 00 00 02 F7 01
- 29 01 00 00 00 02 F8 13
- 29 01 00 00 00 02 F9 01
- 29 01 00 00 00 02 FA 1D
- 29 01 00 00 00 02 FF 02
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 00 01
- 29 01 00 00 00 02 01 26
- 29 01 00 00 00 02 02 01
- 29 01 00 00 00 02 03 2F
- 29 01 00 00 00 02 04 01
- 29 01 00 00 00 02 05 37
- 29 01 00 00 00 02 06 01
- 29 01 00 00 00 02 07 56
- 29 01 00 00 00 02 08 01
- 29 01 00 00 00 02 09 70
- 29 01 00 00 00 02 0A 01
- 29 01 00 00 00 02 0B 9D
- 29 01 00 00 00 02 0C 01
- 29 01 00 00 00 02 0D C2
- 29 01 00 00 00 02 0E 01
- 29 01 00 00 00 02 0F FF
- 29 01 00 00 00 02 10 02
- 29 01 00 00 00 02 11 31
- 29 01 00 00 00 02 12 02
- 29 01 00 00 00 02 13 32
- 29 01 00 00 00 02 14 02
- 29 01 00 00 00 02 15 60
- 29 01 00 00 00 02 16 02
- 29 01 00 00 00 02 17 94
- 29 01 00 00 00 02 18 02
- 29 01 00 00 00 02 19 B5
- 29 01 00 00 00 02 1A 02
- 29 01 00 00 00 02 1B E3
- 29 01 00 00 00 02 1C 03
- 29 01 00 00 00 02 1D 03
- 29 01 00 00 00 02 1E 03
- 29 01 00 00 00 02 1F 2D
- 29 01 00 00 00 02 20 03
- 29 01 00 00 00 02 21 3A
- 29 01 00 00 00 02 22 03
- 29 01 00 00 00 02 23 48
- 29 01 00 00 00 02 24 03
- 29 01 00 00 00 02 25 57
- 29 01 00 00 00 02 26 03
- 29 01 00 00 00 02 27 68
- 29 01 00 00 00 02 28 03
- 29 01 00 00 00 02 29 7B
- 29 01 00 00 00 02 2A 03
- 29 01 00 00 00 02 2B 90
- 29 01 00 00 00 02 2D 03
- 29 01 00 00 00 02 2F A0
- 29 01 00 00 00 02 30 03
- 29 01 00 00 00 02 31 CB
- 29 01 00 00 00 02 32 00
- 29 01 00 00 00 02 33 ED
- 29 01 00 00 00 02 34 00
- 29 01 00 00 00 02 35 F3
- 29 01 00 00 00 02 36 00
- 29 01 00 00 00 02 37 FE
- 29 01 00 00 00 02 38 01
- 29 01 00 00 00 02 39 09
- 29 01 00 00 00 02 3A 01
- 29 01 00 00 00 02 3B 13
- 29 01 00 00 00 02 3D 01
- 29 01 00 00 00 02 3F 1D
- 29 01 00 00 00 02 40 01
- 29 01 00 00 00 02 41 26
- 29 01 00 00 00 02 42 01
- 29 01 00 00 00 02 43 2F
- 29 01 00 00 00 02 44 01
- 29 01 00 00 00 02 45 37
- 29 01 00 00 00 02 46 01
- 29 01 00 00 00 02 47 56
- 29 01 00 00 00 02 48 01
- 29 01 00 00 00 02 49 70
- 29 01 00 00 00 02 4A 01
- 29 01 00 00 00 02 4B 9D
- 29 01 00 00 00 02 4C 01
- 29 01 00 00 00 02 4D C2
- 29 01 00 00 00 02 4E 01
- 29 01 00 00 00 02 4F FF
- 29 01 00 00 00 02 50 02
- 29 01 00 00 00 02 51 31
- 29 01 00 00 00 02 52 02
- 29 01 00 00 00 02 53 32
- 29 01 00 00 00 02 54 02
- 29 01 00 00 00 02 55 60
- 29 01 00 00 00 02 56 02
- 29 01 00 00 00 02 58 94
- 29 01 00 00 00 02 59 02
- 29 01 00 00 00 02 5A B5
- 29 01 00 00 00 02 5B 02
- 29 01 00 00 00 02 5C E3
- 29 01 00 00 00 02 5D 03
- 29 01 00 00 00 02 5E 03
- 29 01 00 00 00 02 5F 03
- 29 01 00 00 00 02 60 2D
- 29 01 00 00 00 02 61 03
- 29 01 00 00 00 02 62 3A
- 29 01 00 00 00 02 63 03
- 29 01 00 00 00 02 64 48
- 29 01 00 00 00 02 65 03
- 29 01 00 00 00 02 66 57
- 29 01 00 00 00 02 67 03
- 29 01 00 00 00 02 68 68
- 29 01 00 00 00 02 69 03
- 29 01 00 00 00 02 6A 7B
- 29 01 00 00 00 02 6B 03
- 29 01 00 00 00 02 6C 90
- 29 01 00 00 00 02 6D 03
- 29 01 00 00 00 02 6E A0
- 29 01 00 00 00 02 6F 03
- 29 01 00 00 00 02 70 CB
- 29 01 00 00 00 02 71 00
- 29 01 00 00 00 02 72 19
- 29 01 00 00 00 02 73 00
- 29 01 00 00 00 02 74 36
- 29 01 00 00 00 02 75 00
- 29 01 00 00 00 02 76 55
- 29 01 00 00 00 02 77 00
- 29 01 00 00 00 02 78 70
- 29 01 00 00 00 02 79 00
- 29 01 00 00 00 02 7A 83
- 29 01 00 00 00 02 7B 00
- 29 01 00 00 00 02 7C 99
- 29 01 00 00 00 02 7D 00
- 29 01 00 00 00 02 7E A8
- 29 01 00 00 00 02 7F 00
- 29 01 00 00 00 02 80 B7
- 29 01 00 00 00 02 81 00
- 29 01 00 00 00 02 82 C5
- 29 01 00 00 00 02 83 00
- 29 01 00 00 00 02 84 F7
- 29 01 00 00 00 02 85 01
- 29 01 00 00 00 02 86 1E
- 29 01 00 00 00 02 87 01
- 29 01 00 00 00 02 88 60
- 29 01 00 00 00 02 89 01
- 29 01 00 00 00 02 8A 95
- 29 01 00 00 00 02 8B 01
- 29 01 00 00 00 02 8C E1
- 29 01 00 00 00 02 8D 02
- 29 01 00 00 00 02 8E 20
- 29 01 00 00 00 02 8F 02
- 29 01 00 00 00 02 90 23
- 29 01 00 00 00 02 91 02
- 29 01 00 00 00 02 92 59
- 29 01 00 00 00 02 93 02
- 29 01 00 00 00 02 94 94
- 29 01 00 00 00 02 95 02
- 29 01 00 00 00 02 96 B4
- 29 01 00 00 00 02 97 02
- 29 01 00 00 00 02 98 E1
- 29 01 00 00 00 02 99 03
- 29 01 00 00 00 02 9A 01
- 29 01 00 00 00 02 9B 03
- 29 01 00 00 00 02 9C 28
- 29 01 00 00 00 02 9D 03
- 29 01 00 00 00 02 9E 30
- 29 01 00 00 00 02 9F 03
- 29 01 00 00 00 02 A0 37
- 29 01 00 00 00 02 A2 03
- 29 01 00 00 00 02 A3 3B
- 29 01 00 00 00 02 A4 03
- 29 01 00 00 00 02 A5 40
- 29 01 00 00 00 02 A6 03
- 29 01 00 00 00 02 A7 50
- 29 01 00 00 00 02 A9 03
- 29 01 00 00 00 02 AA 6D
- 29 01 00 00 00 02 AB 03
- 29 01 00 00 00 02 AC 80
- 29 01 00 00 00 02 AD 03
- 29 01 00 00 00 02 AE CB
- 29 01 00 00 00 02 AF 00
- 29 01 00 00 00 02 B0 19
- 29 01 00 00 00 02 B1 00
- 29 01 00 00 00 02 B2 36
- 29 01 00 00 00 02 B3 00
- 29 01 00 00 00 02 B4 55
- 29 01 00 00 00 02 B5 00
- 29 01 00 00 00 02 B6 70
- 29 01 00 00 00 02 B7 00
- 29 01 00 00 00 02 B8 83
- 29 01 00 00 00 02 B9 00
- 29 01 00 00 00 02 BA 99
- 29 01 00 00 00 02 BB 00
- 29 01 00 00 00 02 BC A8
- 29 01 00 00 00 02 BD 00
- 29 01 00 00 00 02 BE B7
- 29 01 00 00 00 02 BF 00
- 29 01 00 00 00 02 C0 C5
- 29 01 00 00 00 02 C1 00
- 29 01 00 00 00 02 C2 F7
- 29 01 00 00 00 02 C3 01
- 29 01 00 00 00 02 C4 1E
- 29 01 00 00 00 02 C5 01
- 29 01 00 00 00 02 C6 60
- 29 01 00 00 00 02 C7 01
- 29 01 00 00 00 02 C8 95
- 29 01 00 00 00 02 C9 01
- 29 01 00 00 00 02 CA E1
- 29 01 00 00 00 02 CB 02
- 29 01 00 00 00 02 CC 20
- 29 01 00 00 00 02 CD 02
- 29 01 00 00 00 02 CE 23
- 29 01 00 00 00 02 CF 02
- 29 01 00 00 00 02 D0 59
- 29 01 00 00 00 02 D1 02
- 29 01 00 00 00 02 D2 94
- 29 01 00 00 00 02 D3 02
- 29 01 00 00 00 02 D4 B4
- 29 01 00 00 00 02 D5 02
- 29 01 00 00 00 02 D6 E1
- 29 01 00 00 00 02 D7 03
- 29 01 00 00 00 02 D8 01
- 29 01 00 00 00 02 D9 03
- 29 01 00 00 00 02 DA 28
- 29 01 00 00 00 02 DB 03
- 29 01 00 00 00 02 DC 30
- 29 01 00 00 00 02 DD 03
- 29 01 00 00 00 02 DE 37
- 29 01 00 00 00 02 DF 03
- 29 01 00 00 00 02 E0 3B
- 29 01 00 00 00 02 E1 03
- 29 01 00 00 00 02 E2 40
- 29 01 00 00 00 02 E3 03
- 29 01 00 00 00 02 E4 50
- 29 01 00 00 00 02 E5 03
- 29 01 00 00 00 02 E6 6D
- 29 01 00 00 00 02 E7 03
- 29 01 00 00 00 02 E8 80
- 29 01 00 00 00 02 E9 03
- 29 01 00 00 00 02 EA CB
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 02
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 04
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 00
- 29 01 00 00 64 02 11 00
- 29 01 00 00 00 02 FF EE
- 29 01 00 00 00 02 12 50
- 29 01 00 00 00 02 13 02
- 29 01 00 00 00 02 6A 60
- 29 01 00 00 00 02 FF 00
- 29 01 00 00 78 02 29 00];
+ qcom,panel-on-cmds = [29 01 00 00 00 00 02 FF EE
+ 29 01 00 00 00 00 02 26 08
+ 29 01 00 00 00 00 02 26 00
+ 29 01 00 00 10 00 02 FF 00
+ 29 01 00 00 00 00 02 BA 03
+ 29 01 00 00 00 00 02 C2 08
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 00 4A
+ 29 01 00 00 00 00 02 01 33
+ 29 01 00 00 00 00 02 02 53
+ 29 01 00 00 00 00 02 03 55
+ 29 01 00 00 00 00 02 04 55
+ 29 01 00 00 00 00 02 05 33
+ 29 01 00 00 00 00 02 06 22
+ 29 01 00 00 00 00 02 08 56
+ 29 01 00 00 00 00 02 09 8F
+ 29 01 00 00 00 00 02 36 73
+ 29 01 00 00 00 00 02 0B 9F
+ 29 01 00 00 00 00 02 0C 9F
+ 29 01 00 00 00 00 02 0D 2F
+ 29 01 00 00 00 00 02 0E 24
+ 29 01 00 00 00 00 02 11 83
+ 29 01 00 00 00 00 02 12 03
+ 29 01 00 00 00 00 02 71 2C
+ 29 01 00 00 00 00 02 6F 03
+ 29 01 00 00 00 00 02 0F 0A
+ 29 01 00 00 00 00 02 FF 05
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 01 00
+ 29 01 00 00 00 00 02 02 8B
+ 29 01 00 00 00 00 02 03 82
+ 29 01 00 00 00 00 02 04 82
+ 29 01 00 00 00 00 02 05 30
+ 29 01 00 00 00 00 02 06 33
+ 29 01 00 00 00 00 02 07 01
+ 29 01 00 00 00 00 02 08 00
+ 29 01 00 00 00 00 02 09 46
+ 29 01 00 00 00 00 02 0A 46
+ 29 01 00 00 00 00 02 0D 0B
+ 29 01 00 00 00 00 02 0E 1D
+ 29 01 00 00 00 00 02 0F 08
+ 29 01 00 00 00 00 02 10 53
+ 29 01 00 00 00 00 02 11 00
+ 29 01 00 00 00 00 02 12 00
+ 29 01 00 00 00 00 02 14 01
+ 29 01 00 00 00 00 02 15 00
+ 29 01 00 00 00 00 02 16 05
+ 29 01 00 00 00 00 02 17 00
+ 29 01 00 00 00 00 02 19 7F
+ 29 01 00 00 00 00 02 1A FF
+ 29 01 00 00 00 00 02 1B 0F
+ 29 01 00 00 00 00 02 1C 00
+ 29 01 00 00 00 00 02 1D 00
+ 29 01 00 00 00 00 02 1E 00
+ 29 01 00 00 00 00 02 1F 07
+ 29 01 00 00 00 00 02 20 00
+ 29 01 00 00 00 00 02 21 06
+ 29 01 00 00 00 00 02 22 55
+ 29 01 00 00 00 00 02 23 4D
+ 29 01 00 00 00 00 02 2D 02
+ 29 01 00 00 00 00 02 28 01
+ 29 01 00 00 00 00 02 2F 02
+ 29 01 00 00 00 00 02 83 01
+ 29 01 00 00 00 00 02 9E 58
+ 29 01 00 00 00 00 02 9F 6A
+ 29 01 00 00 00 00 02 A0 01
+ 29 01 00 00 00 00 02 A2 10
+ 29 01 00 00 00 00 02 BB 0A
+ 29 01 00 00 00 00 02 BC 0A
+ 29 01 00 00 00 00 02 32 08
+ 29 01 00 00 00 00 02 33 B8
+ 29 01 00 00 00 00 02 36 01
+ 29 01 00 00 00 00 02 37 00
+ 29 01 00 00 00 00 02 43 00
+ 29 01 00 00 00 00 02 4B 21
+ 29 01 00 00 00 00 02 4C 03
+ 29 01 00 00 00 00 02 50 21
+ 29 01 00 00 00 00 02 51 03
+ 29 01 00 00 00 00 02 58 21
+ 29 01 00 00 00 00 02 59 03
+ 29 01 00 00 00 00 02 5D 21
+ 29 01 00 00 00 00 02 5E 03
+ 29 01 00 00 00 00 02 6C 00
+ 29 01 00 00 00 00 02 6D 00
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 75 00
+ 29 01 00 00 00 00 02 76 7D
+ 29 01 00 00 00 00 02 77 00
+ 29 01 00 00 00 00 02 78 8A
+ 29 01 00 00 00 00 02 79 00
+ 29 01 00 00 00 00 02 7A 9C
+ 29 01 00 00 00 00 02 7B 00
+ 29 01 00 00 00 00 02 7C B1
+ 29 01 00 00 00 00 02 7D 00
+ 29 01 00 00 00 00 02 7E BF
+ 29 01 00 00 00 00 02 7F 00
+ 29 01 00 00 00 00 02 80 CF
+ 29 01 00 00 00 00 02 81 00
+ 29 01 00 00 00 00 02 82 DD
+ 29 01 00 00 00 00 02 83 00
+ 29 01 00 00 00 00 02 84 E8
+ 29 01 00 00 00 00 02 85 00
+ 29 01 00 00 00 00 02 86 F2
+ 29 01 00 00 00 00 02 87 01
+ 29 01 00 00 00 00 02 88 1F
+ 29 01 00 00 00 00 02 89 01
+ 29 01 00 00 00 00 02 8A 41
+ 29 01 00 00 00 00 02 8B 01
+ 29 01 00 00 00 00 02 8C 78
+ 29 01 00 00 00 00 02 8D 01
+ 29 01 00 00 00 00 02 8E A5
+ 29 01 00 00 00 00 02 8F 01
+ 29 01 00 00 00 00 02 90 EE
+ 29 01 00 00 00 00 02 91 02
+ 29 01 00 00 00 00 02 92 29
+ 29 01 00 00 00 00 02 93 02
+ 29 01 00 00 00 00 02 94 2A
+ 29 01 00 00 00 00 02 95 02
+ 29 01 00 00 00 00 02 96 5D
+ 29 01 00 00 00 00 02 97 02
+ 29 01 00 00 00 00 02 98 93
+ 29 01 00 00 00 00 02 99 02
+ 29 01 00 00 00 00 02 9A B8
+ 29 01 00 00 00 00 02 9B 02
+ 29 01 00 00 00 00 02 9C E7
+ 29 01 00 00 00 00 02 9D 03
+ 29 01 00 00 00 00 02 9E 07
+ 29 01 00 00 00 00 02 9F 03
+ 29 01 00 00 00 00 02 A0 37
+ 29 01 00 00 00 00 02 A2 03
+ 29 01 00 00 00 00 02 A3 46
+ 29 01 00 00 00 00 02 A4 03
+ 29 01 00 00 00 00 02 A5 56
+ 29 01 00 00 00 00 02 A6 03
+ 29 01 00 00 00 00 02 A7 66
+ 29 01 00 00 00 00 02 A9 03
+ 29 01 00 00 00 00 02 AA 7A
+ 29 01 00 00 00 00 02 AB 03
+ 29 01 00 00 00 00 02 AC 93
+ 29 01 00 00 00 00 02 AD 03
+ 29 01 00 00 00 00 02 AE A3
+ 29 01 00 00 00 00 02 AF 03
+ 29 01 00 00 00 00 02 B0 B4
+ 29 01 00 00 00 00 02 B1 03
+ 29 01 00 00 00 00 02 B2 CB
+ 29 01 00 00 00 00 02 B3 00
+ 29 01 00 00 00 00 02 B4 7D
+ 29 01 00 00 00 00 02 B5 00
+ 29 01 00 00 00 00 02 B6 8A
+ 29 01 00 00 00 00 02 B7 00
+ 29 01 00 00 00 00 02 B8 9C
+ 29 01 00 00 00 00 02 B9 00
+ 29 01 00 00 00 00 02 BA B1
+ 29 01 00 00 00 00 02 BB 00
+ 29 01 00 00 00 00 02 BC BF
+ 29 01 00 00 00 00 02 BD 00
+ 29 01 00 00 00 00 02 BE CF
+ 29 01 00 00 00 00 02 BF 00
+ 29 01 00 00 00 00 02 C0 DD
+ 29 01 00 00 00 00 02 C1 00
+ 29 01 00 00 00 00 02 C2 E8
+ 29 01 00 00 00 00 02 C3 00
+ 29 01 00 00 00 00 02 C4 F2
+ 29 01 00 00 00 00 02 C5 01
+ 29 01 00 00 00 00 02 C6 1F
+ 29 01 00 00 00 00 02 C7 01
+ 29 01 00 00 00 00 02 C8 41
+ 29 01 00 00 00 00 02 C9 01
+ 29 01 00 00 00 00 02 CA 78
+ 29 01 00 00 00 00 02 CB 01
+ 29 01 00 00 00 00 02 CC A5
+ 29 01 00 00 00 00 02 CD 01
+ 29 01 00 00 00 00 02 CE EE
+ 29 01 00 00 00 00 02 CF 02
+ 29 01 00 00 00 00 02 D0 29
+ 29 01 00 00 00 00 02 D1 02
+ 29 01 00 00 00 00 02 D2 2A
+ 29 01 00 00 00 00 02 D3 02
+ 29 01 00 00 00 00 02 D4 5D
+ 29 01 00 00 00 00 02 D5 02
+ 29 01 00 00 00 00 02 D6 93
+ 29 01 00 00 00 00 02 D7 02
+ 29 01 00 00 00 00 02 D8 B8
+ 29 01 00 00 00 00 02 D9 02
+ 29 01 00 00 00 00 02 DA E7
+ 29 01 00 00 00 00 02 DB 03
+ 29 01 00 00 00 00 02 DC 07
+ 29 01 00 00 00 00 02 DD 03
+ 29 01 00 00 00 00 02 DE 37
+ 29 01 00 00 00 00 02 DF 03
+ 29 01 00 00 00 00 02 E0 46
+ 29 01 00 00 00 00 02 E1 03
+ 29 01 00 00 00 00 02 E2 56
+ 29 01 00 00 00 00 02 E3 03
+ 29 01 00 00 00 00 02 E4 66
+ 29 01 00 00 00 00 02 E5 03
+ 29 01 00 00 00 00 02 E6 7A
+ 29 01 00 00 00 00 02 E7 03
+ 29 01 00 00 00 00 02 E8 93
+ 29 01 00 00 00 00 02 E9 03
+ 29 01 00 00 00 00 02 EA A3
+ 29 01 00 00 00 00 02 EB 03
+ 29 01 00 00 00 00 02 EC B4
+ 29 01 00 00 00 00 02 ED 03
+ 29 01 00 00 00 00 02 EE CB
+ 29 01 00 00 00 00 02 EF 00
+ 29 01 00 00 00 00 02 F0 ED
+ 29 01 00 00 00 00 02 F1 00
+ 29 01 00 00 00 00 02 F2 F3
+ 29 01 00 00 00 00 02 F3 00
+ 29 01 00 00 00 00 02 F4 FE
+ 29 01 00 00 00 00 02 F5 01
+ 29 01 00 00 00 00 02 F6 09
+ 29 01 00 00 00 00 02 F7 01
+ 29 01 00 00 00 00 02 F8 13
+ 29 01 00 00 00 00 02 F9 01
+ 29 01 00 00 00 00 02 FA 1D
+ 29 01 00 00 00 00 02 FF 02
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 00 01
+ 29 01 00 00 00 00 02 01 26
+ 29 01 00 00 00 00 02 02 01
+ 29 01 00 00 00 00 02 03 2F
+ 29 01 00 00 00 00 02 04 01
+ 29 01 00 00 00 00 02 05 37
+ 29 01 00 00 00 00 02 06 01
+ 29 01 00 00 00 00 02 07 56
+ 29 01 00 00 00 00 02 08 01
+ 29 01 00 00 00 00 02 09 70
+ 29 01 00 00 00 00 02 0A 01
+ 29 01 00 00 00 00 02 0B 9D
+ 29 01 00 00 00 00 02 0C 01
+ 29 01 00 00 00 00 02 0D C2
+ 29 01 00 00 00 00 02 0E 01
+ 29 01 00 00 00 00 02 0F FF
+ 29 01 00 00 00 00 02 10 02
+ 29 01 00 00 00 00 02 11 31
+ 29 01 00 00 00 00 02 12 02
+ 29 01 00 00 00 00 02 13 32
+ 29 01 00 00 00 00 02 14 02
+ 29 01 00 00 00 00 02 15 60
+ 29 01 00 00 00 00 02 16 02
+ 29 01 00 00 00 00 02 17 94
+ 29 01 00 00 00 00 02 18 02
+ 29 01 00 00 00 00 02 19 B5
+ 29 01 00 00 00 00 02 1A 02
+ 29 01 00 00 00 00 02 1B E3
+ 29 01 00 00 00 00 02 1C 03
+ 29 01 00 00 00 00 02 1D 03
+ 29 01 00 00 00 00 02 1E 03
+ 29 01 00 00 00 00 02 1F 2D
+ 29 01 00 00 00 00 02 20 03
+ 29 01 00 00 00 00 02 21 3A
+ 29 01 00 00 00 00 02 22 03
+ 29 01 00 00 00 00 02 23 48
+ 29 01 00 00 00 00 02 24 03
+ 29 01 00 00 00 00 02 25 57
+ 29 01 00 00 00 00 02 26 03
+ 29 01 00 00 00 00 02 27 68
+ 29 01 00 00 00 00 02 28 03
+ 29 01 00 00 00 00 02 29 7B
+ 29 01 00 00 00 00 02 2A 03
+ 29 01 00 00 00 00 02 2B 90
+ 29 01 00 00 00 00 02 2D 03
+ 29 01 00 00 00 00 02 2F A0
+ 29 01 00 00 00 00 02 30 03
+ 29 01 00 00 00 00 02 31 CB
+ 29 01 00 00 00 00 02 32 00
+ 29 01 00 00 00 00 02 33 ED
+ 29 01 00 00 00 00 02 34 00
+ 29 01 00 00 00 00 02 35 F3
+ 29 01 00 00 00 00 02 36 00
+ 29 01 00 00 00 00 02 37 FE
+ 29 01 00 00 00 00 02 38 01
+ 29 01 00 00 00 00 02 39 09
+ 29 01 00 00 00 00 02 3A 01
+ 29 01 00 00 00 00 02 3B 13
+ 29 01 00 00 00 00 02 3D 01
+ 29 01 00 00 00 00 02 3F 1D
+ 29 01 00 00 00 00 02 40 01
+ 29 01 00 00 00 00 02 41 26
+ 29 01 00 00 00 00 02 42 01
+ 29 01 00 00 00 00 02 43 2F
+ 29 01 00 00 00 00 02 44 01
+ 29 01 00 00 00 00 02 45 37
+ 29 01 00 00 00 00 02 46 01
+ 29 01 00 00 00 00 02 47 56
+ 29 01 00 00 00 00 02 48 01
+ 29 01 00 00 00 00 02 49 70
+ 29 01 00 00 00 00 02 4A 01
+ 29 01 00 00 00 00 02 4B 9D
+ 29 01 00 00 00 00 02 4C 01
+ 29 01 00 00 00 00 02 4D C2
+ 29 01 00 00 00 00 02 4E 01
+ 29 01 00 00 00 00 02 4F FF
+ 29 01 00 00 00 00 02 50 02
+ 29 01 00 00 00 00 02 51 31
+ 29 01 00 00 00 00 02 52 02
+ 29 01 00 00 00 00 02 53 32
+ 29 01 00 00 00 00 02 54 02
+ 29 01 00 00 00 00 02 55 60
+ 29 01 00 00 00 00 02 56 02
+ 29 01 00 00 00 00 02 58 94
+ 29 01 00 00 00 00 02 59 02
+ 29 01 00 00 00 00 02 5A B5
+ 29 01 00 00 00 00 02 5B 02
+ 29 01 00 00 00 00 02 5C E3
+ 29 01 00 00 00 00 02 5D 03
+ 29 01 00 00 00 00 02 5E 03
+ 29 01 00 00 00 00 02 5F 03
+ 29 01 00 00 00 00 02 60 2D
+ 29 01 00 00 00 00 02 61 03
+ 29 01 00 00 00 00 02 62 3A
+ 29 01 00 00 00 00 02 63 03
+ 29 01 00 00 00 00 02 64 48
+ 29 01 00 00 00 00 02 65 03
+ 29 01 00 00 00 00 02 66 57
+ 29 01 00 00 00 00 02 67 03
+ 29 01 00 00 00 00 02 68 68
+ 29 01 00 00 00 00 02 69 03
+ 29 01 00 00 00 00 02 6A 7B
+ 29 01 00 00 00 00 02 6B 03
+ 29 01 00 00 00 00 02 6C 90
+ 29 01 00 00 00 00 02 6D 03
+ 29 01 00 00 00 00 02 6E A0
+ 29 01 00 00 00 00 02 6F 03
+ 29 01 00 00 00 00 02 70 CB
+ 29 01 00 00 00 00 02 71 00
+ 29 01 00 00 00 00 02 72 19
+ 29 01 00 00 00 00 02 73 00
+ 29 01 00 00 00 00 02 74 36
+ 29 01 00 00 00 00 02 75 00
+ 29 01 00 00 00 00 02 76 55
+ 29 01 00 00 00 00 02 77 00
+ 29 01 00 00 00 00 02 78 70
+ 29 01 00 00 00 00 02 79 00
+ 29 01 00 00 00 00 02 7A 83
+ 29 01 00 00 00 00 02 7B 00
+ 29 01 00 00 00 00 02 7C 99
+ 29 01 00 00 00 00 02 7D 00
+ 29 01 00 00 00 00 02 7E A8
+ 29 01 00 00 00 00 02 7F 00
+ 29 01 00 00 00 00 02 80 B7
+ 29 01 00 00 00 00 02 81 00
+ 29 01 00 00 00 00 02 82 C5
+ 29 01 00 00 00 00 02 83 00
+ 29 01 00 00 00 00 02 84 F7
+ 29 01 00 00 00 00 02 85 01
+ 29 01 00 00 00 00 02 86 1E
+ 29 01 00 00 00 00 02 87 01
+ 29 01 00 00 00 00 02 88 60
+ 29 01 00 00 00 00 02 89 01
+ 29 01 00 00 00 00 02 8A 95
+ 29 01 00 00 00 00 02 8B 01
+ 29 01 00 00 00 00 02 8C E1
+ 29 01 00 00 00 00 02 8D 02
+ 29 01 00 00 00 00 02 8E 20
+ 29 01 00 00 00 00 02 8F 02
+ 29 01 00 00 00 00 02 90 23
+ 29 01 00 00 00 00 02 91 02
+ 29 01 00 00 00 00 02 92 59
+ 29 01 00 00 00 00 02 93 02
+ 29 01 00 00 00 00 02 94 94
+ 29 01 00 00 00 00 02 95 02
+ 29 01 00 00 00 00 02 96 B4
+ 29 01 00 00 00 00 02 97 02
+ 29 01 00 00 00 00 02 98 E1
+ 29 01 00 00 00 00 02 99 03
+ 29 01 00 00 00 00 02 9A 01
+ 29 01 00 00 00 00 02 9B 03
+ 29 01 00 00 00 00 02 9C 28
+ 29 01 00 00 00 00 02 9D 03
+ 29 01 00 00 00 00 02 9E 30
+ 29 01 00 00 00 00 02 9F 03
+ 29 01 00 00 00 00 02 A0 37
+ 29 01 00 00 00 00 02 A2 03
+ 29 01 00 00 00 00 02 A3 3B
+ 29 01 00 00 00 00 02 A4 03
+ 29 01 00 00 00 00 02 A5 40
+ 29 01 00 00 00 00 02 A6 03
+ 29 01 00 00 00 00 02 A7 50
+ 29 01 00 00 00 00 02 A9 03
+ 29 01 00 00 00 00 02 AA 6D
+ 29 01 00 00 00 00 02 AB 03
+ 29 01 00 00 00 00 02 AC 80
+ 29 01 00 00 00 00 02 AD 03
+ 29 01 00 00 00 00 02 AE CB
+ 29 01 00 00 00 00 02 AF 00
+ 29 01 00 00 00 00 02 B0 19
+ 29 01 00 00 00 00 02 B1 00
+ 29 01 00 00 00 00 02 B2 36
+ 29 01 00 00 00 00 02 B3 00
+ 29 01 00 00 00 00 02 B4 55
+ 29 01 00 00 00 00 02 B5 00
+ 29 01 00 00 00 00 02 B6 70
+ 29 01 00 00 00 00 02 B7 00
+ 29 01 00 00 00 00 02 B8 83
+ 29 01 00 00 00 00 02 B9 00
+ 29 01 00 00 00 00 02 BA 99
+ 29 01 00 00 00 00 02 BB 00
+ 29 01 00 00 00 00 02 BC A8
+ 29 01 00 00 00 00 02 BD 00
+ 29 01 00 00 00 00 02 BE B7
+ 29 01 00 00 00 00 02 BF 00
+ 29 01 00 00 00 00 02 C0 C5
+ 29 01 00 00 00 00 02 C1 00
+ 29 01 00 00 00 00 02 C2 F7
+ 29 01 00 00 00 00 02 C3 01
+ 29 01 00 00 00 00 02 C4 1E
+ 29 01 00 00 00 00 02 C5 01
+ 29 01 00 00 00 00 02 C6 60
+ 29 01 00 00 00 00 02 C7 01
+ 29 01 00 00 00 00 02 C8 95
+ 29 01 00 00 00 00 02 C9 01
+ 29 01 00 00 00 00 02 CA E1
+ 29 01 00 00 00 00 02 CB 02
+ 29 01 00 00 00 00 02 CC 20
+ 29 01 00 00 00 00 02 CD 02
+ 29 01 00 00 00 00 02 CE 23
+ 29 01 00 00 00 00 02 CF 02
+ 29 01 00 00 00 00 02 D0 59
+ 29 01 00 00 00 00 02 D1 02
+ 29 01 00 00 00 00 02 D2 94
+ 29 01 00 00 00 00 02 D3 02
+ 29 01 00 00 00 00 02 D4 B4
+ 29 01 00 00 00 00 02 D5 02
+ 29 01 00 00 00 00 02 D6 E1
+ 29 01 00 00 00 00 02 D7 03
+ 29 01 00 00 00 00 02 D8 01
+ 29 01 00 00 00 00 02 D9 03
+ 29 01 00 00 00 00 02 DA 28
+ 29 01 00 00 00 00 02 DB 03
+ 29 01 00 00 00 00 02 DC 30
+ 29 01 00 00 00 00 02 DD 03
+ 29 01 00 00 00 00 02 DE 37
+ 29 01 00 00 00 00 02 DF 03
+ 29 01 00 00 00 00 02 E0 3B
+ 29 01 00 00 00 00 02 E1 03
+ 29 01 00 00 00 00 02 E2 40
+ 29 01 00 00 00 00 02 E3 03
+ 29 01 00 00 00 00 02 E4 50
+ 29 01 00 00 00 00 02 E5 03
+ 29 01 00 00 00 00 02 E6 6D
+ 29 01 00 00 00 00 02 E7 03
+ 29 01 00 00 00 00 02 E8 80
+ 29 01 00 00 00 00 02 E9 03
+ 29 01 00 00 00 00 02 EA CB
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 02
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 04
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 00
+ 29 01 00 00 64 00 02 11 00
+ 29 01 00 00 00 00 02 FF EE
+ 29 01 00 00 00 00 02 12 50
+ 29 01 00 00 00 00 02 13 02
+ 29 01 00 00 00 00 02 6A 60
+ 29 01 00 00 00 00 02 FF 00
+ 29 01 00 00 78 00 02 29 00];
+
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
- qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
- 05 01 00 00 78 02 10 00];
+ qcom,panel-off-cmds = [05 01 00 00 32 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
qcom,off-cmds-dsi-state = "DSI_HS_MODE";
};
};
diff --git a/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
index 7bc748d..d742b30 100644
--- a/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
@@ -514,7 +514,8 @@
29 01 00 00 00 00 02 13 02
29 01 00 00 00 00 02 6A 60
29 01 00 00 00 00 02 FF 00
- 29 01 00 00 78 00 02 29 00];
+ 29 01 00 00 78 00 02 29 00
+ 29 01 00 00 78 00 02 53 2C];
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
qcom,panel-off-cmds = [05 01 00 00 32 00 02 28 00
diff --git a/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi b/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi
index b9ed050..befc29c 100644
--- a/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi
+++ b/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi
@@ -40,7 +40,7 @@
qcom,mdss-pan-dsi-mdp-tr = <0x0>;/*todo*/
qcom,mdss-pan-dsi-dma-tr = <0x04>;
qcom,mdss-pan-dsi-frame-rate = <60>;
- qcom,panel-phy-regulatorSettings =[09 08 05 00 20 03];
+ qcom,panel-phy-regulatorSettings =[02 08 05 00 20 03];
qcom,panel-phy-timingSettings = [5D 12 0C 00 33 39
10 16 15 03 04 00];
qcom,panel-phy-strengthCtrl = [ff 06];
diff --git a/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-cmd.dtsi b/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-cmd.dtsi
new file mode 100644
index 0000000..f57a7bd
--- /dev/null
+++ b/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-cmd.dtsi
@@ -0,0 +1,124 @@
+/* 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,dsi_v2_truly_wvga_video {
+ compatible = "qcom,dsi-panel-v2";
+ label = "Truly WVGA command mode dsi panel";
+ qcom,dsi-ctrl-phandle = <&mdss_dsi0>;
+ qcom,rst-gpio = <&msmgpio 41 0>;
+ qcom,mode-selection-gpio = <&msmgpio 7 0>;
+ qcom,te-gpio = <&msmgpio 12 0>;
+ vdda-supply = <&pm8110_l19>;
+ vddio-supply=<&pm8110_l14>;
+ qcom,mdss-pan-res = <480 800>;
+ qcom,mdss-pan-bpp = <24>;
+ qcom,mdss-pan-dest = "display_1";
+ qcom,mdss-pan-porch-values = <40 8 160 10 2 12>;
+ qcom,mdss-pan-underflow-clr = <0xff>;
+ qcom,mdss-pan-bl-levels = <1 255>;
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
+ qcom,mdss-pan-dsi-mode = <1>;
+ qcom,mdss-pan-dsi-h-pulse-mode = <0>;
+ qcom,mdss-pan-dsi-h-power-stop = <0 0 0>;
+ qcom,mdss-pan-dsi-bllp-power-stop = <1 1>;
+ qcom,mdss-pan-dsi-traffic-mode = <1>;
+ qcom,mdss-pan-dsi-dst-format = <8>;
+ qcom,mdss-pan-insert-dcs-cmd = <1>;
+ qcom,mdss-pan-wr-mem-continue = <0x3c>;
+ qcom,mdss-pan-wr-mem-start = <0x2c>;
+ qcom,mdss-pan-dsi-vc = <0>;
+ qcom,mdss-pan-dsi-rgb-swap = <0>;
+ qcom,mdss-pan-dsi-data-lanes = <1 1 0 0>;
+ qcom,mdss-pan-dsi-dlane-swap = <0>;
+ qcom,mdss-pan-dsi-t-clk = <0x1b 0x04>;
+ qcom,mdss-pan-dsi-stream = <0>;
+ qcom,mdss-pan-dsi-mdp-tr = <0x02>;
+ qcom,mdss-pan-dsi-dma-tr = <0x04>;
+ qcom,mdss-pan-dsi-frame-rate = <60>;
+ qcom,panel-phy-regulatorSettings =[02 08 05 00 20 03];
+ qcom,panel-phy-timingSettings = [5D 12 0C 00 33 38
+ 10 16 1E 03 04 00];
+ qcom,panel-phy-strengthCtrl = [ff 06];
+ qcom,panel-phy-bistCtrl = [03 03 00 00 0f 00];
+ qcom,panel-phy-laneConfig =
+ [80 45 00 00 01 66 /*lane0**/
+ 80 45 00 00 01 66 /*lane1*/
+ 80 45 00 00 01 66 /*lane2*/
+ 80 45 00 00 01 66 /*lane3*/
+ 40 67 00 00 01 88]; /*Clk*/
+
+ qcom,on-cmds-dsi-state = "DSI_LP_MODE";
+ qcom,panel-on-cmds = [
+ 05 01 00 00 00 02
+ 01 00
+ 23 01 00 00 00 02
+ b0 04
+ 29 01 00 00 00 03
+ b3 02 00
+ 23 01 00 00 00 02
+ bd 00
+ 29 01 00 00 00 03
+ c0 18 66
+ 29 01 00 00 00 10
+ c1 23 31 99 21 20 00 30 28 0c 0c
+ 00 00 00 21 01
+ 29 01 00 00 00 07
+ c2 00 06 06 01 03 00
+ 29 01 00 00 00 19
+ c8 04 10 18 20 2e 46 3c 28 1f 18
+ 10 04 04 10 18 20 2e 46 3c 28 1f 18 10 04
+ 29 01 00 00 00 19
+ c9 04 10 18 20 2e 46 3c 28 1f 18
+ 10 04 04 10 18 20 2e 46 3c 28 1f 18 10 04
+ 29 01 00 00 00 19
+ ca 04 10 18 20 2e 46 3c 28 1f 18
+ 10 04 04 10 18 20 2e 46 3c 28 1f 18 10 04
+ 29 01 00 00 00 11
+ d0 29 03 ce a6 00 43 20 10 01 00
+ 01 01 00 03 01 00
+ 29 01 00 00 00 08
+ d1 18 0C 23 03 75 02 50
+ 23 01 00 00 00 02
+ d3 11
+ 29 01 00 00 00 03
+ d5 2a 2a
+ 29 01 00 00 00 03
+ de 01 51
+ 23 01 00 00 00 02
+ e6 51
+ 23 01 00 00 00 02
+ fa 03
+ 23 01 00 00 64 02
+ d6 28
+ 15 01 00 00 00 02
+ 36 41
+ 39 01 00 00 00 05
+ 2a 00 00 01 df
+ 39 01 00 00 00 05
+ 2b 00 00 03 1f
+ 15 01 00 00 00 02
+ 35 00
+ 39 01 00 00 00 03
+ 44 00 50
+ 15 01 00 00 00 02
+ 3a 77
+ 05 01 00 00 7D 02
+ 11 00
+ 05 01 00 00 14 02
+ 29 00
+ ];
+ qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
+ 05 01 00 00 78 02 10 00];
+ qcom,off-cmds-dsi-state = "DSI_LP_MODE";
+ };
+};
diff --git a/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-video.dtsi b/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-video.dtsi
index 891eac3..8be8d71 100644
--- a/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-video.dtsi
+++ b/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-video.dtsi
@@ -41,7 +41,7 @@
qcom,mdss-pan-dsi-mdp-tr = <0x0>;/*todo*/
qcom,mdss-pan-dsi-dma-tr = <0x04>;
qcom,mdss-pan-dsi-frame-rate = <60>;
- qcom,panel-phy-regulatorSettings =[09 08 05 00 20 03];
+ qcom,panel-phy-regulatorSettings =[02 08 05 00 20 03];
qcom,panel-phy-timingSettings = [5D 12 0C 00 33 38
10 16 1E 03 04 00];
qcom,panel-phy-strengthCtrl = [ff 06];
@@ -69,7 +69,7 @@
c1 23 31 99 21 20 00 30 28 0c 0c
00 00 00 21 01
29 01 00 00 00 07
- c2 10 06 06 01 03 00
+ c2 00 06 06 01 03 00
29 01 00 00 00 19
c8 04 10 18 20 2e 46 3c 28 1f 18
10 04 04 10 18 20 2e 46 3c 28 1f 18 10 04
@@ -89,7 +89,7 @@
29 01 00 00 00 03
d5 2a 2a
29 01 00 00 00 03
- de 01 41
+ de 01 51
23 01 00 00 00 02
e6 51
23 01 00 00 00 02
diff --git a/arch/arm/boot/dts/mpq8092-iommu.dtsi b/arch/arm/boot/dts/mpq8092-iommu.dtsi
index 56369dc..3a86814 100644
--- a/arch/arm/boot/dts/mpq8092-iommu.dtsi
+++ b/arch/arm/boot/dts/mpq8092-iommu.dtsi
@@ -14,27 +14,205 @@
&jpeg_iommu {
status = "ok";
- vdd-supply = <&gdsc_jpeg>;
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2008
+ 0x200c
+ 0x2010>;
+
+ qcom,iommu-bfb-data = <0x3FFF
+ 0x4
+ 0x4
+ 0x0
+ 0x0
+ 0x10
+ 0x50
+ 0x0
+ 0x2000
+ 0x2804
+ 0x9614
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
};
&mdp_iommu {
status = "ok";
- vdd-supply = <&gdsc_mdss>;
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018>;
+
+ qcom,iommu-bfb-data = <0x7FFFFF
+ 0x4
+ 0x10
+ 0x0
+ 0x5000
+ 0x5a1d
+ 0x1822d
+ 0x0
+ 0x0
+ 0x28
+ 0x68
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
};
&venus_iommu {
status = "ok";
- vdd-supply = <&gdsc_venus>;
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c>;
+
+ qcom,iommu-bfb-data = <0x7FFFFFF
+ 0x4
+ 0x8
+ 0x0
+ 0x13607
+ 0x4201
+ 0x14221
+ 0x0
+ 0x0
+ 0x94
+ 0x114
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+ venus_ns: qcom,iommu-ctx@fdc8c000 {
+ qcom,iommu-ctx-sids = <0 1 2 3 4 5 7>;
+ };
+
+ venus_sec_bitstream: qcom,iommu-ctx@fdc8d000 {
+ qcom,iommu-ctx-sids = <0x80 0x81 0x82 0x83 0x84>;
+ label = "venus_sec_bitstream";
+ };
+
+ venus_sec_pixel: qcom,iommu-ctx@fdc8f000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfdc8f000 0x1000>;
+ interrupts = <0 42 0>;
+ qcom,iommu-ctx-sids = <0x85>;
+ label = "venus_sec_pixel";
+ qcom,secure-context;
+ };
+
+ venus_sec_non_pixel: qcom,iommu-ctx@fdc90000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfdc90000 0x1000>;
+ interrupts = <0 42 0>;
+ qcom,iommu-ctx-sids = <0x87 0xA0>;
+ label = "venus_sec_non_pixel";
+ qcom,secure-context;
+ };
};
&kgsl_iommu {
status = "ok";
- qcom,needs-alt-core-clk;
- vdd-supply = <&gdsc_oxili_cx>;
- qcom,alt-vdd-supply = <&gdsc_oxili_gx>;
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008
+ 0x2600
+ 0x2604
+ 0x2608
+ 0x260c
+ 0x2610
+ 0x2614
+ 0x2618
+ 0x261c
+ 0x2620
+ 0x2624
+ 0x2628
+ 0x262c>;
+
+ qcom,iommu-bfb-data = <0x3
+ 0x8
+ 0x10
+ 0x0
+ 0x0
+ 0x0
+ 0x20
+ 0x0
+ 0x0
+ 0x1
+ 0x101
+ 0x0
+ 0x0
+ 0x7
+ 0x4
+ 0x8
+ 0x14
+ 0x0
+ 0x0
+ 0xc
+ 0x6c
+ 0x0
+ 0x8
+ 0x10
+ 0x0>;
};
-&vfe_iommu {
+&vpu_iommu {
status = "ok";
- vdd-supply = <&gdsc_vfe>;
};
diff --git a/arch/arm/boot/dts/mpq8092-rumi.dtsi b/arch/arm/boot/dts/mpq8092-rumi.dtsi
index 2016998..cc345d8 100644
--- a/arch/arm/boot/dts/mpq8092-rumi.dtsi
+++ b/arch/arm/boot/dts/mpq8092-rumi.dtsi
@@ -24,13 +24,13 @@
};
qcom,sdcc@f9824000 {
- status = "disabled";
qcom,clk-rates = <400000 19200000>;
+ status = "ok";
};
qcom,sdcc@f98a4000 {
- status = "disabled";
qcom,clk-rates = <400000 19200000>;
+ status = "ok";
};
qcom,sps@f998000 {
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index e8674a0..04f0945 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -140,6 +140,38 @@
reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
};
+ usb@f9a55000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>, <0 140 0>;
+ interrupt-names = "core_irq", "async_irq";
+
+ HSUSB_VDDCX-supply = <&pma8084_s8>;
+ HSUSB_1p8-supply = <&pma8084_l22>;
+ HSUSB_3p3-supply = <&pma8084_l24>;
+ qcom,vdd-voltage-level = <1050000 1050000>;
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+
+ qcom,msm_bus,name = "usb_otg";
+ qcom,msm_bus,num_cases = <3>;
+ 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>,
+ <87 512 6000 6000>;
+ };
+
+ android_usb@fe8050c8 {
+ compatible = "qcom,android-usb";
+ reg = <0xfe8050c8 0xc8>;
+ qcom,android-usb-swfi-latency = <1>;
+ };
+
spmi_bus: qcom,spmi@fc4c0000 {
cell-index = <0>;
compatible = "qcom,spmi-pmic-arb";
@@ -210,6 +242,41 @@
qcom,pet-time = <10000>;
qcom,ipi-ping;
};
+
+ qcom,ocmem@fdd00000 {
+ compatible = "qcom,msm-ocmem";
+ reg = <0xfdd00000 0x2000>,
+ <0xfdd02000 0x2000>,
+ <0xfe070000 0x400>,
+ <0xfec00000 0x180000>;
+ reg-names = "ocmem_ctrl_physical", "dm_ctrl_physical", "br_ctrl_physical", "ocmem_physical";
+ interrupts = <0 76 0>, <0 77 0>;
+ interrupt-names = "ocmem_irq", "dm_irq";
+ qcom,ocmem-num-regions = <0x3>;
+ qcom,ocmem-num-macros = <0x18>;
+ qcom,resource-type = <0x706d636f>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0xfec00000 0x180000>;
+
+ partition@0 {
+ reg = <0x0 0x100000>;
+ qcom,ocmem-part-name = "graphics";
+ qcom,ocmem-part-min = <0x80000>;
+ };
+
+ partition@80000 {
+ reg = <0x100000 0x80000>;
+ qcom,ocmem-part-name = "lp_audio";
+ qcom,ocmem-part-min = <0x80000>;
+ };
+
+ partition@100000 {
+ reg = <0x100000 0x80000>;
+ qcom,ocmem-part-name = "video";
+ qcom,ocmem-part-min = <0x55000>;
+ };
+ };
};
&gdsc_venus {
@@ -224,6 +291,10 @@
status = "ok";
};
+&gdsc_vpu {
+ status = "ok";
+};
+
&gdsc_oxili_gx {
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm-gdsc.dtsi b/arch/arm/boot/dts/msm-gdsc.dtsi
index 78234e8..495f65a 100644
--- a/arch/arm/boot/dts/msm-gdsc.dtsi
+++ b/arch/arm/boot/dts/msm-gdsc.dtsi
@@ -19,6 +19,27 @@
status = "disabled";
};
+ gdsc_venus_core0: qcom,gdsc@fd8c1040 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_venus_core0";
+ reg = <0xfd8c1040 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_venus_core1: qcom,gdsc@fd8c1044 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_venus_core1";
+ reg = <0xfd8c1044 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_vpu: qcom,gdsc@fd8c1404 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_vpu";
+ reg = <0xfd8c1404 0x4>;
+ status = "disabled";
+ };
+
gdsc_mdss: qcom,gdsc@fd8c2304 {
compatible = "qcom,gdsc";
regulator-name = "gdsc_mdss";
@@ -60,4 +81,32 @@
reg = <0xfc400404 0x4>;
status = "disabled";
};
+
+ gdsc_pcie_0: qcom,gdsc@fc401ac4 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_pcie_0";
+ reg = <0xfc401ac4 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_pcie_1: qcom,gdsc@fc401b44 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_pcie_1";
+ reg = <0xfc401b44 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_usb30: qcom,gdsc@fc401e84 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_usb30";
+ reg = <0xfc401e84 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_usb30_sec: qcom,gdsc@fc401ec0 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_usb30_sec";
+ reg = <0xfc401ec0 0x4>;
+ status = "disabled";
+ };
};
diff --git a/arch/arm/boot/dts/msm-iommu-v1.dtsi b/arch/arm/boot/dts/msm-iommu-v1.dtsi
index 4492077..ef8677d 100644
--- a/arch/arm/boot/dts/msm-iommu-v1.dtsi
+++ b/arch/arm/boot/dts/msm-iommu-v1.dtsi
@@ -408,6 +408,15 @@
qcom,iommu-ctx-sids = <1>;
label = "gfx3d_priv";
};
+
+ qcom,iommu-ctx@fdb1a000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfdb1a000 0x1000>;
+ interrupts = <0 241 0>;
+ qcom,iommu-ctx-sids = <2>;
+ label = "gfx3d_spare";
+ };
+
};
vfe_iommu: qcom,iommu@fda44000 {
@@ -511,4 +520,540 @@
label = "cpp";
};
};
+
+ copss_iommu: qcom,iommu@f9bc4000 {
+ compatible = "qcom,msm-smmu-v1";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0xf9bc4000 0x10000>;
+ reg-names = "iommu_base";
+ interrupts = <0 153 0>;
+ label = "copss_iommu";
+ qcom,msm-bus,name = "copss_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <88 512 0 0>,
+ <88 512 0 1000>;
+ status = "disabled";
+ qcom,iommu-pmu-ngroups = <1>;
+ qcom,iommu-pmu-ncounters = <8>;
+ qcom,iommu-pmu-event-classes = <0x00
+ 0x01
+ 0x08
+ 0x09
+ 0x0a
+ 0x10
+ 0x11
+ 0x12
+ 0x80
+ 0x81
+ 0x82
+ 0x83
+ 0x90
+ 0x91
+ 0x92
+ 0xb0
+ 0xb1>;
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008>;
+
+ qcom,iommu-bfb-data = <0x3
+ 0x4
+ 0x4
+ 0x0
+ 0x0
+ 0x0
+ 0x1
+ 0x0
+ 0x0
+ 0x40
+ 0x44
+ 0x0
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008>;
+
+ qcom,iommu-lpae-bfb-data = <0x3
+ 0x0
+ 0x4
+ 0x4
+ 0x0
+ 0x5
+ 0x0
+ 0x1
+ 0x0
+ 0x0
+ 0x40
+ 0x44
+ 0x0>;
+
+
+ copss_cb_0: qcom,iommu-ctx@f9bcc000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xf9bcc000 0x1000>;
+ interrupts = <0 142 0>;
+ qcom,iommu-ctx-sids = <0>;
+ label = "copss_cb_0";
+ };
+
+ copss_cb_1: qcom,iommu-ctx@f9bcd000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xf9bcd000 0x1000>;
+ interrupts = <0 142 0>;
+ qcom,iommu-ctx-sids = <1>;
+ label = "copss_cb_1";
+ };
+
+ copss_cb_2: qcom,iommu-ctx@f9bce000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xf9bce000 0x1000>;
+ interrupts = <0 142 0>;
+ qcom,iommu-ctx-sids = <2>;
+ label = "copss_cb_2";
+ };
+
+ copss_cb_3: qcom,iommu-ctx@f9bcf000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xf9bcf000 0x1000>;
+ interrupts = <0 142 0>;
+ qcom,iommu-ctx-sids = <3>;
+ label = "copss_cb_3";
+ };
+
+ copss_cb_4: qcom,iommu-ctx@f9bd0000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xf9bd0000 0x1000>;
+ interrupts = <0 142 0>;
+ qcom,iommu-ctx-sids = <4>;
+ label = "copss_cb_4";
+ };
+
+ copss_cb_5: qcom,iommu-ctx@f9bd1000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xf9bd1000 0x1000>;
+ interrupts = <0 142 0>;
+ qcom,iommu-ctx-sids = <5>;
+ label = "copss_cb_5";
+ };
+
+ copss_cb_6: qcom,iommu-ctx@f9bd2000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xf9bd2000 0x1000>;
+ interrupts = <0 142 0>;
+ qcom,iommu-ctx-sids = <6>;
+ label = "copss_cb_6";
+ };
+
+ copss_cb_7: qcom,iommu-ctx@f9bd3000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xf9bd3000 0x1000>;
+ interrupts = <0 142 0>;
+ qcom,iommu-ctx-sids = <7>;
+ label = "copss_cb_7";
+ };
+ };
+
+ vpu_iommu: qcom,iommu@fdee4000 {
+ compatible = "qcom,msm-smmu-v1";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0xfdee4000 0x10000>;
+ reg-names = "iommu_base";
+ interrupts = <0 147 0>;
+ label = "vpu_iommu";
+ qcom,msm-bus,name = "vpu_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <93 512 0 0>,
+ <93 512 0 1000>;
+ status = "disabled";
+ qcom,iommu-pmu-ngroups = <1>;
+ qcom,iommu-pmu-ncounters = <8>;
+ qcom,iommu-pmu-event-classes = <0x00
+ 0x01
+ 0x08
+ 0x09
+ 0x0a
+ 0x10
+ 0x11
+ 0x12
+ 0x80
+ 0x81
+ 0x82
+ 0x83
+ 0x90
+ 0x91
+ 0x92
+ 0xb0
+ 0xb1>;
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014>;
+
+ qcom,iommu-bfb-data = <0xffff
+ 0x4
+ 0x10
+ 0x0
+ 0x0
+ 0xf
+ 0x4b
+ 0x0
+ 0x1e00
+ 0x1e00
+ 0x5a0f
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014>;
+
+ qcom,iommu-lpae-bfb-data = <0xffff
+ 0x0
+ 0x4
+ 0x10
+ 0x0
+ 0x0
+ 0xf
+ 0x4b
+ 0x1e00
+ 0x5a2d
+ 0x1e00
+ 0x5a0f
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
+
+ vpu_cb_0: qcom,iommu-ctx@fdeec000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfdeec000 0x1000>;
+ interrupts = <0 145 0>;
+ qcom,iommu-ctx-sids = <0 1 3>;
+ label = "vpu_cb_0";
+ };
+
+ vpu_cb_1: qcom,iommu-ctx@fdeed000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfdeed000 0x1000>;
+ interrupts = <0 145 0>;
+ qcom,iommu-ctx-sids = <8 9>;
+ label = "vpu_cb_1";
+ };
+
+ vpu_cb_2: qcom,iommu-ctx@fdeee000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfdeee000 0x1000>;
+ interrupts = <0 145 0>;
+ qcom,iommu-ctx-sids = <5 7 15>;
+ label = "vpu_cb_2";
+ };
+ };
+
+ lpass_qdsp_iommu: qcom,iommu@fe054000 {
+ compatible = "qcom,msm-smmu-v1";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0xfe054000 0x10000>;
+ reg-names = "iommu_base";
+ interrupts = <0 202 0>;
+ label = "lpass_qdsp_iommu";
+ qcom,msm-bus,name = "lpass_qdsp_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <11 512 0 0>,
+ <11 512 0 1000>;
+ status = "disabled";
+ qcom,iommu-pmu-ngroups = <1>;
+ qcom,iommu-pmu-ncounters = <8>;
+ qcom,iommu-pmu-event-classes = <0x00
+ 0x01
+ 0x08
+ 0x09
+ 0x0a
+ 0x10
+ 0x11
+ 0x12
+ 0x80
+ 0x81
+ 0x82
+ 0x83
+ 0x90
+ 0x91
+ 0x92
+ 0xb0
+ 0xb1>;
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008>;
+
+ qcom,iommu-bfb-data = <0x3
+ 0x4
+ 0x4
+ 0x0
+ 0x0
+ 0x0
+ 0x10
+ 0x0
+ 0x0
+ 0x15e
+ 0x19e
+ 0x0
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008>;
+
+ qcom,iommu-lpae-bfb-data = <0x3
+ 0x0
+ 0x4
+ 0x4
+ 0x0
+ 0x20
+ 0x0
+ 0x10
+ 0x0
+ 0x0
+ 0x15e
+ 0x19e
+ 0x0>;
+
+
+ lpass_qdsp_cb_0: qcom,iommu-ctx@fe05c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfe05c000 0x1000>;
+ interrupts = <0 265 0>;
+ qcom,iommu-ctx-sids = <0>;
+ label = "lpass_qdsp_cb_0";
+ };
+
+ lpass_qdsp_cb_1: qcom,iommu-ctx@fe05d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfe05d000 0x1000>;
+ interrupts = <0 265 0>;
+ qcom,iommu-ctx-sids = <1>;
+ label = "lpass_qdsp_cb_1";
+ };
+
+ lpass_qdsp_cb_2: qcom,iommu-ctx@fe05e000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfe05e000 0x1000>;
+ interrupts = <0 265 0>;
+ qcom,iommu-ctx-sids = <2>;
+ label = "lpass_qdsp_cb_2";
+ };
+
+ lpass_qdsp_cb_3: qcom,iommu-ctx@fe05f000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfe05f000 0x1000>;
+ interrupts = <0 265 0>;
+ qcom,iommu-ctx-sids = <3>;
+ label = "lpass_qdsp_cb_3";
+ };
+ };
+
+ lpass_core_iommu: qcom,iommu@fe064000 {
+ compatible = "qcom,msm-smmu-v1";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0xfe064000 0x10000>;
+ reg-names = "iommu_base";
+ interrupts = <0 166 0>;
+ label = "lpass_core_iommu";
+ qcom,msm-bus,name = "lpass_core_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <52 512 0 0>,
+ <52 512 0 1000>;
+ status = "disabled";
+ qcom,iommu-pmu-ngroups = <1>;
+ qcom,iommu-pmu-ncounters = <8>;
+ qcom,iommu-pmu-event-classes = <0x00
+ 0x01
+ 0x08
+ 0x09
+ 0x0a
+ 0x10
+ 0x11
+ 0x12
+ 0x80
+ 0x81
+ 0x82
+ 0x83
+ 0x90
+ 0x91
+ 0x92
+ 0xb0
+ 0xb1>;
+
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008>;
+
+ qcom,iommu-bfb-data = <0x3
+ 0x4
+ 0x4
+ 0x0
+ 0x0
+ 0x0
+ 0x4
+ 0x0
+ 0x0
+ 0x40
+ 0x50
+ 0x0
+ 0x0>;
+
+ qcom,iommu-lpae-bfb-regs = <0x204c
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x22bc
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2494
+ 0x2008>;
+
+ qcom,iommu-lpae-bfb-data = <0x3
+ 0x0
+ 0x4
+ 0x4
+ 0x0
+ 0xc
+ 0x0
+ 0x4
+ 0x0
+ 0x0
+ 0x40
+ 0x50
+ 0x0>;
+
+
+ lpass_core_cb_0: qcom,iommu-ctx@fe06c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfe06c000 0x1000>;
+ interrupts = <0 267 0>;
+ qcom,iommu-ctx-sids = <0>;
+ label = "lpass_core_cb_0";
+ };
+
+ lpass_core_cb_1: qcom,iommu-ctx@fe06d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfe06d000 0x1000>;
+ interrupts = <0 267 0>;
+ qcom,iommu-ctx-sids = <1>;
+ label = "lpass_core_cb_1";
+ };
+
+ lpass_core_cb_2: qcom,iommu-ctx@fe06e000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
+ reg = <0xfe06e000 0x1000>;
+ interrupts = <0 267 0>;
+ qcom,iommu-ctx-sids = <2>;
+ label = "lpass_core_cb_2";
+ };
+ };
};
diff --git a/arch/arm/boot/dts/msm-pm8226-rpm-regulator.dtsi b/arch/arm/boot/dts/msm-pm8226-rpm-regulator.dtsi
index ded9494..1ee7076 100644
--- a/arch/arm/boot/dts/msm-pm8226-rpm-regulator.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226-rpm-regulator.dtsi
@@ -427,6 +427,22 @@
};
};
+ rpm-regulator-ldoa25 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <25>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l25 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8226_l25";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
rpm-regulator-ldoa26 {
compatible = "qcom,rpm-regulator-smd-resource";
qcom,resource-name = "ldoa";
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index 2008e1e..0f93c8e 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -32,10 +32,13 @@
reg = <0x800 0x100>;
interrupts = <0x0 0x8 0x0>,
<0x0 0x8 0x1>,
- <0x0 0x8 0x4>;
- interrupt-names = "kpdpwr", "resin", "resin-bark";
+ <0x0 0x8 0x4>,
+ <0x0 0x8 0x5>;
+ interrupt-names = "kpdpwr", "resin",
+ "resin-bark", "kpdpwr-resin-bark";
qcom,pon-dbc-delay = <15625>;
qcom,system-reset;
+ qcom,s3-debounce = <32>;
qcom,pon_1 {
qcom,pon-type = <0>;
@@ -52,6 +55,15 @@
qcom,s2-type = <1>;
linux,code = <114>;
};
+
+ qcom,pon_3 {
+ qcom,pon-type = <3>;
+ qcom,support-reset = <1>;
+ qcom,pull-up = <1>;
+ qcom,s1-timer = <6720>;
+ qcom,s2-timer = <2000>;
+ qcom,s2-type = <7>;
+ };
};
pm8226_chg: qcom,charger {
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index ce050a4..fc828b7 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -15,1382 +15,1381 @@
#size-cells = <0>;
interrupt-controller;
#interrupt-cells = <3>;
+};
- qcom,pm8941@0 {
- spmi-slave-container;
- reg = <0x0>;
- #address-cells = <1>;
- #size-cells = <1>;
+&pm8941_lsid0 {
+ spmi-slave-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
- pm8941_misc: qcom,misc@900 {
- compatible = "qcom,qpnp-misc";
- reg = <0x900 0x100>;
+ pm8941_misc: qcom,misc@900 {
+ compatible = "qcom,qpnp-misc";
+ reg = <0x900 0x100>;
+ };
+
+ qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8941_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0>,
+ <0x0 0x8 0x1>,
+ <0x0 0x8 0x4>,
+ <0x0 0x8 0x5>;
+ interrupt-names = "kpdpwr", "resin",
+ "resin-bark", "kpdpwr-resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+ qcom,s3-debounce = <32>;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
};
- qcom,revid@100 {
- compatible = "qcom,qpnp-revid";
- reg = <0x100 0x100>;
+ 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>;
};
- qcom,temp-alarm@2400 {
- compatible = "qcom,qpnp-temp-alarm";
- reg = <0x2400 0x100>;
- interrupts = <0x0 0x24 0x0>;
- label = "pm8941_tz";
- qcom,channel-num = <8>;
- qcom,threshold-set = <0>;
- };
-
- qcom,power-on@800 {
- compatible = "qcom,qpnp-power-on";
- reg = <0x800 0x100>;
- interrupts = <0x0 0x8 0x0>,
- <0x0 0x8 0x1>,
- <0x0 0x8 0x4>,
- <0x0 0x8 0x5>;
- interrupt-names = "kpdpwr", "resin",
- "resin-bark", "kpdpwr-resin-bark";
- qcom,pon-dbc-delay = <15625>;
- qcom,system-reset;
- qcom,s3-debounce = <32>;
-
- 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>;
- };
-
- qcom,pon_3 {
- qcom,pon-type = <3>;
- qcom,support-reset = <1>;
- qcom,s1-timer = <6720>;
- qcom,s2-timer = <2000>;
- qcom,s2-type = <7>;
- qcom,pull-up = <1>;
- };
- };
-
- 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_coincell: qcom,coincell@2800 {
- compatible = "qcom,qpnp-coincell";
- reg = <0x2800 0x100>;
- };
-
- pm8941_bms: qcom,bms {
- spmi-dev-container;
- compatible = "qcom,qpnp-bms";
- #address-cells = <1>;
- #size-cells = <1>;
- status = "disabled";
-
- qcom,r-sense-uohm = <10000>;
- qcom,v-cutoff-uv = <3400000>;
- qcom,max-voltage-uv = <4200000>;
- qcom,r-conn-mohm = <0>;
- qcom,shutdown-soc-valid-limit = <20>;
- qcom,adjust-soc-low-threshold = <15>;
- qcom,ocv-voltage-high-threshold-uv = <3750000>;
- qcom,ocv-voltage-low-threshold-uv = <3650000>;
- qcom,low-soc-calculate-soc-threshold = <15>;
- qcom,low-soc-calculate-soc-ms = <5000>;
- qcom,calculate-soc-ms = <20000>;
- qcom,chg-term-ua = <100000>;
- qcom,batt-type = <0>;
- qcom,low-voltage-threshold = <3420000>;
- qcom,tm-temp-margin = <5000>;
- qcom,low-ocv-correction-limit-uv = <100>;
- qcom,high-ocv-correction-limit-uv = <50>;
- qcom,hold-soc-est = <3>;
-
- qcom,bms-iadc@3800 {
- reg = <0x3800 0x100>;
- };
-
- qcom,bms-bms@4000 {
- reg = <0x4000 0x100>;
- interrupts = <0x0 0x40 0x0>,
- <0x0 0x40 0x1>,
- <0x0 0x40 0x2>,
- <0x0 0x40 0x3>,
- <0x0 0x40 0x4>,
- <0x0 0x40 0x5>,
- <0x0 0x40 0x6>,
- <0x0 0x40 0x7>;
-
- interrupt-names = "vsense_for_r",
- "vsense_avg",
- "sw_cc_thr",
- "ocv_thr",
- "charge_begin",
- "good_ocv",
- "ocv_for_r",
- "cc_thr";
- };
- };
-
- clkdiv@5b00 {
- reg = <0x5b00 0x100>;
- compatible = "qcom,qpnp-clkdiv";
- qcom,cxo-freq = <19200000>;
- };
-
- clkdiv@5c00 {
- reg = <0x5c00 0x100>;
- compatible = "qcom,qpnp-clkdiv";
- qcom,cxo-freq = <19200000>;
- };
-
- clkdiv@5d00 {
- reg = <0x5d00 0x1000>;
- compatible = "qcom,qpnp-clkdiv";
- qcom,cxo-freq = <19200000>;
- };
-
- pm8941_chg: qcom,charger {
- spmi-dev-container;
- compatible = "qcom,qpnp-charger";
- #address-cells = <1>;
- #size-cells = <1>;
- status = "disabled";
-
- qcom,vddmax-mv = <4200>;
- qcom,vddsafe-mv = <4230>;
- qcom,vinmin-mv = <4300>;
- qcom,ibatmax-ma = <1500>;
- qcom,ibatterm-ma = <100>;
- qcom,ibatsafe-ma = <1500>;
- qcom,thermal-mitigation = <1500 700 600 325>;
- qcom,cool-bat-decidegc = <100>;
- qcom,cool-bat-mv = <4100>;
- qcom,ibatmax-warm-ma = <350>;
- qcom,warm-bat-decidegc = <450>;
- qcom,warm-bat-mv = <4100>;
- qcom,ibatmax-cool-ma = <350>;
- qcom,vbatdet-delta-mv = <100>;
- qcom,tchg-mins = <150>;
-
- qcom,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,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,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";
-
- };
-
- pm8941_chg_otg: qcom,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,dc-chgpth@1400 {
- status = "disabled";
- reg = <0x1400 0x100>;
- interrupts = <0x0 0x14 0x0>,
- <0x0 0x14 0x1>;
-
- interrupt-names = "coarse-det-dc",
- "dcin-valid";
- };
-
- pm8941_chg_boost: qcom,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>;
- };
- };
-
- pm8941_gpios: gpios {
- spmi-dev-container;
- compatible = "qcom,qpnp-pin";
- gpio-controller;
- #gpio-cells = <2>;
- #address-cells = <1>;
- #size-cells = <1>;
- label = "pm8941-gpio";
-
- gpio@c000 {
- reg = <0xc000 0x100>;
- qcom,pin-num = <1>;
- };
-
- gpio@c100 {
- reg = <0xc100 0x100>;
- qcom,pin-num = <2>;
- };
-
- gpio@c200 {
- reg = <0xc200 0x100>;
- qcom,pin-num = <3>;
- };
-
- gpio@c300 {
- reg = <0xc300 0x100>;
- qcom,pin-num = <4>;
- };
-
- gpio@c400 {
- reg = <0xc400 0x100>;
- qcom,pin-num = <5>;
- };
-
- gpio@c500 {
- reg = <0xc500 0x100>;
- qcom,pin-num = <6>;
- };
-
- gpio@c600 {
- reg = <0xc600 0x100>;
- qcom,pin-num = <7>;
- };
-
- gpio@c700 {
- reg = <0xc700 0x100>;
- qcom,pin-num = <8>;
- };
-
- gpio@c800 {
- reg = <0xc800 0x100>;
- qcom,pin-num = <9>;
- };
-
- gpio@c900 {
- reg = <0xc900 0x100>;
- qcom,pin-num = <10>;
- };
-
- gpio@ca00 {
- reg = <0xca00 0x100>;
- qcom,pin-num = <11>;
- };
-
- gpio@cb00 {
- reg = <0xcb00 0x100>;
- qcom,pin-num = <12>;
- };
-
- gpio@cc00 {
- reg = <0xcc00 0x100>;
- qcom,pin-num = <13>;
- };
-
- gpio@cd00 {
- reg = <0xcd00 0x100>;
- qcom,pin-num = <14>;
- };
-
- gpio@ce00 {
- reg = <0xce00 0x100>;
- qcom,pin-num = <15>;
- };
-
- gpio@cf00 {
- reg = <0xcf00 0x100>;
- qcom,pin-num = <16>;
- };
-
- gpio@d000 {
- reg = <0xd000 0x100>;
- qcom,pin-num = <17>;
- };
-
- gpio@d100 {
- reg = <0xd100 0x100>;
- qcom,pin-num = <18>;
- };
-
- gpio@d200 {
- reg = <0xd200 0x100>;
- qcom,pin-num = <19>;
- };
-
- gpio@d300 {
- reg = <0xd300 0x100>;
- qcom,pin-num = <20>;
- };
-
- gpio@d400 {
- reg = <0xd400 0x100>;
- qcom,pin-num = <21>;
- };
-
- gpio@d500 {
- reg = <0xd500 0x100>;
- qcom,pin-num = <22>;
- };
-
- gpio@d600 {
- reg = <0xd600 0x100>;
- qcom,pin-num = <23>;
- };
-
- gpio@d700 {
- reg = <0xd700 0x100>;
- qcom,pin-num = <24>;
- };
-
- gpio@d800 {
- reg = <0xd800 0x100>;
- qcom,pin-num = <25>;
- };
-
- gpio@d900 {
- reg = <0xd900 0x100>;
- qcom,pin-num = <26>;
- };
-
- gpio@da00 {
- reg = <0xda00 0x100>;
- qcom,pin-num = <27>;
- };
-
- gpio@db00 {
- reg = <0xdb00 0x100>;
- qcom,pin-num = <28>;
- };
-
- gpio@dc00 {
- reg = <0xdc00 0x100>;
- qcom,pin-num = <29>;
- };
-
- gpio@dd00 {
- reg = <0xdd00 0x100>;
- qcom,pin-num = <30>;
- };
-
- gpio@de00 {
- reg = <0xde00 0x100>;
- qcom,pin-num = <31>;
- };
-
- gpio@df00 {
- reg = <0xdf00 0x100>;
- qcom,pin-num = <32>;
- };
-
- gpio@e000 {
- reg = <0xe000 0x100>;
- qcom,pin-num = <33>;
- };
-
- gpio@e100 {
- reg = <0xe100 0x100>;
- qcom,pin-num = <34>;
- };
-
- gpio@e200 {
- reg = <0xe200 0x100>;
- qcom,pin-num = <35>;
- };
-
- gpio@e300 {
- reg = <0xe300 0x100>;
- qcom,pin-num = <36>;
- };
- };
-
- pm8941_mpps: mpps {
- spmi-dev-container;
- compatible = "qcom,qpnp-pin";
- gpio-controller;
- #gpio-cells = <2>;
- #address-cells = <1>;
- #size-cells = <1>;
- label = "pm8941-mpp";
-
- mpp@a000 {
- reg = <0xa000 0x100>;
- qcom,pin-num = <1>;
- };
-
- mpp@a100 {
- reg = <0xa100 0x100>;
- qcom,pin-num = <2>;
- };
-
- mpp@a200 {
- reg = <0xa200 0x100>;
- qcom,pin-num = <3>;
- };
-
- mpp@a300 {
- reg = <0xa300 0x100>;
- qcom,pin-num = <4>;
- };
-
- mpp@a400 {
- reg = <0xa400 0x100>;
- qcom,pin-num = <5>;
- };
-
- mpp@a500 {
- reg = <0xa500 0x100>;
- qcom,pin-num = <6>;
- };
-
- mpp@a600 {
- reg = <0xa600 0x100>;
- qcom,pin-num = <7>;
- };
-
- mpp@a700 {
- reg = <0xa700 0x100>;
- qcom,pin-num = <8>;
- };
- };
-
- qcom,pm8941_rtc {
- spmi-dev-container;
- compatible = "qcom,qpnp-rtc";
- #address-cells = <1>;
- #size-cells = <1>;
- qcom,qpnp-rtc-write = <0>;
- qcom,qpnp-rtc-alarm-pwrup = <0>;
-
- qcom,pm8941_rtc_rw@6000 {
- reg = <0x6000 0x100>;
- };
- qcom,pm8941_rtc_alarm@6100 {
- reg = <0x6100 0x100>;
- interrupts = <0x0 0x61 0x1>;
- };
- };
-
- vadc@3100 {
- compatible = "qcom,qpnp-vadc";
- reg = <0x3100 0x100>;
- #address-cells = <1>;
- #size-cells = <0>;
- interrupts = <0x0 0x31 0x0>;
- interrupt-names = "eoc-int-en-set";
- qcom,adc-bit-resolution = <15>;
- qcom,adc-vdd-reference = <1800>;
-
- chan@0 {
- label = "usb_in";
- reg = <0>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <4>;
- qcom,calibration-type = "absolute";
- qcom,scale-function = <0>;
- qcom,hw-settle-time = <0>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@1 {
- label = "dc_in";
- reg = <1>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <4>;
- qcom,calibration-type = "absolute";
- qcom,scale-function = <0>;
- qcom,hw-settle-time = <0>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@2 {
- label = "vchg_sns";
- reg = <2>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <3>;
- qcom,calibration-type = "absolute";
- qcom,scale-function = <0>;
- qcom,hw-settle-time = <0>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@3 {
- label = "spare1_div3";
- reg = <3>;
- 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>;
- };
-
- chan@4 {
- label = "usb_id_mv";
- reg = <4>;
- 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>;
- };
-
- chan@5 {
- label = "vcoin";
- reg = <5>;
- 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>;
- };
-
- chan@6 {
- label = "vbat_sns";
- reg = <6>;
- 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>;
- };
-
- chan@7 {
- label = "vph_pwr";
- reg = <7>;
- 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>;
- };
-
- chan@8 {
- label = "die_temp";
- reg = <8>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "absolute";
- qcom,scale-function = <3>;
- qcom,hw-settle-time = <0>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@9 {
- label = "ref_625mv";
- reg = <9>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "absolute";
- qcom,scale-function = <0>;
- qcom,hw-settle-time = <0>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@a {
- label = "ref_1250v";
- reg = <0xa>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "absolute";
- qcom,scale-function = <0>;
- qcom,hw-settle-time = <0>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@30 {
- label = "batt_therm";
- reg = <0x30>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <1>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@31 {
- label = "batt_id";
- reg = <0x31>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <0>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@b2 {
- label = "xo_therm_pu2";
- reg = <0xb2>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <4>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@b3 {
- label = "msm_therm";
- reg = <0xb3>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@b4 {
- label = "emmc_therm";
- reg = <0xb4>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@b5 {
- label = "pa_therm0";
- reg = <0xb5>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@b7 {
- label = "pa_therm1";
- reg = <0xb7>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@b8 {
- label = "quiet_therm";
- reg = <0xb8>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@b9 {
- label = "usb_id";
- reg = <0xb9>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <0>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
-
- chan@39 {
- label = "usb_id_nopull";
- reg = <0x39>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <0>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <0>;
- };
- };
-
- iadc@3600 {
- compatible = "qcom,qpnp-iadc";
- reg = <0x3600 0x100>;
- #address-cells = <1>;
- #size-cells = <0>;
- interrupts = <0x0 0x36 0x0>;
- interrupt-names = "eoc-int-en-set";
- qcom,adc-bit-resolution = <16>;
- qcom,adc-vdd-reference = <1800>;
-
- chan@0 {
- label = "internal_rsense";
- reg = <0>;
- 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>;
- };
- };
-
- qcom,vadc@3400 {
- compatible = "qcom,qpnp-adc-tm";
- reg = <0x3400 0x100>;
- #address-cells = <1>;
- #size-cells = <0>;
- interrupts = <0x0 0x34 0x0>,
- <0x0 0x34 0x3>,
- <0x0 0x34 0x4>;
- interrupt-names = "eoc-int-en-set",
- "high-thr-en-set",
- "low-thr-en-set";
- qcom,adc-bit-resolution = <15>;
- qcom,adc-vdd-reference = <1800>;
-
- /* Channel Node */
- chan@b9 {
- label = "usb_id";
- reg = <0xb9>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <3>;
- qcom,btm-channel-number = <0x48>;
- };
-
- chan@30 {
- label = "batt_therm";
- reg = <0x30>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <1>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <3>;
- qcom,btm-channel-number = <0x68>;
- };
-
- chan@8 {
- label = "die_temp";
- reg = <8>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "absolute";
- qcom,scale-function = <3>;
- qcom,hw-settle-time = <0>;
- qcom,fast-avg-setup = <3>;
- qcom,btm-channel-number = <0x70>;
- };
-
- chan@6 {
- label = "vbat_sns";
- reg = <6>;
- 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 = <3>;
- qcom,btm-channel-number = <0x78>;
- };
-
- chan@b5 {
- label = "pa_therm0";
- reg = <0xb5>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <3>;
- qcom,btm-channel-number = <0x80>;
- qcom,thermal-node;
- };
-
- chan@b7 {
- label = "pa_therm1";
- reg = <0xb7>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <3>;
- qcom,btm-channel-number = <0x88>;
- qcom,thermal-node;
- };
-
- chan@b4 {
- label = "emmc_therm";
- reg = <0xb4>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <3>;
- qcom,btm-channel-number = <0x90>;
- qcom,thermal-node;
- };
-
- chan@b3 {
- label = "msm_therm";
- reg = <0xb3>;
- qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "ratiometric";
- qcom,scale-function = <2>;
- qcom,hw-settle-time = <2>;
- qcom,fast-avg-setup = <3>;
- qcom,btm-channel-number = <0x98>;
- qcom,thermal-node;
- };
+ qcom,pon_3 {
+ qcom,pon-type = <3>;
+ qcom,support-reset = <1>;
+ qcom,s1-timer = <6720>;
+ qcom,s2-timer = <2000>;
+ qcom,s2-type = <7>;
+ qcom,pull-up = <1>;
};
};
- qcom,pm8941@1 {
- spmi-slave-container;
- reg = <0x1>;
+ 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_coincell: qcom,coincell@2800 {
+ compatible = "qcom,qpnp-coincell";
+ reg = <0x2800 0x100>;
+ };
+
+ pm8941_bms: qcom,bms {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-bms";
#address-cells = <1>;
#size-cells = <1>;
+ status = "disabled";
- regulator@1400 {
- regulator-name = "8941_s1";
- spmi-dev-container;
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "qcom,qpnp-regulator";
- reg = <0x1400 0x300>;
- status = "disabled";
+ qcom,r-sense-uohm = <10000>;
+ qcom,v-cutoff-uv = <3400000>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,r-conn-mohm = <0>;
+ qcom,shutdown-soc-valid-limit = <20>;
+ qcom,adjust-soc-low-threshold = <15>;
+ qcom,ocv-voltage-high-threshold-uv = <3750000>;
+ qcom,ocv-voltage-low-threshold-uv = <3650000>;
+ qcom,low-soc-calculate-soc-threshold = <15>;
+ qcom,low-soc-calculate-soc-ms = <5000>;
+ qcom,calculate-soc-ms = <20000>;
+ qcom,chg-term-ua = <100000>;
+ qcom,batt-type = <0>;
+ qcom,low-voltage-threshold = <3420000>;
+ qcom,tm-temp-margin = <5000>;
+ qcom,low-ocv-correction-limit-uv = <100>;
+ qcom,high-ocv-correction-limit-uv = <50>;
+ qcom,hold-soc-est = <3>;
- qcom,ctl@1400 {
- reg = <0x1400 0x100>;
- };
- qcom,ps@1500 {
- reg = <0x1500 0x100>;
- };
- qcom,freq@1600 {
- reg = <0x1600 0x100>;
- };
+ qcom,bms-iadc@3800 {
+ reg = <0x3800 0x100>;
};
- regulator@1700 {
- regulator-name = "8941_s2";
- spmi-dev-container;
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "qcom,qpnp-regulator";
- reg = <0x1700 0x300>;
- status = "disabled";
-
- qcom,ctl@1700 {
- reg = <0x1700 0x100>;
- };
- qcom,ps@1800 {
- reg = <0x1800 0x100>;
- };
- qcom,freq@1900 {
- reg = <0x1900 0x100>;
- };
- };
-
- regulator@1a00 {
- regulator-name = "8941_s3";
- spmi-dev-container;
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "qcom,qpnp-regulator";
- reg = <0x1a00 0x300>;
- status = "disabled";
-
- qcom,ctl@1a00 {
- reg = <0x1a00 0x100>;
- };
- qcom,ps@1b00 {
- reg = <0x1b00 0x100>;
- };
- qcom,freq@1c00 {
- reg = <0x1c00 0x100>;
- };
- };
-
- regulator@a000 {
- regulator-name = "8941_boost";
- reg = <0xa000 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@4000 {
- regulator-name = "8941_l1";
+ qcom,bms-bms@4000 {
reg = <0x4000 0x100>;
- compatible = "qcom,qpnp-regulator";
+ interrupts = <0x0 0x40 0x0>,
+ <0x0 0x40 0x1>,
+ <0x0 0x40 0x2>,
+ <0x0 0x40 0x3>,
+ <0x0 0x40 0x4>,
+ <0x0 0x40 0x5>,
+ <0x0 0x40 0x6>,
+ <0x0 0x40 0x7>;
+
+ interrupt-names = "cc_thr",
+ "ocv_for_r",
+ "good_ocv",
+ "charge_begin",
+ "ocv_thr",
+ "sw_cc_thr",
+ "vsense_avg",
+ "vsense_for_r";
+ };
+ };
+
+ clkdiv@5b00 {
+ reg = <0x5b00 0x100>;
+ compatible = "qcom,qpnp-clkdiv";
+ qcom,cxo-freq = <19200000>;
+ };
+
+ clkdiv@5c00 {
+ reg = <0x5c00 0x100>;
+ compatible = "qcom,qpnp-clkdiv";
+ qcom,cxo-freq = <19200000>;
+ };
+
+ clkdiv@5d00 {
+ reg = <0x5d00 0x1000>;
+ compatible = "qcom,qpnp-clkdiv";
+ qcom,cxo-freq = <19200000>;
+ };
+
+ pm8941_chg: qcom,charger {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-charger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+
+ qcom,vddmax-mv = <4200>;
+ qcom,vddsafe-mv = <4230>;
+ qcom,vinmin-mv = <4300>;
+ qcom,ibatmax-ma = <1500>;
+ qcom,ibatterm-ma = <100>;
+ qcom,ibatsafe-ma = <1500>;
+ qcom,thermal-mitigation = <1500 700 600 325>;
+ qcom,cool-bat-decidegc = <100>;
+ qcom,cool-bat-mv = <4100>;
+ qcom,ibatmax-warm-ma = <350>;
+ qcom,warm-bat-decidegc = <450>;
+ qcom,warm-bat-mv = <4100>;
+ qcom,ibatmax-cool-ma = <350>;
+ qcom,vbatdet-delta-mv = <100>;
+ qcom,resume-soc = <99>;
+ qcom,tchg-mins = <150>;
+
+ qcom,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";
};
- regulator@4100 {
- regulator-name = "8941_l2";
- reg = <0x4100 0x100>;
- compatible = "qcom,qpnp-regulator";
+ qcom,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";
};
- regulator@4200 {
- regulator-name = "8941_l3";
- reg = <0x4200 0x100>;
- compatible = "qcom,qpnp-regulator";
+ qcom,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";
+
};
- regulator@4300 {
- regulator-name = "8941_l4";
- reg = <0x4300 0x100>;
- compatible = "qcom,qpnp-regulator";
+ pm8941_chg_otg: qcom,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";
};
- regulator@4400 {
- regulator-name = "8941_l5";
- reg = <0x4400 0x100>;
- compatible = "qcom,qpnp-regulator";
- qcom,force-type = <0x04 0x10>;
+ qcom,dc-chgpth@1400 {
status = "disabled";
+ reg = <0x1400 0x100>;
+ interrupts = <0x0 0x14 0x0>,
+ <0x0 0x14 0x1>;
+
+ interrupt-names = "coarse-det-dc",
+ "dcin-valid";
};
- regulator@4500 {
- regulator-name = "8941_l6";
- reg = <0x4500 0x100>;
- compatible = "qcom,qpnp-regulator";
+ pm8941_chg_boost: qcom,boost@1500 {
status = "disabled";
+ reg = <0x1500 0x100>;
+ interrupts = <0x0 0x15 0x0>,
+ <0x0 0x15 0x1>;
+
+ interrupt-names = "boost-pwr-ok",
+ "limit-error";
};
- regulator@4600 {
- regulator-name = "8941_l7";
- reg = <0x4600 0x100>;
- compatible = "qcom,qpnp-regulator";
- qcom,force-type = <0x04 0x10>;
+ qcom,chg-misc@1600 {
status = "disabled";
+ reg = <0x1600 0x100>;
};
+ };
- regulator@4700 {
- regulator-name = "8941_l8";
- reg = <0x4700 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
+ pm8941_gpios: gpios {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8941-gpio";
- regulator@4800 {
- regulator-name = "8941_l9";
- reg = <0x4800 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@4900 {
- regulator-name = "8941_l10";
- reg = <0x4900 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@4a00 {
- regulator-name = "8941_l11";
- reg = <0x4a00 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@4b00 {
- regulator-name = "8941_l12";
- reg = <0x4b00 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@4c00 {
- regulator-name = "8941_l13";
- reg = <0x4c00 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@4d00 {
- regulator-name = "8941_l14";
- reg = <0x4d00 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@4e00 {
- regulator-name = "8941_l15";
- reg = <0x4e00 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@4f00 {
- regulator-name = "8941_l16";
- reg = <0x4f00 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@5000 {
- regulator-name = "8941_l17";
- reg = <0x5000 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@5100 {
- regulator-name = "8941_l18";
- reg = <0x5100 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@5200 {
- regulator-name = "8941_l19";
- reg = <0x5200 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@5300 {
- regulator-name = "8941_l20";
- reg = <0x5300 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@5400 {
- regulator-name = "8941_l21";
- reg = <0x5400 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@5500 {
- regulator-name = "8941_l22";
- reg = <0x5500 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@5600 {
- regulator-name = "8941_l23";
- reg = <0x5600 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@5700 {
- regulator-name = "8941_l24";
- reg = <0x5700 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@8000 {
- regulator-name = "8941_lvs1";
- reg = <0x8000 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@8100 {
- regulator-name = "8941_lvs2";
- reg = <0x8100 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@8200 {
- regulator-name = "8941_lvs3";
- reg = <0x8200 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@8300 {
- regulator-name = "8941_mvs1";
- reg = <0x8300 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- regulator@8400 {
- regulator-name = "8941_mvs2";
- reg = <0x8400 0x100>;
- compatible = "qcom,qpnp-regulator";
- status = "disabled";
- };
-
- qcom,vibrator@c000 {
- compatible = "qcom,qpnp-vibrator";
+ gpio@c000 {
reg = <0xc000 0x100>;
- label = "vibrator";
- status = "disabled";
+ qcom,pin-num = <1>;
};
- qcom,leds@d000 {
- compatible = "qcom,leds-qpnp";
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ };
+
+ gpio@c800 {
+ reg = <0xc800 0x100>;
+ qcom,pin-num = <9>;
+ };
+
+ gpio@c900 {
+ reg = <0xc900 0x100>;
+ qcom,pin-num = <10>;
+ };
+
+ gpio@ca00 {
+ reg = <0xca00 0x100>;
+ qcom,pin-num = <11>;
+ };
+
+ gpio@cb00 {
+ reg = <0xcb00 0x100>;
+ qcom,pin-num = <12>;
+ };
+
+ gpio@cc00 {
+ reg = <0xcc00 0x100>;
+ qcom,pin-num = <13>;
+ };
+
+ gpio@cd00 {
+ reg = <0xcd00 0x100>;
+ qcom,pin-num = <14>;
+ };
+
+ gpio@ce00 {
+ reg = <0xce00 0x100>;
+ qcom,pin-num = <15>;
+ };
+
+ gpio@cf00 {
+ reg = <0xcf00 0x100>;
+ qcom,pin-num = <16>;
+ };
+
+ gpio@d000 {
reg = <0xd000 0x100>;
- label = "rgb";
+ qcom,pin-num = <17>;
};
- qcom,leds@d100 {
- compatible = "qcom,leds-qpnp";
+ gpio@d100 {
reg = <0xd100 0x100>;
- label = "rgb";
+ qcom,pin-num = <18>;
};
- qcom,leds@d200 {
- compatible = "qcom,leds-qpnp";
+ gpio@d200 {
reg = <0xd200 0x100>;
- label = "rgb";
+ qcom,pin-num = <19>;
};
- qcom,leds@d300 {
- compatible = "qcom,leds-qpnp";
+ gpio@d300 {
reg = <0xd300 0x100>;
- label = "flash";
- flash_boost-supply = <&pm8941_chg_boost>;
+ qcom,pin-num = <20>;
};
- qcom,leds@d400 {
- compatible = "qcom,leds-qpnp";
+ gpio@d400 {
reg = <0xd400 0x100>;
- label = "flash";
+ qcom,pin-num = <21>;
};
- qcom,leds@d500 {
- compatible = "qcom,leds-qpnp";
+ gpio@d500 {
reg = <0xd500 0x100>;
- label = "flash";
+ qcom,pin-num = <22>;
};
- qcom,leds@d600 {
- compatible = "qcom,leds-qpnp";
+ gpio@d600 {
reg = <0xd600 0x100>;
- label = "flash";
+ qcom,pin-num = <23>;
};
- qcom,leds@d700 {
- compatible = "qcom,leds-qpnp";
+ gpio@d700 {
reg = <0xd700 0x100>;
- label = "flash";
+ qcom,pin-num = <24>;
};
- qcom,leds@d800 {
- compatible = "qcom,leds-qpnp";
+ gpio@d800 {
reg = <0xd800 0x100>;
- label = "wled";
+ qcom,pin-num = <25>;
};
- qcom,leds@d900 {
- compatible = "qcom,leds-qpnp";
+ gpio@d900 {
reg = <0xd900 0x100>;
- label = "wled";
+ qcom,pin-num = <26>;
};
- qcom,leds@da00 {
- compatible = "qcom,leds-qpnp";
+ gpio@da00 {
reg = <0xda00 0x100>;
- label = "wled";
+ qcom,pin-num = <27>;
};
- qcom,leds@db00 {
- compatible = "qcom,leds-qpnp";
+ gpio@db00 {
reg = <0xdb00 0x100>;
- label = "wled";
+ qcom,pin-num = <28>;
};
- qcom,leds@dc00 {
- compatible = "qcom,leds-qpnp";
+ gpio@dc00 {
reg = <0xdc00 0x100>;
- label = "wled";
+ qcom,pin-num = <29>;
};
- qcom,leds@dd00 {
- compatible = "qcom,leds-qpnp";
+ gpio@dd00 {
reg = <0xdd00 0x100>;
- label = "wled";
+ qcom,pin-num = <30>;
};
- qcom,leds@de00 {
- compatible = "qcom,leds-qpnp";
+ gpio@de00 {
reg = <0xde00 0x100>;
- label = "wled";
+ qcom,pin-num = <31>;
};
- qcom,leds@df00 {
- compatible = "qcom,leds-qpnp";
+ gpio@df00 {
reg = <0xdf00 0x100>;
- label = "wled";
+ qcom,pin-num = <32>;
};
- qcom,leds@e000 {
- compatible = "qcom,leds-qpnp";
+ gpio@e000 {
reg = <0xe000 0x100>;
- label = "wled";
+ qcom,pin-num = <33>;
};
- qcom,leds@e100 {
- compatible = "qcom,leds-qpnp";
+ gpio@e100 {
reg = <0xe100 0x100>;
- label = "wled";
+ qcom,pin-num = <34>;
};
- pwm@b100 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb100 0x100>,
- <0xb042 0x7e>;
- reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
- qcom,channel-id = <0>;
+ gpio@e200 {
+ reg = <0xe200 0x100>;
+ qcom,pin-num = <35>;
};
- pwm@b200 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb200 0x100>,
- <0xb042 0x7e>;
- reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
- qcom,channel-id = <1>;
+ gpio@e300 {
+ reg = <0xe300 0x100>;
+ qcom,pin-num = <36>;
+ };
+ };
+
+ pm8941_mpps: mpps {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8941-mpp";
+
+ mpp@a000 {
+ reg = <0xa000 0x100>;
+ qcom,pin-num = <1>;
};
- pwm@b300 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb300 0x100>,
- <0xb042 0x7e>;
- reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
- qcom,channel-id = <2>;
+ mpp@a100 {
+ reg = <0xa100 0x100>;
+ qcom,pin-num = <2>;
};
- pwm@b400 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb400 0x100>,
- <0xb042 0x7e>;
- reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
- qcom,channel-id = <3>;
+ mpp@a200 {
+ reg = <0xa200 0x100>;
+ qcom,pin-num = <3>;
};
- pwm@b500 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb500 0x100>,
- <0xb042 0x7e>;
- reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
- qcom,channel-id = <4>;
+ mpp@a300 {
+ reg = <0xa300 0x100>;
+ qcom,pin-num = <4>;
};
- pwm@b600 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb600 0x100>,
- <0xb042 0x7e>;
- reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
- qcom,channel-id = <5>;
+ mpp@a400 {
+ reg = <0xa400 0x100>;
+ qcom,pin-num = <5>;
};
- pwm@b700 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb700 0x100>,
- <0xb042 0x7e>;
- reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
- qcom,channel-id = <6>;
+ mpp@a500 {
+ reg = <0xa500 0x100>;
+ qcom,pin-num = <6>;
};
- pwm@b800 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb800 0x100>,
- <0xb042 0x7e>;
- reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
- qcom,channel-id = <7>;
+ mpp@a600 {
+ reg = <0xa600 0x100>;
+ qcom,pin-num = <7>;
};
+
+ mpp@a700 {
+ reg = <0xa700 0x100>;
+ qcom,pin-num = <8>;
+ };
+ };
+
+ qcom,pm8941_rtc {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-rtc";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,qpnp-rtc-write = <0>;
+ qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+ qcom,pm8941_rtc_rw@6000 {
+ reg = <0x6000 0x100>;
+ };
+ qcom,pm8941_rtc_alarm@6100 {
+ reg = <0x6100 0x100>;
+ interrupts = <0x0 0x61 0x1>;
+ };
+ };
+
+ pm8941_vadc: vadc@3100 {
+ compatible = "qcom,qpnp-vadc";
+ reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x31 0x0>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+
+ chan@0 {
+ label = "usb_in";
+ reg = <0>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <4>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@1 {
+ label = "dc_in";
+ reg = <1>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <4>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@2 {
+ label = "vchg_sns";
+ reg = <2>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <3>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@3 {
+ label = "spare1_div3";
+ reg = <3>;
+ 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>;
+ };
+
+ chan@4 {
+ label = "usb_id_mv";
+ reg = <4>;
+ 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>;
+ };
+
+ chan@5 {
+ label = "vcoin";
+ reg = <5>;
+ 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>;
+ };
+
+ chan@6 {
+ label = "vbat_sns";
+ reg = <6>;
+ 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>;
+ };
+
+ chan@7 {
+ label = "vph_pwr";
+ reg = <7>;
+ 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>;
+ };
+
+ chan@8 {
+ label = "die_temp";
+ reg = <8>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@9 {
+ label = "ref_625mv";
+ reg = <9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@a {
+ label = "ref_1250v";
+ reg = <0xa>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@30 {
+ label = "batt_therm";
+ reg = <0x30>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <1>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@31 {
+ label = "batt_id";
+ reg = <0x31>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@b2 {
+ label = "xo_therm_pu2";
+ reg = <0xb2>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@b3 {
+ label = "msm_therm";
+ reg = <0xb3>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@b4 {
+ label = "emmc_therm";
+ reg = <0xb4>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@b5 {
+ label = "pa_therm0";
+ reg = <0xb5>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@b7 {
+ label = "pa_therm1";
+ reg = <0xb7>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@b8 {
+ label = "quiet_therm";
+ reg = <0xb8>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@b9 {
+ label = "usb_id";
+ reg = <0xb9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@39 {
+ label = "usb_id_nopull";
+ reg = <0x39>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+ };
+
+ pm8941_iadc: iadc@3600 {
+ compatible = "qcom,qpnp-iadc";
+ reg = <0x3600 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x36 0x0>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-bit-resolution = <16>;
+ qcom,adc-vdd-reference = <1800>;
+
+ chan@0 {
+ label = "internal_rsense";
+ reg = <0>;
+ 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>;
+ };
+ };
+
+ pm8941_adc_tm: qcom,vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+
+ /* Channel Node */
+ chan@b9 {
+ label = "usb_id";
+ reg = <0xb9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x48>;
+ };
+
+ chan@30 {
+ label = "batt_therm";
+ reg = <0x30>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <1>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x68>;
+ };
+
+ chan@8 {
+ label = "die_temp";
+ reg = <8>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x70>;
+ };
+
+ chan@6 {
+ label = "vbat_sns";
+ reg = <6>;
+ 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 = <3>;
+ qcom,btm-channel-number = <0x78>;
+ };
+
+ chan@b5 {
+ label = "pa_therm0";
+ reg = <0xb5>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x80>;
+ qcom,thermal-node;
+ };
+
+ chan@b7 {
+ label = "pa_therm1";
+ reg = <0xb7>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x88>;
+ qcom,thermal-node;
+ };
+
+ chan@b4 {
+ label = "emmc_therm";
+ reg = <0xb4>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x90>;
+ qcom,thermal-node;
+ };
+
+ chan@b3 {
+ label = "msm_therm";
+ reg = <0xb3>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x98>;
+ qcom,thermal-node;
+ };
+ };
+};
+
+&pm8941_lsid1 {
+ spmi-slave-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ regulator@1400 {
+ regulator-name = "8941_s1";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1400 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1400 {
+ reg = <0x1400 0x100>;
+ };
+ qcom,ps@1500 {
+ reg = <0x1500 0x100>;
+ };
+ qcom,freq@1600 {
+ reg = <0x1600 0x100>;
+ };
+ };
+
+ regulator@1700 {
+ regulator-name = "8941_s2";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1700 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1700 {
+ reg = <0x1700 0x100>;
+ };
+ qcom,ps@1800 {
+ reg = <0x1800 0x100>;
+ };
+ qcom,freq@1900 {
+ reg = <0x1900 0x100>;
+ };
+ };
+
+ regulator@1a00 {
+ regulator-name = "8941_s3";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1a00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1a00 {
+ reg = <0x1a00 0x100>;
+ };
+ qcom,ps@1b00 {
+ reg = <0x1b00 0x100>;
+ };
+ qcom,freq@1c00 {
+ reg = <0x1c00 0x100>;
+ };
+ };
+
+ regulator@a000 {
+ regulator-name = "8941_boost";
+ reg = <0xa000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4000 {
+ regulator-name = "8941_l1";
+ reg = <0x4000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4100 {
+ regulator-name = "8941_l2";
+ reg = <0x4100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4200 {
+ regulator-name = "8941_l3";
+ reg = <0x4200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4300 {
+ regulator-name = "8941_l4";
+ reg = <0x4300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4400 {
+ regulator-name = "8941_l5";
+ reg = <0x4400 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ qcom,force-type = <0x04 0x10>;
+ status = "disabled";
+ };
+
+ regulator@4500 {
+ regulator-name = "8941_l6";
+ reg = <0x4500 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4600 {
+ regulator-name = "8941_l7";
+ reg = <0x4600 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ qcom,force-type = <0x04 0x10>;
+ status = "disabled";
+ };
+
+ regulator@4700 {
+ regulator-name = "8941_l8";
+ reg = <0x4700 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4800 {
+ regulator-name = "8941_l9";
+ reg = <0x4800 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4900 {
+ regulator-name = "8941_l10";
+ reg = <0x4900 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4a00 {
+ regulator-name = "8941_l11";
+ reg = <0x4a00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4b00 {
+ regulator-name = "8941_l12";
+ reg = <0x4b00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4c00 {
+ regulator-name = "8941_l13";
+ reg = <0x4c00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4d00 {
+ regulator-name = "8941_l14";
+ reg = <0x4d00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4e00 {
+ regulator-name = "8941_l15";
+ reg = <0x4e00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4f00 {
+ regulator-name = "8941_l16";
+ reg = <0x4f00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5000 {
+ regulator-name = "8941_l17";
+ reg = <0x5000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5100 {
+ regulator-name = "8941_l18";
+ reg = <0x5100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5200 {
+ regulator-name = "8941_l19";
+ reg = <0x5200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5300 {
+ regulator-name = "8941_l20";
+ reg = <0x5300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5400 {
+ regulator-name = "8941_l21";
+ reg = <0x5400 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5500 {
+ regulator-name = "8941_l22";
+ reg = <0x5500 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5600 {
+ regulator-name = "8941_l23";
+ reg = <0x5600 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5700 {
+ regulator-name = "8941_l24";
+ reg = <0x5700 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8000 {
+ regulator-name = "8941_lvs1";
+ reg = <0x8000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8100 {
+ regulator-name = "8941_lvs2";
+ reg = <0x8100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8200 {
+ regulator-name = "8941_lvs3";
+ reg = <0x8200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8300 {
+ regulator-name = "8941_mvs1";
+ reg = <0x8300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8400 {
+ regulator-name = "8941_mvs2";
+ reg = <0x8400 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ qcom,vibrator@c000 {
+ compatible = "qcom,qpnp-vibrator";
+ reg = <0xc000 0x100>;
+ label = "vibrator";
+ status = "disabled";
+ };
+
+ qcom,leds@d000 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd000 0x100>;
+ label = "rgb";
+ };
+
+ qcom,leds@d100 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd100 0x100>;
+ label = "rgb";
+ };
+
+ qcom,leds@d200 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd200 0x100>;
+ label = "rgb";
+ };
+
+ qcom,leds@d300 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd300 0x100>;
+ label = "flash";
+ flash_boost-supply = <&pm8941_chg_boost>;
+ };
+
+ qcom,leds@d400 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd400 0x100>;
+ label = "flash";
+ };
+
+ qcom,leds@d500 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd500 0x100>;
+ label = "flash";
+ };
+
+ qcom,leds@d600 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd600 0x100>;
+ label = "flash";
+ };
+
+ qcom,leds@d700 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd700 0x100>;
+ label = "flash";
+ };
+
+ qcom,leds@d800 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd800 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@d900 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd900 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@da00 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xda00 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@db00 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xdb00 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@dc00 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xdc00 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@dd00 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xdd00 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@de00 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xde00 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@df00 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xdf00 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@e000 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xe000 0x100>;
+ label = "wled";
+ };
+
+ qcom,leds@e100 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xe100 0x100>;
+ label = "wled";
+ };
+
+ pwm@b100 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb100 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <0>;
+ };
+
+ pwm@b200 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb200 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <1>;
+ };
+
+ pwm@b300 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb300 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <2>;
+ };
+
+ pwm@b400 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb400 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <3>;
+ };
+
+ pwm@b500 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb500 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <4>;
+ };
+
+ pwm@b600 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb600 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <5>;
+ };
+
+ pwm@b700 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb700 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <6>;
+ };
+
+ pwm@b800 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb800 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <7>;
};
};
diff --git a/arch/arm/boot/dts/msm-pma8084-rpm-regulator.dtsi b/arch/arm/boot/dts/msm-pma8084-rpm-regulator.dtsi
new file mode 100644
index 0000000..31796dd
--- /dev/null
+++ b/arch/arm/boot/dts/msm-pma8084-rpm-regulator.dtsi
@@ -0,0 +1,712 @@
+/* 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.
+ */
+
+&rpm_bus {
+ rpm-regulator-smpa1 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <1>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s1 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s1";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa2 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <2>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s2 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s2";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa3 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <3>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s3 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s3";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa4 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <4>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s4 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s4";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa5 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <5>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s5 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s5";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa6 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <6>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s6 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s6";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa7 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <7>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s7 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s7";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa8 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <8>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s8 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s8";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa9 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <9>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s9 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s9";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa10 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <10>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s10 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s10";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa11 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <11>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s11 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s11";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-smpa12 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpa";
+ qcom,resource-id = <12>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+ status = "disabled";
+
+ regulator-s12 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s12";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa1 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <1>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l1 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l1";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa2 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <2>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l2 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l2";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa3 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <3>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l3 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l3";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa4 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <4>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l4 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l4";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa5 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <5>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l5 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l5";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa6 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <6>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l6 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l6";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa7 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <7>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l7 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l7";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa8 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <8>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l8 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l8";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa9 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <9>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l9 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l9";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa10 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <10>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l10 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l10";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa11 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <11>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l11 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l11";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa12 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <12>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l12 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l12";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa13 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <13>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l13 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l13";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa14 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <14>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l14 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l14";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa15 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <15>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l15 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l15";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa16 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <16>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l16 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l16";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa17 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <17>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l17 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l17";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa18 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <18>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l18 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l18";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa19 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <19>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l19 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l19";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa20 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <20>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l20 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l20";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa21 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <21>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l21 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l21";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa22 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <22>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l22 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l22";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa23 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <23>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l23 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l23";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa24 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <24>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l24 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l24";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa25 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <25>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l25 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l25";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa26 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <26>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l26 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l26";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-ldoa27 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <27>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+ status = "disabled";
+
+ regulator-l27 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l27";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-vsa1 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "vsa";
+ qcom,resource-id = <1>;
+ qcom,regulator-type = <2>;
+ status = "disabled";
+
+ regulator-lvs1 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_lvs1";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-vsa2 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "vsa";
+ qcom,resource-id = <2>;
+ qcom,regulator-type = <2>;
+ status = "disabled";
+
+ regulator-lvs2 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_lvs2";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-vsa3 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "vsa";
+ qcom,resource-id = <3>;
+ qcom,regulator-type = <2>;
+ status = "disabled";
+
+ regulator-lvs3 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_lvs3";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-vsa4 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "vsa";
+ qcom,resource-id = <4>;
+ qcom,regulator-type = <2>;
+ status = "disabled";
+
+ regulator-lvs4 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_lvs4";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+
+ rpm-regulator-vsa5 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "vsa";
+ qcom,resource-id = <5>;
+ qcom,regulator-type = <2>;
+ status = "disabled";
+
+ regulator-mvs1 {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_mvs1";
+ qcom,set = <3>;
+ status = "disabled";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/msm-pma8084.dtsi b/arch/arm/boot/dts/msm-pma8084.dtsi
index 42c48f8..ecbfc53 100644
--- a/arch/arm/boot/dts/msm-pma8084.dtsi
+++ b/arch/arm/boot/dts/msm-pma8084.dtsi
@@ -22,6 +22,68 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0>,
+ <0x0 0x8 0x1>,
+ <0x0 0x8 0x4>,
+ <0x0 0x8 0x5>;
+ interrupt-names = "kpdpwr", "resin",
+ "resin-bark", "kpdpwr-resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+ qcom,s3-debounce = <32>;
+
+ 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>;
+ };
+
+ qcom,pon_3 {
+ qcom,pon-type = <3>;
+ qcom,support-reset = <1>;
+ qcom,s1-timer = <6720>;
+ qcom,s2-timer = <2000>;
+ qcom,s2-type = <7>;
+ qcom,pull-up = <1>;
+ };
+ };
+
+ pma8084_misc: qcom,misc@900 {
+ compatible = "qcom,qpnp-misc";
+ reg = <0x900 0x100>;
+ };
+
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pma8084_tz";
+ qcom,threshold-set = <0>;
+ };
+
+ qcom,coincell@2800 {
+ compatible = "qcom,qpnp-coincell";
+ reg = <0x2800 0x100>;
+ };
+
pma8084_gpios: gpios {
spmi-dev-container;
compatible = "qcom,qpnp-pin";
@@ -261,6 +323,23 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
};
+
+ qcom,rtc {
+ compatible = "qcom,qpnp-rtc";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,qpnp-rtc-write = <0>;
+ qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+ qcom,rtc_rw@6000 {
+ reg = <0x6000 0x100>;
+ };
+ qcom,rtc_alarm@6100 {
+ reg = <0x6100 0x100>;
+ interrupts = <0x0 0x61 0x1>;
+ };
+ };
};
qcom,pma8084@1 {
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
index 41d6b7e..d4b605b 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
@@ -105,4 +105,41 @@
qcom,cci-master = <0>;
status = "ok";
};
+
+ qcom,camera@90 {
+ compatible = "qcom,mt9m114";
+ reg = <0x90 0x0>;
+ qcom,slave-id = <0x90 0x0 0x2481>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,sensor-name = "mt9m114";
+ cam_vdig-supply = <&pm8226_l5>;
+ cam_vana-supply = <&pm8226_l19>;
+ cam_vio-supply = <&pm8226_lvs1>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 1 0>;
+ qcom,cam-vreg-min-voltage = <1200000 0 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 26 0>,
+ <&msmgpio 28 0>,
+ <&msmgpio 35 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-cdp.dtsi b/arch/arm/boot/dts/msm8226-cdp.dtsi
index 308a6f5..259dfdb 100644
--- a/arch/arm/boot/dts/msm8226-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-cdp.dtsi
@@ -20,6 +20,7 @@
qcom,mdss_dsi_nt35590_720p_video {
status = "ok";
+ qcom,cont-splash-enabled;
};
i2c@f9927000 { /* BLSP1 QUP5 */
@@ -226,6 +227,16 @@
status = "ok";
};
+&sdcc3 {
+ qcom,sup-voltages = <1800 1800>;
+ status = "disabled";
+};
+
+&sdhc_3 {
+ qcom,sup-voltages = <1800 1800>;
+ status = "disabled";
+};
+
&spmi_bus {
qcom,pm8226@0 {
qcom,leds@a100 {
@@ -260,8 +271,12 @@
qcom,mode-ctrl = <0x60>;
qcom,pwm-channel = <0>;
qcom,start-idx = <1>;
- qcom,duty-pcts = [00 00 00 00 64
- 64 00 00 00 00];
+ qcom,ramp-step-ms = <120>;
+ qcom,duty-pcts = [00 00 00 00 00
+ 00 00 00 00 00
+ 50 00 00 00 00
+ 00 00 00 00 00
+ 00];
qcom,use-blink;
};
};
@@ -282,8 +297,12 @@
qcom,source-sel = <10>;
qcom,pwm-channel = <5>;
qcom,start-idx = <1>;
- qcom,duty-pcts = [00 00 00 00 64
- 64 00 00 00 00];
+ qcom,ramp-step-ms = <120>;
+ qcom,duty-pcts = [00 00 00 00 00
+ 00 00 00 00 00
+ 50 00 00 00 00
+ 00 00 00 00 00
+ 00];
qcom,use-blink;
};
};
diff --git a/arch/arm/boot/dts/msm8226-coresight.dtsi b/arch/arm/boot/dts/msm8226-coresight.dtsi
index e11c963..11f6369 100644
--- a/arch/arm/boot/dts/msm8226-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8226-coresight.dtsi
@@ -365,8 +365,9 @@
compatible = "qcom,coresight-hwevent";
reg = <0xfd828018 0x80>,
<0xf9011080 0x80>,
- <0xfd4ab160 0x80>;
- reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <29>;
coresight-name = "coresight-hwevent";
@@ -374,4 +375,14 @@
qcom,hwevent-clks = "core_mmss_clk";
};
+
+ fuse: fuse@fc4be024 {
+ compatible = "arm,coresight-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+
+ coresight-id = <30>;
+ coresight-name = "coresight-fuse";
+ coresight-nr-inports = <0>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-ion.dtsi b/arch/arm/boot/dts/msm8226-ion.dtsi
index dee64e5..9574b7d 100644
--- a/arch/arm/boot/dts/msm8226-ion.dtsi
+++ b/arch/arm/boot/dts/msm8226-ion.dtsi
@@ -35,6 +35,13 @@
reg = <25>;
};
+ qcom,ion-heap@22 { /* adsp heap */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <22>;
+ qcom,heap-align = <0x1000>;
+ linux,contiguous-region = <&adsp_mem>;
+ };
+
qcom,ion-heap@27 { /* QSECOM HEAP */
compatible = "qcom,msm-ion-reserve";
reg = <27>;
diff --git a/arch/arm/boot/dts/msm8226-mdss.dtsi b/arch/arm/boot/dts/msm8226-mdss.dtsi
index f580897..b895c64 100644
--- a/arch/arm/boot/dts/msm8226-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8226-mdss.dtsi
@@ -36,6 +36,7 @@
qcom,mdss-wb-off = <0x00011100 0x00013100>;
qcom,mdss-intf-off = <0x00000000 0x00021300>;
qcom,mdss-rot-block-size = <64>;
+ qcom,mdss-smp-mb-per-pipe = <2>;
qcom,vbif-settings = <0x004 0x00000001>,
<0x0D8 0x00000707>,
diff --git a/arch/arm/boot/dts/msm8226-mtp.dtsi b/arch/arm/boot/dts/msm8226-mtp.dtsi
index acab5e4..4b2a4ad 100644
--- a/arch/arm/boot/dts/msm8226-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8226-mtp.dtsi
@@ -20,6 +20,7 @@
qcom,mdss_dsi_nt35590_720p_video {
status = "ok";
+ qcom,cont-splash-enabled;
};
i2c@f9927000 { /* BLSP1 QUP5 */
@@ -37,6 +38,17 @@
};
};
+ i2c@f9925000 { /* BLSP1 QUP3 */
+ nfc-nci@0e {
+ compatible = "qcom,nfc-nci";
+ reg = <0x0e>;
+ qcom,irq-gpio = <&msmgpio 21 0x00>;
+ qcom,dis-gpio = <&msmgpio 20 0x00>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <21 0>;
+ qcom,clk-gpio = <&pm8226_gpios 3 0>;
+ };
+ };
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
@@ -263,8 +275,12 @@
qcom,mode-ctrl = <0x60>;
qcom,pwm-channel = <0>;
qcom,start-idx = <1>;
- qcom,duty-pcts = [00 00 00 00 64
- 64 00 00 00 00];
+ qcom,ramp-step-ms = <120>;
+ qcom,duty-pcts = [00 00 00 00 00
+ 00 00 00 00 00
+ 50 00 00 00 00
+ 00 00 00 00 00
+ 00];
qcom,use-blink;
};
};
@@ -285,8 +301,12 @@
qcom,source-sel = <10>;
qcom,pwm-channel = <5>;
qcom,start-idx = <1>;
- qcom,duty-pcts = [00 00 00 00 64
- 64 00 00 00 00];
+ qcom,ramp-step-ms = <120>;
+ qcom,duty-pcts = [00 00 00 00 00
+ 00 00 00 00 00
+ 50 00 00 00 00
+ 00 00 00 00 00
+ 00];
qcom,use-blink;
};
};
@@ -342,6 +362,11 @@
};
gpio@c200 { /* GPIO 3 */
+ qcom,mode = <0>; /* QPNP_PIN_MODE_DIG_IN */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,src-sel = <2>; /* QPNP_PIN_SEL_FUNC_1 */
+ qcom,master-en = <1>;
};
gpio@c300 { /* GPIO 4 */
diff --git a/arch/arm/boot/dts/msm8226-pm.dtsi b/arch/arm/boot/dts/msm8226-pm.dtsi
index 703ba4b..d9bd5e8 100644
--- a/arch/arm/boot/dts/msm8226-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-pm.dtsi
@@ -18,7 +18,7 @@
reg = <0xf9089000 0x1000>;
qcom,core-id = <0>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -35,7 +35,7 @@
reg = <0xf9099000 0x1000>;
qcom,core-id = <1>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -52,7 +52,7 @@
reg = <0xf90a9000 0x1000>;
qcom,core-id = <2>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -69,7 +69,7 @@
reg = <0xf90b9000 0x1000>;
qcom,core-id = <3>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -367,6 +367,13 @@
qcom,pc-resets-timer;
};
+ qcom,cpu-sleep-status@f9088008{
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x100>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
+ };
+
qcom,rpm-log@fc19dc00 {
compatible = "qcom,rpm-log";
reg = <0xfc19dc00 0x4000>;
@@ -384,4 +391,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-qrd.dtsi b/arch/arm/boot/dts/msm8226-qrd.dtsi
index 7018c6a..8af5ef5 100644
--- a/arch/arm/boot/dts/msm8226-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-qrd.dtsi
@@ -10,16 +10,13 @@
* GNU General Public License for more details.
*/
-/include/ "dsi-panel-nt35590-720p-video.dtsi"
+/include/ "dsi-panel-nt35596-1080p-video.dtsi"
/include/ "msm8226-camera-sensor-qrd.dtsi"
&soc {
serial@f991f000 {
status = "ok";
- };
-
- qcom,mdss_dsi_nt35590_720p_video {
- status = "ok";
+ qcom,cont-splash-enabled;
};
i2c@f9927000 { /* BLSP1 QUP5 */
@@ -35,6 +32,33 @@
synaptics,button-map = <139 102 158>;
synaptics,i2c-pull-up;
};
+ focaltech@38 {
+ compatible = "focaltech,5x06";
+ reg = <0x38>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <17 0x2>;
+ vdd-supply = <&pm8226_l19>;
+ vcc_i2c-supply = <&pm8226_lvs1>;
+ focaltech,family-id = <0x55>;
+ focaltech,reset-gpio = <&msmgpio 16 0x00>;
+ focaltech,irq-gpio = <&msmgpio 17 0x00>;
+ focaltech,display-coords = <0 0 1080 1920>;
+ focaltech,panel-coords = <0 0 1080 2000>;
+ focaltech,button-map= <139 102 158>;
+ focaltech,no-force-update;
+ focaltech,i2c-pull-up;
+ };
+ };
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "ft5x06_ts";
+ qcom,disp-maxx = <1080>;
+ qcom,disp-maxy = <1920>;
+ qcom,panel-maxx = <1080>;
+ qcom,panel-maxy = <2080>;
+ qcom,key-codes = <139 102 158>;
+ qcom,y-offset = <0>;
};
gpio_keys {
@@ -156,7 +180,7 @@
&sdcc2 {
vdd-supply = <&pm8226_l18>;
qcom,vdd-voltage-level = <2950000 2950000>;
- qcom,vdd-current-level = <9000 800000>;
+ qcom,vdd-current-level = <9000 400000>;
vdd-io-supply = <&pm8226_l21>;
qcom,vdd-io-voltage-level = <1800000 2950000>;
@@ -172,7 +196,7 @@
qcom,xpc;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
- qcom,current-limit = <600>;
+ qcom,current-limit = <400>;
#address-cells = <0>;
interrupt-parent = <&sdcc2>;
@@ -191,7 +215,7 @@
&sdhc_2 {
vdd-supply = <&pm8226_l18>;
qcom,vdd-voltage-level = <2950000 2950000>;
- qcom,vdd-current-level = <9000 800000>;
+ qcom,vdd-current-level = <9000 400000>;
vdd-io-supply = <&pm8226_l21>;
qcom,vdd-io-voltage-level = <1800000 2950000>;
@@ -241,7 +265,7 @@
qcom,led_mpp_4 {
label = "mpp";
linux,name = "green";
- linux,default-trigger = "none";
+ linux,default-trigger = "battery-full";
qcom,default-state = "off";
qcom,max-current = <40>;
qcom,current-setting = <5>;
@@ -252,8 +276,12 @@
qcom,mode-ctrl = <0x60>;
qcom,pwm-channel = <0>;
qcom,start-idx = <1>;
- qcom,duty-pcts = [00 00 00 00 64
- 64 00 00 00 00];
+ qcom,ramp-step-ms = <120>;
+ qcom,duty-pcts = [00 00 00 00 00
+ 00 00 00 00 00
+ 50 00 00 00 00
+ 00 00 00 00 00
+ 00];
qcom,use-blink;
};
};
@@ -263,7 +291,7 @@
qcom,led_mpp_6 {
label = "mpp";
linux,name = "red";
- linux,default-trigger = "none";
+ linux,default-trigger = "battery-charging";
qcom,default-state = "off";
qcom,max-current = <40>;
qcom,current-setting = <5>;
@@ -274,8 +302,12 @@
qcom,source-sel = <10>;
qcom,pwm-channel = <5>;
qcom,start-idx = <1>;
- qcom,duty-pcts = [00 00 00 00 64
- 64 00 00 00 00];
+ qcom,ramp-step-ms = <120>;
+ qcom,duty-pcts = [00 00 00 00 00
+ 00 00 00 00 00
+ 50 00 00 00 00
+ 00 00 00 00 00
+ 00];
qcom,use-blink;
};
};
@@ -323,8 +355,9 @@
&pm8226_chg {
status = "okay";
- qcom,chg-vddmax-mv = <4350>;
- qcom,chg-vddsafe-mv = <4350>;
+ qcom,vddmax-mv = <4350>;
+ qcom,vddsafe-mv = <4380>;
+ qcom,tchg-mins = <240>;
};
&pm8226_gpios {
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index e1a0f0b..571ddc3 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -30,19 +30,22 @@
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>, <0xf9011064 4>, <0xfc4b80b0 8>,
- <0xfc4bc450 16>;
- reg-names = "rbcpr", "rbcpr_clk", "pvs_efuse", "cpr_efuse";
+ reg = <0xf9018000 0x1000>, <0xf9011064 4>, <0xfc4b8000 0x1000>;
+ reg-names = "rbcpr", "rbcpr_clk", "efuse_addr";
interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
regulator-max-microvolt = <3>;
- qcom,num-efuse-bits = <5>;
+
+ qcom,pvs-fuse-redun-sel = <22 24 3 2>;
+ qcom,pvs-fuse = <22 6 5>;
+ qcom,pvs-fuse-redun = <22 27 5>;
+
qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
qcom,pvs-corner-ceiling-slow = <1155000 1160000 1275000>;
- qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
+ qcom,pvs-corner-ceiling-nom = <1050000 1075000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <1050000 1050000 1140000>;
vdd-apc-supply = <&pm8226_s2>;
vdd-mx-supply = <&pm8226_l3_ao>;
@@ -63,6 +66,16 @@
qcom,vdd-apc-step-down-limit = <1>;
qcom,cpr-apc-volt-step = <5000>;
+ qcom,cpr-fuse-redun-sel = <138 57 1 1>;
+ qcom,cpr-fuse-row = <138>;
+ qcom,cpr-fuse-bp-cpr-disable = <36>;
+ qcom,cpr-fuse-bp-scheme = <37>;
+ qcom,cpr-fuse-target-quot = <24 12 0>;
+ qcom,cpr-fuse-ro-sel = <54 38 41>;
+ qcom,cpr-fuse-redun-row = <139>;
+ qcom,cpr-fuse-redun-target-quot = <24 12 0>;
+ qcom,cpr-fuse-redun-ro-sel = <46 36 39>;
+
qcom,cpr-enable;
};
};
@@ -95,6 +108,15 @@
qcom,use-voltage-corner;
qcom,consumer-supplies = "vdd_sr2_dig", "";
};
+ pm8226_s1_floor_corner: regulator-s1-floor-corner {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8226_s1_floor_corner";
+ qcom,set = <3>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-floor-corner;
+ qcom,always-send-voltage;
+ };
};
rpm-regulator-smpa3 {
@@ -110,9 +132,9 @@
rpm-regulator-smpa4 {
status = "okay";
pm8226_s4: regulator-s4 {
- regulator-min-microvolt = <2100000>;
- regulator-max-microvolt = <2100000>;
- qcom,init-voltage = <2100000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2200000>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
};
@@ -402,6 +424,17 @@
};
};
+ rpm-regulator-ldoa25 {
+ status = "okay";
+ pm8226_l25: regulator-l25 {
+ regulator-name = "8226_l25";
+ regulator-min-microvolt = <1775000>;
+ regulator-max-microvolt = <2125000>;
+ qcom,init-voltage = <1775000>;
+ status = "okay";
+ };
+ };
+
rpm-regulator-ldoa26 {
status = "okay";
pm8226_l26: regulator-l26 {
diff --git a/arch/arm/boot/dts/msm8226-v1-qrd.dts b/arch/arm/boot/dts/msm8226-v1-qrd-dvt.dts
similarity index 73%
copy from arch/arm/boot/dts/msm8226-v1-qrd.dts
copy to arch/arm/boot/dts/msm8226-v1-qrd-dvt.dts
index d2aabac..d36e93e 100644
--- a/arch/arm/boot/dts/msm8226-v1-qrd.dts
+++ b/arch/arm/boot/dts/msm8226-v1-qrd-dvt.dts
@@ -13,13 +13,19 @@
/dts-v1/;
/include/ "msm8226-v1.dtsi"
/include/ "msm8226-qrd.dtsi"
+/include/ "dsi-panel-hx8394a-720p-video.dtsi"
/ {
model = "Qualcomm MSM 8226 QRD";
compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
- qcom,msm-id = <145 11 0>,
- <158 11 0>,
- <159 11 0>,
- <198 11 0>,
- <205 11 0>;
+ qcom,msm-id = <145 0x2000b 0>,
+ <158 0x2000b 0>,
+ <159 0x2000b 0>,
+ <198 0x2000b 0>;
+};
+
+&soc {
+ qcom,mdss_dsi_hx8394a_720p_video {
+ status = "ok";
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-v1-qrd.dts b/arch/arm/boot/dts/msm8226-v1-qrd-evt.dts
similarity index 82%
rename from arch/arm/boot/dts/msm8226-v1-qrd.dts
rename to arch/arm/boot/dts/msm8226-v1-qrd-evt.dts
index d2aabac..a081308 100644
--- a/arch/arm/boot/dts/msm8226-v1-qrd.dts
+++ b/arch/arm/boot/dts/msm8226-v1-qrd-evt.dts
@@ -13,6 +13,7 @@
/dts-v1/;
/include/ "msm8226-v1.dtsi"
/include/ "msm8226-qrd.dtsi"
+/include/ "dsi-panel-nt35590-720p-video.dtsi"
/ {
model = "Qualcomm MSM 8226 QRD";
@@ -23,3 +24,10 @@
<198 11 0>,
<205 11 0>;
};
+
+&soc {
+ qcom,mdss_dsi_nt35590_720p_video {
+ status = "ok";
+ qcom,cont-splash-enabled;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-v1-qrd-skuf.dts b/arch/arm/boot/dts/msm8226-v1-qrd-skuf.dts
new file mode 100644
index 0000000..cb5e299
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226-v1-qrd-skuf.dts
@@ -0,0 +1,58 @@
+/* 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.
+ */
+
+/dts-v1/;
+/include/ "msm8226-v1.dtsi"
+/include/ "msm8226-qrd.dtsi"
+/include/ "dsi-panel-nt35590-720p-video.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8226 QRD";
+ compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
+ qcom,board-id = <11 2>;
+ qcom,msm-id = <145 0>,
+ <158 0>,
+ <159 0>,
+ <198 0>;
+};
+
+&soc {
+ qcom,mdss_dsi_nt35590_720p_video {
+ status = "ok";
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_dcs";
+ };
+
+ sound {
+ qcom,model = "msm8226-tapan-skuf-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "SPK_OUT", "MCLK",
+ "SPK_OUT", "EXT_VDD_SPKR",
+ "Lineout_1 amp", "LINEOUT1",
+ "Lineout_2 amp", "LINEOUT2",
+ "AMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS1 External",
+ "MIC BIAS1 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic";
+
+ qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
+ qcom,cdc-lineout-spkr-gpios = <&pm8226_gpios 2 0>;
+ qcom,cdc-vdd-spkr-gpios;
+ qcom,cdc-us-euro-gpios;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-v1.dtsi b/arch/arm/boot/dts/msm8226-v1.dtsi
index 7d3977f..1db1f12 100644
--- a/arch/arm/boot/dts/msm8226-v1.dtsi
+++ b/arch/arm/boot/dts/msm8226-v1.dtsi
@@ -17,9 +17,8 @@
*/
/include/ "msm8226.dtsi"
-&soc {
- qcom,acpuclk@f9011050 {
- reg = <0xf9011050 0x8>;
- reg-names = "rcg_base";
- };
+
+&tsens {
+ qcom,sensors = <4>;
+ qcom,slope = <2901 2846 3038 2955>;
};
diff --git a/arch/arm/boot/dts/msm8226-v2-qrd.dts b/arch/arm/boot/dts/msm8226-v2-qrd-dvt.dts
similarity index 70%
copy from arch/arm/boot/dts/msm8226-v2-qrd.dts
copy to arch/arm/boot/dts/msm8226-v2-qrd-dvt.dts
index ad6d154..f35e2e4 100644
--- a/arch/arm/boot/dts/msm8226-v2-qrd.dts
+++ b/arch/arm/boot/dts/msm8226-v2-qrd-dvt.dts
@@ -14,13 +14,20 @@
/include/ "msm8226-v2.dtsi"
/include/ "msm8226-qrd.dtsi"
/include/ "msm8226-camera-sensor-cdp.dtsi"
+/include/ "dsi-panel-hx8394a-720p-video.dtsi"
/ {
model = "Qualcomm MSM 8226v2 QRD";
compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
- qcom,msm-id = <145 11 0x20000>,
- <158 11 0x20000>,
- <159 11 0x20000>,
- <198 11 0x20000>,
- <205 11 0x20000>;
+ qcom,msm-id = <145 0x2000b 0x20000>,
+ <158 0x2000b 0x20000>,
+ <159 0x2000b 0x20000>,
+ <198 0x2000b 0x20000>,
+ <205 0x2000b 0x20000>;
+};
+
+&soc {
+ qcom,mdss_dsi_hx8394a_720p_video {
+ status = "ok";
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-v2-qrd.dts b/arch/arm/boot/dts/msm8226-v2-qrd-evt.dts
similarity index 83%
rename from arch/arm/boot/dts/msm8226-v2-qrd.dts
rename to arch/arm/boot/dts/msm8226-v2-qrd-evt.dts
index ad6d154..a2ad682 100644
--- a/arch/arm/boot/dts/msm8226-v2-qrd.dts
+++ b/arch/arm/boot/dts/msm8226-v2-qrd-evt.dts
@@ -14,6 +14,7 @@
/include/ "msm8226-v2.dtsi"
/include/ "msm8226-qrd.dtsi"
/include/ "msm8226-camera-sensor-cdp.dtsi"
+/include/ "dsi-panel-nt35590-720p-video.dtsi"
/ {
model = "Qualcomm MSM 8226v2 QRD";
@@ -24,3 +25,10 @@
<198 11 0x20000>,
<205 11 0x20000>;
};
+
+&soc {
+ qcom,mdss_dsi_nt35590_720p_video {
+ status = "ok";
+ qcom,cont-splash-enabled;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-v2-qrd-skuf.dts b/arch/arm/boot/dts/msm8226-v2-qrd-skuf.dts
new file mode 100644
index 0000000..ac8b437
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226-v2-qrd-skuf.dts
@@ -0,0 +1,59 @@
+/* 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.
+ */
+
+/dts-v1/;
+/include/ "msm8226-v2.dtsi"
+/include/ "msm8226-qrd.dtsi"
+/include/ "dsi-panel-nt35590-720p-video.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8226v2 QRD";
+ compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
+ qcom,board-id = <0xb 0x2>;
+ qcom,msm-id = <145 0x20000>,
+ <158 0x20000>,
+ <159 0x20000>,
+ <198 0x20000>,
+ <205 0x20000>;
+};
+
+&soc {
+ qcom,mdss_dsi_nt35590_720p_video {
+ status = "ok";
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_dcs";
+ };
+
+ sound {
+ qcom,model = "msm8226-tapan-skuf-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "SPK_OUT", "MCLK",
+ "SPK_OUT", "EXT_VDD_SPKR",
+ "Lineout_1 amp", "LINEOUT1",
+ "Lineout_2 amp", "LINEOUT2",
+ "AMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS1 External",
+ "MIC BIAS1 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic";
+
+ qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
+ qcom,cdc-lineout-spkr-gpios = <&pm8226_gpios 2 0>;
+ qcom,cdc-vdd-spkr-gpios;
+ qcom,cdc-us-euro-gpios;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-v2.dtsi b/arch/arm/boot/dts/msm8226-v2.dtsi
index db2f4e6..b44cb681 100644
--- a/arch/arm/boot/dts/msm8226-v2.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2.dtsi
@@ -43,14 +43,19 @@
qcom,pvs-bin-process = <1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2
2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3>;
qcom,pvs-corner-ceiling-slow = <1160000 1160000 1280000>;
- qcom,pvs-corner-ceiling-nom = <980000 1080000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
+ qcom,pvs-corner-ceiling-nom = <1050000 1080000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <1050000 1050000 1140000>;
qcom,cpr-step-quotient = <30>;
qcom,cpr-up-threshold = <0>;
qcom,cpr-down-threshold = <5>;
qcom,cpr-apc-volt-step = <10000>;
};
+&msm_gpu {
+ /* Updated chip ID */
+ qcom,chipid = <0x03000512>;
+};
+
&soc {
qcom,acpuclk@f9011050 {
reg = <0xf9011050 0x8>,
@@ -58,3 +63,8 @@
reg-names = "rcg_base", "pte_efuse";
};
};
+
+&tsens {
+ qcom,sensors = <6>;
+ qcom,slope = <2901 2846 3038 2955 2901 2846>;
+};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index e22f590..b836100 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -21,6 +21,7 @@
spi0 = &spi_0;
sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
sdhc2 = &sdhc_2; /* SDC2 SD card slot */
+ sdhc3 = &sdhc_3; /* SDC3 SDIO slot */
};
memory {
@@ -30,6 +31,12 @@
label = "secure_mem";
};
+ adsp_mem: adsp_region {
+ linux,contiguous-region;
+ reg = <0 0x2000000>;
+ label = "adsp_mem";
+ };
+
qsecom_mem: qsecom_region {
linux,contiguous-region;
reg = <0 0x780000>;
@@ -260,11 +267,12 @@
qcom,dp-manual-pullup;
qcom,msm-bus,name = "usb";
- qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-cases = <3>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps =
<87 512 0 0>,
- <87 512 60000 960000>;
+ <87 512 60000 960000>,
+ <87 512 6000 6000>;
};
android_usb@fe8050c8 {
@@ -302,7 +310,7 @@
qcom,cdc-reset-gpio = <&msmgpio 72 0>;
cdc-vdd-buck-supply = <&pm8226_s4>;
- qcom,cdc-vdd-buck-voltage = <2100000 2100000>;
+ qcom,cdc-vdd-buck-voltage = <1800000 2150000>;
qcom,cdc-vdd-buck-current = <650000>;
cdc-vdd-h-supply = <&pm8226_l6>;
@@ -502,15 +510,19 @@
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <32773>;
};
+
+ qcom,msm-dai-q6-incall-music-2-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32770>;
+ };
};
qcom,msm-pcm-hostless {
compatible = "qcom,msm-pcm-hostless";
};
- qcom,msm-auxpcm {
- compatible = "qcom,msm-auxpcm-resource";
- qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
+ qcom,msm-pri-auxpcm {
+ compatible = "qcom,msm-auxpcm-dev";
qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
@@ -518,16 +530,7 @@
qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
qcom,msm-cpudai-auxpcm-data = <0>, <0>;
qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
-
- qcom,msm-prim-auxpcm-rx {
- qcom,msm-auxpcm-dev-id = <4106>;
- compatible = "qcom,msm-auxpcm-dev";
- };
-
- qcom,msm-prim-auxpcm-tx {
- qcom,msm-auxpcm-dev-id = <4107>;
- compatible = "qcom,msm-auxpcm-dev";
- };
+ qcom,msm-auxpcm-interface = "primary";
};
qcom,wcnss-wlan@fb000000 {
@@ -658,6 +661,21 @@
interrupt-names = "core_irq", "bam_irq";
qcom,bus-width = <8>;
+
+ qcom,msm-bus,name = "sdcc1";
+ qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <78 512 0 0>, /* No vote */
+ <78 512 1600 3200>, /* 400 KB/s*/
+ <78 512 80000 160000>, /* 20 MB/s */
+ <78 512 100000 200000>, /* 25 MB/s */
+ <78 512 200000 400000>, /* 50 MB/s */
+ <78 512 400000 800000>, /* 100 MB/s */
+ <78 512 400000 800000>, /* 200 MB/s */
+ <78 512 2048000 4096000>; /* Max. bandwidth */
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 4294967295>;
+
status = "disabled";
};
@@ -670,6 +688,21 @@
interrupt-names = "hc_irq", "pwr_irq";
qcom,bus-width = <8>;
+
+ qcom,msm-bus,name = "sdhc1";
+ qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <78 512 0 0>, /* No vote */
+ <78 512 1600 3200>, /* 400 KB/s*/
+ <78 512 80000 160000>, /* 20 MB/s */
+ <78 512 100000 200000>, /* 25 MB/s */
+ <78 512 200000 400000>, /* 50 MB/s */
+ <78 512 400000 800000>, /* 100 MB/s */
+ <78 512 400000 800000>, /* 200 MB/s */
+ <78 512 2048000 4096000>; /* Max. bandwidth */
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 4294967295>;
+
status = "disabled";
};
@@ -685,6 +718,21 @@
interrupt-names = "core_irq", "bam_irq";
qcom,bus-width = <4>;
+
+ qcom,msm-bus,name = "sdcc2";
+ qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <81 512 0 0>, /* No vote */
+ <81 512 1600 3200>, /* 400 KB/s*/
+ <81 512 80000 160000>, /* 20 MB/s */
+ <81 512 100000 200000>, /* 25 MB/s */
+ <81 512 200000 400000>, /* 50 MB/s */
+ <81 512 400000 800000>, /* 100 MB/s */
+ <81 512 400000 800000>, /* 200 MB/s */
+ <81 512 2048000 4096000>; /* Max. bandwidth */
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 4294967295>;
+
status = "disabled";
};
@@ -697,6 +745,110 @@
interrupt-names = "hc_irq", "pwr_irq";
qcom,bus-width = <4>;
+
+ qcom,msm-bus,name = "sdhc2";
+ qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <81 512 0 0>, /* No vote */
+ <81 512 1600 3200>, /* 400 KB/s*/
+ <81 512 80000 160000>, /* 20 MB/s */
+ <81 512 100000 200000>, /* 25 MB/s */
+ <81 512 200000 400000>, /* 50 MB/s */
+ <81 512 400000 800000>, /* 100 MB/s */
+ <81 512 400000 800000>, /* 200 MB/s */
+ <81 512 2048000 4096000>; /* Max. bandwidth */
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 4294967295>;
+
+ status = "disabled";
+ };
+
+ sdcc3: qcom,sdcc@f9864000 {
+ cell-index = <3>;
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf9864000 0x800>,
+ <0xf9864800 0x100>,
+ <0xf9844000 0x7000>;
+ reg-names = "core_mem", "dml_mem", "bam_mem";
+
+ qcom,bus-width = <4>;
+ gpios = <&msmgpio 44 0>, /* CLK */
+ <&msmgpio 43 0>, /* CMD */
+ <&msmgpio 42 0>, /* DATA0 */
+ <&msmgpio 41 0>, /* DATA1 */
+ <&msmgpio 40 0>, /* DATA2 */
+ <&msmgpio 39 0>; /* DATA3 */
+ qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000>;
+
+ qcom,msm-bus,name = "sdcc3";
+ qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <79 512 0 0>, /* No vote */
+ <79 512 1600 3200>, /* 400 KB/s*/
+ <79 512 80000 160000>, /* 20 MB/s */
+ <79 512 100000 200000>, /* 25 MB/s */
+ <79 512 200000 400000>, /* 50 MB/s */
+ <79 512 400000 800000>, /* 100 MB/s */
+ <79 512 400000 800000>, /* 200 MB/s */
+ <79 512 2048000 4096000>; /* Max. bandwidth */
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 4294967295>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdcc3>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 127 0
+ 1 &intc 0 223 0
+ 2 &msmgpio 41 0x8>;
+ interrupt-names = "core_irq", "bam_irq", "sdiowakeup_irq";
+
+ status = "disabled";
+ };
+
+ sdhc_3: sdhci@f9864900 {
+ compatible = "qcom,sdhci-msm";
+ reg = <0xf9864900 0x11c>, <0xf9864000 0x800>;
+ reg-names = "hc_mem", "core_mem";
+
+ qcom,bus-width = <4>;
+ gpios = <&msmgpio 44 0>, /* CLK */
+ <&msmgpio 43 0>, /* CMD */
+ <&msmgpio 42 0>, /* DATA0 */
+ <&msmgpio 41 0>, /* DATA1 */
+ <&msmgpio 40 0>, /* DATA2 */
+ <&msmgpio 39 0>; /* DATA3 */
+ qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000>;
+
+ qcom,msm-bus,name = "sdhc3";
+ qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <79 512 0 0>, /* No vote */
+ <79 512 1600 3200>, /* 400 KB/s*/
+ <79 512 80000 160000>, /* 20 MB/s */
+ <79 512 100000 200000>, /* 25 MB/s */
+ <79 512 200000 400000>, /* 50 MB/s */
+ <79 512 400000 800000>, /* 100 MB/s */
+ <79 512 400000 800000>, /* 200 MB/s */
+ <79 512 2048000 4096000>; /* Max. bandwidth */
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 4294967295>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_3>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 127 0
+ 1 &intc 0 224 0
+ 2 &msmgpio 41 0x8>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
status = "disabled";
};
@@ -714,6 +866,18 @@
qcom,pmic-arb-channel = <0>;
};
+ i2c@f9925000 { /* BLSP-1 QUP-3 */
+ cell-index = <2>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9925000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 97 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <400000>;
+ qcom,i2c-src-freq = <19200000>;
+ };
i2c@f9926000 { /* BLSP-1 QUP-4 */
cell-index = <0>;
compatible = "qcom,i2c-qup";
@@ -741,6 +905,8 @@
qcom,acpuclk@f9011050 {
compatible = "qcom,acpuclk-a7";
+ reg = <0xf9011050 0x8>;
+ reg-names = "rcg_base";
a7_cpu-supply = <&apc_vreg_corner>;
};
@@ -859,7 +1025,7 @@
tsens: tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b8000 0x1000>;
+ <0xfc4bc000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
qcom,sensors = <4>;
@@ -875,6 +1041,24 @@
qcom,temp-hysteresis = <10>;
qcom,freq-step = <2>;
qcom,freq-control-mask = <0xf>;
+ qcom,core-limit-temp = <80>;
+ qcom,core-temp-hysteresis = <10>;
+ qcom,core-control-mask = <0xe>;
+ qcom,vdd-restriction-temp = <5>;
+ qcom,vdd-restriction-temp-hysteresis = <10>;
+ vdd-dig-supply = <&pm8226_s1_floor_corner>;
+
+ qcom,vdd-dig-rstr{
+ qcom,vdd-rstr-reg = "vdd-dig";
+ qcom,levels = <5 7 7>; /* Nominal, Super Turbo, Super Turbo */
+ qcom,min-level = <1>; /* No Request */
+ };
+
+ qcom,vdd-apps-rstr{
+ qcom,vdd-rstr-reg = "vdd-apps";
+ qcom,levels = <600000 787200 998400>;
+ qcom,freq-req;
+ };
};
spi_0: spi@f9923000 { /* BLSP1 QUP1 */
@@ -929,6 +1113,12 @@
reg = <0x0fe805720 0x1000>;
};
+ jtag_fuse: jtagfuse@fc4be024 {
+ compatible = "qcom,jtag-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+ };
+
jtag_mm0: jtagmm@fc33c000 {
compatible = "qcom,jtag-mm";
reg = <0xfc33c000 0x1000>,
diff --git a/arch/arm/boot/dts/msm8610-bus.dtsi b/arch/arm/boot/dts/msm8610-bus.dtsi
index d9bb6ab..cef04ef 100644
--- a/arch/arm/boot/dts/msm8610-bus.dtsi
+++ b/arch/arm/boot/dts/msm8610-bus.dtsi
@@ -934,9 +934,9 @@
qcom,qport = <0>;
qcom,ws = <10000>;
qcom,mas-hw-id = <0>;
- qcom,prio-rd = <1>;
- qcom,prio-wr = <1>;
- qcom,prio-lvl = <1>;
+ qcom,prio-rd = <0>;
+ qcom,prio-wr = <0>;
+ qcom,prio-lvl = <0>;
};
mas-mss-proc {
diff --git a/arch/arm/boot/dts/msm8610-cdp.dts b/arch/arm/boot/dts/msm8610-cdp.dts
index 6891b90..a62a53c 100644
--- a/arch/arm/boot/dts/msm8610-cdp.dts
+++ b/arch/arm/boot/dts/msm8610-cdp.dts
@@ -149,12 +149,12 @@
compatible = "qcom,msm8x10-wcd-i2c";
reg = <0x0d>;
cdc-vdda-cp-supply = <&pm8110_s4>;
- qcom,cdc-vdda-cp-voltage = <2150000 2150000>;
+ qcom,cdc-vdda-cp-voltage = <1800000 2150000>;
qcom,cdc-vdda-cp-current = <650000>;
cdc-vdda-h-supply = <&pm8110_l6>;
qcom,cdc-vdda-h-voltage = <1800000 1800000>;
- qcom,cdc-vdda-h-current = <250000>;
+ qcom,cdc-vdda-h-current = <25000>;
cdc-vdd-px-supply = <&pm8110_l6>;
qcom,cdc-vdd-px-voltage = <1800000 1800000>;
@@ -171,6 +171,13 @@
qcom,cdc-micbias-cfilt-sel = <0x0>;
qcom,cdc-micbias-cfilt-mv = <1800000>;
qcom,cdc-mclk-clk-rate = <12288000>;
+
+ qcom,cdc-static-supplies = "cdc-vdda-h",
+ "cdc-vdd-px",
+ "cdc-vdd-1p2v";
+
+ qcom,cdc-on-demand-supplies = "cdc-vdda-cp",
+ "cdc-vdd-mic-bias";
};
msm8x10_wcd_codec@77{
@@ -215,9 +222,13 @@
qcom,default-state = "on";
qcom,max-current = <40>;
qcom,id = <6>;
- qcom,source-sel = <1>;
+ qcom,source-sel = <8>;
qcom,mode-ctrl = <0x10>;
- qcom,mode = "manual";
+ qcom,pwm-channel = <0>;
+ qcom,pwm-us = <14>;
+ qcom,vin-ctrl = <0x03>;
+ qcom,mode = "pwm";
+ qcom,min-brightness = <19>;
};
};
};
diff --git a/arch/arm/boot/dts/msm8610-coresight.dtsi b/arch/arm/boot/dts/msm8610-coresight.dtsi
index 516522e..2041bf6 100644
--- a/arch/arm/boot/dts/msm8610-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8610-coresight.dtsi
@@ -345,8 +345,9 @@
compatible = "qcom,coresight-hwevent";
reg = <0xfd820018 0x80>,
<0xf9011080 0x80>,
- <0xfd4ab160 0x80>;
- reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <27>;
coresight-name = "coresight-hwevent";
@@ -354,4 +355,14 @@
qcom,hwevent-clks = "core_mmss_clk";
};
+
+ fuse: fuse@fc4be024 {
+ compatible = "arm,coresight-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+
+ coresight-id = <28>;
+ coresight-name = "coresight-fuse";
+ coresight-nr-inports = <0>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-mtp.dts b/arch/arm/boot/dts/msm8610-mtp.dts
index ddbe3a0..72a6efb 100644
--- a/arch/arm/boot/dts/msm8610-mtp.dts
+++ b/arch/arm/boot/dts/msm8610-mtp.dts
@@ -42,6 +42,7 @@
atmel,display-coords = <0 0 480 800>;
atmel,i2c-pull-up;
atmel,no-force-update;
+ atmel,no-lpm-support;
atmel,cfg_1 {
atmel,family-id = <0x81>;
atmel,variant-id = <0x15>;
@@ -90,6 +91,33 @@
};
};
+ i2c@f9925000 {
+ mpu3050@68 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+ reg = <0x68>;
+ compatible = "invn,mpu3050";
+ interrupt-parent = <&msmgpio>;
+ interrupts = <84 0x2>;
+ vlogic-supply = <&pm8110_l14>;
+ vdd-supply = <&pm8110_l19>;
+ invn,gpio-int = <&msmgpio 84 0x2>;
+ invn,gpio-en = <&pm8110_gpios 2 0x2>;
+ invn,poll-interval = <200>;
+ };
+
+ bmp180@77 {
+ reg = <0x77>;
+ compatible = "bosch,bmp18x";
+ vdd-supply = <&pm8110_l19>;
+ vddio-supply = <&pm8110_l14>;
+ bosch,chip-id = <0x55>;
+ bosch,oversample = <3>;
+ bosch,period = <1000>;
+ };
+ };
+
gen-vkeys {
compatible = "qcom,gen-vkeys";
label = "atmel_mxt_ts";
@@ -149,12 +177,12 @@
compatible = "qcom,msm8x10-wcd-i2c";
reg = <0x0d>;
cdc-vdda-cp-supply = <&pm8110_s4>;
- qcom,cdc-vdda-cp-voltage = <2150000 2150000>;
+ qcom,cdc-vdda-cp-voltage = <1800000 2150000>;
qcom,cdc-vdda-cp-current = <650000>;
cdc-vdda-h-supply = <&pm8110_l6>;
qcom,cdc-vdda-h-voltage = <1800000 1800000>;
- qcom,cdc-vdda-h-current = <250000>;
+ qcom,cdc-vdda-h-current = <25000>;
cdc-vdd-px-supply = <&pm8110_l6>;
qcom,cdc-vdd-px-voltage = <1800000 1800000>;
@@ -171,6 +199,13 @@
qcom,cdc-micbias-cfilt-sel = <0x0>;
qcom,cdc-micbias-cfilt-mv = <1800000>;
qcom,cdc-mclk-clk-rate = <12288000>;
+
+ qcom,cdc-static-supplies = "cdc-vdda-h",
+ "cdc-vdd-px",
+ "cdc-vdd-1p2v";
+
+ qcom,cdc-on-demand-supplies = "cdc-vdda-cp",
+ "cdc-vdd-mic-bias";
};
msm8x10_wcd_codec@77{
@@ -215,9 +250,13 @@
qcom,default-state = "on";
qcom,max-current = <40>;
qcom,id = <6>;
- qcom,source-sel = <1>;
+ qcom,source-sel = <8>;
qcom,mode-ctrl = <0x10>;
- qcom,mode = "manual";
+ qcom,pwm-channel = <0>;
+ qcom,pwm-us = <14>;
+ qcom,vin-ctrl = <0x03>;
+ qcom,mode = "pwm";
+ qcom,min-brightness = <19>;
};
};
};
diff --git a/arch/arm/boot/dts/msm8610-pm.dtsi b/arch/arm/boot/dts/msm8610-pm.dtsi
index faa7a41..c48eaf7 100644
--- a/arch/arm/boot/dts/msm8610-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-pm.dtsi
@@ -18,7 +18,7 @@
reg = <0xf9089000 0x1000>;
qcom,core-id = <0>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -35,7 +35,7 @@
reg = <0xf9099000 0x1000>;
qcom,core-id = <1>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -52,7 +52,7 @@
reg = <0xf90a9000 0x1000>;
qcom,core-id = <2>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -69,7 +69,7 @@
reg = <0xf90b9000 0x1000>;
qcom,core-id = <3>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -369,6 +369,13 @@
qcom,pc-resets-timer;
};
+ qcom,cpu-sleep-status@f9088008{
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x100>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
+ };
+
qcom,rpm-log@fc19dc00 {
compatible = "qcom,rpm-log";
reg = <0xfc19dc00 0x4000>;
@@ -386,4 +393,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi b/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
index e3bd631..e73573a 100644
--- a/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
@@ -45,7 +45,42 @@
qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
};
-
-
+ qcom,camera@78 {
+ compatible = "shinetech,gc0339";
+ reg = <0x78>;
+ qcom,slave-id = <0x42 0x00 0xc8>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <180>;
+ qcom,sensor-name = "SKUAA_ST_gc0339";
+ cam_vdig-supply = <&pm8110_l14>;
+ cam_vana-supply = <&pm8110_l19>;
+ cam_vio-supply = <&pm8110_l14>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 1 0>;
+ qcom,cam-vreg-min-voltage = <1800000 1800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1800000 1800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 14 0>,
+ <&msmgpio 15 0>,
+ <&msmgpio 88 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0xe4>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-qrd-skuaa.dts b/arch/arm/boot/dts/msm8610-qrd-skuaa.dts
new file mode 100644
index 0000000..220f642
--- /dev/null
+++ b/arch/arm/boot/dts/msm8610-qrd-skuaa.dts
@@ -0,0 +1,42 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+/include/ "msm8610-qrd.dtsi"
+/include/ "dsi-v2-panel-hx8379a-wvga-video.dtsi"
+/include/ "msm8610-qrd-camera-sensor.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8610 QRD";
+ compatible = "qcom,msm8610-qrd", "qcom,msm8610", "qcom,qrd";
+ qcom,board-id = <11 1>, <11 0>;
+ qcom,msm-id = <147 0>, <165 0>, <161 0>, <162 0>,
+ <163 0>, <164 0>, <166 0>;
+};
+
+&soc {
+ sound {
+ qcom,model = "msm8x10-skuaa-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "INT_LDO_H", "MCLK",
+ "Lineout amp", "LINEOUT",
+ "MIC BIAS Internal1", "Handset Mic",
+ "MIC BIAS Internal2", "Headset Mic",
+ "AMIC1", "MIC BIAS Internal1",
+ "AMIC2", "MIC BIAS Internal2";
+
+ qcom,ext-spk-amp-gpio = <&msmgpio 92 0x0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8610-qrd-skuab.dts b/arch/arm/boot/dts/msm8610-qrd-skuab.dts
new file mode 100644
index 0000000..24c2490
--- /dev/null
+++ b/arch/arm/boot/dts/msm8610-qrd-skuab.dts
@@ -0,0 +1,81 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+/include/ "msm8610-qrd.dtsi"
+/include/ "msm8612-qrd-camera-sensor.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8610 QRD";
+ compatible = "qcom,msm8610-qrd", "qcom,msm8610", "qcom,qrd";
+ qcom,board-id = <11 3>;
+ qcom,msm-id = <147 0>, <165 0>, <161 0>, <162 0>,
+ <163 0>, <164 0>, <166 0>;
+};
+
+&soc {
+ sound {
+ qcom,model = "msm8x10-skuab-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "INT_LDO_H", "MCLK",
+ "Lineout amp", "LINEOUT",
+ "MIC BIAS Internal1", "Handset Mic",
+ "MIC BIAS Internal2", "Headset Mic",
+ "AMIC1", "MIC BIAS Internal1",
+ "AMIC2", "MIC BIAS Internal2";
+
+ qcom,ext-spk-amp-gpio = <&msmgpio 92 0x0>;
+ };
+ i2c@f9923000{
+ focaltech@38{
+ compatible = "focaltech,5x06";
+ reg = <0x38>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <1 0x2>;
+ vdd-supply = <&pm8110_l19>;
+ vcc_i2c-supply = <&pm8110_l14>;
+ focaltech,family-id = <0x06>;
+ focaltech,reset-gpio = <&msmgpio 0 0x00>;
+ focaltech,irq-gpio = <&msmgpio 1 0x00>;
+ focaltech,display-coords = <0 0 480 854>;
+ focaltech,panel-coords = <0 0 480 946>;
+ focaltech,button-map= <139 102 158>;
+ focaltech,no-force-update;
+ focaltech,i2c-pull-up;
+ };
+ };
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "ft5x06_ts";
+ qcom,disp-maxx = <480>;
+ qcom,disp-maxy = <854>;
+ qcom,panel-maxx = <481>;
+ qcom,panel-maxy = <946>;
+ qcom,key-codes = <139 102 158>;
+ qcom,y-offset = <0>;
+ };
+ i2c@f9925000 {
+ fsl@1c {
+ compatible = "fsl,mma8x5x";
+ reg = <0x1c>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <81 0x2>;
+ vdd-supply = <&pm8110_l19>;
+ vio-supply = <&pm8110_l14>;
+ fsl,irq-gpio = <&msmgpio 81 0x00>;
+ fsl,sensors-position = <5>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/msm8610-qrd.dts b/arch/arm/boot/dts/msm8610-qrd.dtsi
similarity index 72%
rename from arch/arm/boot/dts/msm8610-qrd.dts
rename to arch/arm/boot/dts/msm8610-qrd.dtsi
index 90225c0..bd1705f 100644
--- a/arch/arm/boot/dts/msm8610-qrd.dts
+++ b/arch/arm/boot/dts/msm8610-qrd.dtsi
@@ -10,18 +10,23 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+/* This is a QRD reference design common file. The common device
+ * tree approach would help OEM during development of their extended
+ * device tree. Each new QRD OEM target can select its own include
+ * files and provide board specific overrides in the top level DTS
+ * file.
+ *
+ * For example:
+ * msm8xxx-qrd.dtsi: QRD reference common node
+ * msm8xxx-qrd-skuxx.dts:
+ * /include/ "msm8610-qrd.dtsi"
+ * / {
+ * List skuxx specific node which is different with QRD
+ * reference design.
+ * };
+ */
/include/ "msm8610.dtsi"
-/include/ "dsi-v2-panel-hx8379a-wvga-video.dtsi"
-/include/ "msm8610-qrd-camera-sensor.dtsi"
-
-/ {
- model = "Qualcomm MSM 8610 QRD";
- compatible = "qcom,msm8610-qrd", "qcom,msm8610", "qcom,qrd";
- qcom,msm-id = <147 11 0>, <165 11 0>, <161 11 0>, <162 11 0>,
- <163 11 0>, <164 11 0>, <166 11 0>;
-};
&soc {
i2c@f9923000{
@@ -65,11 +70,11 @@
interrupts = <81 0x2>;
vdd-supply = <&pm8110_l19>;
vio-supply = <&pm8110_l14>;
- kionix,min_interval = <5>;
- kionix,init_interval = <200>;
- kionix,axis_map_x = <1>;
- kionix,axis_map_y = <0>;
- kionix,axis_map_z = <2>;
+ kionix,min-interval = <5>;
+ kionix,init-interval = <200>;
+ kionix,axis-map-x = <1>;
+ kionix,axis-map-y = <0>;
+ kionix,axis-map-z = <2>;
kionix,g-range = <2>;
kionix,negate-x;
kionix,negate-y;
@@ -78,6 +83,15 @@
};
};
+ flashlight {
+ compatible = "qcom,leds-gpio-flash";
+ status = "okay";
+ qcom,flash-en = <&msmgpio 18 0>;
+ qcom,flash-now = <&msmgpio 19 0>;
+ linux,name = "flashlight";
+ linux,default-trigger = "flashlight-trigger";
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
@@ -110,51 +124,6 @@
};
};
- i2c@f9927000 {
- msm8x10_wcd_codec@0d{
- compatible = "qcom,msm8x10-wcd-i2c";
- reg = <0x0d>;
- cdc-vdda-cp-supply = <&pm8110_s4>;
- qcom,cdc-vdda-cp-voltage = <2150000 2150000>;
- qcom,cdc-vdda-cp-current = <650000>;
-
- cdc-vdda-h-supply = <&pm8110_l6>;
- qcom,cdc-vdda-h-voltage = <1800000 1800000>;
- qcom,cdc-vdda-h-current = <250000>;
-
- cdc-vdd-px-supply = <&pm8110_l6>;
- qcom,cdc-vdd-px-voltage = <1800000 1800000>;
- qcom,cdc-vdd-px-current = <10000>;
-
- cdc-vdd-1p2v-supply = <&pm8110_l4>;
- qcom,cdc-vdd-1p2v-voltage = <1200000 1200000>;
- qcom,cdc-vdd-1p2v-current = <5000>;
-
- cdc-vdd-mic-bias-supply = <&pm8110_l20>;
- qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
- qcom,cdc-vdd-mic-bias-current = <25000>;
-
- qcom,cdc-micbias-cfilt-sel = <0x0>;
- qcom,cdc-micbias-cfilt-mv = <1800000>;
- qcom,cdc-mclk-clk-rate = <12288000>;
- };
-
- msm8x10_wcd_codec@77{
- compatible = "qcom,msm8x10-wcd-i2c";
- reg = <0x77>;
- };
-
- msm8x10_wcd_codec@66{
- compatible = "qcom,msm8x10-wcd-i2c";
- reg = <0x66>;
- };
-
- msm8x10_wcd_codec@55{
- compatible = "qcom,msm8x10-wcd-i2c";
- reg = <0x55>;
- };
- };
-
sound {
qcom,audio-routing =
"RX_BIAS", "MCLK",
@@ -166,6 +135,58 @@
};
};
+&i2c_cdc {
+ msm8x10_wcd_codec@0d{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x0d>;
+ cdc-vdda-cp-supply = <&pm8110_s4>;
+ qcom,cdc-vdda-cp-voltage = <1800000 2150000>;
+ qcom,cdc-vdda-cp-current = <650000>;
+
+ cdc-vdda-h-supply = <&pm8110_l6>;
+ qcom,cdc-vdda-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdda-h-current = <25000>;
+
+ cdc-vdd-px-supply = <&pm8110_l6>;
+ qcom,cdc-vdd-px-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-px-current = <10000>;
+
+ cdc-vdd-1p2v-supply = <&pm8110_l4>;
+ qcom,cdc-vdd-1p2v-voltage = <1200000 1200000>;
+ qcom,cdc-vdd-1p2v-current = <5000>;
+
+ cdc-vdd-mic-bias-supply = <&pm8110_l20>;
+ qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
+ qcom,cdc-vdd-mic-bias-current = <25000>;
+
+ qcom,cdc-micbias-cfilt-sel = <0x0>;
+ qcom,cdc-micbias-cfilt-mv = <1800000>;
+ qcom,cdc-mclk-clk-rate = <12288000>;
+
+ qcom,cdc-static-supplies = "cdc-vdda-h",
+ "cdc-vdd-px",
+ "cdc-vdd-1p2v";
+
+ qcom,cdc-on-demand-supplies = "cdc-vdda-cp",
+ "cdc-vdd-mic-bias";
+ };
+
+ msm8x10_wcd_codec@77{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x77>;
+ };
+
+ msm8x10_wcd_codec@66{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x66>;
+ };
+
+ msm8x10_wcd_codec@55{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x55>;
+ };
+};
+
&spmi_bus {
qcom,pm8110@0 {
qcom,leds@a100 {
@@ -192,9 +213,13 @@
qcom,default-state = "on";
qcom,max-current = <40>;
qcom,id = <6>;
- qcom,source-sel = <1>;
+ qcom,source-sel = <8>;
qcom,mode-ctrl = <0x10>;
- qcom,mode = "manual";
+ qcom,pwm-channel = <0>;
+ qcom,pwm-us = <14>;
+ qcom,vin-ctrl = <0x03>;
+ qcom,mode = "pwm";
+ qcom,min-brightness = <19>;
};
};
};
diff --git a/arch/arm/boot/dts/msm8610-regulator.dtsi b/arch/arm/boot/dts/msm8610-regulator.dtsi
index 09520c5..cf54098 100644
--- a/arch/arm/boot/dts/msm8610-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8610-regulator.dtsi
@@ -30,19 +30,22 @@
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>, <0xf9011064 4>, <0xfc4b80b0 8>,
- <0xfc4bc450 16>;
- reg-names = "rbcpr", "rbcpr_clk", "pvs_efuse", "cpr_efuse";
+ reg = <0xf9018000 0x1000>, <0xf9011064 4>, <0xfc4b8000 0x1000>;
+ reg-names = "rbcpr", "rbcpr_clk", "efuse_addr";
interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
regulator-max-microvolt = <3>;
- qcom,num-efuse-bits = <5>;
- qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
- 2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
+
+ qcom,pvs-fuse-redun-sel = <53 25 3 2>;
+ qcom,pvs-fuse = <23 6 5>;
+ qcom,pvs-fuse-redun = <61 47 5>;
+
+ qcom,pvs-bin-process = <1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1>;
qcom,pvs-corner-ceiling-slow = <1150000 1150000 1275000>;
- qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
+ qcom,pvs-corner-ceiling-nom = <1075000 1075000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <1000000 1000000 1140000>;
vdd-apc-supply = <&pm8110_s2>;
vdd-mx-supply = <&pm8110_l3_ao>;
@@ -62,6 +65,18 @@
qcom,vdd-apc-step-up-limit = <1>;
qcom,vdd-apc-step-down-limit = <1>;
qcom,cpr-apc-volt-step = <5000>;
+
+ qcom,cpr-fuse-redun-sel = <53 25 3 2>;
+ qcom,cpr-fuse-row = <61>;
+ qcom,cpr-fuse-bp-cpr-disable = <39>;
+ qcom,cpr-fuse-bp-scheme = <40>;
+ qcom,cpr-fuse-target-quot = <27 15 3>;
+ qcom,cpr-fuse-ro-sel = <47 41 44>;
+ qcom,cpr-fuse-redun-row = <52>;
+ qcom,cpr-fuse-redun-bp-cpr-disable = <24>;
+ qcom,cpr-fuse-redun-bp-scheme = <25>;
+ qcom,cpr-fuse-redun-target-quot = <32 12 0>;
+ qcom,cpr-fuse-redun-ro-sel = <44 26 29>;
};
};
@@ -96,6 +111,16 @@
qcom,use-voltage-corner;
qcom,consumer-supplies = "vdd_sr2_dig", "";
};
+
+ pm8110_s1_floor_corner: regulator-s1-floor-corner {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8110_s1_floor_corner";
+ qcom,set = <3>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-floor-corner;
+ qcom,always-send-voltage;
+ };
};
rpm-regulator-smpa3 {
@@ -111,7 +136,7 @@
rpm-regulator-smpa4 {
status = "okay";
pm8110_s4: regulator-s4 {
- regulator-min-microvolt = <2150000>;
+ regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2150000>;
qcom,init-voltage = <2150000>;
status = "okay";
@@ -172,6 +197,7 @@
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
qcom,init-voltage = <1200000>;
+ regulator-always-on;
status = "okay";
};
};
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 72d9317..f6e0247 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -219,6 +219,8 @@
HSUSB_1p8-supply = <&pm8110_l10>;
HSUSB_3p3-supply = <&pm8110_l20>;
+ qcom,hsusb-otg-phy-init-seq =
+ <0x44 0x80 0x68 0x81 0x24 0x82 0x13 0x83 0xffffffff>;
qcom,hsusb-otg-phy-type = <2>;
qcom,hsusb-otg-mode = <1>;
qcom,hsusb-otg-otg-control = <2>;
@@ -699,6 +701,7 @@
gpios = <&msmgpio 23 0>, <&msmgpio 24 0>, <&msmgpio 25 0>, <&msmgpio 26 0>, <&msmgpio 27 0>;
qcom,has-pronto-hw;
qcom,wlan-rx-buff-count = <256>;
+ qcom,has-autodetect-xo;
};
qcom,mss@fc880000 {
@@ -752,7 +755,7 @@
tsens: tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b8000 0x1000>;
+ <0xfc4bc000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
qcom,sensors = <2>;
@@ -769,6 +772,24 @@
qcom,temp-hysteresis = <10>;
qcom,freq-step = <2>;
qcom,freq-control-mask = <0xf>;
+ qcom,core-limit-temp = <80>;
+ qcom,core-temp-hysteresis = <10>;
+ qcom,core-control-mask = <0xe>;
+ qcom,vdd-restriction-temp = <5>;
+ qcom,vdd-restriction-temp-hysteresis = <10>;
+ vdd-dig-supply = <&pm8110_s1_floor_corner>;
+
+ qcom,vdd-dig-rstr{
+ qcom,vdd-rstr-reg = "vdd-dig";
+ qcom,levels = <5 7 7>; /* Nominal, Super Turbo, Super Turbo */
+ qcom,min-level = <1>; /* No Request */
+ };
+
+ qcom,vdd-apps-rstr{
+ qcom,vdd-rstr-reg = "vdd-apps";
+ qcom,levels = <600000 787200 998400>;
+ qcom,freq-req;
+ };
};
qcom,ipc-spinlock@fd484000 {
@@ -819,10 +840,10 @@
qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
};
- qcom,msm-contig-mem {
- compatible = "qcom,msm-contig-mem";
- qcom,memory-reservation-type = "EBI1";
- qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
+ jtag_fuse: jtagfuse@fc4be024 {
+ compatible = "qcom,jtag-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
};
jtag_mm0: jtagmm@fc34c000 {
@@ -894,6 +915,11 @@
<55 512 393600 3936000>;
};
+ cpu-pmu {
+ compatible = "arm,cortex-a7-pmu";
+ qcom,irq-is-percpu;
+ interrupts = <1 7 0xf00>;
+ };
};
&gdsc_vfe {
diff --git a/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi b/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi
new file mode 100644
index 0000000..25554eb
--- /dev/null
+++ b/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+&i2c {
+
+ qcom,camera@20 {
+ compatible = "shinetech,s5k4e1";
+ reg = <0x20>;
+ qcom,slave-id = <0x20 0x0000 0x4e10>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
+ qcom,sensor-name = "SKUAB_ST_s5k4e1";
+ cam_vdig-supply = <&pm8110_l2>;
+ cam_vana-supply = <&pm8110_l19>;
+ cam_vio-supply = <&pm8110_l14>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 0 0>;
+ qcom,cam-vreg-min-voltage = <1200000 1800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 1800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 8000 80000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 13 0>,
+ <&msmgpio 21 0>,
+ <&msmgpio 20 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
+ qcom,csi-lane-assign = <0xe4>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
+ qcom,sensor-type = <0>;
+ qcom,cci-master = <0>;
+ };
+ qcom,camera@78 {
+ compatible = "shinetech,gc0339";
+ reg = <0x78>;
+ qcom,slave-id = <0x42 0x00 0xc8>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,sensor-name = "skuab_shinetech_gc0339";
+ cam_vdig-supply = <&pm8110_l14>;
+ cam_vana-supply = <&pm8110_l19>;
+ cam_vio-supply = <&pm8110_l14>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 1 0>;
+ qcom,cam-vreg-min-voltage = <1800000 1800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1800000 1800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 14 0>,
+ <&msmgpio 15 0>,
+ <&msmgpio 85 0>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0xe4>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
index 31f3a90..2f8e558 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
@@ -36,6 +36,8 @@
qcom,mount-angle = <0>;
qcom,actuator-src = <&actuator0>;
qcom,sensor-name = "s5k3l1yx";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -48,12 +50,15 @@
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>;
+ <&msmgpio 90 0>,
+ <&msmgpio 89 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
+ "CAM_RESET1",
+ "CAM_STANDBY";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
@@ -73,6 +78,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "imx135";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
qcom,actuator-src = <&actuator1>;
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
@@ -86,12 +93,15 @@
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>;
+ <&msmgpio 90 0>,
+ <&msmgpio 89 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
+ "CAM_RESET1",
+ "CAM_STANDBY";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
@@ -111,6 +121,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <180>;
qcom,sensor-name = "ov2720";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -146,6 +158,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "mt9m114";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 2a60df4..8a3a77e 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -48,6 +48,7 @@
atmel,i2c-pull-up;
atmel,no-force-update;
atmel,cfg_1 {
+ atmel,fw-name = "atmel_8974_fluid_v1_0_AA.hex";
atmel,family-id = <0x82>;
atmel,variant-id = <0x19>;
atmel,version = <0x10>;
@@ -251,63 +252,61 @@
};
};
-&spmi_bus {
- qcom,pm8941@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 = <1>;
- 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>;
- };
+&pm8941_lsid1 {
+ 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 = <1>;
+ 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>;
};
+ };
- qcom,leds@d900 {
- status = "disabled";
- };
+ qcom,leds@d900 {
+ status = "disabled";
+ };
- qcom,leds@da00 {
- status = "disabled";
- };
+ qcom,leds@da00 {
+ status = "disabled";
+ };
- qcom,leds@db00 {
- status = "disabled";
- };
+ qcom,leds@db00 {
+ status = "disabled";
+ };
- qcom,leds@dc00 {
- status = "disabled";
- };
+ qcom,leds@dc00 {
+ status = "disabled";
+ };
- qcom,leds@dd00 {
- status = "disabled";
- };
+ qcom,leds@dd00 {
+ status = "disabled";
+ };
- qcom,leds@de00 {
- status = "disabled";
- };
+ qcom,leds@de00 {
+ status = "disabled";
+ };
- qcom,leds@df00 {
- status = "disabled";
- };
+ qcom,leds@df00 {
+ status = "disabled";
+ };
- qcom,leds@e000 {
- status = "disabled";
- };
+ qcom,leds@e000 {
+ status = "disabled";
+ };
- qcom,leds@e100 {
- status = "disabled";
- };
+ qcom,leds@e100 {
+ status = "disabled";
};
};
@@ -406,6 +405,8 @@
qcom,rx-gpio = <&msmgpio 42 0x00>;
qcom,cts-gpio = <&msmgpio 43 0x00>;
qcom,rfr-gpio = <&msmgpio 44 0x00>;
+ qcom,inject-rx-on-wakeup = <1>;
+ qcom,rx-char-to-inject = <0xFD>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-clock.dtsi b/arch/arm/boot/dts/msm8974-clock.dtsi
index bed5d70..8d90771 100644
--- a/arch/arm/boot/dts/msm8974-clock.dtsi
+++ b/arch/arm/boot/dts/msm8974-clock.dtsi
@@ -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
@@ -10,18 +10,14 @@
* GNU General Public License for more details.
*/
-&spmi_bus {
+&pm8941_lsid0 {
+ pm8941_clkdiv1: clkdiv@5b00 {
+ qcom,cxo-div = <2>;
+ };
- qcom,pm8941@0 {
+ pm8941_clkdiv2: clkdiv@5c00 {
+ };
- pm8941_clkdiv1: clkdiv@5b00 {
- qcom,cxo-div = <2>;
- };
-
- pm8941_clkdiv2: clkdiv@5c00 {
- };
-
- pm8941_clkdiv3: clkdiv@5d00 {
- };
+ pm8941_clkdiv3: clkdiv@5d00 {
};
};
diff --git a/arch/arm/boot/dts/msm8974-coresight.dtsi b/arch/arm/boot/dts/msm8974-coresight.dtsi
index 1610f1f..e41adac 100644
--- a/arch/arm/boot/dts/msm8974-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8974-coresight.dtsi
@@ -369,8 +369,9 @@
compatible = "qcom,coresight-hwevent";
reg = <0xfdf30018 0x80>,
<0xf9011080 0x80>,
- <0xfd4ab160 0x80>;
- reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <29>;
coresight-name = "coresight-hwevent";
@@ -378,4 +379,14 @@
qcom,hwevent-clks = "core_mmss_clk";
};
+
+ fuse: fuse@fc4be024 {
+ compatible = "arm,coresight-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+
+ coresight-id = <30>;
+ coresight-name = "coresight-fuse";
+ coresight-nr-inports = <0>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 3d2308d..af21e43 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -43,6 +43,7 @@
atmel,i2c-pull-up;
atmel,no-force-update;
atmel,cfg_1 {
+ atmel,fw-name = "atmel_8974_fluid_v1_0_AA.hex";
atmel,family-id = <0x82>;
atmel,variant-id = <0x19>;
atmel,version = <0x10>;
@@ -250,63 +251,61 @@
};
};
-&spmi_bus {
- qcom,pm8941@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 = <1>;
- 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>;
- };
+&pm8941_lsid1 {
+ 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 = <1>;
+ 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>;
};
+ };
- qcom,leds@d900 {
- status = "disabled";
- };
+ qcom,leds@d900 {
+ status = "disabled";
+ };
- qcom,leds@da00 {
- status = "disabled";
- };
+ qcom,leds@da00 {
+ status = "disabled";
+ };
- qcom,leds@db00 {
- status = "disabled";
- };
+ qcom,leds@db00 {
+ status = "disabled";
+ };
- qcom,leds@dc00 {
- status = "disabled";
- };
+ qcom,leds@dc00 {
+ status = "disabled";
+ };
- qcom,leds@dd00 {
- status = "disabled";
- };
+ qcom,leds@dd00 {
+ status = "disabled";
+ };
- qcom,leds@de00 {
- status = "disabled";
- };
+ qcom,leds@de00 {
+ status = "disabled";
+ };
- qcom,leds@df00 {
- status = "disabled";
- };
+ qcom,leds@df00 {
+ status = "disabled";
+ };
- qcom,leds@e000 {
- status = "disabled";
- };
+ qcom,leds@e000 {
+ status = "disabled";
+ };
- qcom,leds@e100 {
- status = "disabled";
- };
+ qcom,leds@e100 {
+ status = "disabled";
};
};
@@ -407,6 +406,7 @@
qcom,min-fcc-learning-soc = <20>;
qcom,min-fcc-ocv-pc = <30>;
qcom,min-fcc-learning-samples = <5>;
+ qcom,fcc-resolution = <10>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-leds.dtsi b/arch/arm/boot/dts/msm8974-leds.dtsi
index 5e91f45..ab57468 100644
--- a/arch/arm/boot/dts/msm8974-leds.dtsi
+++ b/arch/arm/boot/dts/msm8974-leds.dtsi
@@ -11,119 +11,116 @@
* GNU General Public License for more details.
*/
-&spmi_bus {
- qcom,pm8941@1 {
- qcom,leds@d000 {
- status = "okay";
- qcom,rgb_0 {
- label = "rgb";
- linux,name = "led:rgb_red";
- qcom,mode = "pwm";
- qcom,pwm-channel = <6>;
- qcom,pwm-us = <1000>;
- qcom,max-current = <12>;
- qcom,default-state = "off";
- qcom,id = <3>;
- linux,default-trigger =
- "battery-charging";
- };
-
- qcom,rgb_1 {
- label = "rgb";
- linux,name = "led:rgb_green";
- qcom,mode = "pwm";
- qcom,pwm-channel = <5>;
- qcom,pwm-us = <1000>;
- qcom,max-current = <12>;
- qcom,default-state = "off";
- qcom,id = <4>;
- linux,default-trigger = "battery-full";
- };
-
- qcom,rgb_2 {
- label = "rgb";
- linux,name = "led:rgb_blue";
- qcom,mode = "pwm";
- qcom,pwm-channel = <4>;
- qcom,pwm-us = <1000>;
- qcom,max-current = <12>;
- qcom,id = <5>;
- status = "disabled";
- };
+&pm8941_lsid1 {
+ qcom,leds@d000 {
+ status = "okay";
+ qcom,rgb_0 {
+ label = "rgb";
+ linux,name = "led:rgb_red";
+ qcom,mode = "pwm";
+ qcom,pwm-channel = <6>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ qcom,id = <3>;
+ linux,default-trigger =
+ "battery-charging";
};
- qcom,leds@d100 {
- status = "disabled";
+ qcom,rgb_1 {
+ label = "rgb";
+ linux,name = "led:rgb_green";
+ qcom,mode = "pwm";
+ qcom,pwm-channel = <5>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ qcom,id = <4>;
+ linux,default-trigger = "battery-full";
};
- qcom,leds@d200 {
- status = "disabled";
- };
-
- qcom,leds@d300 {
- status = "okay";
- pm8941_flash0: qcom,flash_0 {
- qcom,max-current = <1000>;
- qcom,default-state = "off";
- qcom,headroom = <3>;
- qcom,duration = <1280>;
- qcom,clamp-curr = <200>;
- qcom,startup-dly = <3>;
- qcom,safety-timer;
- label = "flash";
- linux,default-trigger =
- "flash0_trigger";
- qcom,id = <1>;
- linux,name = "led:flash_0";
- qcom,current = <625>;
- };
-
- pm8941_flash1: qcom,flash_1 {
- qcom,max-current = <1000>;
- qcom,default-state = "off";
- qcom,headroom = <3>;
- qcom,duration = <1280>;
- qcom,clamp-curr = <200>;
- qcom,startup-dly = <3>;
- qcom,safety-timer;
- linux,default-trigger =
- "flash1_trigger";
- label = "flash";
- qcom,id = <2>;
- linux,name = "led:flash_1";
- qcom,current = <625>;
- };
-
- pm8941_torch: qcom,flash_torch {
- qcom,max-current = <200>;
- qcom,default-state = "off";
- qcom,headroom = <0>;
- qcom,startup-dly = <1>;
- linux,default-trigger =
- "torch_trigger";
- label = "flash";
- qcom,id = <2>;
- linux,name = "led:flash_torch";
- qcom,current = <200>;
- qcom,torch-enable;
- };
- };
-
- qcom,leds@d400 {
- status = "disabled";
- };
-
- qcom,leds@d500 {
- status = "disabled";
- };
-
- qcom,leds@d600 {
- status = "disabled";
- };
-
- qcom,leds@d700 {
+ qcom,rgb_2 {
+ label = "rgb";
+ linux,name = "led:rgb_blue";
+ qcom,mode = "pwm";
+ qcom,pwm-channel = <4>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,id = <5>;
status = "disabled";
};
};
-};
+ qcom,leds@d100 {
+ status = "disabled";
+ };
+
+ qcom,leds@d200 {
+ status = "disabled";
+ };
+
+ qcom,leds@d300 {
+ status = "okay";
+ pm8941_flash0: qcom,flash_0 {
+ qcom,max-current = <1000>;
+ qcom,default-state = "off";
+ qcom,headroom = <3>;
+ qcom,duration = <1280>;
+ qcom,clamp-curr = <200>;
+ qcom,startup-dly = <3>;
+ qcom,safety-timer;
+ label = "flash";
+ linux,default-trigger =
+ "flash0_trigger";
+ qcom,id = <1>;
+ linux,name = "led:flash_0";
+ qcom,current = <625>;
+ };
+
+ pm8941_flash1: qcom,flash_1 {
+ qcom,max-current = <1000>;
+ qcom,default-state = "off";
+ qcom,headroom = <3>;
+ qcom,duration = <1280>;
+ qcom,clamp-curr = <200>;
+ qcom,startup-dly = <3>;
+ qcom,safety-timer;
+ linux,default-trigger =
+ "flash1_trigger";
+ label = "flash";
+ qcom,id = <2>;
+ linux,name = "led:flash_1";
+ qcom,current = <625>;
+ };
+
+ pm8941_torch: qcom,flash_torch {
+ qcom,max-current = <200>;
+ qcom,default-state = "off";
+ qcom,headroom = <0>;
+ qcom,startup-dly = <1>;
+ linux,default-trigger =
+ "torch_trigger";
+ label = "flash";
+ qcom,id = <2>;
+ linux,name = "led:flash_torch";
+ qcom,current = <200>;
+ qcom,torch-enable;
+ };
+ };
+
+ qcom,leds@d400 {
+ status = "disabled";
+ };
+
+ qcom,leds@d500 {
+ status = "disabled";
+ };
+
+ qcom,leds@d600 {
+ status = "disabled";
+ };
+
+ qcom,leds@d700 {
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 2dc52b6..82bb7dc 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -782,14 +782,12 @@
};
};
-&spmi_bus {
- qcom,pm8941@1 {
- qcom,leds@d000 {
- qcom,rgb_2 {
- status = "ok";
- qcom,default-state = "on";
- qcom,turn-off-delay-ms = <1000>;
- };
+&pm8941_lsid1 {
+ qcom,leds@d000 {
+ qcom,rgb_2 {
+ status = "ok";
+ qcom,default-state = "on";
+ qcom,turn-off-delay-ms = <1000>;
};
};
};
@@ -813,7 +811,7 @@
};
qcom,dc-chgpth@1400 {
- status = "ok";
+ status = "disabled";
};
qcom,boost@1500 {
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index 6b8d600..46bb71a 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -41,6 +41,7 @@
qcom,mdss-intf-off = <0x00021100 0x00021300
0x00021500 0x00021700>;
+ qcom,mdss-has-wfd-blk;
qcom,vbif-settings = <0x0004 0x00000001>,
<0x00D8 0x00000707>,
<0x00F0 0x00000030>,
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index 28111fa..8e3b7cc 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -43,6 +43,7 @@
atmel,i2c-pull-up;
atmel,no-force-update;
atmel,cfg_1 {
+ atmel,fw-name = "atmel_8974_fluid_v1_0_AA.hex";
atmel,family-id = <0x82>;
atmel,variant-id = <0x19>;
atmel,version = <0x10>;
@@ -191,63 +192,61 @@
};
};
-&spmi_bus {
- qcom,pm8941@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 = <1>;
- 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>;
- };
+&pm8941_lsid1 {
+ 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 = <1>;
+ 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>;
};
+ };
- qcom,leds@d900 {
- status = "disabled";
- };
+ qcom,leds@d900 {
+ status = "disabled";
+ };
- qcom,leds@da00 {
- status = "disabled";
- };
+ qcom,leds@da00 {
+ status = "disabled";
+ };
- qcom,leds@db00 {
- status = "disabled";
- };
+ qcom,leds@db00 {
+ status = "disabled";
+ };
- qcom,leds@dc00 {
- status = "disabled";
- };
+ qcom,leds@dc00 {
+ status = "disabled";
+ };
- qcom,leds@dd00 {
- status = "disabled";
- };
+ qcom,leds@dd00 {
+ status = "disabled";
+ };
- qcom,leds@de00 {
- status = "disabled";
- };
+ qcom,leds@de00 {
+ status = "disabled";
+ };
- qcom,leds@df00 {
- status = "disabled";
- };
+ qcom,leds@df00 {
+ status = "disabled";
+ };
- qcom,leds@e000 {
- status = "disabled";
- };
+ qcom,leds@e000 {
+ status = "disabled";
+ };
- qcom,leds@e100 {
- status = "disabled";
- };
+ qcom,leds@e100 {
+ status = "disabled";
};
};
@@ -356,6 +355,7 @@
qcom,min-fcc-learning-soc = <20>;
qcom,min-fcc-ocv-pc = <30>;
qcom,min-fcc-learning-samples = <5>;
+ qcom,fcc-resolution = <10>;
status = "ok";
};
@@ -666,21 +666,6 @@
};
};
-&pm8841_mpps {
-
- mpp@a000 { /* MPP 1 */
- };
-
- mpp@a100 { /* MPP 2 */
- };
-
- mpp@a200 { /* MPP 3 */
- };
-
- mpp@a300 { /* MPP 4 */
- };
-};
-
&slim_msm {
taiko_codec {
qcom,cdc-micbias1-ext-cap;
diff --git a/arch/arm/boot/dts/msm8974-regulator.dtsi b/arch/arm/boot/dts/msm8974-regulator.dtsi
index 7c191fc..eae9032 100644
--- a/arch/arm/boot/dts/msm8974-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8974-regulator.dtsi
@@ -13,48 +13,44 @@
/* QPNP controlled regulators: */
-&spmi_bus {
+&pm8941_lsid1 {
+ pm8941_boost: regulator@a000 {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ qcom,enable-time = <500>;
+ status = "okay";
+ };
- qcom,pm8941@1 {
+ pm8941_mvs1: regulator@8300 {
+ parent-supply = <&pm8941_chg_otg>;
+ qcom,enable-time = <1000>;
+ qcom,pull-down-enable = <1>;
+ interrupts = <0x1 0x83 0x2>;
+ interrupt-names = "ocp";
+ qcom,ocp-enable = <1>;
+ qcom,ocp-max-retries = <10>;
+ qcom,ocp-retry-delay = <30>;
+ qcom,soft-start-enable = <1>;
+ qcom,vs-soft-start-strength = <0>;
+ qcom,hpm-enable = <1>;
+ qcom,auto-mode-enable = <0>;
+ status = "okay";
+ };
- pm8941_boost: regulator@a000 {
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- qcom,enable-time = <500>;
- status = "okay";
- };
-
- pm8941_mvs1: regulator@8300 {
- parent-supply = <&pm8941_chg_otg>;
- qcom,enable-time = <1000>;
- qcom,pull-down-enable = <1>;
- interrupts = <0x1 0x83 0x2>;
- interrupt-names = "ocp";
- qcom,ocp-enable = <1>;
- qcom,ocp-max-retries = <10>;
- qcom,ocp-retry-delay = <30>;
- qcom,soft-start-enable = <1>;
- qcom,vs-soft-start-strength = <0>;
- qcom,hpm-enable = <1>;
- qcom,auto-mode-enable = <0>;
- status = "okay";
- };
-
- pm8941_mvs2: regulator@8400 {
- parent-supply = <&pm8941_boost>;
- qcom,enable-time = <1000>;
- qcom,pull-down-enable = <1>;
- interrupts = <0x1 0x84 0x2>;
- interrupt-names = "ocp";
- qcom,ocp-enable = <1>;
- qcom,ocp-max-retries = <10>;
- qcom,ocp-retry-delay = <30>;
- qcom,soft-start-enable = <1>;
- qcom,vs-soft-start-strength = <0>;
- qcom,hpm-enable = <1>;
- qcom,auto-mode-enable = <0>;
- status = "okay";
- };
+ pm8941_mvs2: regulator@8400 {
+ parent-supply = <&pm8941_boost>;
+ qcom,enable-time = <1000>;
+ qcom,pull-down-enable = <1>;
+ interrupts = <0x1 0x84 0x2>;
+ interrupt-names = "ocp";
+ qcom,ocp-enable = <1>;
+ qcom,ocp-max-retries = <10>;
+ qcom,ocp-retry-delay = <30>;
+ qcom,soft-start-enable = <1>;
+ qcom,vs-soft-start-strength = <0>;
+ qcom,hpm-enable = <1>;
+ qcom,auto-mode-enable = <0>;
+ status = "okay";
};
};
@@ -468,6 +464,7 @@
#size-cells = <1>;
ranges;
qcom,pfm-threshold = <73>;
+ qcom,use-phase-scaling-factor;
krait0_vreg: regulator@f9088000 {
compatible = "qcom,krait-regulator";
diff --git a/arch/arm/boot/dts/msm8974-v1.dtsi b/arch/arm/boot/dts/msm8974-v1.dtsi
index caec2dc..cfacac6 100644
--- a/arch/arm/boot/dts/msm8974-v1.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1.dtsi
@@ -17,6 +17,21 @@
*/
/include/ "msm8974.dtsi"
+
+/include/ "msm-pm8x41-rpm-regulator.dtsi"
+/include/ "msm-pm8841.dtsi"
+&spmi_bus {
+ pm8941_lsid0: qcom,pm8941@0 {
+ reg = <0x0>;
+ };
+ pm8941_lsid1: qcom,pm8941@1 {
+ reg = <0x1>;
+ };
+};
+/include/ "msm-pm8941.dtsi"
+/include/ "msm8974-regulator.dtsi"
+/include/ "msm8974-clock.dtsi"
+
/include/ "msm8974-v1-iommu.dtsi"
/include/ "msm8974-v1-iommu-domains.dtsi"
/include/ "msm8974-v1-pm.dtsi"
diff --git a/arch/arm/boot/dts/apq8026-xpm.dts b/arch/arm/boot/dts/msm8974-v2-2-cdp.dts
similarity index 69%
copy from arch/arm/boot/dts/apq8026-xpm.dts
copy to arch/arm/boot/dts/msm8974-v2-2-cdp.dts
index 67152af..cb8895f 100644
--- a/arch/arm/boot/dts/apq8026-xpm.dts
+++ b/arch/arm/boot/dts/msm8974-v2-2-cdp.dts
@@ -10,13 +10,15 @@
* GNU General Public License for more details.
*/
-
/dts-v1/;
-/include/ "apq8026.dtsi"
-/include/ "msm8226-cdp.dtsi"
+
+/include/ "msm8974-v2-2.dtsi"
+/include/ "msm8974-cdp.dtsi"
/ {
- model = "Qualcomm APQ 8026 XPM";
- compatible = "qcom,apq8026-xpm", "qcom,apq8026", "qcom,xpm";
- qcom,msm-id = <199 14 0>;
+ model = "Qualcomm MSM 8974v2 CDP";
+ compatible = "qcom,msm8974-cdp", "qcom,msm8974", "qcom,cdp";
+ qcom,msm-id = <126 1 0x20002>,
+ <185 1 0x20002>,
+ <186 1 0x20002>;
};
diff --git a/arch/arm/boot/dts/apq8026-xpm.dts b/arch/arm/boot/dts/msm8974-v2-2-fluid.dts
similarity index 68%
copy from arch/arm/boot/dts/apq8026-xpm.dts
copy to arch/arm/boot/dts/msm8974-v2-2-fluid.dts
index 67152af..8e04c18 100644
--- a/arch/arm/boot/dts/apq8026-xpm.dts
+++ b/arch/arm/boot/dts/msm8974-v2-2-fluid.dts
@@ -10,13 +10,15 @@
* GNU General Public License for more details.
*/
-
/dts-v1/;
-/include/ "apq8026.dtsi"
-/include/ "msm8226-cdp.dtsi"
+
+/include/ "msm8974-v2-2.dtsi"
+/include/ "msm8974-fluid.dtsi"
/ {
- model = "Qualcomm APQ 8026 XPM";
- compatible = "qcom,apq8026-xpm", "qcom,apq8026", "qcom,xpm";
- qcom,msm-id = <199 14 0>;
+ model = "Qualcomm MSM 8974v2 FLUID";
+ compatible = "qcom,msm8974-fluid", "qcom,msm8974", "qcom,fluid";
+ qcom,msm-id = <126 3 0x20002>,
+ <185 3 0x20002>,
+ <186 3 0x20002>;
};
diff --git a/arch/arm/boot/dts/apq8026-xpm.dts b/arch/arm/boot/dts/msm8974-v2-2-liquid.dts
similarity index 68%
copy from arch/arm/boot/dts/apq8026-xpm.dts
copy to arch/arm/boot/dts/msm8974-v2-2-liquid.dts
index 67152af..7128abe 100644
--- a/arch/arm/boot/dts/apq8026-xpm.dts
+++ b/arch/arm/boot/dts/msm8974-v2-2-liquid.dts
@@ -10,13 +10,15 @@
* GNU General Public License for more details.
*/
-
/dts-v1/;
-/include/ "apq8026.dtsi"
-/include/ "msm8226-cdp.dtsi"
+
+/include/ "msm8974-v2-2.dtsi"
+/include/ "msm8974-liquid.dtsi"
/ {
- model = "Qualcomm APQ 8026 XPM";
- compatible = "qcom,apq8026-xpm", "qcom,apq8026", "qcom,xpm";
- qcom,msm-id = <199 14 0>;
+ model = "Qualcomm MSM 8974v2 LIQUID";
+ compatible = "qcom,msm8974-liquid", "qcom,msm8974", "qcom,liquid";
+ qcom,msm-id = <126 9 0x20002>,
+ <185 9 0x20002>,
+ <186 9 0x20002>;
};
diff --git a/arch/arm/boot/dts/apq8026-xpm.dts b/arch/arm/boot/dts/msm8974-v2-2-mtp.dts
similarity index 65%
copy from arch/arm/boot/dts/apq8026-xpm.dts
copy to arch/arm/boot/dts/msm8974-v2-2-mtp.dts
index 67152af..b7e35cf 100644
--- a/arch/arm/boot/dts/apq8026-xpm.dts
+++ b/arch/arm/boot/dts/msm8974-v2-2-mtp.dts
@@ -10,13 +10,19 @@
* GNU General Public License for more details.
*/
-
/dts-v1/;
-/include/ "apq8026.dtsi"
-/include/ "msm8226-cdp.dtsi"
+
+/include/ "msm8974-v2-2.dtsi"
+/include/ "msm8974-mtp.dtsi"
/ {
- model = "Qualcomm APQ 8026 XPM";
- compatible = "qcom,apq8026-xpm", "qcom,apq8026", "qcom,xpm";
- qcom,msm-id = <199 14 0>;
+ model = "Qualcomm MSM 8974v2 MTP";
+ compatible = "qcom,msm8974-mtp", "qcom,msm8974", "qcom,mtp";
+ qcom,msm-id = <126 8 0x20002>,
+ <185 8 0x20002>,
+ <186 8 0x20002>;
+};
+
+&pm8941_chg {
+ qcom,bpd-detection = "bpd_thm";
};
diff --git a/arch/arm/boot/dts/msm8974-v2-2.dtsi b/arch/arm/boot/dts/msm8974-v2-2.dtsi
new file mode 100644
index 0000000..09455b1
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-v2-2.dtsi
@@ -0,0 +1,105 @@
+/* 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.
+ */
+
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8974.dtsi file.
+ */
+
+/include/ "msm8974-v2.dtsi"
+
+/* GPU overrides */
+&msm_gpu {
+ /* Updated chip ID */
+ qcom,chipid = <0x03030001>;
+
+ qcom,initial-pwrlevel = <4>;
+
+ /* Updated bus bandwidth requirements */
+ qcom,msm-bus,vectors-KBps =
+ /* Off */
+ <26 512 0 0>, <89 604 0 0>,
+ /* SVS */
+ <26 512 0 2400000>, <89 604 0 3000000>,
+ /* Nominal / SVS */
+ <26 512 0 4656000>, <89 604 0 3000000>,
+ /* Nominal */
+ <26 512 0 4656000>, <89 604 0 5120000>,
+ /* Turbo / Nominal */
+ <26 512 0 7464000>, <89 604 0 5120000>,
+ /* Turbo */
+ <26 512 0 7464000>, <89 604 0 6400000>;
+
+ qcom,gpu-pwrlevels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "qcom,gpu-pwrlevels";
+
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <450000000>;
+ qcom,bus-freq = <5>;
+ qcom,io-fraction = <33>;
+ };
+
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <389000000>;
+ qcom,bus-freq = <4>;
+ qcom,io-fraction = <33>;
+ };
+
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <389000000>;
+ qcom,bus-freq = <3>;
+ qcom,io-fraction = <66>;
+ };
+
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <4>;
+ qcom,io-fraction = <66>;
+ };
+
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <3>;
+ qcom,io-fraction = <66>;
+ };
+
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <200000000>;
+ qcom,bus-freq = <2>;
+ qcom,io-fraction = <100>;
+ };
+
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <200000000>;
+ qcom,bus-freq = <1>;
+ qcom,io-fraction = <100>;
+ };
+
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
+ qcom,gpu-freq = <27000000>;
+ qcom,bus-freq = <0>;
+ qcom,io-fraction = <0>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-v2-cdp.dts b/arch/arm/boot/dts/msm8974-v2-cdp.dts
index 85d478b..f4014aa 100644
--- a/arch/arm/boot/dts/msm8974-v2-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-v2-cdp.dts
@@ -22,15 +22,3 @@
<185 1 0x20000>,
<186 1 0x20000>;
};
-
-&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 133 0
- 0x0 1 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "pmic_id_irq";
-
- qcom,misc-ref = <&pm8941_misc>;
-};
diff --git a/arch/arm/boot/dts/msm8974-v2-fluid.dts b/arch/arm/boot/dts/msm8974-v2-fluid.dts
index d83d130..9c9e3c0 100644
--- a/arch/arm/boot/dts/msm8974-v2-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-v2-fluid.dts
@@ -22,15 +22,3 @@
<185 3 0x20000>,
<186 3 0x20000>;
};
-
-&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 133 0
- 0x0 1 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "pmic_id_irq";
-
- qcom,misc-ref = <&pm8941_misc>;
-};
diff --git a/arch/arm/boot/dts/msm8974-v2-liquid.dts b/arch/arm/boot/dts/msm8974-v2-liquid.dts
index 53983dc..ddae6fe 100644
--- a/arch/arm/boot/dts/msm8974-v2-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-v2-liquid.dts
@@ -22,15 +22,3 @@
<185 9 0x20000>,
<186 9 0x20000>;
};
-
-&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 133 0
- 0x0 1 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "pmic_id_irq";
-
- qcom,misc-ref = <&pm8941_misc>;
-};
diff --git a/arch/arm/boot/dts/msm8974-v2-mtp.dts b/arch/arm/boot/dts/msm8974-v2-mtp.dts
index 792a78c..021b626 100644
--- a/arch/arm/boot/dts/msm8974-v2-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v2-mtp.dts
@@ -23,18 +23,6 @@
<186 8 0x20000>;
};
-&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 133 0
- 0x0 1 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "pmic_id_irq";
-
- qcom,misc-ref = <&pm8941_misc>;
-};
-
&pm8941_chg {
qcom,bpd-detection = "bpd_thm";
};
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index eed1aae..a162bb7 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -26,12 +26,11 @@
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 c0 a0 90 03 d0 98 a2 c0
- 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
- qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d8 5B 03 d8 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 80 10 E8 5B 07 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
};
qcom,spm@f9099000 {
@@ -49,12 +48,11 @@
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 c0 a0 90 03 d0 98 a2 c0
- 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
- qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d8 5B 03 d8 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 80 10 E8 5B 07 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
};
qcom,spm@f90a9000 {
@@ -72,12 +70,11 @@
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 c0 a0 90 03 d0 98 a2 c0
- 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
- qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d8 5B 03 d8 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 80 10 E8 5B 07 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
};
qcom,spm@f90b9000 {
@@ -95,12 +92,11 @@
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 c0 a0 90 03 d0 98 a2 c0
- 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
- qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d8 5B 03 d8 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 80 10 E8 5B 07 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
};
qcom,spm@f9012000 {
@@ -125,7 +121,7 @@
qcom,pfm-port = <0x2>;
qcom,saw2-spm-cmd-ret = [1f 00 03 00 0f];
qcom,saw2-spm-cmd-gdhs = [00 32 42 07 44 50 02 32 50 0f];
- qcom,saw2-spm-cmd-pc = [00 32 b0 11 42 07 01 b0 44
+ qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
50 02 32 50 0f];
};
@@ -230,6 +226,23 @@
qcom,lpm-level@3 {
reg = <0x3>;
+ qcom,mode = "standalone_pc";
+ qcom,xo = "xo_on";
+ qcom,l2 = "l2_cache_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,gpio-detectable;
+ qcom,latency-us = <300>;
+ qcom,ss-power = <476>;
+ qcom,energy-overhead = <225300>;
+ qcom,time-overhead = <350>;
+ };
+
+ qcom,lpm-level@4 {
+ reg = <0x4>;
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_gdhs";
@@ -245,8 +258,8 @@
qcom,time-overhead = <5067>;
};
- qcom,lpm-level@4 {
- reg = <0x4>;
+ qcom,lpm-level@5 {
+ reg = <0x5>;
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_pc";
@@ -262,8 +275,8 @@
qcom,time-overhead = <6605>;
};
- qcom,lpm-level@5 {
- reg = <0x5>;
+ qcom,lpm-level@6 {
+ reg = <0x6>;
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
@@ -277,8 +290,8 @@
qcom,time-overhead = <8812>;
};
- qcom,lpm-level@6 {
- reg = <0x6>;
+ qcom,lpm-level@7 {
+ reg = <0x7>;
qcom,mode= "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
@@ -292,8 +305,8 @@
qcom,time-overhead = <10140>;
};
- qcom,lpm-level@7 {
- reg = <0x7>;
+ qcom,lpm-level@8 {
+ reg = <0x8>;
qcom,mode= "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
@@ -453,4 +466,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index 0240039..0b3b60f 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -17,6 +17,21 @@
*/
/include/ "msm8974.dtsi"
+
+/include/ "msm-pm8x41-rpm-regulator.dtsi"
+/include/ "msm-pm8841.dtsi"
+&spmi_bus {
+ pm8941_lsid0: qcom,pm8941@0 {
+ reg = <0x0>;
+ };
+ pm8941_lsid1: qcom,pm8941@1 {
+ reg = <0x1>;
+ };
+};
+/include/ "msm-pm8941.dtsi"
+/include/ "msm8974-regulator.dtsi"
+/include/ "msm8974-clock.dtsi"
+
/include/ "msm8974-v2-iommu.dtsi"
/include/ "msm8974-v2-iommu-domains.dtsi"
/include/ "msm8974-v2-pm.dtsi"
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index a098ce9..aaa5f3e 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -258,6 +258,8 @@
qcom,hsusb-otg-mode = <1>;
qcom,hsusb-otg-otg-control = <1>;
qcom,hsusb-otg-disable-reset;
+ qcom,hsusb-otg-mpm-dpsehv-int = <49>;
+ qcom,hsusb-otg-mpm-dmsehv-int = <58>;
qcom,msm-bus,name = "usb2";
qcom,msm-bus,num-cases = <2>;
@@ -585,11 +587,11 @@
spi-max-frequency = <19200000>;
#address-cells = <1>;
#size-cells = <0>;
- gpios = <&msmgpio 56 0>, /* CLK */
- <&msmgpio 54 0>, /* MISO */
- <&msmgpio 53 0>; /* MOSI */
- cs-gpios = <&msmgpio 55 0>;
qcom,master-id = <84>;
+ qcom,gpio-mosi = <&msmgpio 53 0>;
+ qcom,gpio-miso = <&msmgpio 54 0>;
+ qcom,gpio-clk = <&msmgpio 56 0>;
+ qcom,gpio-cs0 = <&msmgpio 55 0>;
};
tspp: msm_tspp@f99d8000 {
@@ -648,6 +650,8 @@
reg-names = "slimbus_physical", "slimbus_bam_physical";
interrupts = <0 163 0 0 164 0>;
interrupt-names = "slimbus_irq", "slimbus_bam_irq";
+ qcom,apps-ch-pipes = <0x60000000>;
+ qcom,ea-pc = <0x30>;
taiko_codec {
compatible = "qcom,taiko-slim-pgd";
@@ -819,11 +823,11 @@
spi-max-frequency = <19200000>;
#address-cells = <1>;
#size-cells = <0>;
- gpios = <&msmgpio 3 0>, /* CLK */
- <&msmgpio 1 0>, /* MISO */
- <&msmgpio 0 0>; /* MOSI */
- cs-gpios = <&msmgpio 9 0>;
qcom,master-id = <86>;
+ qcom,gpio-mosi = <&msmgpio 0 0>;
+ qcom,gpio-miso = <&msmgpio 1 0>;
+ qcom,gpio-clk = <&msmgpio 3 0>;
+ qcom,gpio-cs2 = <&msmgpio 9 0>;
};
qcom,acpuclk@f9000000 {
@@ -854,8 +858,15 @@
#address-cells = <1>;
#size-cells = <1>;
ranges;
- interrupts = <0 133 0>;
- interrupt-names = "hs_phy_irq";
+
+ interrupt-parent = <&usb3>;
+ interrupts = <0 1>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0x0 0xffffffff>;
+ interrupt-map = <0x0 0 &intc 0 133 0
+ 0x0 1 &spmi_bus 0x0 0x0 0x9 0x0>;
+ interrupt-names = "hs_phy_irq", "pmic_id_irq";
+
ssusb_vdd_dig-supply = <&pm8841_s2_corner>;
SSUSB_1p8-supply = <&pm8941_l6>;
hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
@@ -864,7 +875,8 @@
vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>;
qcom,vdd-voltage-level = <1 5 7>;
- qcom,dwc-hsphy-init = <0x00D195A4>;
+ qcom,dwc-hsphy-init = <0x00D191A4>;
+ qcom,misc-ref = <&pm8941_misc>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
@@ -1092,9 +1104,8 @@
};
};
- qcom,msm-auxpcm {
- compatible = "qcom,msm-auxpcm-resource";
- qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
+ qcom,msm-pri-auxpcm {
+ compatible = "qcom,msm-auxpcm-dev";
qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
@@ -1102,26 +1113,19 @@
qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
qcom,msm-cpudai-auxpcm-data = <0>, <0>;
qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+ qcom,msm-auxpcm-interface = "primary";
+ };
- qcom,msm-prim-auxpcm-rx {
- qcom,msm-auxpcm-dev-id = <4106>;
- compatible = "qcom,msm-auxpcm-dev";
- };
-
- qcom,msm-prim-auxpcm-tx {
- qcom,msm-auxpcm-dev-id = <4107>;
- compatible = "qcom,msm-auxpcm-dev";
- };
-
- qcom,msm-sec-auxpcm-rx {
- qcom,msm-auxpcm-dev-id = <4108>;
- compatible = "qcom,msm-auxpcm-dev";
- };
-
- qcom,msm-sec-auxpcm-tx {
- qcom,msm-auxpcm-dev-id = <4109>;
- compatible = "qcom,msm-auxpcm-dev";
- };
+ qcom,msm-sec-auxpcm {
+ compatible = "qcom,msm-auxpcm-dev";
+ qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+ qcom,msm-auxpcm-interface = "secondary";
};
qcom,msm-dai-mi2s {
@@ -1360,7 +1364,7 @@
tsens: tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b8000 0x1000>;
+ <0xfc4bc000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
qcom,sensors = <11>;
@@ -1369,6 +1373,12 @@
qcom,calib-mode = "fuse_map1";
};
+ jtag_fuse: jtagfuse@fc4be024 {
+ compatible = "qcom,jtag-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+ };
+
qcom,msm-rtb {
compatible = "qcom,msm-rtb";
qcom,memory-reservation-type = "EBI1";
@@ -1477,24 +1487,24 @@
qcom,vdd-restriction-temp-hysteresis = <10>;
qcom,pmic-sw-mode-temp = <85>;
qcom,pmic-sw-mode-temp-hysteresis = <75>;
- qcom,pmic-sw-mode-regs = "vdd_dig";
- vdd_dig-supply = <&pm8841_s2_floor_corner>;
- vdd_gfx-supply = <&pm8841_s4_floor_corner>;
+ qcom,pmic-sw-mode-regs = "vdd-dig";
+ vdd-dig-supply = <&pm8841_s2_floor_corner>;
+ vdd-gfx-supply = <&pm8841_s4_floor_corner>;
qcom,vdd-dig-rstr{
- qcom,vdd-rstr-reg = "vdd_dig";
+ qcom,vdd-rstr-reg = "vdd-dig";
qcom,levels = <5 7 7>; /* Nominal, Super Turbo, Super Turbo */
qcom,min-level = <1>; /* No Request */
};
qcom,vdd-gfx-rstr{
- qcom,vdd-rstr-reg = "vdd_gfx";
+ qcom,vdd-rstr-reg = "vdd-gfx";
qcom,levels = <5 7 7>; /* Nominal, Super Turbo, Super Turbo */
qcom,min-level = <1>; /* No Request */
};
qcom,vdd-apps-rstr{
- qcom,vdd-rstr-reg = "vdd_apps";
+ qcom,vdd-rstr-reg = "vdd-apps";
qcom,levels = <1881600 1958400 2265600>;
qcom,freq-req;
};
@@ -1518,8 +1528,15 @@
reg = <0xf995d000 0x1000>,
<0xf9944000 0x19000>;
reg-names = "core_mem", "bam_mem";
- interrupts = <0 113 0>, <0 239 0>;
- interrupt-names = "core_irq", "bam_irq";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&uart7>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 113 0
+ 1 &intc 0 239 0
+ 2 &msmgpio 42 0>;
qcom,bam-tx-ep-pipe-index = <0>;
qcom,bam-rx-ep-pipe-index = <1>;
@@ -1635,12 +1652,16 @@
&gdsc_venus {
qcom,clock-names = "core_clk";
qcom,skip-logic-collapse;
+ qcom,retain-periph;
+ qcom,retain-mem;
status = "ok";
};
&gdsc_mdss {
qcom,clock-names = "core_clk", "lut_clk";
+ qcom,skip-logic-collapse;
qcom,retain-periph;
+ qcom,retain-mem;
status = "ok";
};
@@ -1669,9 +1690,3 @@
&gdsc_usb_hsic {
status = "ok";
};
-
-/include/ "msm-pm8x41-rpm-regulator.dtsi"
-/include/ "msm-pm8841.dtsi"
-/include/ "msm-pm8941.dtsi"
-/include/ "msm8974-regulator.dtsi"
-/include/ "msm8974-clock.dtsi"
diff --git a/arch/arm/boot/dts/msm8226-v2-qrd.dts b/arch/arm/boot/dts/msm8974pro-ab-cdp.dts
similarity index 61%
copy from arch/arm/boot/dts/msm8226-v2-qrd.dts
copy to arch/arm/boot/dts/msm8974pro-ab-cdp.dts
index ad6d154..74bd23d 100644
--- a/arch/arm/boot/dts/msm8226-v2-qrd.dts
+++ b/arch/arm/boot/dts/msm8974pro-ab-cdp.dts
@@ -11,16 +11,18 @@
*/
/dts-v1/;
-/include/ "msm8226-v2.dtsi"
-/include/ "msm8226-qrd.dtsi"
-/include/ "msm8226-camera-sensor-cdp.dtsi"
+
+/include/ "msm8974pro-ab.dtsi"
+/include/ "msm8974-cdp.dtsi"
/ {
- model = "Qualcomm MSM 8226v2 QRD";
- compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
- qcom,msm-id = <145 11 0x20000>,
- <158 11 0x20000>,
- <159 11 0x20000>,
- <198 11 0x20000>,
- <205 11 0x20000>;
+ model = "Qualcomm MSM 8974Pro CDP";
+ compatible = "qcom,msm8974-cdp", "qcom,msm8974", "qcom,cdp";
+ qcom,msm-id = <209 1 0x10000>,
+ <211 1 0x10000>,
+ <212 1 0x10000>,
+ <214 1 0x10000>,
+ <215 1 0x10000>,
+ <217 1 0x10000>,
+ <218 1 0x10000>;
};
diff --git a/arch/arm/boot/dts/apq8026-xpm.dts b/arch/arm/boot/dts/msm8974pro-ab-fluid.dts
similarity index 61%
copy from arch/arm/boot/dts/apq8026-xpm.dts
copy to arch/arm/boot/dts/msm8974pro-ab-fluid.dts
index 67152af..9a31834 100644
--- a/arch/arm/boot/dts/apq8026-xpm.dts
+++ b/arch/arm/boot/dts/msm8974pro-ab-fluid.dts
@@ -10,13 +10,19 @@
* GNU General Public License for more details.
*/
-
/dts-v1/;
-/include/ "apq8026.dtsi"
-/include/ "msm8226-cdp.dtsi"
+
+/include/ "msm8974pro-ab.dtsi"
+/include/ "msm8974-fluid.dtsi"
/ {
- model = "Qualcomm APQ 8026 XPM";
- compatible = "qcom,apq8026-xpm", "qcom,apq8026", "qcom,xpm";
- qcom,msm-id = <199 14 0>;
+ model = "Qualcomm MSM 8974Pro FLUID";
+ compatible = "qcom,msm8974-fluid", "qcom,msm8974", "qcom,fluid";
+ qcom,msm-id = <209 3 0x10000>,
+ <211 3 0x10000>,
+ <212 3 0x10000>,
+ <214 3 0x10000>,
+ <215 3 0x10000>,
+ <217 3 0x10000>,
+ <218 3 0x10000>;
};
diff --git a/arch/arm/boot/dts/msm8226-v2-qrd.dts b/arch/arm/boot/dts/msm8974pro-ab-liquid.dts
similarity index 60%
copy from arch/arm/boot/dts/msm8226-v2-qrd.dts
copy to arch/arm/boot/dts/msm8974pro-ab-liquid.dts
index ad6d154..0ec9d8a 100644
--- a/arch/arm/boot/dts/msm8226-v2-qrd.dts
+++ b/arch/arm/boot/dts/msm8974pro-ab-liquid.dts
@@ -11,16 +11,18 @@
*/
/dts-v1/;
-/include/ "msm8226-v2.dtsi"
-/include/ "msm8226-qrd.dtsi"
-/include/ "msm8226-camera-sensor-cdp.dtsi"
+
+/include/ "msm8974pro-ab.dtsi"
+/include/ "msm8974-liquid.dtsi"
/ {
- model = "Qualcomm MSM 8226v2 QRD";
- compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
- qcom,msm-id = <145 11 0x20000>,
- <158 11 0x20000>,
- <159 11 0x20000>,
- <198 11 0x20000>,
- <205 11 0x20000>;
+ model = "Qualcomm MSM 8974Pro LIQUID";
+ compatible = "qcom,msm8974-liquid", "qcom,msm8974", "qcom,liquid";
+ qcom,msm-id = <209 9 0x10000>,
+ <211 9 0x10000>,
+ <212 9 0x10000>,
+ <214 9 0x10000>,
+ <215 9 0x10000>,
+ <217 9 0x10000>,
+ <218 9 0x10000>;
};
diff --git a/arch/arm/boot/dts/msm8974pro-ab-mtp.dts b/arch/arm/boot/dts/msm8974pro-ab-mtp.dts
new file mode 100644
index 0000000..d1566ae
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974pro-ab-mtp.dts
@@ -0,0 +1,32 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+/include/ "msm8974pro-ab.dtsi"
+/include/ "msm8974-mtp.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8974Pro MTP";
+ compatible = "qcom,msm8974-mtp", "qcom,msm8974", "qcom,mtp";
+ qcom,msm-id = <209 8 0x10000>,
+ <211 8 0x10000>,
+ <212 8 0x10000>,
+ <214 8 0x10000>,
+ <215 8 0x10000>,
+ <217 8 0x10000>,
+ <218 8 0x10000>;
+};
+
+&sdhc_1 {
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000>;
+};
diff --git a/arch/arm/boot/dts/msm8974pro-ab.dtsi b/arch/arm/boot/dts/msm8974pro-ab.dtsi
new file mode 100644
index 0000000..88687bd
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974pro-ab.dtsi
@@ -0,0 +1,41 @@
+/* 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.
+ */
+
+/*
+ * As a general rule, only chipset-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8974.dtsi / msm8974pro.dtsi file(s).
+ */
+
+/include/ "msm8974pro.dtsi"
+
+/include/ "msm-pm8x41-rpm-regulator.dtsi"
+/include/ "msm-pm8841.dtsi"
+&spmi_bus {
+ pm8941_lsid0: qcom,pm8941@0 {
+ reg = <0x0>;
+ };
+ pm8941_lsid1: qcom,pm8941@1 {
+ reg = <0x1>;
+ };
+};
+/include/ "msm-pm8941.dtsi"
+/include/ "msm8974-regulator.dtsi"
+/include/ "msm8974-clock.dtsi"
+
+&krait_pdn {
+ qcom,use-phase-switching;
+};
+
+&tspp {
+ vdd_cx-supply = <&pm8841_s2_corner>;
+};
diff --git a/arch/arm/boot/dts/msm8226-v2-qrd.dts b/arch/arm/boot/dts/msm8974pro-ac-mtp.dts
similarity index 60%
copy from arch/arm/boot/dts/msm8226-v2-qrd.dts
copy to arch/arm/boot/dts/msm8974pro-ac-mtp.dts
index ad6d154..b8df576 100644
--- a/arch/arm/boot/dts/msm8226-v2-qrd.dts
+++ b/arch/arm/boot/dts/msm8974pro-ac-mtp.dts
@@ -11,16 +11,19 @@
*/
/dts-v1/;
-/include/ "msm8226-v2.dtsi"
-/include/ "msm8226-qrd.dtsi"
-/include/ "msm8226-camera-sensor-cdp.dtsi"
+
+/include/ "msm8974pro-ac-pm8941.dtsi"
+/include/ "msm8974pro-ac-mtp.dtsi"
/ {
- model = "Qualcomm MSM 8226v2 QRD";
- compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
- qcom,msm-id = <145 11 0x20000>,
- <158 11 0x20000>,
- <159 11 0x20000>,
- <198 11 0x20000>,
- <205 11 0x20000>;
+ model = "Qualcomm MSM 8974Pro-AC MTP";
+ compatible = "qcom,msm8974-mtp", "qcom,msm8974", "qcom,mtp";
+ qcom,msm-id = <194 8 0x10000>,
+ <210 8 0x10000>,
+ <213 8 0x10000>,
+ <216 8 0x10000>;
+};
+
+&sdhc_1 {
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000>;
};
diff --git a/arch/arm/boot/dts/msm8974pro-ac-mtp.dtsi b/arch/arm/boot/dts/msm8974pro-ac-mtp.dtsi
new file mode 100644
index 0000000..250afd2
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974pro-ac-mtp.dtsi
@@ -0,0 +1,302 @@
+/* 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/ "msm8974-mtp.dtsi"
+
+&cci {
+ qcom,camera@6e {
+ qcom,vdd-cx-supply = <&pma8084_s2>;
+ cam_vdig-supply = <&pma8084_l3>;
+ cam_vana-supply = <&pma8084_l17>;
+ cam_vio-supply = <&pma8084_lvs4>;
+ cam_vaf-supply = <&pma8084_l23>;
+ };
+
+ qcom,camera@20 {
+ qcom,vdd-cx-supply = <&pma8084_s2>;
+ cam_vdig-supply = <&pma8084_l3>;
+ cam_vana-supply = <&pma8084_l17>;
+ cam_vio-supply = <&pma8084_lvs4>;
+ cam_vaf-supply = <&pma8084_l23>;
+ };
+
+ qcom,camera@6c {
+ qcom,vdd-cx-supply = <&pma8084_s2>;
+ cam_vdig-supply = <&pma8084_l3>;
+ cam_vana-supply = <&pma8084_l17>;
+ cam_vio-supply = <&pma8084_lvs4>;
+ };
+
+ qcom,camera@90 {
+ qcom,vdd-cx-supply = <&pma8084_s2>;
+ cam_vdig-supply = <&pma8084_l3>;
+ cam_vana-supply = <&pma8084_l17>;
+ cam_vio-supply = <&pma8084_lvs4>;
+ };
+};
+
+&soc {
+ i2c@f9924000 {
+ atmel_mxt_ts@4a {
+ vdd_ana-supply = <&pma8084_l18>;
+ vcc_i2c-supply = <&pma8084_lvs1>;
+ };
+ };
+
+ i2c@f9967000 {
+ isa1200@48 {
+ vcc_i2c-supply = <&pma8084_s4>;
+ };
+ };
+
+ qcom,ssusb@f9200000 {
+ vbus_dwc3-supply = <&pm8941_mvs1>;
+ };
+
+ qcom,mdss_dsi_toshiba_720p_video {
+ qcom,rst-gpio = <&pma8084_gpios 20 0>;
+ };
+
+ gpio_keys {
+ camera_snapshot {
+ gpios = <&pma8084_gpios 3 0x1>;
+ };
+
+ camera_focus {
+ gpios = <&pma8084_gpios 4 0x1>;
+ };
+
+ vol_up {
+ gpios = <&pma8084_gpios 5 0x1>;
+ };
+ };
+
+ spi@f9923000 {
+ ethernet-switch@2 {
+ rst-gpio = <&pma8084_mpps 6 0>;
+ };
+ };
+};
+
+&sdhc_1 {
+ vdd-supply = <&pma8084_l20>;
+ vdd-io-supply = <&pma8084_s4>;
+};
+
+&sdhc_2 {
+ vdd-supply = <&pma8084_l21>;
+ vdd-io-supply = <&pma8084_l13>;
+};
+
+&pma8084_gpios {
+ gpio@c000 { /* GPIO 1 */
+ /* Unused */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <0>; /* Pull up 30 uA */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ /* Unused */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <0>; /* Pull up 30 uA */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ /* Snapshot button */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <0>; /* Pull up 30 uA */
+ qcom,vin-sel = <2>; /* PMA8084 S4 = 1.8 V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ /* Focus button */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <0>; /* Pull up 30 uA */
+ qcom,vin-sel = <2>; /* PMA8084 S4 = 1.8 V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ /* Volume up button */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <0>; /* Pull up 30 uA */
+ qcom,vin-sel = <2>; /* PMA8084 S4 = 1.8 V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ /* Flash LED now */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <4>; /* Pull down */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ /* GRFC_12 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ /* Unused */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <0>; /* Pull up 30 uA */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c800 { /* GPIO 9 */
+ /* Unused */
+ qcom,mode = <1>; /* Digital output */
+ qcom,out-strength = <1>; /* Low */
+ qcom,src-sel = <2>; /* Function 1 */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c900 { /* GPIO 10 */
+ /* NFC clock request */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <4>; /* Pull down */
+ qcom,master-en = <1>;
+ };
+
+ gpio@ca00 { /* GPIO 11 */
+ /* Unused */
+ qcom,mode = <1>; /* Digital output */
+ qcom,out-strength = <1>; /* Low */
+ qcom,src-sel = <2>; /* Function 1 */
+ qcom,master-en = <1>;
+ };
+
+ gpio@cb00 { /* GPIO 12 */
+ /* Unused */
+ qcom,mode = <1>; /* Digital output */
+ qcom,out-strength = <1>; /* Low */
+ qcom,src-sel = <2>; /* Function 1 */
+ qcom,master-en = <1>;
+ };
+
+ gpio@cc00 { /* GPIO 13 */
+ /* TS_CHGR_IN */
+ qcom,mode = <0>; /* Digital input */
+ qcom,pull = <4>; /* Pull down */
+ qcom,master-en = <1>;
+ };
+
+ gpio@cd00 { /* GPIO 14 */
+ /* Unused */
+ qcom,mode = <1>; /* Digital output */
+ qcom,out-strength = <1>; /* Low */
+ qcom,src-sel = <2>; /* Function 1 */
+ qcom,master-en = <1>;
+ };
+
+ gpio@ce00 { /* GPIO 15 */
+ /* Codec clock */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <2>; /* PMA8084 S4 = 1.8 V */
+ qcom,out-strength = <1>; /* Low */
+ qcom,src-sel = <2>; /* Function 1 - DIVCLK1 */
+ qcom,master-en = <1>;
+ };
+
+ gpio@cf00 { /* GPIO 16 */
+ /* Haptics clock */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <2>; /* PMA8084 S4 = 1.8 V */
+ qcom,out-strength = <3>; /* High */
+ qcom,src-sel = <3>; /* Function 2 - SLEEPCLK3 */
+ qcom,master-en = <1>;
+ };
+
+ gpio@d000 { /* GPIO 17 */
+ /* QPA clock */
+ };
+
+ gpio@d100 { /* GPIO 18 */
+ /* Unused */
+ };
+
+ gpio@d200 { /* GPIO 19 */
+ /* BOOST_BYP */
+ };
+
+ gpio@d300 { /* GPIO 20 */
+ /* Display enable */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <2>; /* PMA8084 S4 = 1.8 V */
+ qcom,out-strength = <1>; /* Low */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>;
+ };
+
+ gpio@d400 { /* GPIO 21 */
+ /* BATT_GONE */
+ };
+
+ gpio@d500 { /* GPIO 22 */
+ /* BATT_REM_ALARM */
+ };
+};
+
+&pma8084_mpps {
+ mpp@a000 { /* MPP 1 */
+ /* SDC_UIM_VBIAS */
+ status = "disabled";
+ };
+
+ mpp@a100 { /* MPP 2 */
+ /* PM8941_PON_1 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ /* VREF_DAC */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ /* Unused */
+ };
+
+ mpp@a400 { /* MPP 5 */
+ /* Unused */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ /* SPI ethernet enable */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <2>; /* PMA8084 S4 = 1.8V > 1.6V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,out-strength = <1>; /* Low */
+ qcom,master-en = <1>;
+ };
+
+ mpp@a600 { /* MPP 7 */
+ /* NFC disable */
+ qcom,mode = <1>; /* Digital output */
+ qcom,out-strength = <1>; /* Low */
+ qcom,master-en = <1>;
+ };
+
+ mpp@a700 { /* MPP 8 */
+ /* Unused */
+ qcom,mode = <1>; /* Digital output */
+ qcom,out-strength = <1>; /* Low */
+ qcom,master-en = <1>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974pro-ac-pm8941.dtsi b/arch/arm/boot/dts/msm8974pro-ac-pm8941.dtsi
new file mode 100644
index 0000000..4d10ded
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974pro-ac-pm8941.dtsi
@@ -0,0 +1,250 @@
+/* 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/ "msm8974pro-ac.dtsi"
+&spmi_bus {
+ pm8941_lsid0: qcom,pm8941@2 {
+ reg = <0x2>;
+ };
+ pm8941_lsid1: qcom,pm8941@3 {
+ reg = <0x3>;
+ };
+};
+/include/ "msm-pm8941.dtsi"
+
+&pma8084_vadc {
+ status = "disabled";
+};
+
+&pma8084_adc_tm {
+ status = "disabled";
+};
+
+&pm8941_lsid0 {
+ qcom,power-on@800 {
+ status = "disabled";
+ };
+
+ clkdiv@5b00 {
+ status = "disabled";
+ };
+
+ clkdiv@5c00 {
+ status = "disabled";
+ };
+
+ clkdiv@5d00 {
+ status = "disabled";
+ };
+
+ qcom,pm8941_rtc {
+ status = "disabled";
+ };
+
+ gpios {
+ status = "disabled";
+ };
+
+ mpps {
+ status = "disabled";
+ };
+};
+
+&pm8941_lsid1 {
+ pm8941_boost: regulator@a000 {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ qcom,enable-time = <500>;
+ status = "okay";
+ };
+
+ pm8941_mvs1: regulator@8300 {
+ parent-supply = <&pm8941_chg_otg>;
+ qcom,enable-time = <1000>;
+ qcom,pull-down-enable = <1>;
+ interrupts = <0x3 0x83 0x2>;
+ interrupt-names = "ocp";
+ qcom,ocp-enable = <1>;
+ qcom,ocp-max-retries = <10>;
+ qcom,ocp-retry-delay = <30>;
+ qcom,soft-start-enable = <1>;
+ qcom,vs-soft-start-strength = <0>;
+ qcom,hpm-enable = <1>;
+ qcom,auto-mode-enable = <0>;
+ status = "okay";
+ };
+};
+
+&pma8084_mvs1 {
+ parent-supply = <&pm8941_boost>;
+};
+
+&pm8941_chg {
+ otg-parent-supply = <&pm8941_boost>;
+};
+
+&pm8941_chg_boost {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-name = "8941_smbb_boost";
+};
+
+&pm8941_chg_otg {
+ regulator-name = "8941_smbb_otg";
+};
+
+/* Correct PM8941 local slave ID 0 to use global SID 4 for all interrupts. */
+&pm8941_lsid0 {
+ qcom,temp-alarm@2400 {
+ interrupts = <0x2 0x24 0x0>;
+ };
+
+ qcom,power-on@800 {
+ interrupts = <0x2 0x8 0x0>,
+ <0x2 0x8 0x1>,
+ <0x2 0x8 0x4>,
+ <0x2 0x8 0x5>;
+ interrupt-names = "kpdpwr", "resin",
+ "resin-bark", "kpdpwr-resin-bark";
+ };
+
+ qcom,bsi@1b00 {
+ interrupts = <0x2 0x1b 0x0>,
+ <0x2 0x1b 0x1>,
+ <0x2 0x1b 0x2>,
+ <0x2 0x12 0x0>;
+ interrupt-names = "err",
+ "rx",
+ "tx",
+ "batt-present";
+ };
+
+ qcom,bms {
+ qcom,bms-bms@4000 {
+ interrupts = <0x2 0x40 0x0>,
+ <0x2 0x40 0x1>,
+ <0x2 0x40 0x2>,
+ <0x2 0x40 0x3>,
+ <0x2 0x40 0x4>,
+ <0x2 0x40 0x5>,
+ <0x2 0x40 0x6>,
+ <0x2 0x40 0x7>;
+ interrupt-names = "cc_thr",
+ "ocv_for_r",
+ "good_ocv",
+ "charge_begin",
+ "ocv_thr",
+ "sw_cc_thr",
+ "vsense_avg",
+ "vsense_for_r";
+ };
+ };
+
+ qcom,charger {
+ qcom,chgr@1000 {
+ interrupts = <0x2 0x10 0x0>,
+ <0x2 0x10 0x1>,
+ <0x2 0x10 0x2>,
+ <0x2 0x10 0x3>,
+ <0x2 0x10 0x4>,
+ <0x2 0x10 0x5>,
+ <0x2 0x10 0x6>,
+ <0x2 0x10 0x7>;
+ interrupt-names = "vbat-det-lo",
+ "vbat-det-hi",
+ "chgwdog",
+ "state-change",
+ "trkl-chg-on",
+ "fast-chg-on",
+ "chg-failed",
+ "chg-done";
+ };
+
+ qcom,buck@1100 {
+ interrupts = <0x2 0x11 0x0>,
+ <0x2 0x11 0x1>,
+ <0x2 0x11 0x2>,
+ <0x2 0x11 0x3>,
+ <0x2 0x11 0x4>,
+ <0x2 0x11 0x5>,
+ <0x2 0x11 0x6>;
+ interrupt-names = "vbat-ov",
+ "vreg-ov",
+ "overtemp",
+ "vchg-loop",
+ "ichg-loop",
+ "ibat-loop",
+ "vdd-loop";
+ };
+
+ qcom,bat-if@1200 {
+ interrupts = <0x2 0x12 0x0>,
+ <0x2 0x12 0x1>,
+ <0x2 0x12 0x2>,
+ <0x2 0x12 0x3>,
+ <0x2 0x12 0x4>;
+ interrupt-names = "batt-pres",
+ "bat-temp-ok",
+ "bat-fet-on",
+ "vcp-on",
+ "psi";
+ };
+
+ qcom,usb-chgpth@1300 {
+ interrupts = <0x2 0x13 0x0>,
+ <0x2 0x13 0x1>,
+ <0x2 0x13 0x2>;
+ interrupt-names = "coarse-det-usb",
+ "usbin-valid",
+ "chg-gone";
+ };
+
+ qcom,dc-chgpth@1400 {
+ interrupts = <0x2 0x14 0x0>,
+ <0x2 0x14 0x1>;
+ interrupt-names = "coarse-det-dc",
+ "dcin-valid";
+ };
+
+ qcom,boost@1500 {
+ interrupts = <0x2 0x15 0x0>,
+ <0x2 0x15 0x1>;
+ interrupt-names = "boost-pwr-ok",
+ "limit-error";
+ };
+ };
+
+ qcom,pm8941_rtc {
+ qcom,pm8941_rtc_alarm@6100 {
+ interrupts = <0x2 0x61 0x1>;
+ };
+ };
+
+ vadc@3100 {
+ interrupts = <0x2 0x31 0x0>;
+ interrupt-names = "eoc-int-en-set";
+ };
+
+ iadc@3600 {
+ interrupts = <0x2 0x36 0x0>;
+ interrupt-names = "eoc-int-en-set";
+ };
+
+ qcom,vadc@3400 {
+ interrupts = <0x2 0x34 0x0>,
+ <0x2 0x34 0x3>,
+ <0x2 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974pro-ac-regulator.dtsi b/arch/arm/boot/dts/msm8974pro-ac-regulator.dtsi
new file mode 100644
index 0000000..9d7f316
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974pro-ac-regulator.dtsi
@@ -0,0 +1,566 @@
+/* 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.
+ */
+
+
+/* QPNP controlled regulators: */
+
+&spmi_bus {
+ qcom,pma8084@1 {
+ pma8084_mvs1: regulator@8400 {
+ qcom,enable-time = <1000>;
+ qcom,pull-down-enable = <1>;
+ interrupts = <0x1 0x84 0x2>;
+ interrupt-names = "ocp";
+ qcom,ocp-enable = <1>;
+ qcom,ocp-max-retries = <10>;
+ qcom,ocp-retry-delay = <30>;
+ qcom,soft-start-enable = <1>;
+ qcom,vs-soft-start-strength = <0>;
+ qcom,hpm-enable = <1>;
+ qcom,auto-mode-enable = <0>;
+ status = "okay";
+ };
+ };
+};
+
+/* RPM controlled regulators: */
+
+&rpm_bus {
+ rpm-regulator-smpa1 {
+ status = "okay";
+ pma8084_s1: regulator-s1 {
+ regulator-min-microvolt = <675000>;
+ regulator-max-microvolt = <1050000>;
+ status = "okay";
+ };
+ pma8084_s1_ao: regulator-s1-ao {
+ regulator-name = "8084_s1_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <675000>;
+ regulator-max-microvolt = <1050000>;
+ status = "okay";
+ compatible = "qcom,rpm-regulator-smd";
+ };
+ pma8084_s1_so: regulator-s1-so {
+ regulator-name = "8084_s1_so";
+ qcom,set = <2>;
+ regulator-min-microvolt = <675000>;
+ regulator-max-microvolt = <1050000>;
+ qcom,init-voltage = <675000>;
+ status = "okay";
+ compatible = "qcom,rpm-regulator-smd";
+ };
+ };
+
+ rpm-regulator-smpa2 {
+ status = "okay";
+ pma8084_s2: regulator-s2 {
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1050000>;
+ status = "okay";
+ };
+ pma8084_s2_corner: regulator-s2-corner {
+ regulator-name = "8084_s2_corner";
+ qcom,set = <3>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-corner;
+ compatible = "qcom,rpm-regulator-smd";
+ qcom,consumer-supplies = "vdd_dig", "";
+ };
+ pma8084_s2_corner_ao: regulator-s2-corner-ao {
+ regulator-name = "8084_s2_corner_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-corner;
+ compatible = "qcom,rpm-regulator-smd";
+ };
+ pma8084_s2_floor_corner: regulator-s2-floor-corner {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s2_floor_corner";
+ qcom,set = <3>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-floor-corner;
+ qcom,always-send-voltage;
+ };
+ };
+
+
+ rpm-regulator-smpa3 {
+ status = "okay";
+ pma8084_s3: regulator-s3 {
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ qcom,init-voltage = <1300000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-smpa4 {
+ status = "okay";
+ pma8084_s4: regulator-s4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-smpa5 {
+ status = "okay";
+ pma8084_s5: regulator-s5 {
+ regulator-min-microvolt = <2150000>;
+ regulator-max-microvolt = <2150000>;
+ qcom,init-voltage = <2150000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-smpa6 {
+ status = "okay";
+ pma8084_s6: regulator-s6 {
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ qcom,init-voltage = <1050000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-smpa7 {
+ status = "okay";
+ pma8084_s7: regulator-s7 {
+ regulator-min-microvolt = <815000>;
+ regulator-max-microvolt = <900000>;
+ status = "okay";
+ };
+ pma8084_s7_corner: regulator-s7-corner {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s7_corner";
+ qcom,set = <3>;
+ qcom,use-voltage-corner;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,init-voltage-corner = <3>; /* SVS SOC */
+ };
+ pma8084_s7_floor_corner: regulator-s7-floor-corner {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s7_floor_corner";
+ qcom,set = <3>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-floor-corner;
+ qcom,always-send-voltage;
+ };
+ };
+
+ rpm-regulator-ldoa1 {
+ status = "okay";
+ pma8084_l1: regulator-l1 {
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,init-voltage = <1225000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa2 {
+ status = "okay";
+ pma8084_l2: regulator-l2 {
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ qcom,init-voltage = <1200000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa3 {
+ status = "okay";
+ pma8084_l3: regulator-l3 {
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,init-voltage = <1225000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa4 {
+ status = "okay";
+ pma8084_l4: regulator-l4 {
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,init-voltage = <1225000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa5 {
+ status = "okay";
+ pma8084_l5: regulator-l5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa6 {
+ status = "okay";
+ pma8084_l6: regulator-l6 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa7 {
+ status = "okay";
+ pma8084_l7: regulator-l7 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa8 {
+ status = "okay";
+ pma8084_l8: regulator-l8 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa9 {
+ status = "okay";
+ pma8084_l9: regulator-l9 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,init-voltage = <2950000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa10 {
+ status = "okay";
+ pma8084_l10: regulator-l10 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,init-voltage = <2950000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa11 {
+ status = "okay";
+ pma8084_l11: regulator-l11 {
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ qcom,init-voltage = <1300000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa12 {
+ status = "okay";
+ pma8084_l12: regulator-l12 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ status = "okay";
+ };
+ pma8084_l12_ao: regulator-l12-ao {
+ regulator-name = "8084_l12_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ status = "okay";
+ compatible = "qcom,rpm-regulator-smd";
+ };
+ };
+
+ rpm-regulator-ldoa13 {
+ status = "okay";
+ pma8084_l13: regulator-l13 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,init-voltage = <2950000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa14 {
+ status = "okay";
+ pma8084_l14: regulator-l14 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa15 {
+ status = "okay";
+ pma8084_l15: regulator-l15 {
+ regulator-min-microvolt = <2050000>;
+ regulator-max-microvolt = <2050000>;
+ qcom,init-voltage = <2050000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa16 {
+ status = "okay";
+ pma8084_l16: regulator-l16 {
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <2700000>;
+ qcom,init-voltage = <2700000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa17 {
+ status = "okay";
+ pma8084_l17: regulator-l17 {
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ qcom,init-voltage = <2850000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa18 {
+ status = "okay";
+ pma8084_l18: regulator-l18 {
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ qcom,init-voltage = <2850000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa19 {
+ status = "okay";
+ pma8084_l19: regulator-l19 {
+ regulator-min-microvolt = <2900000>;
+ regulator-max-microvolt = <3300000>;
+ qcom,init-voltage = <2900000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa20 {
+ status = "okay";
+ pma8084_l20: regulator-l20 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,init-voltage = <2950000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa21 {
+ status = "okay";
+ pma8084_l21: regulator-l21 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,init-voltage = <2950000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa22 {
+ status = "okay";
+ pma8084_l22: regulator-l22 {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ qcom,init-voltage = <3000000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa23 {
+ status = "okay";
+ pma8084_l23: regulator-l23 {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ qcom,init-voltage = <3000000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa24 {
+ status = "okay";
+ pma8084_l24: regulator-l24 {
+ regulator-min-microvolt = <3075000>;
+ regulator-max-microvolt = <3075000>;
+ qcom,init-voltage = <3075000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa25 {
+ status = "okay";
+ pma8084_l25: regulator-l25 {
+ regulator-min-microvolt = <2100000>;
+ regulator-max-microvolt = <2100000>;
+ qcom,init-voltage = <2100000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa26 {
+ status = "okay";
+ pma8084_l26: regulator-l26 {
+ regulator-min-microvolt = <2050000>;
+ regulator-max-microvolt = <2050000>;
+ qcom,init-voltage = <2050000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa27 {
+ status = "okay";
+ pma8084_l27: regulator-l27 {
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,init-voltage = <1200000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-vsa1 {
+ status = "okay";
+ pma8084_lvs1: regulator-lvs1 {
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-vsa2 {
+ status = "okay";
+ pma8084_lvs2: regulator-lvs2 {
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-vsa3 {
+ status = "okay";
+ pma8084_lvs3: regulator-lvs3 {
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-vsa4 {
+ status = "okay";
+ pma8084_lvs4: regulator-lvs4 {
+ status = "okay";
+ };
+ };
+};
+
+&soc {
+ krait_pdn: krait-pdn@f9011000 {
+ reg = <0xf9011000 0x1000>,
+ <0xfc4b80b0 8>;
+ reg-names = "apcs_gcc", "phase-scaling-efuse";
+ compatible = "qcom,krait-pdn";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ qcom,pfm-threshold = <73>;
+
+ 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>;
+ };
+
+ 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>;
+ };
+
+ 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 expects a regulator device, but no hardware is present. */
+ spi_eth_vreg: spi_eth_phy_vreg {
+ compatible = "regulator-fixed";
+ regulator-name = "ethernet_phy";
+ regulator-always-on;
+ };
+
+ /*
+ * vph_pwr_vreg represents the unregulated battery voltage supply
+ * VPH_PWR that is present whenever the device is powered on.
+ */
+ vph_pwr_vreg: vph_pwr_vreg {
+ compatible = "regulator-fixed";
+ status = "disabled";
+ regulator-name = "vph_pwr";
+ regulator-always-on;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974pro-ac.dtsi b/arch/arm/boot/dts/msm8974pro-ac.dtsi
new file mode 100644
index 0000000..032c256
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974pro-ac.dtsi
@@ -0,0 +1,190 @@
+/* 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.
+ */
+
+/*
+ * As a general rule, only chipset-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8974.dtsi / msm8974pro.dtsi file(s).
+ */
+
+/include/ "msm8974pro.dtsi"
+
+/include/ "msm-pma8084-rpm-regulator.dtsi"
+/include/ "msm-pma8084.dtsi"
+/include/ "msm8974pro-ac-regulator.dtsi"
+
+/*
+ * Override PM8841 and PM8941 resources with proper PMA8084 resources for
+ * MSM8974Pro AC.
+ */
+
+&soc {
+ qcom,csid@fda08000 {
+ qcom,mipi-csi-vdd-supply = <&pma8084_l12>;
+ };
+
+ qcom,csid@fda08400 {
+ qcom,mipi-csi-vdd-supply = <&pma8084_l12>;
+ };
+
+ qcom,csid@fda08800 {
+ qcom,mipi-csi-vdd-supply = <&pma8084_l12>;
+ };
+
+ qcom,csid@fda08C00 {
+ qcom,mipi-csi-vdd-supply = <&pma8084_l12>;
+ };
+
+ tpiu@fc318000 {
+ vdd-supply = <&pma8084_l21>;
+ };
+
+ qcom,mdss_dsi@fd922800 {
+ vdd-supply = <&pma8084_l22>;
+ vddio-supply = <&pma8084_l12>;
+ vdda-supply = <&pma8084_l2>;
+ };
+
+ qcom,mdss_dsi@fd922e00 {
+ vdd-supply = <&pma8084_l22>;
+ vddio-supply = <&pma8084_l12>;
+ vdda-supply = <&pma8084_l2>;
+ };
+
+ qcom,hdmi_tx@fd922100 {
+ hpd-5v-supply = <&pma8084_mvs1>;
+ core-vdda-supply = <&pma8084_l12>;
+ core-vcc-supply = <&pma8084_s4>;
+ };
+
+ qcom,mdss_edp@fd923400 {
+ vdda-supply = <&pma8084_l12>;
+ };
+
+ usb@f9a55000 {
+ HSUSB_VDDCX-supply = <&pma8084_s2_corner>;
+ HSUSB_1p8-supply = <&pma8084_l6>;
+ HSUSB_3p3-supply = <&pma8084_l24>;
+ };
+
+ qcom,sdcc@f9824000 {
+ vdd-supply = <&pma8084_l20>;
+ vdd-io-supply = <&pma8084_s4>;
+ };
+
+ qcom,sdcc@f98a4000 {
+ vdd-supply = <&pma8084_l21>;
+ vdd-io-supply = <&pma8084_l13>;
+ };
+
+ slim@fe12f000 {
+ taiko_codec {
+ cdc-vdd-buck-supply = <&pma8084_s5>;
+ cdc-vdd-tx-h-supply = <&pma8084_s4>;
+ cdc-vdd-rx-h-supply = <&pma8084_s4>;
+ cdc-vddpx-1-supply = <&pma8084_s4>;
+ cdc-vdd-a-1p2v-supply = <&pma8084_l1>;
+ cdc-vddcx-1-supply = <&pma8084_l1>;
+ cdc-vddcx-2-supply = <&pma8084_l1>;
+ };
+ };
+
+ qcom,acpuclk@f9000000 {
+ krait0_mem-supply = <&pma8084_s1_ao>;
+ krait1_mem-supply = <&pma8084_s1_ao>;
+ krait2_mem-supply = <&pma8084_s1_ao>;
+ krait3_mem-supply = <&pma8084_s1_ao>;
+ krait0_dig-supply = <&pma8084_s2_corner_ao>;
+ krait1_dig-supply = <&pma8084_s2_corner_ao>;
+ krait2_dig-supply = <&pma8084_s2_corner_ao>;
+ krait3_dig-supply = <&pma8084_s2_corner_ao>;
+ krait0_hfpll-supply = <&pma8084_l12_ao>;
+ krait1_hfpll-supply = <&pma8084_l12_ao>;
+ krait2_hfpll-supply = <&pma8084_l12_ao>;
+ krait3_hfpll-supply = <&pma8084_l12_ao>;
+ l2_hfpll-supply = <&pma8084_l12_ao>;
+ };
+
+ qcom,ssusb@f9200000 {
+ ssusb_vdd_dig-supply = <&pma8084_s2_corner>;
+ SSUSB_1p8-supply = <&pma8084_l6>;
+ hsusb_vdd_dig-supply = <&pma8084_s2_corner>;
+ HSUSB_1p8-supply = <&pma8084_l6>;
+ HSUSB_3p3-supply = <&pma8084_l24>;
+ };
+
+ qcom,ehci-host@f9a55000 {
+ HSUSB_VDDCX-supply = <&pma8084_s2>;
+ HSUSB_1p8-supply = <&pma8084_l6>;
+ HSUSB_3p3-supply = <&pma8084_l24>;
+ };
+
+ qcom,gdsc@fd8c4024 {
+ parent-supply = <&pma8084_s7_corner>;
+ };
+
+ qcom,lpass@fe200000 {
+ vdd_cx-supply = <&pma8084_s2_corner>;
+ };
+
+ qcom,mss@fc880000 {
+ vdd_mss-supply = <&pma8084_s6>;
+ vdd_cx-supply = <&pma8084_s2_corner>;
+ vdd_mx-supply = <&pma8084_s1>;
+ vdd_pll-supply = <&pma8084_l12>;
+ };
+
+ qcom,pronto@fb21b000 {
+ vdd_pronto_pll-supply = <&pma8084_l12>;
+ };
+
+ qcom,wcnss-wlan@fb000000 {
+ qcom,pronto-vddmx-supply = <&pma8084_s1>;
+ qcom,pronto-vddcx-supply = <&pma8084_s2>;
+ qcom,pronto-vddpx-supply = <&pma8084_s4>;
+ qcom,iris-vddxo-supply = <&pma8084_l6>;
+ qcom,iris-vddrfa-supply = <&pma8084_l11>;
+ qcom,iris-vddpa-supply = <&pma8084_l19>;
+ qcom,iris-vdddig-supply = <&pma8084_s4>;
+ };
+
+ qcom,msm-thermal {
+ vdd-dig-supply = <&pma8084_s2_floor_corner>;
+ vdd-gfx-supply = <&pma8084_s7_floor_corner>;
+ };
+
+ qcom,lpm-resources {
+ qcom,lpm-resources@0 {
+ qcom,name = "vdd-dig";
+ qcom,type = <0x61706d73>; /* "smpa" */
+ qcom,id = <2>;
+ };
+
+ qcom,lpm-resources@1 {
+ qcom,name = "vdd-mem";
+ qcom,type = <0x61706d73>; /* "smpa" */
+ qcom,id = <1>;
+ };
+ };
+
+ sound {
+ qcom,cdc-mclk-gpios = <&pma8084_gpios 15 0>;
+ };
+};
+
+&krait_pdn {
+ qcom,use-phase-switching;
+};
+
+&tspp {
+ vdd_cx-supply = <&pma8084_s2_corner>;
+};
diff --git a/arch/arm/boot/dts/msm8974pro.dtsi b/arch/arm/boot/dts/msm8974pro.dtsi
new file mode 100644
index 0000000..b80eb0f
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974pro.dtsi
@@ -0,0 +1,179 @@
+/* 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.
+ */
+
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8974.dtsi file.
+ */
+
+/include/ "msm8974.dtsi"
+/include/ "msm8974-v2-iommu.dtsi"
+/include/ "msm8974-v2-iommu-domains.dtsi"
+/include/ "msm8974-v2-pm.dtsi"
+
+&soc {
+ android_usb@fe8050c8 {
+ compatible = "qcom,android-usb";
+ reg = <0xfe8050c8 0xc8>;
+ qcom,android-usb-swfi-latency = <1>;
+ };
+
+ qcom,msm-imem@fe805000 {
+ compatible = "qcom,msm-imem";
+ reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
+ };
+};
+
+/* GPU overrides */
+&msm_gpu {
+ /* Updated chip ID */
+ qcom,chipid = <0x03030002>;
+
+ /* Updated bus bandwidth requirements */
+ qcom,msm-bus,vectors-KBps =
+ /* Off */
+ <26 512 0 0>, <89 604 0 0>,
+ /* SVS */
+ <26 512 0 2400000>, <89 604 0 3000000>,
+ /* Nominal / SVS */
+ <26 512 0 4656000>, <89 604 0 3000000>,
+ /* Nominal */
+ <26 512 0 4656000>, <89 604 0 5120000>,
+ /* Turbo / Nominal */
+ <26 512 0 7464000>, <89 604 0 5120000>,
+ /* Turbo */
+ <26 512 0 7464000>, <89 604 0 6400000>;
+
+ qcom,gpu-pwrlevels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "qcom,gpu-pwrlevels";
+
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <550000000>;
+ qcom,bus-freq = <5>;
+ qcom,io-fraction = <33>;
+ };
+
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <4>;
+ qcom,io-fraction = <66>;
+ };
+
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <3>;
+ qcom,io-fraction = <66>;
+ };
+
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <200000000>;
+ qcom,bus-freq = <2>;
+ qcom,io-fraction = <100>;
+ };
+
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <200000000>;
+ qcom,bus-freq = <1>;
+ qcom,io-fraction = <100>;
+ };
+
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <27000000>;
+ qcom,bus-freq = <0>;
+ qcom,io-fraction = <0>;
+ };
+ };
+};
+
+&mdss_mdp {
+ qcom,vbif-settings = <0x0004 0x00000001>;
+
+ qcom,mdss-wb-off = <0x00011100 0x00011500
+ 0x00011900 0x00011D00 0x00012100>;
+ qcom,mdss-intf-off = <0x00012500 0x00012700
+ 0x00012900 0x00012b00>;
+ qcom,mdss-pingpong-off = <0x00012D00 0x00012E00 0x00012F00>;
+ qcom,mdss-has-bwc;
+ qcom,mdss-has-decimation;
+ qcom,mdss-ad-off = <0x0013100 0x00013300>;
+};
+
+&mdss_hdmi_tx {
+ reg = <0xfd922100 0x370>,
+ <0xfd922500 0x7C>,
+ <0xfc4b8000 0x60F0>;
+ reg-names = "core_physical", "phy_physical", "qfprom_physical";
+};
+
+&msm_vidc {
+ qcom,vidc-ns-map = <0x40000000 0x40000000>;
+ qcom,load-freq-tbl = <979200 465000000>,
+ <783360 465000000>,
+ <489600 266670000>,
+ <244800 133330000>;
+ qcom,reg-presets = <0x80004 0x1>,
+ <0x80070 0x11FFF>,
+ <0x80074 0xA4>,
+ <0x800A8 0x1FFF>,
+ <0x80124 0x3>,
+ <0xE0020 0x5555556>,
+ <0xE0024 0x0>;
+ qcom,bus-ports = <1>;
+ qcom,enc-ocmem-ab-ib = <0 0>,
+ <138000 1034000>,
+ <414000 1034000>,
+ <940000 1034000>,
+ <1880000 2068000>,
+ <3008000 3309000>,
+ <3760000 4136000>,
+ <4468000 2457000>;
+ qcom,dec-ocmem-ab-ib = <0 0>,
+ <176000 519000>,
+ <456000 519000>,
+ <864000 519000>,
+ <1728000 1038000>,
+ <2766000 1661000>,
+ <3456000 2076000>,
+ <3662000 2198000>;
+ qcom,enc-ddr-ab-ib = <0 0>,
+ <120000 302000>,
+ <364000 302000>,
+ <804000 302000>,
+ <1608000 604000>,
+ <2576000 967000>,
+ <4680000 1404000>,
+ <49880000 1496000>;
+ qcom,dec-ddr-ab-ib = <0 0>,
+ <208000 303000>,
+ <536000 303000>,
+ <1012000 303000>,
+ <2024000 606000>,
+ <3240000 970000>,
+ <4048000 1212000>,
+ <4264000 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>;
+};
diff --git a/arch/arm/boot/dts/msm9625-coresight.dtsi b/arch/arm/boot/dts/msm9625-coresight.dtsi
index bde734e..60472c8 100644
--- a/arch/arm/boot/dts/msm9625-coresight.dtsi
+++ b/arch/arm/boot/dts/msm9625-coresight.dtsi
@@ -249,11 +249,22 @@
hwevent: hwevent@f9011038 {
compatible = "qcom,coresight-hwevent";
reg = <0xf9011038 0x8>,
- <0xfd4ab160 0x80>;
- reg-names = "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <20>;
coresight-name = "coresight-hwevent";
coresight-nr-inports = <0>;
};
+
+ fuse: fuse@fc4be024 {
+ compatible = "arm,coresight-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+
+ coresight-id = <21>;
+ coresight-name = "coresight-fuse";
+ coresight-nr-inports = <0>;
+ };
};
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 8eb1119..e18e143 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -271,8 +271,9 @@
<37 71>;
};
- qcom,pm-8x60 {
+ qcom,pm-8x60@fe805664 {
compatible = "qcom,pm-8x60";
+ reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_ext";
qcom,use-sync-timer;
};
@@ -294,4 +295,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm9625-v2.1.dtsi b/arch/arm/boot/dts/msm9625-v2.1.dtsi
index 65ff96a..5720700 100644
--- a/arch/arm/boot/dts/msm9625-v2.1.dtsi
+++ b/arch/arm/boot/dts/msm9625-v2.1.dtsi
@@ -34,3 +34,7 @@
&ipa_hw {
qcom,ipa-hw-ver = <2>; /* IPA h-w revision */
};
+
+&hsusb_otg {
+ qcom,hsusb-l1-supported;
+};
diff --git a/arch/arm/boot/dts/msm9625-v2.dtsi b/arch/arm/boot/dts/msm9625-v2.dtsi
index b078309..14105cb 100644
--- a/arch/arm/boot/dts/msm9625-v2.dtsi
+++ b/arch/arm/boot/dts/msm9625-v2.dtsi
@@ -46,3 +46,7 @@
&ldrex_spinlock {
status = "ok";
};
+
+&hsusb_otg {
+ qcom,hsusb-l1-supported;
+};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 636c0f8..5520401 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -148,7 +148,7 @@
interrupts = <0 109 0>;
};
- usb@f9a55000 {
+ hsusb_otg: usb@f9a55000 {
compatible = "qcom,hsusb-otg";
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0 0 140 0>;
@@ -173,7 +173,7 @@
qcom,msm-bus,vectors-KBps =
<87 512 0 0>,
<87 512 40000 640000>;
- qcom,hsusb-log2-itc = <5>;
+ qcom,hsusb-log2-itc = <4>;
};
hsic_host: hsic@f9a15000 {
@@ -195,6 +195,7 @@
hsic,consider-ipa-handshake;
hsic,log2-itc = <3>;
qcom,ahb-async-bridge-bypass;
+ hsic,disable-cerr;
};
qcom,usbbam@f9a44000 {
@@ -510,7 +511,7 @@
tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b8000 0x1000>;
+ <0xfc4bc000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
qcom,sensors = <5>;
@@ -748,9 +749,8 @@
compatible = "qcom,msm-stub-codec";
};
- qcom,msm-auxpcm {
- compatible = "qcom,msm-auxpcm-resource";
- qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
+ qcom,msm-pri-auxpcm {
+ compatible = "qcom,msm-auxpcm-dev";
qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
@@ -758,16 +758,7 @@
qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
qcom,msm-cpudai-auxpcm-data = <0>, <0>;
qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
-
- qcom,msm-auxpcm-rx {
- qcom,msm-auxpcm-dev-id = <4106>;
- compatible = "qcom,msm-auxpcm-dev";
- };
-
- qcom,msm-auxpcm-tx {
- qcom,msm-auxpcm-dev-id = <4107>;
- compatible = "qcom,msm-auxpcm-dev";
- };
+ qcom,msm-auxpcm-interface = "primary";
};
qcom,msm-dai-mi2s {
@@ -868,6 +859,12 @@
qcom,bam-pipe-pair = <2>;
};
+ jtag_fuse: jtagfuse@fc4be024 {
+ compatible = "qcom,jtag-fuse";
+ reg = <0xfc4be024 0x8>;
+ reg-names = "fuse-base";
+ };
+
jtag_mm: jtagmm@fc332000 {
compatible = "qcom,jtag-mm";
reg = <0xfc332000 0x1000>,
diff --git a/arch/arm/boot/dts/msmkrypton.dtsi b/arch/arm/boot/dts/msmkrypton.dtsi
index ba6377c..3743c54 100644
--- a/arch/arm/boot/dts/msmkrypton.dtsi
+++ b/arch/arm/boot/dts/msmkrypton.dtsi
@@ -50,6 +50,11 @@
reg = <0xfe807800 0x1000>; /* Address and size of IMEM */
};
+ qcom,msm-mem-hole {
+ compatible = "qcom,msm-mem-hole";
+ qcom,memblock-remove = <0x02000000 0x06000000>; /* Address and Size of Hole */
+ };
+
timer@f9020000 {
#address-cells = <1>;
#size-cells = <1>;
@@ -123,6 +128,15 @@
status = "disabled";
};
+ qcom,sps@f9980000 {
+ compatible = "qcom,msm_sps";
+ reg = <0xf9984000 0x15000>,
+ <0xf9999000 0xb000>,
+ <0xfe803000 0xd000>,
+ <0xfe805000 0x1000>;
+ interrupts = <0 94 0>;
+ };
+
spi_6: spi@f9928000 { /* BLSP1 QUP6 */
cell-index = <0>;
compatible = "qcom,spi-qup-v2";
diff --git a/arch/arm/boot/dts/msmsamarium-sim.dts b/arch/arm/boot/dts/msmsamarium-sim.dts
index 4acffae..d23bd89 100644
--- a/arch/arm/boot/dts/msmsamarium-sim.dts
+++ b/arch/arm/boot/dts/msmsamarium-sim.dts
@@ -53,3 +53,7 @@
status = "ok";
};
+
+&usb_otg {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msmsamarium-smp2p.dtsi b/arch/arm/boot/dts/msmsamarium-smp2p.dtsi
new file mode 100644
index 0000000..3d8441b
--- /dev/null
+++ b/arch/arm/boot/dts/msmsamarium-smp2p.dtsi
@@ -0,0 +1,160 @@
+/* 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.
+ */
+&soc {
+ qcom,smp2p-modem {
+ compatible = "qcom,smp2p";
+ reg = <0xf9011008 0x4>;
+ qcom,remote-pid = <1>;
+ qcom,irq-bitmask = <0x4000>;
+ interrupts = <0 27 1>;
+ };
+
+ qcom,smp2p-adsp {
+ compatible = "qcom,smp2p";
+ reg = <0xf9011008 0x4>;
+ qcom,remote-pid = <2>;
+ qcom,irq-bitmask = <0x400>;
+ interrupts = <0 158 1>;
+ };
+
+ qcom,smp2p-wcnss {
+ compatible = "qcom,smp2p";
+ reg = <0xf9011008 0x4>;
+ qcom,remote-pid = <4>;
+ qcom,irq-bitmask = <0x40000>;
+ interrupts = <0 143 1>;
+ };
+
+ smp2pgpio_smp2p_7_in: qcom,smp2pgpio-smp2p-7-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_7_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_in";
+ gpios = <&smp2pgpio_smp2p_7_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_7_out: qcom,smp2pgpio-smp2p-7-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_7_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_out";
+ gpios = <&smp2pgpio_smp2p_7_out 0 0>;
+ };
+
+ smp2pgpio_smp2p_1_in: qcom,smp2pgpio-smp2p-1-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <1>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_1_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_1_in";
+ gpios = <&smp2pgpio_smp2p_1_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_1_out: qcom,smp2pgpio-smp2p-1-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_1_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_1_out";
+ gpios = <&smp2pgpio_smp2p_1_out 0 0>;
+ };
+
+ smp2pgpio_smp2p_2_in: qcom,smp2pgpio-smp2p-2-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <2>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_2_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_2_in";
+ gpios = <&smp2pgpio_smp2p_2_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_2_out: qcom,smp2pgpio-smp2p-2-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_2_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_2_out";
+ gpios = <&smp2pgpio_smp2p_2_out 0 0>;
+ };
+
+ smp2pgpio_smp2p_4_in: qcom,smp2pgpio-smp2p-4-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <4>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_4_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_4_in";
+ gpios = <&smp2pgpio_smp2p_4_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_4_out: qcom,smp2pgpio-smp2p-4-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_4_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_4_out";
+ gpios = <&smp2pgpio_smp2p_4_out 0 0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msmsamarium.dtsi b/arch/arm/boot/dts/msmsamarium.dtsi
index 968daff..fdf2680 100644
--- a/arch/arm/boot/dts/msmsamarium.dtsi
+++ b/arch/arm/boot/dts/msmsamarium.dtsi
@@ -20,6 +20,7 @@
};
/include/ "msmsamarium-ion.dtsi"
+/include/ "msmsamarium-smp2p.dtsi"
&soc {
#address-cells = <1>;
@@ -96,4 +97,124 @@
qcom,pet-time = <10000>;
qcom,ipi-ping;
};
+
+ qcom,msm-mem-hole {
+ compatible = "qcom,msm-mem-hole";
+ qcom,memblock-remove = <0x07f00000 0x8000000>; /* Address and size of hole */
+ };
+
+ qcom,ipc-spinlock@fd484000 {
+ compatible = "qcom,ipc-spinlock-sfpb";
+ reg = <0xfd484000 0x400>;
+ qcom,num-locks = <8>;
+ };
+
+ qcom,smem@fa00000 {
+ compatible = "qcom,smem";
+ reg = <0xfa00000 0x200000>,
+ <0xf9011000 0x1000>,
+ <0xfc428000 0x4000>;
+ reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+ qcom,smd-modem {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <0>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1000>;
+ qcom,pil-string = "modem";
+ interrupts = <0 25 1>;
+ };
+
+ qcom,smsm-modem {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <0>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x2000>;
+ interrupts = <0 26 1>;
+ };
+
+ qcom,smd-adsp {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <1>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x100>;
+ qcom,pil-string = "adsp";
+ interrupts = <0 156 1>;
+ };
+
+ qcom,smsm-adsp {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <1>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x200>;
+ interrupts = <0 157 1>;
+ };
+
+ qcom,smd-wcnss {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <6>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x20000>;
+ qcom,pil-string = "wcnss";
+ interrupts = <0 142 1>;
+ };
+
+ qcom,smsm-wcnss {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <6>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x80000>;
+ interrupts = <0 144 1>;
+ };
+
+ qcom,smd-rpm {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <15>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1>;
+ interrupts = <0 168 1>;
+ qcom,irq-no-suspend;
+ };
+ };
+
+ android_usb@fe8050c8 {
+ compatible = "qcom,android-usb";
+ reg = <0xfe8050c8 0xc8>;
+ qcom,android-usb-swfi-latency = <1>;
+ };
+
+ usb_otg: usb@f9a55000 {
+ compatible = "qcom,hsusb-otg";
+ status = "disabled";
+
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>, <0 140 0>;
+ interrupt-names = "core_irq", "async_irq";
+ HSUSB_VDDCX-supply = "";
+ HSUSB_1p8-supply = "";
+ HSUSB_3p3-supply = "";
+ qcom,vdd-voltage-level = <1 5 7>;
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-phy-init-seq =
+ <0x44 0x80 0x68 0x81 0x24 0x82 0x13 0x83 0xffffffff>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+
+ qcom,msm-bus,name = "usb2";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <87 512 0 0>,
+ <87 512 60000 960000>,
+ <87 512 6000 6000>;
+ };
+
+ qcom,bam_dmux@fc834000 {
+ compatible = "qcom,bam_dmux";
+ reg = <0xfc834000 0x7000>;
+ interrupts = <0 29 1>;
+ qcom,rx-ring-size = <64>;
+ };
};
diff --git a/arch/arm/configs/apq8084_defconfig b/arch/arm/configs/apq8084_defconfig
index 9cd37d1..2965607 100644
--- a/arch/arm/configs/apq8084_defconfig
+++ b/arch/arm/configs/apq8084_defconfig
@@ -399,3 +399,8 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_TMC=y
+CONFIG_CORESIGHT_FUNNEL=y
+CONFIG_CORESIGHT_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
diff --git a/arch/arm/configs/mpq8092_defconfig b/arch/arm/configs/mpq8092_defconfig
index 5d05596..0ee406a 100644
--- a/arch/arm/configs/mpq8092_defconfig
+++ b/arch/arm/configs/mpq8092_defconfig
@@ -239,6 +239,7 @@
CONFIG_INPUT_UINPUT=y
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
CONFIG_I2C=y
@@ -274,6 +275,7 @@
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
@@ -284,6 +286,7 @@
# CONFIG_LCD_CLASS_DEVICE is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_SOUND=y
CONFIG_USB=y
CONFIG_USB_SUSPEND=y
CONFIG_USB_EHCI_HCD=y
@@ -303,6 +306,11 @@
CONFIG_USB_STORAGE_KARMA=y
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_STORAGE_ENE_UB6250=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -334,6 +342,7 @@
CONFIG_QPNP_CLKDIV=y
CONFIG_MSM_IOMMU_V1=y
CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_IOMMU_NON_SECURE=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/msm8226-perf_defconfig b/arch/arm/configs/msm8226-perf_defconfig
index 4f0872a..452cd8a 100644
--- a/arch/arm/configs/msm8226-perf_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -27,6 +27,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
+CONFIG_KPROBES=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
@@ -79,6 +80,7 @@
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
CONFIG_COMPACTION=y
+CONFIG_CP_ACCESS=y
CONFIG_USE_OF=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
@@ -242,6 +244,8 @@
CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
+CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
@@ -292,6 +296,7 @@
CONFIG_MSM_ISPIF=y
CONFIG_MSMB_CAMERA=y
CONFIG_OV9724=y
+CONFIG_MT9M114=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_WFD=y
@@ -327,6 +332,18 @@
CONFIG_USB_EHCI_EHSET=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_SERIAL=y
CONFIG_USB_SERIAL_CSVT=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
@@ -386,29 +403,20 @@
CONFIG_MAGIC_SYSRQ=y
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
-CONFIG_DEBUG_KMEMLEAK=y
-CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
-CONFIG_DEBUG_SPINLOCK=y
-CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_ATOMIC_SLEEP=y
-CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_MEMORY_INIT=y
-CONFIG_DEBUG_LIST=y
-CONFIG_FAULT_INJECTION=y
-CONFIG_FAIL_PAGE_ALLOC=y
-CONFIG_FAULT_INJECTION_DEBUG_FS=y
-CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
-CONFIG_DEBUG_PAGEALLOC=y
CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_KEYS=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_TWOFISH=y
+CONFIG_NFC_QNCI=y
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
index 3705a55..6c59c38 100644
--- a/arch/arm/configs/msm8226_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -27,6 +27,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
+CONFIG_KPROBES=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
@@ -70,6 +71,7 @@
CONFIG_MSM_RTB_SEPARATE_CPUS=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_XPU_ERR_FATAL=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
@@ -79,6 +81,7 @@
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
CONFIG_COMPACTION=y
+CONFIG_CP_ACCESS=y
CONFIG_USE_OF=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
@@ -243,6 +246,8 @@
CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
+CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
@@ -295,6 +300,7 @@
CONFIG_MSM_ISPIF=y
CONFIG_MSMB_CAMERA=y
CONFIG_OV9724=y
+CONFIG_MT9M114=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_WFD=y
@@ -352,6 +358,18 @@
CONFIG_USB_EHCI_EHSET=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_SERIAL=y
CONFIG_USB_SERIAL_CSVT=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
@@ -397,6 +415,7 @@
CONFIG_QPNP_REVID=y
CONFIG_MSM_IOMMU_V1=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_FUSE=y
CONFIG_CORESIGHT_TMC=y
CONFIG_CORESIGHT_TPIU=y
CONFIG_CORESIGHT_FUNNEL=y
@@ -439,12 +458,15 @@
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
CONFIG_EARLY_PRINTK=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_KEYS=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_TWOFISH=y
+CONFIG_NFC_QNCI=y
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 9869fbf..5e39b1f 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -241,6 +241,9 @@
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
+CONFIG_INPUT_MPU3050=y
+CONFIG_BMP18X=y
+CONFIG_BMP18X_I2C=y
CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
CONFIG_SPI_QUP=y
@@ -272,6 +275,7 @@
CONFIG_VIDEO_V4L2_SUBDEV_API=y
# CONFIG_MSM_CAMERA is not set
CONFIG_OV8825=y
+CONFIG_s5k4e1=y
CONFIG_HI256=y
CONFIG_MSM_CAMERA_SENSOR=y
# CONFIG_MSM_CPP is not set
@@ -374,6 +378,7 @@
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_KEYS=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_ARC4=y
@@ -381,3 +386,6 @@
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
CONFIG_INPUT_KXTJ9=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
+CONFIG_SENSORS_STK3X1X=y
+CONFIG_SENSORS_MMA8X5X=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 24c5d9c..0305580 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -239,6 +239,9 @@
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
+CONFIG_INPUT_MPU3050=y
+CONFIG_BMP18X=y
+CONFIG_BMP18X_I2C=y
CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
CONFIG_SPI_QUP=y
@@ -270,6 +273,7 @@
CONFIG_VIDEO_V4L2_SUBDEV_API=y
# CONFIG_MSM_CAMERA is not set
CONFIG_OV8825=y
+CONFIG_s5k4e1=y
CONFIG_HI256=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_CCI=y
@@ -352,6 +356,7 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_MSM_GPIO_FLASH=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_SWITCH=y
CONFIG_RTC_CLASS=y
@@ -374,6 +379,7 @@
CONFIG_QPNP_REVID=y
CONFIG_MSM_IOMMU_V0=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_FUSE=y
CONFIG_CORESIGHT_TMC=y
CONFIG_CORESIGHT_TPIU=y
CONFIG_CORESIGHT_FUNNEL=y
@@ -414,6 +420,7 @@
CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_KEYS=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_ARC4=y
@@ -423,3 +430,6 @@
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
CONFIG_INPUT_KXTJ9=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
+CONFIG_SENSORS_STK3X1X=y
+CONFIG_SENSORS_MMA8X5X=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index d770e3f..a26d8e6 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -63,6 +63,7 @@
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_EVENT_TIMER=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
@@ -397,8 +398,6 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_STORAGE_ENE_UB6250=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
-CONFIG_USB_QCOM_DIAG_BRIDGE=y
-CONFIG_USB_QCOM_KS_BRIDGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
@@ -471,6 +470,7 @@
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_KEYS=y
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_XCBC=y
@@ -480,3 +480,4 @@
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 8eecf79..8447dd55 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -62,6 +62,7 @@
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_EVENT_TIMER=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUSPM_DEV=m
CONFIG_MSM_WATCHDOG_V2=y
@@ -403,8 +404,6 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_STORAGE_ENE_UB6250=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
-CONFIG_USB_QCOM_DIAG_BRIDGE=y
-CONFIG_USB_QCOM_KS_BRIDGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
@@ -455,6 +454,7 @@
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_FUSE=y
CONFIG_CORESIGHT_TMC=y
CONFIG_CORESIGHT_TPIU=y
CONFIG_CORESIGHT_FUNNEL=y
@@ -503,6 +503,7 @@
CONFIG_DEBUG_LL=y
CONFIG_EARLY_PRINTK=y
CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_KEYS=y
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_XCBC=y
@@ -512,3 +513,4 @@
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
diff --git a/arch/arm/configs/msm9625-perf_defconfig b/arch/arm/configs/msm9625-perf_defconfig
index d02f5da..6471673 100644
--- a/arch/arm/configs/msm9625-perf_defconfig
+++ b/arch/arm/configs/msm9625-perf_defconfig
@@ -317,3 +317,4 @@
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 64c8535..863e14f 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -286,6 +286,7 @@
CONFIG_QPNP_POWER_ON=y
CONFIG_IPA=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_FUSE=y
CONFIG_CORESIGHT_TMC=y
CONFIG_CORESIGHT_TPIU=y
CONFIG_CORESIGHT_FUNNEL=y
@@ -327,3 +328,4 @@
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msmkrypton_defconfig b/arch/arm/configs/msmkrypton_defconfig
index f073d2b..6609c32 100644
--- a/arch/arm/configs/msmkrypton_defconfig
+++ b/arch/arm/configs/msmkrypton_defconfig
@@ -104,6 +104,9 @@
CONFIG_SPI_SPIDEV=m
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 6e94b32..0241da1 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -902,11 +902,13 @@
static int perf_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
void *v)
{
+ struct pmu *pmu;
switch (cmd) {
case CPU_PM_ENTER:
if (cpu_has_active_perf((int)v)) {
armpmu_update_counters();
- perf_pmu_disable(&cpu_pmu->pmu);
+ pmu = &cpu_pmu->pmu;
+ pmu->pmu_disable(pmu);
}
break;
@@ -919,7 +921,8 @@
*/
__get_cpu_var(from_idle) = 1;
cpu_pmu->reset(NULL);
- perf_pmu_enable(&cpu_pmu->pmu);
+ pmu = &cpu_pmu->pmu;
+ pmu->pmu_enable(pmu);
}
break;
}
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index e0f9e8a..a8eee29 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -429,6 +429,7 @@
select GPIO_MSM_V3
select MAY_HAVE_SPARSE_IRQ
select SPARSE_IRQ
+ select MEMORY_HOLE_CARVEOUT
config ARCH_MSM8610
bool "MSM8610"
@@ -528,6 +529,7 @@
select ARM_HAS_SG_CHAIN
select MSM_RUN_QUEUE_STATS
select ARCH_WANT_KMAP_ATOMIC_FLUSH
+ select QMI_ENCDEC
endmenu
choice
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index e9a236e..e5a2bb84f 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -62,8 +62,8 @@
obj-$(CONFIG_MSM_AVS_HW) += avs.o
obj-$(CONFIG_CPU_V6) += idle-v6.o
obj-$(CONFIG_CPU_V7) += idle-v7.o
-obj-$(CONFIG_MSM_JTAG) += jtag.o
-obj-$(CONFIG_MSM_JTAG_MM) += jtag-mm.o
+obj-$(CONFIG_MSM_JTAG) += jtag-fuse.o jtag.o
+obj-$(CONFIG_MSM_JTAG_MM) += jtag-fuse.o jtag-mm.o
msm-etm-objs := etm.o
obj-$(CONFIG_MSM_ETM) += msm-etm.o
@@ -87,7 +87,7 @@
obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V4) += pil-q6v4.o pil-q6v4-lpass.o
obj-$(CONFIG_MSM_PIL_MODEM_QDSP6V4) += pil-q6v4.o pil-q6v4-mss.o
obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V5) += pil-q6v5.o pil-q6v5-lpass.o
-obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-q6v5-mss.o
+obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o
obj-$(CONFIG_MSM_PIL_RIVA) += pil-riva.o
obj-$(CONFIG_MSM_PIL_TZAPPS) += pil-tzapps.o
obj-$(CONFIG_MSM_PIL_VIDC) += pil-vidc.o
@@ -295,6 +295,7 @@
obj-$(CONFIG_ARCH_MSM8226) += gdsc.o
obj-$(CONFIG_ARCH_MSM8610) += gdsc.o
obj-$(CONFIG_ARCH_MPQ8092) += gdsc.o
+obj-$(CONFIG_ARCH_APQ8084) += gdsc.o
obj-$(CONFIG_ARCH_MSM8974) += krait-regulator.o
obj-$(CONFIG_ARCH_MSMKRYPTON) += board-krypton.o board-krypton-gpiomux.o
obj-$(CONFIG_ARCH_MSMSAMARIUM) += board-samarium.o board-samarium-gpiomux.o
@@ -309,6 +310,7 @@
obj-$(CONFIG_ARCH_MSM8610) += board-8610.o board-8610-gpiomux.o
obj-$(CONFIG_ARCH_MSM8610) += clock-local2.o clock-pll.o clock-8610.o clock-rpm.o clock-voter.o
obj-$(CONFIG_ARCH_MSM8610) += clock-dsi-8610.o
+obj-$(CONFIG_ARCH_MSMKRYPTON) += clock-local2.o clock-pll.o clock-krypton.o clock-rpm.o clock-voter.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 3469c66..8b0acc6 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -57,6 +57,7 @@
dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-fluid.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-liquid.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += apq8074-v2-cdp.dtb
dtb-$(CONFIG_ARCH_MSM8974) += apq8074-v2-liquid.dtb
dtb-$(CONFIG_ARCH_MSM8974) += apq8074-v2-dragonboard.dtb
@@ -86,15 +87,21 @@
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-sim.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-cdp.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-mtp.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-qrd.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-qrd-evt.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-qrd-dvt.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-cdp.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-mtp.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-qrd.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-qrd-evt.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-qrd-dvt.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8926-cdp.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8926-mtp.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8926-qrd.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += apq8026-xpm.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += apq8026-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-qrd-skuf.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-qrd-skuf.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += apq8026-v1-xpm.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += apq8026-v1-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += apq8026-v2-xpm.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += apq8026-v2-mtp.dtb
# FSM9XXX
zreladdr-$(CONFIG_ARCH_FSM9XXX) := 0x10008000
@@ -113,6 +120,8 @@
zreladdr-$(CONFIG_ARCH_MSM8610) := 0x00008000
dtb-$(CONFIG_ARCH_MSM8610) += msm8610-rumi.dtb
dtb-$(CONFIG_ARCH_MSM8610) += msm8610-sim.dtb
+ dtb-$(CONFIG_ARCH_MSM8610) += msm8610-qrd-skuaa.dtb
+ dtb-$(CONFIG_ARCH_MSM8610) += msm8610-qrd-skuab.dtb
# MSMSAMARIUM
zreladdr-$(CONFIG_ARCH_MSMSAMARIUM) := 0x00008000
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index a61f5ca..6d848d2 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -270,7 +270,7 @@
[7] = { { 806400, HFPLL, 1, 42 }, LVL_NOM, 950000, 4 },
[8] = { { 883200, HFPLL, 1, 46 }, LVL_NOM, 950000, 5 },
[9] = { { 960000, HFPLL, 1, 50 }, LVL_NOM, 950000, 5 },
- [10] = { { 1036800, HFPLL, 1, 54 }, LVL_NOM, 950000, 6 },
+ [10] = { { 1036800, HFPLL, 1, 54 }, LVL_NOM, 950000, 5 },
[11] = { { 1113600, HFPLL, 1, 58 }, LVL_HIGH, 1050000, 6 },
[12] = { { 1190400, HFPLL, 1, 62 }, LVL_HIGH, 1050000, 6 },
[13] = { { 1267200, HFPLL, 1, 66 }, LVL_HIGH, 1050000, 7 },
@@ -899,6 +899,39 @@
{ 0, { 0 } }
};
+static struct acpu_level acpu_freq_tbl_pro_pvs0[] __initdata = {
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 999 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 999 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 800000, 999 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 805000, 999 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 815000, 999 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 825000, 999 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 835000, 999 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 845000, 999 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 855000, 999 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 865000, 999 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 875000, 999 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 890000, 999 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 900000, 999 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 915000, 999 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 925000, 999 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 940000, 999 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 950000, 999 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 965000, 999 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 980000, 999 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 995000, 999 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1010000, 999 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1025000, 999 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1040000, 999 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1055000, 999 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1070000, 999 },
+ { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1085000, 999 },
+ { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1100000, 999 },
+ /* higher frequencies aren't available for bring up */
+ { 0, { 0 } }
+};
+
+
static struct pvs_table pvs_v1[NUM_SPEED_BINS][NUM_PVS] __initdata = {
/* 8974v1 1.7GHz Parts */
[0][0] = { acpu_freq_tbl_v1_pvs0, sizeof(acpu_freq_tbl_v1_pvs0) },
@@ -929,7 +962,7 @@
[1][6] = { acpu_freq_tbl_2p3g_pvs6, sizeof(acpu_freq_tbl_2p3g_pvs6) },
[1][7] = { acpu_freq_tbl_2p3g_pvs6, sizeof(acpu_freq_tbl_2p3g_pvs6) },
- /* 8974v2 2.0GHz Parts */
+ /* 8974v2 2.2GHz Parts */
[2][0] = { acpu_freq_tbl_2p2g_pvs0, sizeof(acpu_freq_tbl_2p2g_pvs0) },
[2][1] = { acpu_freq_tbl_2p2g_pvs1, sizeof(acpu_freq_tbl_2p2g_pvs1) },
[2][2] = { acpu_freq_tbl_2p2g_pvs2, sizeof(acpu_freq_tbl_2p2g_pvs2) },
@@ -938,7 +971,48 @@
[2][5] = { acpu_freq_tbl_2p2g_pvs5, sizeof(acpu_freq_tbl_2p2g_pvs5) },
[2][6] = { acpu_freq_tbl_2p2g_pvs6, sizeof(acpu_freq_tbl_2p2g_pvs6) },
[2][7] = { acpu_freq_tbl_2p2g_pvs6, sizeof(acpu_freq_tbl_2p2g_pvs6) },
+};
+static struct pvs_table pvs_pro[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+ /* Not used by 8974Pro */
+ [0][0] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [0][1] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [0][2] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [0][3] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [0][4] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [0][5] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [0][6] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [0][7] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+
+ /* 8974Pro AB Bringup */
+ [1][0] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [1][1] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [1][2] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [1][3] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [1][4] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [1][5] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [1][6] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [1][7] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+
+ /* Not used by 8974Pro */
+ [2][0] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [2][1] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [2][2] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [2][3] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [2][4] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [2][5] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [2][6] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [2][7] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+
+ /* 8974Pro Bringup */
+ [3][0] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [3][1] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [3][2] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [3][3] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [3][4] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [3][5] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [3][6] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
+ [3][7] = { acpu_freq_tbl_pro_pvs0, sizeof(acpu_freq_tbl_pro_pvs0) },
};
static struct msm_bus_scale_pdata bus_scale_data __initdata = {
@@ -961,6 +1035,11 @@
.stby_khz = 300000,
};
+static void __init apply_pro_bringup_workaround(void)
+{
+ acpuclk_8974_params.pvs_tables = pvs_pro;
+}
+
static void __init apply_v1_l2_workaround(void)
{
static struct l2_level resticted_l2_tbl[] __initdata = {
@@ -980,9 +1059,13 @@
acpuclk_8974_params.l2_freq_tbl_size = sizeof(resticted_l2_tbl);
}
+#define cpu_is_msm8974pro() (cpu_is_msm8974pro_aa() || cpu_is_msm8974pro_ab() \
+ || cpu_is_msm8974pro_ac())
+
static int __init acpuclk_8974_probe(struct platform_device *pdev)
{
- if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1) {
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1
+ && cpu_is_msm8974()) {
acpuclk_8974_params.pvs_tables = pvs_v1;
acpuclk_8974_params.l2_freq_tbl = l2_freq_tbl_v1;
bus_scale_data.usecase = bw_level_tbl_v1;
@@ -1000,6 +1083,9 @@
apply_v1_l2_workaround();
}
+ if (cpu_is_msm8974pro())
+ apply_pro_bringup_workaround();
+
return acpuclk_krait_init(&pdev->dev, &acpuclk_8974_params);
}
diff --git a/arch/arm/mach-msm/acpuclock.c b/arch/arm/mach-msm/acpuclock.c
index c722304..2c120da 100644
--- a/arch/arm/mach-msm/acpuclock.c
+++ b/arch/arm/mach-msm/acpuclock.c
@@ -44,6 +44,8 @@
uint32_t acpuclk_get_switch_time(void)
{
+ if (!acpuclk_data)
+ return 0;
return acpuclk_data->switch_time_us;
}
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index b5a7552..21a940d 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -232,6 +232,7 @@
/* A2 power collaspe */
#define UL_TIMEOUT_DELAY 1000 /* in ms */
#define ENABLE_DISCONNECT_ACK 0x1
+#define SHUTDOWN_TIMEOUT_MS 500
static void toggle_apps_ack(void);
static void reconnect_to_bam(void);
static void disconnect_to_bam(void);
@@ -272,6 +273,7 @@
static int power_management_only_mode;
static int in_ssr;
static int ssr_skipped_disconnect;
+static struct completion shutdown_completion;
struct outside_notify_func {
void (*notify)(void *, int, unsigned long);
@@ -1066,6 +1068,7 @@
goto fail;
}
polling_mode = 0;
+ complete_all(&shutdown_completion);
release_wakelock();
/* handle any rx packets before interrupt was enabled */
@@ -1266,6 +1269,7 @@
" not disabled\n", __func__, ret);
break;
}
+ INIT_COMPLETION(shutdown_completion);
grab_wakelock();
polling_mode = 1;
/*
@@ -1745,6 +1749,14 @@
struct list_head *node;
struct rx_pkt_info *info;
unsigned long flags;
+ unsigned long time_remaining;
+
+ time_remaining = wait_for_completion_timeout(&shutdown_completion,
+ msecs_to_jiffies(SHUTDOWN_TIMEOUT_MS));
+ if (time_remaining == 0) {
+ pr_err("%s: shutdown completion timed out\n", __func__);
+ ssrestart_check();
+ }
bam_connection_is_active = 0;
@@ -2365,6 +2377,8 @@
init_completion(&ul_wakeup_ack_completion);
init_completion(&bam_connection_completion);
init_completion(&dfab_unvote_completion);
+ init_completion(&shutdown_completion);
+ complete_all(&shutdown_completion);
INIT_DELAYED_WORK(&ul_timeout_work, ul_timeout);
INIT_DELAYED_WORK(&queue_rx_work, queue_rx_work_func);
wake_lock_init(&bam_wakelock, WAKE_LOCK_SUSPEND, "bam_dmux_wakelock");
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index f5a9070..362f34d 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -71,7 +71,6 @@
#include <mach/msm_serial_hs.h>
#include <sound/cs8427.h>
#include <media/gpio-ir-recv.h>
-#include <linux/fmem.h>
#include <mach/msm_pcie.h>
#include <mach/restart.h>
#include <mach/msm_iomap.h>
@@ -165,9 +164,6 @@
};
#endif
-struct fmem_platform_data apq8064_fmem_pdata = {
-};
-
static struct memtype_reserve apq8064_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
},
@@ -191,36 +187,28 @@
return MEMTYPE_EBI1;
}
-#define FMEM_ENABLED 0
-
#ifdef CONFIG_ION_MSM
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
static struct ion_cp_heap_pdata cp_mm_apq8064_ion_pdata = {
.permission_type = IPT_TYPE_MM_CARVEOUT,
.align = PAGE_SIZE,
- .reusable = FMEM_ENABLED,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_MIDDLE,
};
static struct ion_cp_heap_pdata cp_mfc_apq8064_ion_pdata = {
.permission_type = IPT_TYPE_MFC_SHAREDMEM,
.align = PAGE_SIZE,
- .reusable = 0,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_HIGH,
};
static struct ion_co_heap_pdata co_apq8064_ion_pdata = {
.adjacent_mem_id = INVALID_HEAP_ID,
.align = PAGE_SIZE,
- .mem_is_fmem = 0,
};
static struct ion_co_heap_pdata fw_co_apq8064_ion_pdata = {
.adjacent_mem_id = ION_CP_MM_HEAP_ID,
.align = SZ_128K,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_LOW,
};
#endif
@@ -342,12 +330,6 @@
};
#endif
-static struct platform_device apq8064_fmem_device = {
- .name = "fmem",
- .id = 1,
- .dev = { .platform_data = &apq8064_fmem_pdata },
-};
-
static void __init reserve_mem_for_ion(enum ion_memory_types mem_type,
unsigned long size)
{
@@ -373,15 +355,10 @@
}
/**
- * Reserve memory for ION and calculate amount of reusable memory for fmem.
- * We only reserve memory for heaps that are not reusable. However, we only
- * support one reusable heap at the moment so we ignore the reusable flag for
- * other than the first heap with reusable flag set. Also handle special case
+ * Reserve memory for ION. Also handle special case
* for video heaps (MM,FW, and MFC). Video requires heaps MM and MFC to be
* at a higher address than FW in addition to not more than 256MB away from the
- * base address of the firmware. This means that if MM is reusable the other
- * two heaps must be allocated in the same region as FW. This is handled by the
- * mem_is_fmem flag in the platform data. In addition the MM heap must be
+ * base address of the firmware. In addition the MM heap must be
* adjacent to the FW heap for content protection purposes.
*/
static void __init reserve_ion_memory(void)
@@ -2502,7 +2479,6 @@
&apq8064_device_hsusb_host,
&android_usb_device,
&msm_device_wcnss_wlan,
- &apq8064_fmem_device,
#ifdef CONFIG_ION_MSM
&apq8064_ion_dev,
#endif
@@ -2619,7 +2595,6 @@
&android_usb_device,
&msm_device_wcnss_wlan,
&msm_device_iris_fm,
- &apq8064_fmem_device,
#ifdef CONFIG_ION_MSM
&apq8064_ion_dev,
#endif
diff --git a/arch/arm/mach-msm/board-8084.c b/arch/arm/mach-msm/board-8084.c
index 7dc9a90..2a6bbb7 100644
--- a/arch/arm/mach-msm/board-8084.c
+++ b/arch/arm/mach-msm/board-8084.c
@@ -28,6 +28,7 @@
#include <mach/restart.h>
#include <mach/socinfo.h>
#include <mach/clk-provider.h>
+#include <mach/msm_smem.h>
#include "board-dt.h"
#include "clock.h"
#include "devices.h"
@@ -84,6 +85,7 @@
*/
void __init apq8084_add_drivers(void)
{
+ msm_smem_init();
msm_init_modem_notifier_list();
msm_smd_init();
msm_clock_init(&msm8084_clock_init_data);
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index 56826c7..500731c 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -73,6 +73,8 @@
}
static struct of_dev_auxdata mpq8092_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \
+ "msm_otg", NULL),
OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
"msm_serial_hsl.0", NULL),
OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF9922000, \
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index 378edc8..fd831fc 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -136,8 +136,8 @@
static struct gpiomux_setting lcd_rst_act_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
- .dir = GPIOMUX_OUT_LOW,
+ .pull = GPIOMUX_PULL_UP,
+ .dir = GPIOMUX_OUT_HIGH,
};
static struct gpiomux_setting lcd_rst_sus_cfg = {
@@ -211,6 +211,18 @@
[GPIOMUX_SUSPENDED] = &gpio_spi_cs_eth_config,
},
},
+ { /* NFC */
+ .gpio = 10, /* BLSP1 QUP3 I2C_DAT */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ { /* NFC */
+ .gpio = 11, /* BLSP1 QUP3 I2C_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
};
static struct msm_gpiomux_config msm_synaptics_configs[] __initdata = {
@@ -456,6 +468,91 @@
},
};
+#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
+static struct gpiomux_setting sdc3_clk_actv_cfg = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdc3_cmd_data_0_3_actv_cfg = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_suspend_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting sdc3_data_1_suspend_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config msm8226_sdc3_configs[] __initdata = {
+ {
+ /* DAT3 */
+ .gpio = 39,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspend_cfg,
+ },
+ },
+ {
+ /* DAT2 */
+ .gpio = 40,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspend_cfg,
+ },
+ },
+ {
+ /* DAT1 */
+ .gpio = 41,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_data_1_suspend_cfg,
+ },
+ },
+ {
+ /* DAT0 */
+ .gpio = 42,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspend_cfg,
+ },
+ },
+ {
+ /* CMD */
+ .gpio = 43,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspend_cfg,
+ },
+ },
+ {
+ /* CLK */
+ .gpio = 44,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_clk_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspend_cfg,
+ },
+ },
+};
+
+static void msm_gpiomux_sdc3_install(void)
+{
+ msm_gpiomux_install(msm8226_sdc3_configs,
+ ARRAY_SIZE(msm8226_sdc3_configs));
+}
+#else
+static void msm_gpiomux_sdc3_install(void) {}
+#endif /* CONFIG_MMC_MSM_SDC3_SUPPORT */
+
void __init msm8226_init_gpiomux(void)
{
int rc;
@@ -488,4 +585,6 @@
if (of_board_is_cdp() || of_board_is_mtp() || of_board_is_xpm())
msm_gpiomux_install(usb_otg_sw_configs,
ARRAY_SIZE(usb_otg_sw_configs));
+
+ msm_gpiomux_sdc3_install();
}
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index 39efcd8..0d62b7a 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -44,6 +44,7 @@
#include <mach/msm_smd.h>
#include <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
+#include <mach/msm_smem.h>
#include <linux/msm_thermal.h>
#include "board-dt.h"
#include "clock.h"
@@ -74,10 +75,14 @@
"msm_sdcc.1", NULL),
OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
"msm_sdcc.2", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9864000, \
+ "msm_sdcc.3", NULL),
OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF9824900, \
"msm_sdcc.1", NULL),
OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF98A4900, \
"msm_sdcc.2", NULL),
+ OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF9864900, \
+ "msm_sdcc.3", NULL),
{}
};
@@ -107,6 +112,7 @@
*/
void __init msm8226_add_drivers(void)
{
+ msm_smem_init();
msm_init_modem_notifier_list();
msm_smd_init();
msm_rpm_driver_init();
diff --git a/arch/arm/mach-msm/board-8610-gpiomux.c b/arch/arm/mach-msm/board-8610-gpiomux.c
index b261ce4..f8ca143 100644
--- a/arch/arm/mach-msm/board-8610-gpiomux.c
+++ b/arch/arm/mach-msm/board-8610-gpiomux.c
@@ -16,6 +16,7 @@
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/gpiomux.h>
+#include <mach/socinfo.h>
static struct gpiomux_setting gpio_spi_config = {
.func = GPIOMUX_FUNC_1,
@@ -59,6 +60,30 @@
.pull = GPIOMUX_PULL_DOWN,
};
+static struct gpiomux_setting focaltech_int_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting focaltech_int_sus_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting focaltech_reset_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting focaltech_reset_sus_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
static struct gpiomux_setting wcnss_5wire_suspend_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
@@ -110,6 +135,31 @@
.pull = GPIOMUX_PULL_NONE,
};
+/* define gpio used as interrupt input */
+static struct gpiomux_setting gpio_int_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting gpio_int_sus_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+ .dir = GPIOMUX_IN,
+};
+
+static struct msm_gpiomux_config msm_gpio_int_configs[] __initdata = {
+ {
+ .gpio = 84,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &gpio_int_act_cfg,
+ [GPIOMUX_SUSPENDED] = &gpio_int_sus_cfg,
+ },
+ },
+};
+
static struct msm_gpiomux_config msm_lcd_configs[] __initdata = {
{
.gpio = 41,
@@ -214,6 +264,48 @@
},
};
+static struct msm_gpiomux_config msm_focaltech_configs[] __initdata = {
+ {
+ .gpio = 0, /* TOUCH RESET */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &focaltech_reset_act_cfg,
+ [GPIOMUX_SUSPENDED] = &focaltech_reset_sus_cfg,
+ },
+ },
+ {
+ .gpio = 1, /* TOUCH INT */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &focaltech_int_act_cfg,
+ [GPIOMUX_SUSPENDED] = &focaltech_int_sus_cfg,
+ },
+ },
+ {
+ .gpio = 86, /* BLSP1 QUP4 SPI_DATA_MOSI */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 87, /* BLSP1 QUP4 SPI_DATA_MISO */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 89, /* BLSP1 QUP4 SPI_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 88, /* BLSP1 QUP4 SPI_CS */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+};
+
+
static struct msm_gpiomux_config wcnss_5wire_interface[] = {
{
.gpio = 23,
@@ -430,8 +522,13 @@
}
msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
- msm_gpiomux_install(msm_atmel_configs,
+ if (of_board_is_qrd()) {
+ msm_gpiomux_install(msm_focaltech_configs,
+ ARRAY_SIZE(msm_focaltech_configs));
+ } else {
+ msm_gpiomux_install(msm_atmel_configs,
ARRAY_SIZE(msm_atmel_configs));
+ }
msm_gpiomux_install(wcnss_5wire_interface,
ARRAY_SIZE(wcnss_5wire_interface));
msm_gpiomux_install(msm_lcd_configs, ARRAY_SIZE(msm_lcd_configs));
@@ -439,4 +536,6 @@
ARRAY_SIZE(msm_keypad_configs));
msm_gpiomux_install(sd_card_det, ARRAY_SIZE(sd_card_det));
msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
+ msm_gpiomux_install(msm_gpio_int_configs,
+ ARRAY_SIZE(msm_gpio_int_configs));
}
diff --git a/arch/arm/mach-msm/board-8610.c b/arch/arm/mach-msm/board-8610.c
index b4d77f1..c6c9d14 100644
--- a/arch/arm/mach-msm/board-8610.c
+++ b/arch/arm/mach-msm/board-8610.c
@@ -45,6 +45,7 @@
#include <mach/msm_smd.h>
#include <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
+#include <mach/msm_smem.h>
#include <linux/msm_thermal.h>
#include "board-dt.h"
#include "clock.h"
@@ -102,6 +103,7 @@
void __init msm8610_add_drivers(void)
{
+ msm_smem_init();
msm_init_modem_notifier_list();
msm_smd_init();
msm_rpm_driver_init();
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index e097faf..d79464a 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -79,7 +79,6 @@
#include <mach/ion.h>
#include <mach/mdm2.h>
#include <mach/msm_rtb.h>
-#include <linux/fmem.h>
#include <mach/msm_cache_dump.h>
#include <mach/kgsl.h>
@@ -183,9 +182,6 @@
early_param("msm_contig_mem_size", msm_contig_mem_size_setup);
#endif
-struct fmem_platform_data msm8930_fmem_pdata = {
-};
-
#define DSP_RAM_BASE_8960 0x8da00000
#define DSP_RAM_SIZE_8960 0x1800000
static int dspcrashd_pdata_8960 = 0xDEADDEAD;
@@ -230,35 +226,28 @@
return MEMTYPE_EBI1;
}
-#define FMEM_ENABLED 0
#ifdef CONFIG_ION_MSM
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
static struct ion_cp_heap_pdata cp_mm_msm8930_ion_pdata = {
.permission_type = IPT_TYPE_MM_CARVEOUT,
.align = PAGE_SIZE,
- .reusable = FMEM_ENABLED,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_MIDDLE,
};
static struct ion_cp_heap_pdata cp_mfc_msm8930_ion_pdata = {
.permission_type = IPT_TYPE_MFC_SHAREDMEM,
.align = PAGE_SIZE,
- .reusable = 0,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_HIGH,
};
static struct ion_co_heap_pdata co_msm8930_ion_pdata = {
.adjacent_mem_id = INVALID_HEAP_ID,
.align = PAGE_SIZE,
- .mem_is_fmem = 0,
};
static struct ion_co_heap_pdata fw_co_msm8930_ion_pdata = {
.adjacent_mem_id = ION_CP_MM_HEAP_ID,
.align = SZ_128K,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_LOW,
};
#endif
@@ -382,12 +371,6 @@
};
#endif
-struct platform_device msm8930_fmem_device = {
- .name = "fmem",
- .id = 1,
- .dev = { .platform_data = &msm8930_fmem_pdata },
-};
-
static void __init reserve_mem_for_ion(enum ion_memory_types mem_type,
unsigned long size)
{
@@ -413,15 +396,10 @@
}
/**
- * Reserve memory for ION and calculate amount of reusable memory for fmem.
- * We only reserve memory for heaps that are not reusable. However, we only
- * support one reusable heap at the moment so we ignore the reusable flag for
- * other than the first heap with reusable flag set. Also handle special case
+ * Reserve memory for ION. Also handle special case
* for video heaps (MM,FW, and MFC). Video requires heaps MM and MFC to be
* at a higher address than FW in addition to not more than 256MB away from the
- * base address of the firmware. This means that if MM is reusable the other
- * two heaps must be allocated in the same region as FW. This is handled by the
- * mem_is_fmem flag in the platform data. In addition the MM heap must be
+ * base address of the firmware. In addition the MM heap must be
* adjacent to the FW heap for content protection purposes.
*/
static void __init reserve_ion_memory(void)
@@ -2274,7 +2252,6 @@
#ifdef CONFIG_MSM_FAKE_BATTERY
&fish_battery_device,
#endif
- &msm8930_fmem_device,
&msm_device_bam_dmux,
&msm_fm_platform_init,
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index b45e690..37567ed 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -86,7 +86,6 @@
#include <mach/iommu_domains.h>
#include <mach/kgsl.h>
-#include <linux/fmem.h>
#include "timer.h"
#include "devices.h"
@@ -194,9 +193,6 @@
early_param("msm_contig_mem_size", msm_contig_mem_size_setup);
#endif
-struct fmem_platform_data msm8960_fmem_pdata = {
-};
-
#define DSP_RAM_BASE_8960 0x8da00000
#define DSP_RAM_SIZE_8960 0x1800000
static int dspcrashd_pdata_8960 = 0xDEADDEAD;
@@ -247,8 +243,6 @@
static struct ion_cp_heap_pdata cp_mm_msm8960_ion_pdata = {
.permission_type = IPT_TYPE_MM_CARVEOUT,
.align = SZ_64K,
- .reusable = FMEM_ENABLED,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_MIDDLE,
.iommu_map_all = 1,
.iommu_2x_map_domain = VIDEO_DOMAIN,
@@ -257,21 +251,17 @@
static struct ion_cp_heap_pdata cp_mfc_msm8960_ion_pdata = {
.permission_type = IPT_TYPE_MFC_SHAREDMEM,
.align = PAGE_SIZE,
- .reusable = 0,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_HIGH,
};
static struct ion_co_heap_pdata co_msm8960_ion_pdata = {
.adjacent_mem_id = INVALID_HEAP_ID,
.align = PAGE_SIZE,
- .mem_is_fmem = 0,
};
static struct ion_co_heap_pdata fw_co_msm8960_ion_pdata = {
.adjacent_mem_id = ION_CP_MM_HEAP_ID,
.align = SZ_128K,
- .mem_is_fmem = FMEM_ENABLED,
.fixed_position = FIXED_LOW,
};
#endif
@@ -394,12 +384,6 @@
};
#endif
-struct platform_device msm8960_fmem_device = {
- .name = "fmem",
- .id = 1,
- .dev = { .platform_data = &msm8960_fmem_pdata },
-};
-
static void __init adjust_mem_for_liquid(void)
{
unsigned int i;
@@ -450,15 +434,10 @@
}
/**
- * Reserve memory for ION and calculate amount of reusable memory for fmem.
- * We only reserve memory for heaps that are not reusable. However, we only
- * support one reusable heap at the moment so we ignore the reusable flag for
- * other than the first heap with reusable flag set. Also handle special case
+ * Reserve memory for ION. Also handle special case
* for video heaps (MM,FW, and MFC). Video requires heaps MM and MFC to be
* at a higher address than FW in addition to not more than 256MB away from the
- * base address of the firmware. This means that if MM is reusable the other
- * two heaps must be allocated in the same region as FW. This is handled by the
- * mem_is_fmem flag in the platform data. In addition the MM heap must be
+ * base address of the firmware. In addition the MM heap must be
* adjacent to the FW heap for content protection purposes.
*/
static void __init reserve_ion_memory(void)
@@ -2729,7 +2708,6 @@
#ifdef CONFIG_BATTERY_BCL
&battery_bcl_device,
#endif
- &msm8960_fmem_device,
&msm_device_bam_dmux,
&msm_fm_platform_init,
#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE)
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index c8a88d7..66f2cd3 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -180,6 +180,12 @@
.pull = GPIOMUX_PULL_DOWN,
};
+static struct gpiomux_setting gpio_epm_marker_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
static struct gpiomux_setting wcnss_5wire_suspend_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
@@ -519,6 +525,27 @@
},
};
+static struct msm_gpiomux_config msm_epm_configs[] __initdata = {
+ {
+ .gpio = 81, /* EPM enable */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_epm_config,
+ },
+ },
+ {
+ .gpio = 85, /* EPM MARKER2 */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_epm_marker_config,
+ },
+ },
+ {
+ .gpio = 96, /* EPM MARKER1 */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_epm_marker_config,
+ },
+ },
+};
+
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
{
@@ -616,12 +643,6 @@
[GPIOMUX_SUSPENDED] = &gpio_suspend_config[0],
},
},
- {
- .gpio = 81, /* EPM enable */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_epm_config,
- },
- },
};
static struct msm_gpiomux_config msm8974_slimbus_config[] __initdata = {
@@ -822,6 +843,135 @@
},
};
+static struct msm_gpiomux_config msm_sensor_configs_dragonboard[] __initdata = {
+ {
+ .gpio = 15, /* CAM_MCLK0 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &cam_settings[1],
+ },
+ },
+ {
+ .gpio = 16, /* CAM_MCLK1 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &cam_settings[1],
+ },
+ },
+ {
+ .gpio = 17, /* CAM_MCLK2 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &cam_settings[1],
+ },
+ },
+ {
+ .gpio = 18, /* WEBCAM1_RESET_N / CAM_MCLK3 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &cam_settings[4],
+ },
+ },
+ {
+ .gpio = 19, /* CCI_I2C_SDA0 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[0],
+ },
+ },
+ {
+ .gpio = 20, /* CCI_I2C_SCL0 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[0],
+ },
+ },
+ {
+ .gpio = 21, /* CCI_I2C_SDA1 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[0],
+ },
+ },
+ {
+ .gpio = 22, /* CCI_I2C_SCL1 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[0],
+ },
+ },
+ {
+ .gpio = 23, /* FLASH_LED_EN */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 24, /* FLASH_LED_NOW */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 25, /* WEBCAM2_RESET_N */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 26, /* CAM_IRQ */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &cam_settings[1],
+ },
+ },
+ {
+ .gpio = 27, /* OIS_SYNC */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 28, /* WEBCAM1_STANDBY */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 89, /* CAM1_STANDBY_N */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 90, /* CAM1_RST_N */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 91, /* CAM2_STANDBY_N */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 94, /* CAM2_RST_N */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+};
+
static struct gpiomux_setting auxpcm_act_cfg = {
.func = GPIOMUX_FUNC_1,
.drv = GPIOMUX_DRV_8MA,
@@ -1195,7 +1345,9 @@
}
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
- msm_gpiomux_install(msm_eth_configs, ARRAY_SIZE(msm_eth_configs));
+ if (!(of_board_is_dragonboard() && machine_is_apq8074()))
+ msm_gpiomux_install(msm_eth_configs, \
+ ARRAY_SIZE(msm_eth_configs));
#endif
msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
msm_gpiomux_install(msm_blsp2_uart7_configs,
@@ -1212,7 +1364,12 @@
msm_gpiomux_install(hap_lvl_shft_config,
ARRAY_SIZE(hap_lvl_shft_config));
- msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
+ if (of_board_is_dragonboard() && machine_is_apq8074())
+ msm_gpiomux_install(msm_sensor_configs_dragonboard, \
+ ARRAY_SIZE(msm_sensor_configs_dragonboard));
+ else
+ msm_gpiomux_install(msm_sensor_configs, \
+ ARRAY_SIZE(msm_sensor_configs));
msm_gpiomux_install(&sd_card_det, 1);
@@ -1220,7 +1377,8 @@
of_board_is_dragonboard()))
msm_gpiomux_sdc3_install();
- msm_gpiomux_sdc4_install();
+ if (!(of_board_is_dragonboard() && machine_is_apq8074()))
+ msm_gpiomux_sdc4_install();
msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
@@ -1233,15 +1391,20 @@
msm_gpiomux_install(msm_mhl_configs,
ARRAY_SIZE(msm_mhl_configs));
- if (of_board_is_liquid())
+ if (of_board_is_liquid() ||
+ (of_board_is_dragonboard() && machine_is_apq8074()))
msm_gpiomux_install(msm8974_pri_ter_auxpcm_configs,
ARRAY_SIZE(msm8974_pri_ter_auxpcm_configs));
else
msm_gpiomux_install(msm8974_pri_pri_auxpcm_configs,
ARRAY_SIZE(msm8974_pri_pri_auxpcm_configs));
- msm_gpiomux_install(msm8974_sec_auxpcm_configs,
+ if (of_board_is_cdp())
+ msm_gpiomux_install(msm8974_sec_auxpcm_configs,
ARRAY_SIZE(msm8974_sec_auxpcm_configs));
+ else
+ msm_gpiomux_install(msm_epm_configs,
+ ARRAY_SIZE(msm_epm_configs));
msm_gpiomux_install_nowrite(msm_lcd_configs,
ARRAY_SIZE(msm_lcd_configs));
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 771359c..68af757 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -40,6 +40,7 @@
#include <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
#include <mach/socinfo.h>
+#include <mach/msm_smem.h>
#include "board-dt.h"
#include "clock.h"
#include "devices.h"
@@ -91,6 +92,7 @@
*/
void __init msm8974_add_drivers(void)
{
+ msm_smem_init();
msm_init_modem_notifier_list();
msm_smd_init();
msm_rpm_driver_init();
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 56246dd..36ad755 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -39,6 +39,7 @@
#include <mach/rpm-regulator-smd.h>
#include "board-dt.h"
#include <mach/msm_bus_board.h>
+#include <mach/msm_smem.h>
#include "clock.h"
#include "modem_notifier.h"
#include "lpm_resources.h"
@@ -116,6 +117,10 @@
"usb_bam", NULL),
OF_DEV_AUXDATA("qcom,hsic-host", 0xF9A15000, \
"msm_hsic_host", NULL),
+ OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF98A4900, \
+ "msm_sdcc.2", NULL),
+ OF_DEV_AUXDATA("qcom,sdhci-msm", 0xF9864900, \
+ "msm_sdcc.3", NULL),
{}
};
@@ -231,6 +236,7 @@
*/
void __init msm9625_add_drivers(void)
{
+ msm_smem_init();
msm_init_modem_notifier_list();
msm_smd_init();
msm_rpm_driver_init();
diff --git a/arch/arm/mach-msm/board-krypton.c b/arch/arm/mach-msm/board-krypton.c
index b40fcfa..e80244b 100644
--- a/arch/arm/mach-msm/board-krypton.c
+++ b/arch/arm/mach-msm/board-krypton.c
@@ -31,17 +31,20 @@
#include "clock.h"
#include "devices.h"
-static struct clk_lookup msm_clocks_dummy[] = {
- CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
- CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
- CLK_DUMMY("core_clk", SPI_CLK, "f9928000.spi", OFF),
- CLK_DUMMY("iface_clk", SPI_P_CLK, "f9928000.spi", OFF),
-
+static struct memtype_reserve msmkrypton_reserve_table[] __initdata = {
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
};
-static struct clock_init_data msm_dummy_clock_init_data __initdata = {
- .table = msm_clocks_dummy,
- .size = ARRAY_SIZE(msm_clocks_dummy),
+static int msmkrypton_paddr_to_memtype(unsigned int paddr)
+{
+ return MEMTYPE_EBI1;
+}
+
+static struct reserve_info msmkrypton_reserve_info __initdata = {
+ .memtype_reserve_table = msmkrypton_reserve_table,
+ .paddr_to_memtype = msmkrypton_paddr_to_memtype,
};
static struct of_dev_auxdata msmkrypton_auxdata_lookup[] __initdata = {
@@ -57,9 +60,14 @@
void __init msmkrypton_add_drivers(void)
{
msm_smd_init();
- msm_clock_init(&msm_dummy_clock_init_data);
+ msm_clock_init(&msmkrypton_clock_init_data);
}
+static void __init msmkrypton_early_memory(void)
+{
+ reserve_info = &msmkrypton_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_hole, msmkrypton_reserve_table);
+}
static void __init msmkrypton_map_io(void)
{
msm_map_msmkrypton_io();
@@ -87,5 +95,6 @@
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
.dt_compat = msmkrypton_dt_match,
+ .init_very_early = msmkrypton_early_memory,
.restart = msm_restart,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-samarium.c b/arch/arm/mach-msm/board-samarium.c
index a656cee..be09b54 100644
--- a/arch/arm/mach-msm/board-samarium.c
+++ b/arch/arm/mach-msm/board-samarium.c
@@ -26,10 +26,13 @@
#include <mach/restart.h>
#include <mach/socinfo.h>
#include <mach/clk-provider.h>
+#include <mach/msm_smem.h>
+#include <mach/msm_smd.h>
#include "board-dt.h"
#include "clock.h"
#include "devices.h"
#include "platsmp.h"
+#include "modem_notifier.h"
static struct clk_lookup msm_clocks_dummy[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
@@ -38,6 +41,9 @@
CLK_DUMMY("iface_clk", SDC1_P_CLK, "msm_sdcc.1", OFF),
CLK_DUMMY("core_clk", SDC2_CLK, "msm_sdcc.2", OFF),
CLK_DUMMY("iface_clk", SDC2_P_CLK, "msm_sdcc.2", OFF),
+ CLK_DUMMY("core_clk", USB_HS_SYSTEM_CLK, "msm_otg", OFF),
+ CLK_DUMMY("iface_clk", USB_HS_AHB_CLK, "msm_otg", OFF),
+ CLK_DUMMY("xo", CXO_OTG_CLK, "msm_otg", OFF),
};
static struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -50,9 +56,45 @@
"msm_sdcc.1", NULL),
OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
"msm_sdcc.2", NULL),
+ OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \
+ "msm_otg", NULL),
{},
};
+static struct memtype_reserve msmsamarium_reserve_table[] __initdata = {
+ [MEMTYPE_SMI] = {
+ },
+ [MEMTYPE_EBI0] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+};
+
+static int msmsamarium_paddr_to_memtype(phys_addr_t paddr)
+{
+ return MEMTYPE_EBI1;
+}
+
+static struct reserve_info msmsamarium_reserve_info __initdata = {
+ .memtype_reserve_table = msmsamarium_reserve_table,
+ .paddr_to_memtype = msmsamarium_paddr_to_memtype,
+};
+
+void __init msmsamarium_reserve(void)
+{
+ reserve_info = &msmsamarium_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msmsamarium_reserve_table);
+ msm_reserve();
+}
+
+static void __init msmsamarium_early_memory(void)
+{
+ reserve_info = &msmsamarium_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_hole, msmsamarium_reserve_table);
+}
+
/*
* Used to satisfy dependencies for devices that need to be
* run early or in a particular order. Most likely your device doesn't fall
@@ -61,6 +103,9 @@
*/
void __init msmsamarium_add_drivers(void)
{
+ msm_smem_init();
+ msm_init_modem_notifier_list();
+ msm_smd_init();
msm_clock_init(&msm_dummy_clock_init_data);
}
@@ -81,6 +126,11 @@
msmsamarium_add_drivers();
}
+void __init msmsamarium_init_very_early(void)
+{
+ msmsamarium_early_memory();
+}
+
static const char *msmsamarium_dt_match[] __initconst = {
"qcom,msmsamarium",
NULL
@@ -93,6 +143,8 @@
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
.dt_compat = msmsamarium_dt_match,
+ .reserve = msmsamarium_reserve,
+ .init_very_early = msmsamarium_init_very_early,
.restart = msm_restart,
.smp = &msm8974_smp_ops,
MACHINE_END
diff --git a/arch/arm/mach-msm/clock-8084.c b/arch/arm/mach-msm/clock-8084.c
index bec9f1b4..184338e 100644
--- a/arch/arm/mach-msm/clock-8084.c
+++ b/arch/arm/mach-msm/clock-8084.c
@@ -137,7 +137,8 @@
CLK_DUMMY("", gcc_lpass_mport_axi_clk.c, "", OFF),
CLK_DUMMY("", gcc_lpass_q6_axi_clk.c, "", OFF),
CLK_DUMMY("", gcc_lpass_sway_clk.c, "", OFF),
- CLK_DUMMY("", gcc_mmss_bimc_gfx_clk.c, "", OFF),
+ CLK_DUMMY("mem_iface_clk", gcc_mmss_bimc_gfx_clk.c,
+ "fdb00000.qcom,kgsl-3d0", OFF),
CLK_DUMMY("", gcc_mmss_vpu_maple_sys_noc_axi_clk.c, "", OFF),
CLK_DUMMY("", gcc_ocmem_noc_cfg_ahb_clk.c, "", OFF),
CLK_DUMMY("", gcc_msg_ram_ahb_clk.c, "", OFF),
@@ -307,23 +308,28 @@
CLK_DUMMY("", camss_vfe_vfe1_clk.c, "", OFF),
CLK_DUMMY("", camss_vfe_vfe_ahb_clk.c, "", OFF),
CLK_DUMMY("", camss_vfe_vfe_axi_clk.c, "", OFF),
- CLK_DUMMY("", mdss_ahb_clk.c, "", OFF),
- CLK_DUMMY("", mdss_axi_clk.c, "", OFF),
- CLK_DUMMY("", mdss_byte0_clk.c, "", OFF),
- CLK_DUMMY("", mdss_byte1_clk.c, "", OFF),
+ CLK_DUMMY("iface_clk", mdss_ahb_clk.c, "fd900000.qcom,mdss_mdp", OFF),
+ CLK_DUMMY("bus_clk", mdss_axi_clk.c, "fd900000.qcom,mdss_mdp", OFF),
+ CLK_DUMMY("core_clk_src", mdp_clk_src.c, "fd900000.qcom,mdss_mdp", OFF),
CLK_DUMMY("", mdss_edpaux_clk.c, "", OFF),
CLK_DUMMY("", mdss_edplink_clk.c, "", OFF),
CLK_DUMMY("", mdss_edppixel_clk.c, "", OFF),
- CLK_DUMMY("", mdss_esc0_clk.c, "", OFF),
- CLK_DUMMY("", mdss_esc1_clk.c, "", OFF),
+ CLK_DUMMY("byte_clk", mdss_byte0_clk.c, "fd922800.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("byte_clk", mdss_byte1_clk.c, "fd922e00.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("core_clk", mdss_esc0_clk.c, "fd922800.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("core_clk", mdss_esc1_clk.c, "fd922e00.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("iface_clk", mdss_ahb_clk.c, "fd922800.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("iface_clk", mdss_ahb_clk.c, "fd922e00.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("bus_clk", mdss_axi_clk.c, "fd922800.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("bus_clk", mdss_axi_clk.c, "fd922e00.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("pixel_clk", mdss_pclk0_clk.c, "fd922800.qcom,mdss_dsi", OFF),
+ CLK_DUMMY("pixel_clk", mdss_pclk1_clk.c, "fd922e00.qcom,mdss_dsi", OFF),
CLK_DUMMY("", mdss_extpclk_clk.c, "", OFF),
CLK_DUMMY("", mdss_hdmi_ahb_clk.c, "", OFF),
CLK_DUMMY("", mdss_hdmi_clk.c, "", OFF),
- CLK_DUMMY("", mdss_mdp_clk.c, "", OFF),
- CLK_DUMMY("", mdss_mdp_lut_clk.c, "", OFF),
- CLK_DUMMY("", mdss_pclk0_clk.c, "", OFF),
- CLK_DUMMY("", mdss_pclk1_clk.c, "", OFF),
- CLK_DUMMY("", mdss_vsync_clk.c, "", OFF),
+ CLK_DUMMY("core_clk", mdss_mdp_clk.c, "fd900000.qcom,mdss_mdp", OFF),
+ CLK_DUMMY("lut_clk", mdss_mdp_lut_clk.c, "fd900000.qcom,mdss_mdp", OFF),
+ CLK_DUMMY("vsync_clk", mdss_vsync_clk.c, "fd900000.qcom,mdss_mdp", OFF),
CLK_DUMMY("", mmss_misc_ahb_clk.c, "", OFF),
CLK_DUMMY("", mmss_mmssnoc_ahb_clk.c, "", OFF),
CLK_DUMMY("", mmss_mmssnoc_axi_clk.c, "", OFF),
@@ -332,15 +338,23 @@
CLK_DUMMY("iface_clk", ocmemcx_ocmemnoc_clk.c,
"fdd00000.qcom,ocmem", OFF),
CLK_DUMMY("", oxili_ocmemgx_clk.c, "", OFF),
- CLK_DUMMY("", oxili_gfx3d_clk.c, "", OFF),
+ CLK_DUMMY("core_clk", oxili_gfx3d_clk.c,
+ "fdb00000.qcom,kgsl-3d0", OFF),
CLK_DUMMY("", oxili_rbbmtimer_clk.c, "", OFF),
- CLK_DUMMY("", oxilicx_ahb_clk.c, "", OFF),
+ CLK_DUMMY("iface_clk", oxilicx_ahb_clk.c,
+ "fdb00000.qcom,kgsl-3d0", OFF),
CLK_DUMMY("", venus0_ahb_clk.c, "", OFF),
CLK_DUMMY("", venus0_axi_clk.c, "", OFF),
CLK_DUMMY("", venus0_core0_vcodec_clk.c, "", OFF),
CLK_DUMMY("", venus0_core1_vcodec_clk.c, "", OFF),
CLK_DUMMY("", venus0_ocmemnoc_clk.c, "", OFF),
CLK_DUMMY("", venus0_vcodec0_clk.c, "", OFF),
+ CLK_DUMMY("iface_clk", venus0_ahb_clk.c, "fdce0000.qcom,venus", OFF),
+ CLK_DUMMY("bus_clk", venus0_axi_clk.c, "fdce0000.qcom,venus", OFF),
+ CLK_DUMMY("mem_clk", venus0_ocmemnoc_clk.c,
+ "fdce0000.qcom,venus", OFF),
+ CLK_DUMMY("core_clk", venus0_vcodec0_clk.c,
+ "fdce0000.qcom,venus", OFF),
CLK_DUMMY("", vpu_ahb_clk.c, "", OFF),
CLK_DUMMY("", vpu_axi_clk.c, "", OFF),
CLK_DUMMY("", vpu_bus_clk.c, "", OFF),
@@ -369,6 +383,27 @@
CLK_DUMMY("core_clk", NULL, "fe054000.qcom,iommu", OFF),
CLK_DUMMY("iface_clk", NULL, "fe064000.qcom,iommu", OFF),
CLK_DUMMY("core_clk", NULL, "fe064000.qcom,iommu", OFF),
+
+ /* CoreSight clocks */
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc326000.tmc", OFF),
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc324000.replicator", OFF),
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc325000.tmc", OFF),
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc323000.funnel", OFF),
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc321000.funnel", OFF),
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc322000.funnel", OFF),
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc345000.funnel", OFF),
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc36c000.funnel", OFF),
+ CLK_DUMMY("core_clk", qdss_clk.c, "fc302000.stm", OFF),
+
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc326000.tmc", OFF),
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc324000.replicator", OFF),
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc325000.tmc", OFF),
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc323000.funnel", OFF),
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc321000.funnel", OFF),
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc322000.funnel", OFF),
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc345000.funnel", OFF),
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc36c000.funnel", OFF),
+ CLK_DUMMY("core_a_clk", qdss_a_clk.c, "fc302000.stm", OFF),
};
struct clock_init_data msm8084_clock_init_data __initdata = {
diff --git a/arch/arm/mach-msm/clock-8092.c b/arch/arm/mach-msm/clock-8092.c
index 9de97ea..bb46c4a 100644
--- a/arch/arm/mach-msm/clock-8092.c
+++ b/arch/arm/mach-msm/clock-8092.c
@@ -290,6 +290,16 @@
CLK_DUMMY("", vpu_sleep_clk.c, "", OFF),
CLK_DUMMY("", vpu_vdp_clk.c, "", OFF),
CLK_DUMMY("", vpu_vdp_xin_clk.c, "", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fda64000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fda64000.qcom,iommu", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "fda64000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fd928000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fd928000.qcom,iommu", oFF),
+ CLK_DUMMY("core_clk", NULL, "fdb10000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fdb10000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fdc84000.qcom,iommu", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "fdc84000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fdc84000.qcom,iommu", OFF),
/* BCSS broadcast */
CLK_DUMMY("", bcc_dem_core_b_clk_src.c, "", OFF),
CLK_DUMMY("", adc_01_clk_src.c, "", OFF),
@@ -319,6 +329,11 @@
CLK_DUMMY("", nidaq_out_clk.c, "", OFF),
CLK_DUMMY("", gcc_bcss_axi_clk.c, "", OFF),
CLK_DUMMY("", bcc_lnb_core_clk.c, "", OFF),
+
+ /* USB */
+ CLK_DUMMY("core_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("iface_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("xo", NULL, "msm_otg", OFF),
};
struct clock_init_data mpq8092_clock_init_data __initdata = {
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index 7f0a394..452bc28 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -2857,7 +2857,6 @@
static struct clk_freq_tbl ftbl_kpss_ahb_clk[] = {
F_GCC(19200000, xo_a_clk, 0, 0, 0),
F_GCC(37500000, gpll0, 16, 0, 0),
- F_GCC(75000000, gpll0, 8, 0, 0),
F_END
};
@@ -2870,7 +2869,6 @@
.c = {
.dbg_name = "kpss_ahb_clk_src",
.ops = &clk_ops_rcg,
- VDD_DIG_FMAX_MAP2(LOW, 37500000, NOMINAL, 75000000),
CLK_INIT(kpss_ahb_clk_src.c),
},
};
@@ -2901,6 +2899,7 @@
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_mss_clk, &xo.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_wlan_clk, &xo.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_pronto_clk, &xo.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &xo.c);
#ifdef CONFIG_DEBUG_FS
@@ -3113,6 +3112,9 @@
CLK_LOOKUP("apc3_m_clk", apc3_m_clk, ""),
CLK_LOOKUP("l2_m_clk", l2_m_clk, ""),
+ /* LPM Resources */
+ CLK_LOOKUP("xo", cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
+
/* PIL-LPASS */
CLK_LOOKUP("xo", cxo_pil_lpass_clk.c, "fe200000.qcom,lpass"),
CLK_LOOKUP("core_clk", q6ss_xo_clk.c, "fe200000.qcom,lpass"),
@@ -3125,7 +3127,8 @@
CLK_LOOKUP("bus_clk", gcc_mss_q6_bimc_axi_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("iface_clk", gcc_mss_cfg_ahb_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, "fc880000.qcom,mss"),
-
+ /* NFC */
+ CLK_LOOKUP("ref_clk", cxo_d1_a_pin.c, "2-000e"),
/* PIL-PRONTO */
CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
@@ -3250,6 +3253,9 @@
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9927000.i2c"),
CLK_LOOKUP("core_clk", gcc_blsp1_qup5_i2c_apps_clk.c, "f9927000.i2c"),
+ /* I2C Clocks nfc */
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9925000.i2c"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, "f9925000.i2c"),
/* lsuart-v14 Clocks */
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f991f000.serial"),
CLK_LOOKUP("core_clk", gcc_blsp1_uart3_apps_clk.c, "f991f000.serial"),
@@ -3315,7 +3321,6 @@
CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup3_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup4_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup4_spi_apps_clk.c, ""),
@@ -3371,10 +3376,10 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6f.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "90.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6d.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6f.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "90.qcom,camera"),
+ CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6d.qcom,camera"),
/* CCI clocks */
diff --git a/arch/arm/mach-msm/clock-8610.c b/arch/arm/mach-msm/clock-8610.c
index 2f4677f..b1b9397 100644
--- a/arch/arm/mach-msm/clock-8610.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -510,6 +510,15 @@
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_iommu_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_BRANCH_VOTER(cxo_otg_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpass_pil_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_pil_pronto_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_mss_pil_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_pil_mba_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_wlan_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_acpu_clk, &gcc_xo_clk_src.c);
+
static DEFINE_CLK_MEASURE(apc0_m_clk);
static DEFINE_CLK_MEASURE(apc1_m_clk);
static DEFINE_CLK_MEASURE(apc2_m_clk);
@@ -2740,17 +2749,18 @@
};
static struct clk_lookup msm_clocks_8610[] = {
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "msm_otg"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fe200000.qcom,lpass"),
+ CLK_LOOKUP("xo", cxo_otg_clk.c, "msm_otg"),
+ CLK_LOOKUP("xo", cxo_lpass_pil_clk.c, "fe200000.qcom,lpass"),
+ CLK_LOOKUP("xo", cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fc880000.qcom,mss"),
+ CLK_LOOKUP("xo", cxo_mss_pil_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("bus_clk", gcc_mss_q6_bimc_axi_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("iface_clk", gcc_mss_cfg_ahb_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, "fc880000.qcom,mss"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "pil-mba"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fb000000.qcom,wcnss-wlan"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fb21b000.qcom,pronto"),
+ CLK_LOOKUP("xo", cxo_pil_mba_clk.c, "pil-mba"),
+ CLK_LOOKUP("xo", cxo_wlan_clk.c, "fb000000.qcom,wcnss-wlan"),
+ CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f991f000.serial"),
@@ -3000,9 +3010,15 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006f"),
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-007d"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006d"),
+ CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "6-0078"),
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-0020"),
CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006f"),
+ CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-007d"),
CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006d"),
+ CLK_LOOKUP("cam_clk", mclk1_clk.c, "6-0078"),
+ CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-0020"),
/* CSIPHY clocks */
@@ -3070,7 +3086,7 @@
CLK_LOOKUP("iface_clk", q6ss_ahb_lfabif_clk.c, "fe200000.qcom,lpass"),
CLK_LOOKUP("reg_clk", q6ss_ahbm_clk.c, "fe200000.qcom,lpass"),
- CLK_LOOKUP("xo", gcc_xo_a_clk_src.c, "f9011050.qcom,acpuclk"),
+ CLK_LOOKUP("xo", cxo_acpu_clk.c, "f9011050.qcom,acpuclk"),
CLK_LOOKUP("gpll0", gpll0_ao_clk_src.c, "f9011050.qcom,acpuclk"),
CLK_LOOKUP("a7sspll", a7sspll.c, "f9011050.qcom,acpuclk"),
@@ -3080,7 +3096,7 @@
CLK_LOOKUP("measure_clk", apc3_m_clk, ""),
CLK_LOOKUP("measure_clk", l2_m_clk, ""),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fb000000.qcom,wcnss-wlan"),
+ CLK_LOOKUP("xo", cxo_wlan_clk.c, "fb000000.qcom,wcnss-wlan"),
CLK_LOOKUP("rf_clk", cxo_a1.c, "fb000000.qcom,wcnss-wlan"),
CLK_LOOKUP("iface_clk", mdp_ahb_clk.c, "fd900000.qcom,mdss_mdp"),
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 1e91d5b..1514bba 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -3047,7 +3047,7 @@
return branch_reset(&to_pix_rdi_clk(c)->b, action);
}
-static int pix_rdi_clk_list_rate(struct clk *c, unsigned n)
+static long pix_rdi_clk_list_rate(struct clk *c, unsigned n)
{
if (pix_rdi_mux_map[n])
return n;
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 628250d..11886a4 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -336,6 +336,8 @@
#define USB_HS_AHB_CBCR 0x0488
#define SDCC1_APPS_CBCR 0x04C4
#define SDCC1_AHB_CBCR 0x04C8
+#define SDCC1_CDCCAL_SLEEP_CBCR 0x04E4
+#define SDCC1_CDCCAL_FF_CBCR 0x04E8
#define SDCC2_APPS_CBCR 0x0504
#define SDCC2_AHB_CBCR 0x0508
#define SDCC3_APPS_CBCR 0x0544
@@ -810,7 +812,6 @@
static DEFINE_CLK_VOTER(snoc_msmbus_clk, &snoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(cnoc_msmbus_clk, &cnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_msmbus_a_clk, &pnoc_a_clk.c, LONG_MAX);
-static DEFINE_CLK_VOTER(pnoc_pm_clk, &pnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(cnoc_msmbus_a_clk, &cnoc_a_clk.c, LONG_MAX);
@@ -822,6 +823,7 @@
static DEFINE_CLK_VOTER(ocmemgx_msmbus_a_clk, &ocmemgx_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(ocmemgx_core_clk, &ocmemgx_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_keepalive_a_clk, &pnoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, 0);
static DEFINE_CLK_BRANCH_VOTER(cxo_otg_clk, &cxo_clk_src.c);
@@ -831,6 +833,7 @@
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 DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &cxo_clk_src.c);
static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = {
F(125000000, gpll0, 1, 5, 24),
@@ -1398,7 +1401,7 @@
F_END
};
-static struct clk_freq_tbl ftbl_gcc_ce1_v3_clk[] = {
+static struct clk_freq_tbl ftbl_gcc_ce1_pro_clk[] = {
F( 50000000, gpll0, 12, 0, 0),
F( 75000000, gpll0, 8, 0, 0),
F(100000000, gpll0, 6, 0, 0),
@@ -1426,7 +1429,7 @@
F_END
};
-static struct clk_freq_tbl ftbl_gcc_ce2_v3_clk[] = {
+static struct clk_freq_tbl ftbl_gcc_ce2_pro_clk[] = {
F( 50000000, gpll0, 12, 0, 0),
F( 75000000, gpll0, 8, 0, 0),
F(100000000, gpll0, 6, 0, 0),
@@ -2314,6 +2317,28 @@
},
};
+static struct branch_clk gcc_sdcc1_cdccal_ff_clk = {
+ .cbcr_reg = SDCC1_CDCCAL_FF_CBCR,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .parent = &cxo_clk_src.c,
+ .dbg_name = "gcc_sdcc1_cdccal_ff_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc1_cdccal_ff_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc1_cdccal_sleep_clk = {
+ .cbcr_reg = SDCC1_CDCCAL_SLEEP_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc1_cdccal_sleep_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc1_cdccal_sleep_clk.c),
+ },
+};
+
static struct branch_clk gcc_sdcc2_ahb_clk = {
.cbcr_reg = SDCC2_AHB_CBCR,
.has_sibling = 1,
@@ -2920,12 +2945,12 @@
F_END
};
-static struct clk_freq_tbl ftbl_camss_mclk0_3_v3_clk[] = {
+static struct clk_freq_tbl ftbl_camss_mclk0_3_pro_clk[] = {
F_MM( 4800000, cxo, 4, 0, 0),
F_MM( 6000000, gpll0, 10, 1, 10),
F_MM( 8000000, gpll0, 15, 1, 5),
F_MM( 9600000, cxo, 2, 0, 0),
- F_MM(16000000, gpll0, 10, 1, 5),
+ F_MM(16000000, gpll0, 12.5, 1, 3),
F_MM(19200000, cxo, 1, 0, 0),
F_MM(24000000, gpll0, 5, 1, 5),
F_MM(32000000, mmpll0, 5, 1, 5),
@@ -4431,6 +4456,8 @@
{&gcc_usb30_master_clk.c, GCC_BASE, 0x0050},
{&gcc_blsp2_qup3_i2c_apps_clk.c, GCC_BASE, 0x00b4},
{&gcc_usb_hsic_system_clk.c, GCC_BASE, 0x0059},
+ {&gcc_sdcc1_cdccal_sleep_clk.c, GCC_BASE, 0x006a},
+ {&gcc_sdcc1_cdccal_ff_clk.c, GCC_BASE, 0x006b},
{&gcc_blsp2_uart3_apps_clk.c, GCC_BASE, 0x00b5},
{&gcc_usb_hsic_io_cal_clk.c, GCC_BASE, 0x005b},
{&gcc_ce2_axi_clk.c, GCC_BASE, 0x0141},
@@ -4653,7 +4680,8 @@
clk->multiplier = 4;
clk_sel = 0x16A;
- if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1) {
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1 &&
+ cpu_is_msm8974()) {
if (measure_mux[i].debug_mux == M_L2)
regval = BIT(7)|BIT(0);
else
@@ -4842,7 +4870,13 @@
CLK_DUMMY("core_clk", NULL, "fdc84000.qcom,iommu", oFF),
};
-static struct clk_lookup msm_clocks_8974[] = {
+static struct clk_lookup msm_clocks_8974ac_only[] __initdata = {
+ CLK_LOOKUP("gpll4", gpll4_clk_src.c, ""),
+ CLK_LOOKUP("sleep_clk", gcc_sdcc1_cdccal_sleep_clk.c, "msm_sdcc.1"),
+ CLK_LOOKUP("cal_clk", gcc_sdcc1_cdccal_ff_clk.c, "msm_sdcc.1"),
+};
+
+static struct clk_lookup msm_clocks_8974_common[] __initdata = {
CLK_LOOKUP("xo", cxo_otg_clk.c, "msm_otg"),
CLK_LOOKUP("xo", cxo_pil_lpass_clk.c, "fe200000.qcom,lpass"),
CLK_LOOKUP("xo", cxo_pil_mss_clk.c, "fc880000.qcom,mss"),
@@ -4851,7 +4885,7 @@
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("pll", gpll4_clk_src.c, ""),
+ CLK_LOOKUP("xo", cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
@@ -5257,6 +5291,7 @@
CLK_LOOKUP("core_clk", gcc_prng_ahb_clk.c, "msm_rng"),
CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
+ CLK_LOOKUP("bus_clk", pnoc_keepalive_a_clk.c, ""),
CLK_LOOKUP("bus_clk", snoc_clk.c, ""),
CLK_LOOKUP("bus_clk", pnoc_clk.c, ""),
@@ -5274,7 +5309,6 @@
CLK_LOOKUP("bus_clk", snoc_msmbus_clk.c, "msm_sys_noc"),
CLK_LOOKUP("bus_a_clk", snoc_msmbus_a_clk.c, "msm_sys_noc"),
CLK_LOOKUP("bus_clk", pnoc_msmbus_clk.c, "msm_periph_noc"),
- CLK_LOOKUP("bus_clk", pnoc_pm_clk.c, "pm_8x60"),
CLK_LOOKUP("bus_a_clk", pnoc_msmbus_a_clk.c, "msm_periph_noc"),
CLK_LOOKUP("mem_clk", bimc_msmbus_clk.c, "msm_bimc"),
CLK_LOOKUP("mem_a_clk", bimc_msmbus_a_clk.c, "msm_bimc"),
@@ -5366,6 +5400,9 @@
CLK_LOOKUP("", byte_clk_src_8974.c, ""),
};
+static struct clk_lookup msm_clocks_8974[ARRAY_SIZE(msm_clocks_8974_common)
+ + ARRAY_SIZE(msm_clocks_8974ac_only)];
+
static struct pll_config_regs mmpll0_regs __initdata = {
.l_reg = (void __iomem *)MMPLL0_L_REG,
.m_reg = (void __iomem *)MMPLL0_M_REG,
@@ -5476,15 +5513,21 @@
.mn_ena_mask = BIT(24),
.main_output_val = BIT(0),
.main_output_mask = BIT(0),
+ .aux_output_val = BIT(1),
+ .aux_output_mask = BIT(1),
};
+#define cpu_is_msm8974pro() (cpu_is_msm8974pro_aa() || cpu_is_msm8974pro_ab() \
+ || cpu_is_msm8974pro_ac())
+
static void __init reg_init(void)
{
u32 regval;
configure_sr_hpm_lp_pll(&mmpll0_config, &mmpll0_regs, 1);
- if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2
+ || cpu_is_msm8974pro()) {
configure_sr_hpm_lp_pll(&mmpll1_v2_config, &mmpll1_regs, 1);
configure_sr_hpm_lp_pll(&mmpll3_v2_config, &mmpll3_regs, 0);
} else {
@@ -5501,7 +5544,8 @@
* V2 requires additional votes to allow the LPASS and MMSS
* controllers to use GPLL0.
*/
- if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2
+ || cpu_is_msm8974pro()) {
regval = readl_relaxed(
GCC_REG_BASE(APCS_CLOCK_BRANCH_ENA_VOTE));
writel_relaxed(regval | BIT(26) | BIT(25),
@@ -5511,7 +5555,8 @@
static void __init msm8974_clock_post_init(void)
{
- if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2
+ || cpu_is_msm8974pro()) {
clk_set_rate(&axi_clk_src.c, 291750000);
clk_set_rate(&ocmemnoc_clk_src.c, 291750000);
} else {
@@ -5527,6 +5572,12 @@
clk_prepare_enable(&mmssnoc_ahb_a_clk.c);
/*
+ * Hold an active set vote for the PNOC AHB source. Sleep set vote is 0.
+ */
+ clk_set_rate(&pnoc_keepalive_a_clk.c, 19200000);
+ clk_prepare_enable(&pnoc_keepalive_a_clk.c);
+
+ /*
* Hold an active set vote for CXO; this is because CXO is expected
* to remain on whenever CPUs aren't power collapsed.
*/
@@ -5619,39 +5670,52 @@
qup_i2c_clks[i][0]->parent = qup_i2c_clks[i][1];
}
-/* v2 to v3 clock changes */
-static void __init msm8974_v3_clock_override(void)
+/* v2 to pro clock changes */
+static void __init msm8974_pro_clock_override(void)
{
ce1_clk_src.c.fmax[VDD_DIG_LOW] = 75000000;
ce1_clk_src.c.fmax[VDD_DIG_NOMINAL] = 150000000;
- ce1_clk_src.freq_tbl = ftbl_gcc_ce1_v3_clk;
+ ce1_clk_src.freq_tbl = ftbl_gcc_ce1_pro_clk;
ce2_clk_src.c.fmax[VDD_DIG_LOW] = 75000000;
ce2_clk_src.c.fmax[VDD_DIG_NOMINAL] = 150000000;
- ce2_clk_src.freq_tbl = ftbl_gcc_ce2_v3_clk;
+ ce2_clk_src.freq_tbl = ftbl_gcc_ce2_pro_clk;
- sdcc1_apps_clk_src.c.fmax[VDD_DIG_LOW] = 200000000;
- sdcc1_apps_clk_src.c.fmax[VDD_DIG_NOMINAL] = 400000000;
+ if (cpu_is_msm8974pro_ac()) {
+ sdcc1_apps_clk_src.c.fmax[VDD_DIG_LOW] = 200000000;
+ sdcc1_apps_clk_src.c.fmax[VDD_DIG_NOMINAL] = 400000000;
+ }
vfe0_clk_src.c.fmax[VDD_DIG_LOW] = 150000000;
vfe0_clk_src.c.fmax[VDD_DIG_NOMINAL] = 320000000;
- vfe0_clk_src.c.fmax[VDD_DIG_HIGH] = 465000000;
vfe1_clk_src.c.fmax[VDD_DIG_LOW] = 150000000;
vfe1_clk_src.c.fmax[VDD_DIG_NOMINAL] = 320000000;
- vfe1_clk_src.c.fmax[VDD_DIG_HIGH] = 465000000;
cpp_clk_src.c.fmax[VDD_DIG_LOW] = 150000000;
cpp_clk_src.c.fmax[VDD_DIG_NOMINAL] = 320000000;
- cpp_clk_src.c.fmax[VDD_DIG_HIGH] = 465000000;
+
+ if (cpu_is_msm8974pro_ab() || cpu_is_msm8974pro_ac()) {
+ vfe0_clk_src.c.fmax[VDD_DIG_HIGH] = 465000000;
+ vfe1_clk_src.c.fmax[VDD_DIG_HIGH] = 465000000;
+ cpp_clk_src.c.fmax[VDD_DIG_HIGH] = 465000000;
+ } else if (cpu_is_msm8974pro_aa()) {
+ vfe0_clk_src.c.fmax[VDD_DIG_HIGH] = 320000000;
+ vfe1_clk_src.c.fmax[VDD_DIG_HIGH] = 320000000;
+ cpp_clk_src.c.fmax[VDD_DIG_HIGH] = 320000000;
+ }
mdp_clk_src.c.fmax[VDD_DIG_NOMINAL] = 266670000;
- mclk0_clk_src.freq_tbl = ftbl_camss_mclk0_3_v3_clk;
- mclk1_clk_src.freq_tbl = ftbl_camss_mclk0_3_v3_clk;
- mclk2_clk_src.freq_tbl = ftbl_camss_mclk0_3_v3_clk;
- mclk3_clk_src.freq_tbl = ftbl_camss_mclk0_3_v3_clk;
+ mclk0_clk_src.freq_tbl = ftbl_camss_mclk0_3_pro_clk;
+ mclk1_clk_src.freq_tbl = ftbl_camss_mclk0_3_pro_clk;
+ mclk2_clk_src.freq_tbl = ftbl_camss_mclk0_3_pro_clk;
+ mclk3_clk_src.freq_tbl = ftbl_camss_mclk0_3_pro_clk;
mclk0_clk_src.set_rate = set_rate_mnd;
mclk1_clk_src.set_rate = set_rate_mnd;
mclk2_clk_src.set_rate = set_rate_mnd;
mclk3_clk_src.set_rate = set_rate_mnd;
+ mclk0_clk_src.c.ops = &clk_ops_rcg_mnd;
+ mclk1_clk_src.c.ops = &clk_ops_rcg_mnd;
+ mclk2_clk_src.c.ops = &clk_ops_rcg_mnd;
+ mclk3_clk_src.c.ops = &clk_ops_rcg_mnd;
}
static void __init msm8974_clock_pre_init(void)
@@ -5682,11 +5746,21 @@
reg_init();
+ memcpy(msm_clocks_8974, msm_clocks_8974_common,
+ sizeof(msm_clocks_8974_common));
+ msm8974_clock_init_data.size -= ARRAY_SIZE(msm_clocks_8974ac_only);
+
/* version specific changes */
- if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2)
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2
+ || cpu_is_msm8974pro())
msm8974_v2_clock_override();
- if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 3)
- msm8974_v3_clock_override();
+ if (cpu_is_msm8974pro()) {
+ msm8974_pro_clock_override();
+ memcpy(msm_clocks_8974 + ARRAY_SIZE(msm_clocks_8974_common),
+ msm_clocks_8974ac_only, sizeof(msm_clocks_8974ac_only));
+ msm8974_clock_init_data.size +=
+ ARRAY_SIZE(msm_clocks_8974ac_only);
+ }
clk_ops_pixel_clock = clk_ops_pixel;
clk_ops_pixel_clock.set_rate = set_rate_pixel;
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 14648ec..3b07069 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -430,9 +430,10 @@
static DEFINE_CLK_VOTER(pnoc_sdcc2_clk, &pnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_sdcc3_clk, &pnoc_clk.c, LONG_MAX);
-
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &cxo_clk_src.c);
+
static struct clk_freq_tbl ftbl_gcc_ipa_clk[] = {
F( 50000000, gpll0, 12, 0, 0),
F( 92310000, gpll0, 6.5, 0, 0),
@@ -1762,6 +1763,7 @@
static struct clk_lookup msm_clocks_9625[] = {
CLK_LOOKUP("xo", cxo_clk_src.c, ""),
+ CLK_LOOKUP("xo", cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
CLK_LOOKUP("pll0", gpll0_activeonly_clk_src.c, "f9010008.qcom,acpuclk"),
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index fe43b72..c91af54 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -22,6 +22,8 @@
#include <linux/list.h>
#include <linux/clkdev.h>
#include <linux/uaccess.h>
+#include <linux/mutex.h>
+
#include <mach/clk-provider.h>
#include "clock.h"
@@ -196,11 +198,12 @@
static int list_rates_show(struct seq_file *m, void *unused)
{
struct clk *clock = m->private;
- int rate, level, fmax = 0, i = 0;
+ int level, i = 0;
+ unsigned long rate, fmax = 0;
/* Find max frequency supported within voltage constraints. */
if (!clock->vdd_class) {
- fmax = INT_MAX;
+ fmax = ULONG_MAX;
} else {
for (level = 0; level < clock->num_fmax; level++)
if (clock->fmax[level])
@@ -211,9 +214,9 @@
* List supported frequencies <= fmax. Higher frequencies may appear in
* the frequency table, but are not valid and should not be listed.
*/
- while ((rate = clock->ops->list_rate(clock, i++)) >= 0) {
+ while (!IS_ERR_VALUE(rate = clock->ops->list_rate(clock, i++))) {
if (rate <= fmax)
- seq_printf(m, "%u\n", rate);
+ seq_printf(m, "%lu\n", rate);
}
return 0;
@@ -351,49 +354,24 @@
debugfs_remove_recursive(clk_dir);
return -ENOMEM;
}
-
-/**
- * clock_debug_register() - Add additional clocks to clock debugfs hierarchy
- * @table: Table of clocks to create debugfs nodes for
- * @size: Size of @table
- *
- * Use this function to register additional clocks in debugfs. The clock debugfs
- * hierarchy must have already been initialized with clock_debug_init() prior to
- * calling this function. Unlike clock_debug_init(), this may be called multiple
- * times with different clock lists and can be used after the kernel has
- * finished booting.
- */
-int clock_debug_register(struct clk_lookup *table, size_t size)
-{
- struct clk_table *clk_table;
- unsigned long flags;
- int i;
-
- clk_table = kmalloc(sizeof(*clk_table), GFP_KERNEL);
- if (!clk_table)
- return -ENOMEM;
-
- clk_table->clocks = table;
- clk_table->num_clocks = size;
-
- spin_lock_irqsave(&clk_list_lock, flags);
- list_add_tail(&clk_table->node, &clk_list);
- spin_unlock_irqrestore(&clk_list_lock, flags);
-
- for (i = 0; i < size; i++)
- clock_debug_add(table[i].clk);
-
- return 0;
-}
+static DEFINE_MUTEX(clk_debug_lock);
+static int clk_debug_init_once;
/**
* clock_debug_init() - Initialize clock debugfs
+ * Lock clk_debug_lock before invoking this function.
*/
-int __init clock_debug_init(void)
+static int clock_debug_init(void)
{
+ if (clk_debug_init_once)
+ return 0;
+
+ clk_debug_init_once = 1;
+
debugfs_base = debugfs_create_dir("clk", NULL);
if (!debugfs_base)
return -ENOMEM;
+
if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR,
debugfs_base, &debug_suspend)) {
debugfs_remove_recursive(debugfs_base);
@@ -407,6 +385,45 @@
return 0;
}
+/**
+ * clock_debug_register() - Add additional clocks to clock debugfs hierarchy
+ * @table: Table of clocks to create debugfs nodes for
+ * @size: Size of @table
+ *
+ */
+int clock_debug_register(struct clk_lookup *table, size_t size)
+{
+ struct clk_table *clk_table;
+ unsigned long flags;
+ int i, ret;
+
+ mutex_lock(&clk_debug_lock);
+
+ ret = clock_debug_init();
+ if (ret)
+ goto out;
+
+ clk_table = kmalloc(sizeof(*clk_table), GFP_KERNEL);
+ if (!clk_table) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ clk_table->clocks = table;
+ clk_table->num_clocks = size;
+
+ spin_lock_irqsave(&clk_list_lock, flags);
+ list_add_tail(&clk_table->node, &clk_list);
+ spin_unlock_irqrestore(&clk_list_lock, flags);
+
+ for (i = 0; i < size; i++)
+ clock_debug_add(table[i].clk);
+
+out:
+ mutex_unlock(&clk_debug_lock);
+ return ret;
+}
+
static int clock_debug_print_clock(struct clk *c)
{
char *start = "";
diff --git a/arch/arm/mach-msm/clock-generic.c b/arch/arm/mach-msm/clock-generic.c
index 4d74533..b0d32a0 100644
--- a/arch/arm/mach-msm/clock-generic.c
+++ b/arch/arm/mach-msm/clock-generic.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/errno.h>
+#include <linux/err.h>
#include <linux/clk.h>
#include <mach/clk-provider.h>
@@ -67,10 +68,12 @@
{
struct mux_clk *mux = to_mux_clk(c);
int i;
- long prate, max_prate = 0, rrate = LONG_MAX;
+ unsigned long prate, max_prate = 0, rrate = ULONG_MAX;
for (i = 0; i < mux->num_parents; i++) {
prate = clk_round_rate(mux->parents[i].src, rate);
+ if (IS_ERR_VALUE(prate))
+ continue;
if (prate < rate) {
max_prate = max(prate, max_prate);
continue;
@@ -78,7 +81,7 @@
rrate = min(rrate, prate);
}
- if (rrate == LONG_MAX)
+ if (rrate == ULONG_MAX)
rrate = max_prate;
return rrate ? rrate : -EINVAL;
@@ -90,6 +93,7 @@
struct clk *new_parent = NULL;
int rc = 0, i;
unsigned long new_par_curr_rate;
+ unsigned long flags;
for (i = 0; i < mux->num_parents; i++) {
if (clk_round_rate(mux->parents[i].src, rate) == rate) {
@@ -105,8 +109,16 @@
* same and the parent might temporarily turn off while switching
* rates.
*/
- if (mux->safe_sel >= 0)
+ if (mux->safe_sel >= 0) {
+ /*
+ * Some mux implementations might switch to/from a low power
+ * parent as part of their disable/enable ops. Grab the
+ * enable lock to avoid racing with these implementations.
+ */
+ spin_lock_irqsave(&c->lock, flags);
rc = mux->ops->set_mux_sel(mux, mux->safe_sel);
+ spin_unlock_irqrestore(&c->lock, flags);
+ }
if (rc)
return rc;
@@ -200,7 +212,7 @@
{
struct div_clk *d = to_div_clk(c);
unsigned int div, min_div, max_div;
- long p_rrate, rrate = LONG_MAX;
+ unsigned long p_rrate, rrate = ULONG_MAX;
rate = max(rate, 1UL);
@@ -208,12 +220,12 @@
min_div = max_div = d->div;
else {
min_div = max(d->min_div, 1U);
- max_div = min(d->max_div, (unsigned int) (LONG_MAX / rate));
+ max_div = min(d->max_div, (unsigned int) (ULONG_MAX / rate));
}
for (div = min_div; div <= max_div; div++) {
p_rrate = clk_round_rate(c->parent, rate * div);
- if (p_rrate < 0)
+ if (IS_ERR_VALUE(p_rrate))
break;
p_rrate /= div;
@@ -225,7 +237,7 @@
* for a higher divider. So, stop trying higher dividers.
*/
if (p_rrate < rate) {
- if (rrate == LONG_MAX) {
+ if (rrate == ULONG_MAX) {
rrate = p_rrate;
if (best_div)
*best_div = div;
@@ -242,7 +254,7 @@
break;
}
- if (rrate == LONG_MAX)
+ if (rrate == ULONG_MAX)
return -EINVAL;
return rrate;
@@ -263,6 +275,12 @@
if (rrate != rate)
return -EINVAL;
+ /*
+ * For fixed divider clock we don't want to return an error if the
+ * requested rate matches the achievable rate. So, don't check for
+ * !d->ops and return an error. __div_round_rate() ensures div ==
+ * d->div if !d->ops.
+ */
if (div > d->div)
rc = d->ops->set_div(d, div);
if (rc)
@@ -296,7 +314,7 @@
static int div_enable(struct clk *c)
{
struct div_clk *d = to_div_clk(c);
- if (d->ops->enable)
+ if (d->ops && d->ops->enable)
return d->ops->enable(d);
return 0;
}
@@ -304,7 +322,7 @@
static void div_disable(struct clk *c)
{
struct div_clk *d = to_div_clk(c);
- if (d->ops->disable)
+ if (d->ops && d->ops->disable)
return d->ops->disable(d);
}
@@ -312,7 +330,7 @@
{
struct div_clk *d = to_div_clk(c);
- if (d->ops->get_div)
+ if (d->ops && d->ops->get_div)
d->div = max(d->ops->get_div(d), 1);
d->div = max(d->div, 1U);
c->rate = clk_get_rate(c->parent) / d->div;
@@ -386,8 +404,13 @@
if (div == d->div)
return 0;
- if (d->ops->set_div)
- rc = d->ops->set_div(d, div);
+ /*
+ * For fixed divider clock we don't want to return an error if the
+ * requested rate matches the achievable rate. So, don't check for
+ * !d->ops and return an error. __slave_div_round_rate() ensures
+ * div == d->div if !d->ops.
+ */
+ rc = d->ops->set_div(d, div);
if (rc)
return rc;
diff --git a/arch/arm/mach-msm/clock-krypton.c b/arch/arm/mach-msm/clock-krypton.c
new file mode 100644
index 0000000..0b615cc
--- /dev/null
+++ b/arch/arm/mach-msm/clock-krypton.c
@@ -0,0 +1,127 @@
+/* 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/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/rpm-regulator-smd.h>
+#include <mach/socinfo.h>
+#include <mach/rpm-smd.h>
+
+#include "clock-local2.h"
+#include "clock-pll.h"
+#include "clock-rpm.h"
+#include "clock-voter.h"
+#include "clock.h"
+
+static struct clk_lookup msm_clocks_dummy[] = {
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("core_clk", SPI_CLK, "f9928000.spi", OFF),
+ CLK_DUMMY("iface_clk", SPI_P_CLK, "f9928000.spi", OFF),
+
+ CLK_DUMMY("bus_clk", cnoc_msmbus_clk.c, "msm_config_noc", OFF),
+ CLK_DUMMY("bus_a_clk", cnoc_msmbus_a_clk.c, "msm_config_noc", OFF),
+ CLK_DUMMY("bus_clk", snoc_msmbus_clk.c, "msm_sys_noc", OFF),
+ CLK_DUMMY("bus_a_clk", snoc_msmbus_a_clk.c, "msm_sys_noc", OFF),
+ CLK_DUMMY("bus_clk", pnoc_msmbus_clk.c, "msm_periph_noc", OFF),
+ CLK_DUMMY("bus_a_clk", pnoc_msmbus_a_clk.c, "msm_periph_noc", OFF),
+ CLK_DUMMY("mem_clk", bimc_msmbus_clk.c, "msm_bimc", OFF),
+ CLK_DUMMY("mem_a_clk", bimc_msmbus_a_clk.c, "msm_bimc", OFF),
+ CLK_DUMMY("mem_clk", bimc_acpu_a_clk.c, "", OFF),
+ CLK_DUMMY("dfab_clk", DFAB_CLK, "msm_sps", OFF),
+ CLK_DUMMY("dma_bam_pclk", DMA_BAM_P_CLK, "msm_sps", OFF),
+
+ CLK_DUMMY("clktype", gcc_imem_axi_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_imem_cfg_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_mss_cfg_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_mss_q6_bimc_axi_clk , "drivername", OFF),
+ CLK_DUMMY("mem_clk", gcc_usb30_master_clk , "drivername", OFF),
+ CLK_DUMMY("sleep_clk", gcc_usb30_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("utmi_clk", gcc_usb30_mock_utmi_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_usb_hsic_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_usb_hsic_system_clk , "drivername", OFF),
+ CLK_DUMMY("phyclk", gcc_usb_hsic_clk , "drivername", OFF),
+ CLK_DUMMY("cal_clk", gcc_usb_hsic_io_cal_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_usb_hs_system_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_usb_hs_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("sleep_a_clk", gcc_usb2a_phy_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("sleep_b_clk", gcc_usb2b_phy_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_sdcc2_apps_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_sdcc2_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_sdcc3_apps_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_sdcc3_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", sdcc3_apps_clk_src , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_blsp1_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup1_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup1_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart1_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup2_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup2_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart2_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup3_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup3_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart3_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup4_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup4_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart4_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup5_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup5_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart5_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup6_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup6_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart6_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", blsp1_uart6_apps_clk_src , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_pdm_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_pdm2_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_prng_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("dma_bam_pclk", gcc_bam_dma_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("mem_clk", gcc_boot_rom_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_ce1_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_ce1_axi_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_ce1_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk_src", ce1_clk_src , "drivername", OFF),
+ CLK_DUMMY("bus_clk", gcc_lpass_q6_axi_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", pcie_pipe_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_gp1_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gp1_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_gp2_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gp2_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_gp3_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gp3_clk_src , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_ipa_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_ipa_cnoc_clk , "drivername", OFF),
+ CLK_DUMMY("inactivity_clk", gcc_ipa_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk_src", ipa_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_dcs_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", dcs_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_cfg_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_pipe_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_axi_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_axi_mstr_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", pcie_pipe_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", pcie_aux_clk_src , "drivername", OFF),
+};
+
+struct clock_init_data msmkrypton_clock_init_data __initdata = {
+ .table = msm_clocks_dummy,
+ .size = ARRAY_SIZE(msm_clocks_dummy),
+};
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 0b8240c..18ea514 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -579,7 +579,7 @@
}
/* Return the nth supported frequency for a given clock. */
-static int rcg_clk_list_rate(struct clk *c, unsigned n)
+static long rcg_clk_list_rate(struct clk *c, unsigned n)
{
struct rcg_clk *rcg = to_rcg_clk(c);
@@ -944,7 +944,7 @@
return rate > to_cdiv_clk(c)->max_div ? -EPERM : rate;
}
-static int cdiv_clk_list_rate(struct clk *c, unsigned n)
+static long cdiv_clk_list_rate(struct clk *c, unsigned n)
{
return n > to_cdiv_clk(c)->max_div ? -ENXIO : n;
}
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index fd790e2..b7852fe 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -210,7 +210,7 @@
}
/* Return the nth supported frequency for a given clock. */
-static int rcg_clk_list_rate(struct clk *c, unsigned n)
+static long rcg_clk_list_rate(struct clk *c, unsigned n)
{
struct rcg_clk *rcg = to_rcg_clk(c);
@@ -459,7 +459,7 @@
return clk_get_rate(c->parent);
}
-static int branch_clk_list_rate(struct clk *c, unsigned n)
+static long branch_clk_list_rate(struct clk *c, unsigned n)
{
int level, fmax = 0, rate;
struct branch_clk *branch = to_branch_clk(c);
@@ -934,7 +934,7 @@
return ERR_PTR(-EPERM);
}
-static int cam_mux_clk_list_rate(struct clk *c, unsigned n)
+static long cam_mux_clk_list_rate(struct clk *c, unsigned n)
{
struct cam_mux_clk *mux = to_cam_mux_clk(c);
int i;
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index 1245287..70b6bea 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -854,11 +854,24 @@
return div;
}
+static void dsi_pll_toggle_lock_detect(void)
+{
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG2,
+ 0x05);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG2,
+ 0x04);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG2,
+ 0x05);
+}
+
static int dsi_pll_lock_status(void)
{
u32 status;
int pll_locked = 0;
+ dsi_pll_toggle_lock_detect();
+
/* poll for PLL ready status */
if (readl_poll_timeout_noirq((mdss_dsi_base +
DSI_0_PHY_PLL_UNIPHY_PLL_STATUS),
@@ -906,18 +919,19 @@
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(1000);
- do {
+ pll_locked = dsi_pll_lock_status();
+ for (i = 0; (i < 4) && !pll_locked; i++) {
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
+ if (i != 0)
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
pll_locked = dsi_pll_lock_status();
- if (!pll_locked) {
- DSS_REG_W(mdss_dsi_base,
- DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
- DSS_REG_W(mdss_dsi_base,
- DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
- udelay(1000);
- i++;
- }
- } while ((i < 3) && !pll_locked);
+ }
if (pll_locked)
pr_debug("%s: PLL Locked at attempt #%d\n", __func__, i);
@@ -940,17 +954,17 @@
* PLL to successfully lock
*/
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
- udelay(1);
+ udelay(1000);
pll_locked = dsi_pll_lock_status();
pr_debug("%s: PLL status = %s\n", __func__,
@@ -1029,6 +1043,7 @@
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0d);
+ udelay(1);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(1000);
diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c
index 908107e..a251784 100644
--- a/arch/arm/mach-msm/clock-pll.c
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -57,6 +57,18 @@
#define ENABLE_WAIT_MAX_LOOPS 200
#define PLL_LOCKED_BIT BIT(16)
+static int fixed_pll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ if (rate != c->rate)
+ return -EINVAL;
+ return 0;
+}
+
+static long fixed_pll_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ return c->rate;
+}
+
static int pll_vote_clk_enable(struct clk *c)
{
u32 ena, count;
@@ -119,6 +131,8 @@
.enable = pll_vote_clk_enable,
.disable = pll_vote_clk_disable,
.is_enabled = pll_vote_clk_is_enabled,
+ .round_rate = fixed_pll_clk_round_rate,
+ .set_rate = fixed_pll_clk_set_rate,
.handoff = pll_vote_clk_handoff,
};
@@ -574,6 +588,8 @@
struct clk_ops clk_ops_pll = {
.enable = pll_clk_enable,
.disable = pll_clk_disable,
+ .round_rate = fixed_pll_clk_round_rate,
+ .set_rate = fixed_pll_clk_set_rate,
.handoff = pll_clk_handoff,
.is_enabled = pll_clk_is_enabled,
};
@@ -625,6 +641,8 @@
struct clk_ops clk_ops_pll_acpu_vote = {
.enable = pll_acpu_vote_clk_enable,
.disable = pll_acpu_vote_clk_disable,
+ .round_rate = fixed_pll_clk_round_rate,
+ .set_rate = fixed_pll_clk_set_rate,
.is_enabled = pll_vote_clk_is_enabled,
.handoff = pll_acpu_vote_clk_handoff,
};
@@ -676,6 +694,12 @@
regval |= config->main_output_val;
}
+ /* Enable the aux output */
+ if (config->aux_output_mask) {
+ regval &= ~config->aux_output_mask;
+ regval |= config->aux_output_val;
+ }
+
/* Set pre-divider and post-divider values */
regval &= ~config->pre_div_mask;
regval |= config->pre_div_val;
diff --git a/arch/arm/mach-msm/clock-pll.h b/arch/arm/mach-msm/clock-pll.h
index c4addb2..58c00b9 100644
--- a/arch/arm/mach-msm/clock-pll.h
+++ b/arch/arm/mach-msm/clock-pll.h
@@ -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
@@ -184,6 +184,8 @@
u32 mn_ena_mask;
u32 main_output_val;
u32 main_output_mask;
+ u32 aux_output_val;
+ u32 aux_output_mask;
};
struct pll_config_regs {
diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c
index 8356207..1d7a5e2 100644
--- a/arch/arm/mach-msm/clock-rpm.c
+++ b/arch/arm/mach-msm/clock-rpm.c
@@ -42,7 +42,7 @@
int rc;
struct msm_rpm_iv_pair iv = { .id = r->rpm_status_id, };
rc = msm_rpm_get_status(&iv, 1);
- return (rc < 0) ? rc : iv.value * r->factor;
+ return (rc < 0) ? rc : iv.value * 1000;
}
static int clk_rpmrs_handoff(struct rpm_clk *r)
@@ -54,7 +54,7 @@
return rc;
if (!r->branch)
- r->c.rate = iv.value * r->factor;
+ r->c.rate = iv.value * 1000;
return 0;
}
@@ -122,7 +122,7 @@
unsigned long *active_khz, unsigned long *sleep_khz)
{
/* Convert the rate (hz) to khz */
- *active_khz = DIV_ROUND_UP(rate, r->factor);
+ *active_khz = DIV_ROUND_UP(rate, 1000);
/*
* Active-only clocks don't care what the rate is during sleep. So,
diff --git a/arch/arm/mach-msm/clock-rpm.h b/arch/arm/mach-msm/clock-rpm.h
index b20c3d6..d283861 100644
--- a/arch/arm/mach-msm/clock-rpm.h
+++ b/arch/arm/mach-msm/clock-rpm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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,9 +39,7 @@
const bool active_only;
bool enabled;
bool branch; /* true: RPM only accepts 1 for ON and 0 for OFF */
- unsigned factor;
struct clk_rpmrs_data *rpmrs_data;
-
struct rpm_clk *peer;
struct clk c;
};
@@ -69,7 +67,6 @@
.rpm_status_id = (stat_id), \
.rpm_key = (key), \
.peer = &active, \
- .factor = 1000, \
.rpmrs_data = (rpmrsdata),\
.c = { \
.ops = &clk_ops_rpm, \
@@ -85,7 +82,6 @@
.rpm_key = (key), \
.peer = &name, \
.active_only = true, \
- .factor = 1000, \
.rpmrs_data = (rpmrsdata),\
.c = { \
.ops = &clk_ops_rpm, \
@@ -104,7 +100,6 @@
.rpm_status_id = (stat_id), \
.rpm_key = (key), \
.peer = &active, \
- .factor = 1000, \
.branch = true, \
.rpmrs_data = (rpmrsdata),\
.c = { \
@@ -121,7 +116,6 @@
.rpm_key = (key), \
.peer = &name, \
.active_only = true, \
- .factor = 1000, \
.branch = true, \
.rpmrs_data = (rpmrsdata),\
.c = { \
@@ -132,46 +126,13 @@
}, \
};
-#define __DEFINE_CLK_RPM_QDSS(name, active, type, r_id, stat_id, \
- key, rpmrsdata) \
- static struct rpm_clk active; \
- static struct rpm_clk name = { \
- .rpm_res_type = (type), \
- .rpm_clk_id = (r_id), \
- .rpm_status_id = (stat_id), \
- .rpm_key = (key), \
- .peer = &active, \
- .factor = 1, \
- .rpmrs_data = (rpmrsdata),\
- .c = { \
- .ops = &clk_ops_rpm, \
- .dbg_name = #name, \
- CLK_INIT(name.c), \
- }, \
- }; \
- static struct rpm_clk active = { \
- .rpm_res_type = (type), \
- .rpm_clk_id = (r_id), \
- .rpm_status_id = (stat_id), \
- .rpm_key = (key), \
- .peer = &name, \
- .active_only = true, \
- .factor = 1, \
- .rpmrs_data = (rpmrsdata),\
- .c = { \
- .ops = &clk_ops_rpm, \
- .dbg_name = #active, \
- CLK_INIT(active.c), \
- }, \
- };
-
#define DEFINE_CLK_RPM(name, active, r_id, dep) \
__DEFINE_CLK_RPM(name, active, 0, MSM_RPM_ID_##r_id##_CLK, \
MSM_RPM_STATUS_ID_##r_id##_CLK, dep, 0, &clk_rpmrs_data)
#define DEFINE_CLK_RPM_QDSS(name, active) \
- __DEFINE_CLK_RPM_QDSS(name, active, 0, MSM_RPM_ID_QDSS_CLK, \
- MSM_RPM_STATUS_ID_QDSS_CLK, 0, &clk_rpmrs_data)
+ __DEFINE_CLK_RPM(name, active, 0, MSM_RPM_ID_QDSS_CLK, \
+ MSM_RPM_STATUS_ID_QDSS_CLK, 0, 0, &clk_rpmrs_data)
#define DEFINE_CLK_RPM_BRANCH(name, active, r_id, r) \
__DEFINE_CLK_RPM_BRANCH(name, active, 0, MSM_RPM_ID_##r_id##_CLK, \
@@ -186,8 +147,8 @@
RPM_SMD_KEY_ENABLE, &clk_rpmrs_data_smd)
#define DEFINE_CLK_RPM_SMD_QDSS(name, active, type, r_id) \
- __DEFINE_CLK_RPM_QDSS(name, active, type, r_id, \
- 0, RPM_SMD_KEY_STATE, &clk_rpmrs_data_smd)
+ __DEFINE_CLK_RPM(name, active, type, r_id, \
+ 0, 0, RPM_SMD_KEY_STATE, &clk_rpmrs_data_smd)
/*
* The RPM XO buffer clock management code aggregates votes for pin-control mode
* and software mode separately. Software-enable has higher priority over pin-
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 582bccf..527d73d 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -23,6 +23,7 @@
#include <linux/clkdev.h>
#include <linux/list.h>
#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
#include <trace/events/power.h>
#include <mach/clk-provider.h>
#include "clock.h"
@@ -39,6 +40,8 @@
};
static LIST_HEAD(handoff_vdd_list);
+static DEFINE_MUTEX(msm_clock_init_lock);
+
/* Find the voltage level required for a given rate. */
int find_vdd_level(struct clk *clk, unsigned long rate)
{
@@ -482,11 +485,16 @@
start_rate = clk->rate;
+ if (clk->ops->pre_set_rate)
+ rc = clk->ops->pre_set_rate(clk, rate);
+ if (rc)
+ goto out;
+
/* Enforce vdd requirements for target frequency. */
if (clk->prepare_count) {
rc = vote_rate_vdd(clk, rate);
if (rc)
- goto out;
+ goto err_vote_vdd;
}
rc = clk->ops->set_rate(clk, rate);
@@ -498,6 +506,9 @@
if (clk->prepare_count)
unvote_rate_vdd(clk, start_rate);
+ if (clk->ops->post_set_rate)
+ clk->ops->post_set_rate(clk, start_rate);
+
out:
mutex_unlock(&clk->prepare_lock);
return rc;
@@ -505,6 +516,10 @@
err_set_rate:
if (clk->prepare_count)
unvote_rate_vdd(clk, rate);
+err_vote_vdd:
+ /* clk->rate is still the old rate. So, pass the new rate instead. */
+ if (clk->ops->post_set_rate)
+ clk->ops->post_set_rate(clk, rate);
goto out;
}
EXPORT_SYMBOL(clk_set_rate);
@@ -573,7 +588,7 @@
}
EXPORT_SYMBOL(clk_set_flags);
-static struct clock_init_data *clk_init_data;
+static LIST_HEAD(initdata_list);
static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
{
@@ -588,33 +603,6 @@
}
}
-/**
- * msm_clock_register() - Register additional clock tables
- * @table: Table of clocks
- * @size: Size of @table
- *
- * Upon return, clock APIs may be used to control clocks registered using this
- * function. This API may only be used after msm_clock_init() has completed.
- * Unlike msm_clock_init(), this function may be called multiple times with
- * different clock lists and used after the kernel has finished booting.
- */
-int msm_clock_register(struct clk_lookup *table, size_t size)
-{
- if (!clk_init_data)
- return -ENODEV;
-
- if (!table)
- return -EINVAL;
-
- init_sibling_lists(table, size);
- clkdev_add_table(table, size);
- clock_debug_register(table, size);
-
- return 0;
-}
-EXPORT_SYMBOL(msm_clock_register);
-
-
static void vdd_class_init(struct clk_vdd_class *vdd)
{
struct handoff_vdd *v;
@@ -646,7 +634,7 @@
list_add_tail(&v->list, &handoff_vdd_list);
}
-static int __init __handoff_clk(struct clk *clk)
+static int __handoff_clk(struct clk *clk)
{
enum handoff state = HANDOFF_DISABLED_CLK;
struct handoff_clk *h = NULL;
@@ -726,29 +714,20 @@
}
/**
- * msm_clock_init() - Register and initialize a clock driver
- * @data: Driver-specific clock initialization data
+ * msm_clock_register() - Register additional clock tables
+ * @table: Table of clocks
+ * @size: Size of @table
*
- * Upon return from this call, clock APIs may be used to control
- * clocks registered with this API.
+ * Upon return, clock APIs may be used to control clocks registered using this
+ * function.
*/
-int __init msm_clock_init(struct clock_init_data *data)
+int msm_clock_register(struct clk_lookup *table, size_t size)
{
- unsigned n;
- struct clk_lookup *clock_tbl;
- size_t num_clocks;
+ int n = 0;
- if (!data)
- return -EINVAL;
+ mutex_lock(&msm_clock_init_lock);
- clk_init_data = data;
- if (clk_init_data->pre_init)
- clk_init_data->pre_init();
-
- clock_tbl = data->table;
- num_clocks = data->size;
-
- init_sibling_lists(clock_tbl, num_clocks);
+ init_sibling_lists(table, size);
/*
* Enable regulators and temporarily set them up at maximum voltage.
@@ -757,23 +736,50 @@
* late_init, by which time we assume all the clocks would have been
* handed off.
*/
- for (n = 0; n < num_clocks; n++)
- vdd_class_init(clock_tbl[n].clk->vdd_class);
+ for (n = 0; n < size; n++)
+ vdd_class_init(table[n].clk->vdd_class);
/*
* Detect and preserve initial clock state until clock_late_init() or
* a driver explicitly changes it, whichever is first.
*/
- for (n = 0; n < num_clocks; n++)
- __handoff_clk(clock_tbl[n].clk);
+ for (n = 0; n < size; n++)
+ __handoff_clk(table[n].clk);
- clkdev_add_table(clock_tbl, num_clocks);
+ clkdev_add_table(table, size);
- if (clk_init_data->post_init)
- clk_init_data->post_init();
+ clock_debug_register(table, size);
- clock_debug_init();
- clock_debug_register(clock_tbl, num_clocks);
+ mutex_unlock(&msm_clock_init_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_clock_register);
+
+/**
+ * msm_clock_init() - Register and initialize a clock driver
+ * @data: Driver-specific clock initialization data
+ *
+ * Upon return from this call, clock APIs may be used to control
+ * clocks registered with this API.
+ */
+int __init msm_clock_init(struct clock_init_data *data)
+{
+ if (!data)
+ return -EINVAL;
+
+ if (data->pre_init)
+ data->pre_init();
+
+ mutex_lock(&msm_clock_init_lock);
+ if (data->late_init)
+ list_add(&data->list, &initdata_list);
+ mutex_unlock(&msm_clock_init_lock);
+
+ msm_clock_register(data->table, data->size);
+
+ if (data->post_init)
+ data->post_init();
return 0;
}
@@ -782,12 +788,21 @@
{
struct handoff_clk *h, *h_temp;
struct handoff_vdd *v, *v_temp;
+ struct clock_init_data *initdata, *initdata_temp;
int ret = 0;
- if (clk_init_data->late_init)
- ret = clk_init_data->late_init();
-
pr_info("%s: Removing enables held for handed-off clocks\n", __func__);
+
+ mutex_lock(&msm_clock_init_lock);
+
+ list_for_each_entry_safe(initdata, initdata_temp,
+ &initdata_list, list) {
+ ret = initdata->late_init();
+ if (ret)
+ pr_err("%s: %pS failed late_init.\n", __func__,
+ initdata);
+ }
+
list_for_each_entry_safe(h, h_temp, &handoff_list, list) {
clk_disable_unprepare(h->clk);
list_del(&h->list);
@@ -800,6 +815,11 @@
kfree(v);
}
+ mutex_unlock(&msm_clock_init_lock);
+
return ret;
}
-late_initcall(clock_late_init);
+/* clock_late_init should run only after all deferred probing
+ * (excluding DLKM probes) has completed.
+ */
+late_initcall_sync(clock_late_init);
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 2a65d2f..e294f4c 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -26,6 +26,7 @@
* @late_init: called during late init
*/
struct clock_init_data {
+ struct list_head list;
struct clk_lookup *table;
size_t size;
void (*pre_init)(void);
@@ -55,16 +56,15 @@
extern struct clock_init_data msm8226_rumi_clock_init_data;
extern struct clock_init_data msm8084_clock_init_data;
extern struct clock_init_data mpq8092_clock_init_data;
+extern struct clock_init_data msmkrypton_clock_init_data;
int msm_clock_init(struct clock_init_data *data);
int find_vdd_level(struct clk *clk, unsigned long rate);
#ifdef CONFIG_DEBUG_FS
-int clock_debug_init(void);
int clock_debug_register(struct clk_lookup *t, size_t s);
void clock_debug_print_enabled(void);
#else
-static inline int clock_debug_init(void) { return 0; }
static inline int clock_debug_register(struct clk_lookup *t, size_t s)
{
return 0;
diff --git a/arch/arm/mach-msm/cpr-regulator.c b/arch/arm/mach-msm/cpr-regulator.c
index 5550282..a2936c2 100644
--- a/arch/arm/mach-msm/cpr-regulator.c
+++ b/arch/arm/mach-msm/cpr-regulator.c
@@ -125,21 +125,7 @@
#define CPR_FUSE_RO_SEL_BITS 3
#define CPR_FUSE_RO_SEL_BITS_MASK ((1<<CPR_FUSE_RO_SEL_BITS)-1)
-#define CPR_FUSE_TARGET_QUOT_TURBO_SHIFT 0
-#define CPR_FUSE_TARGET_QUOT_NOMINAL_SHIFT 12
-#define CPR_FUSE_TARGET_QUOT_SVS_SHIFT 24
-
-#define CPR_FUSE_DISABLE_CPR_SHIFT 36
-#define CPR_FUSE_LOCAL_APPROACH_SHIFT 37
-#define CPR_FUSE_REDUNDANT_SHIFT 57
-
-/* PVS eFuse parameters */
-#define PVS_FUSE_REDUNDANT_SHIFT 24
-#define PVS_FUSE_REDUNDANT_BITS 3
-#define PVS_FUSE_REDUNDANT_MASK ((1<<PVS_FUSE_REDUNDANT_BITS)-1)
-
-#define PVS_FUSE_BINS_SHIFT 6
-#define PVS_FUSE_BINS_REDUNDANT_SHIFT 27
+#define BYTES_PER_FUSE_ROW 8
enum voltage_change_dir {
NO_CHANGE,
@@ -154,9 +140,11 @@
int corner;
int ceiling_max;
+ /* eFuse parameters */
+ phys_addr_t efuse_addr;
+ void __iomem *efuse_base;
+
/* Process voltage parameters */
- phys_addr_t pvs_efuse;
- u32 num_efuse_bits;
u32 pvs_bin_process[CPR_PVS_EFUSE_BINS_MAX];
u32 pvs_corner_v[NUM_APC_PVS][CPR_CORNER_MAX];
/* Process voltage variables */
@@ -173,12 +161,9 @@
int vdd_mx_vmin;
/* CPR parameters */
- phys_addr_t cpr_fuse_addr;
u64 cpr_fuse_bits;
- u64 cpr_fuse_bits_2;
bool cpr_fuse_disable;
bool cpr_fuse_local;
- bool cpr_fuse_redundancy;
int cpr_fuse_target_quot[CPR_CORNER_MAX];
int cpr_fuse_ro_sel[CPR_CORNER_MAX];
int gcnt;
@@ -838,7 +823,7 @@
#define cpr_regulator_resume NULL
#endif
-static int cpr_config(struct cpr_regulator *cpr_vreg)
+static int __devinit cpr_config(struct cpr_regulator *cpr_vreg)
{
int i;
u32 val, gcnt, reg;
@@ -928,47 +913,80 @@
return 0;
}
-static int __init cpr_pvs_init(struct cpr_regulator *cpr_vreg)
+static int __devinit cpr_is_fuse_redundant(struct cpr_regulator *cpr_vreg,
+ u32 redun_sel[4])
{
- void __iomem *efuse_base;
- u32 efuse_bits, redundant, shift, mask;
- int i, process;
+ u32 fuse_bits;
+ int redundant;
- efuse_base = ioremap(cpr_vreg->pvs_efuse, 4);
- if (!efuse_base) {
- pr_err("Unable to map pvs_efuse 0x%08x\n",
- cpr_vreg->pvs_efuse);
- return -EINVAL;
+ fuse_bits = readl_relaxed(cpr_vreg->efuse_base
+ + redun_sel[0] * BYTES_PER_FUSE_ROW);
+ fuse_bits = (fuse_bits >> redun_sel[1]) & ((1 << redun_sel[2]) - 1);
+ if (fuse_bits == redun_sel[3])
+ redundant = 1;
+ else
+ redundant = 0;
+
+ pr_info("[row:%d] = 0x%x @%d:%d = %d?: redundant=%d\n",
+ redun_sel[0], fuse_bits,
+ redun_sel[1], redun_sel[2], redun_sel[3], redundant);
+ return redundant;
+}
+
+static int __devinit cpr_pvs_init(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
+{
+ struct device_node *of_node = pdev->dev.of_node;
+ u32 efuse_bits;
+ int rc, process;
+ u32 pvs_fuse[3], pvs_fuse_redun_sel[4];
+ bool redundant;
+ size_t pvs_bins;
+
+ rc = of_property_read_u32_array(of_node, "qcom,pvs-fuse-redun-sel",
+ pvs_fuse_redun_sel, 4);
+ if (rc < 0) {
+ pr_err("pvs-fuse-redun-sel missing: rc=%d\n", rc);
+ return rc;
}
- efuse_bits = readl_relaxed(efuse_base);
+ redundant = cpr_is_fuse_redundant(cpr_vreg, pvs_fuse_redun_sel);
+
+ if (redundant) {
+ rc = of_property_read_u32_array(of_node, "qcom,pvs-fuse-redun",
+ pvs_fuse, 3);
+ if (rc < 0) {
+ pr_err("pvs-fuse-redun missing: rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = of_property_read_u32_array(of_node, "qcom,pvs-fuse",
+ pvs_fuse, 3);
+ if (rc < 0) {
+ pr_err("pvs-fuse missing: rc=%d\n", rc);
+ return rc;
+ }
+ }
/* Construct PVS process # from the efuse bits */
- redundant = (efuse_bits >> PVS_FUSE_REDUNDANT_SHIFT)
- & PVS_FUSE_REDUNDANT_MASK;
- if (redundant == 2)
- shift = PVS_FUSE_BINS_REDUNDANT_SHIFT;
- else
- shift = PVS_FUSE_BINS_SHIFT;
- mask = (1 << cpr_vreg->num_efuse_bits) - 1;
- cpr_vreg->pvs_bin = (efuse_bits >> shift) & mask;
+ efuse_bits = readl_relaxed(cpr_vreg->efuse_base +
+ pvs_fuse[0] * BYTES_PER_FUSE_ROW);
+ cpr_vreg->pvs_bin = (efuse_bits >> pvs_fuse[1]) &
+ ((1 << pvs_fuse[2]) - 1);
- /* Set ceiling max and use it for APC_PVS_NO */
- cpr_vreg->ceiling_max =
- cpr_vreg->pvs_corner_v[APC_PVS_SLOW][CPR_CORNER_TURBO];
-
- iounmap(efuse_base);
+ pvs_bins = 1 << pvs_fuse[2];
+ rc = of_property_read_u32_array(of_node, "qcom,pvs-bin-process",
+ cpr_vreg->pvs_bin_process,
+ pvs_bins);
+ if (rc < 0) {
+ pr_err("pvs-bin-process missing: rc=%d\n", rc);
+ return rc;
+ }
process = cpr_vreg->pvs_bin_process[cpr_vreg->pvs_bin];
- pr_info("[0x%08X] = 0x%08X, n_bits=%d, bin=%d (%d) [redundant=%d]\n",
- cpr_vreg->pvs_efuse, efuse_bits, cpr_vreg->num_efuse_bits,
- cpr_vreg->pvs_bin, process, redundant);
- for (i = APC_PVS_SLOW; i < NUM_APC_PVS; i++) {
- pr_info("[%d] [%d %d %d] uV\n", i,
- cpr_vreg->pvs_corner_v[i][CPR_CORNER_SVS],
- cpr_vreg->pvs_corner_v[i][CPR_CORNER_NORMAL],
- cpr_vreg->pvs_corner_v[i][CPR_CORNER_TURBO]);
- }
+ pr_info("[row:%d] = 0x%08X, n_bits=%d, bin=%d (%d)\n",
+ pvs_fuse[0], efuse_bits, pvs_fuse[2],
+ cpr_vreg->pvs_bin, process);
if (process == APC_PVS_NO || process >= NUM_APC_PVS) {
pr_err("Bin=%d (%d) is out of spec. Assume SLOW.\n",
@@ -994,7 +1012,7 @@
} \
} while (0)
-static int __init cpr_apc_init(struct platform_device *pdev,
+static int __devinit cpr_apc_init(struct platform_device *pdev,
struct cpr_regulator *cpr_vreg)
{
struct device_node *of_node = pdev->dev.of_node;
@@ -1055,81 +1073,124 @@
}
}
-static int __init cpr_init_cpr_efuse(struct cpr_regulator *cpr_vreg)
+static int __devinit cpr_init_cpr_efuse(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
{
- void __iomem *efuse_base;
+ struct device_node *of_node = pdev->dev.of_node;
+ int i, rc = 0;
+ bool redundant;
+ u32 cpr_fuse_redun_sel[4];
+ char *targ_quot_str, *ro_sel_str;
+ u32 cpr_fuse_row;
+ u32 bp_cpr_disable, bp_scheme;
+ int bp_target_quot[CPR_CORNER_MAX];
+ int bp_ro_sel[CPR_CORNER_MAX];
u32 ro_sel, val;
- u64 fuse_bits;
- int ro_sel_shift[CPR_CORNER_MAX];
+ u64 fuse_bits, fuse_bits_2;
- efuse_base = ioremap(cpr_vreg->cpr_fuse_addr, 16);
- if (!efuse_base) {
- pr_err("Unable to map cpr_fuse_addr 0x%08x\n",
- cpr_vreg->cpr_fuse_addr);
- return -EINVAL;
+ rc = of_property_read_u32_array(of_node, "qcom,cpr-fuse-redun-sel",
+ cpr_fuse_redun_sel, 4);
+ if (rc < 0) {
+ pr_err("cpr-fuse-redun-sel missing: rc=%d\n", rc);
+ return rc;
}
- cpr_vreg->cpr_fuse_bits = readll_relaxed(efuse_base);
- cpr_vreg->cpr_fuse_bits_2 = readll_relaxed(efuse_base + 8);
+ redundant = cpr_is_fuse_redundant(cpr_vreg, cpr_fuse_redun_sel);
- iounmap(efuse_base);
+ if (redundant) {
+ CPR_PROP_READ_U32(of_node, "cpr-fuse-redun-row",
+ &cpr_fuse_row, rc);
+ targ_quot_str = "qcom,cpr-fuse-redun-target-quot";
+ ro_sel_str = "qcom,cpr-fuse-redun-ro-sel";
+ } else {
+ CPR_PROP_READ_U32(of_node, "cpr-fuse-row",
+ &cpr_fuse_row, rc);
+ targ_quot_str = "qcom,cpr-fuse-target-quot";
+ ro_sel_str = "qcom,cpr-fuse-ro-sel";
+ }
+ if (rc)
+ return rc;
+
+ rc = of_property_read_u32_array(of_node,
+ targ_quot_str,
+ &bp_target_quot[CPR_CORNER_SVS],
+ CPR_CORNER_MAX - CPR_CORNER_SVS);
+ if (rc < 0) {
+ pr_err("missing %s: rc=%d\n", targ_quot_str, rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32_array(of_node,
+ ro_sel_str,
+ &bp_ro_sel[CPR_CORNER_SVS],
+ CPR_CORNER_MAX - CPR_CORNER_SVS);
+ if (rc < 0) {
+ pr_err("missing %s: rc=%d\n", ro_sel_str, rc);
+ return rc;
+ }
/* Read the control bits of eFuse */
- cpr_vreg->cpr_fuse_disable = (cpr_vreg->cpr_fuse_bits >>
- CPR_FUSE_DISABLE_CPR_SHIFT) & 0x01;
- cpr_vreg->cpr_fuse_local = (cpr_vreg->cpr_fuse_bits >>
- CPR_FUSE_LOCAL_APPROACH_SHIFT) & 0x01;
- cpr_vreg->cpr_fuse_redundancy = (cpr_vreg->cpr_fuse_bits >>
- CPR_FUSE_REDUNDANT_SHIFT) & 0x01;
+ fuse_bits = readll_relaxed(cpr_vreg->efuse_base
+ + cpr_fuse_row * BYTES_PER_FUSE_ROW);
+ pr_info("[row:%d] = 0x%llx\n", cpr_fuse_row, fuse_bits);
- pr_info("[0x%08X] = 0x%llx\n", cpr_vreg->cpr_fuse_addr,
- cpr_vreg->cpr_fuse_bits);
- pr_info("disable = %d, local = %d, redundancy = %d\n",
- cpr_vreg->cpr_fuse_disable,
- cpr_vreg->cpr_fuse_local,
- cpr_vreg->cpr_fuse_redundancy);
- pr_info("[0x%08X] = 0x%llx\n", cpr_vreg->cpr_fuse_addr + 8,
- cpr_vreg->cpr_fuse_bits_2);
+ if (redundant) {
+ if (of_property_read_bool(of_node,
+ "qcom,cpr-fuse-redun-bp-cpr-disable")) {
+ CPR_PROP_READ_U32(of_node,
+ "cpr-fuse-redun-bp-cpr-disable",
+ &bp_cpr_disable, rc);
+ CPR_PROP_READ_U32(of_node,
+ "cpr-fuse-redun-bp-scheme",
+ &bp_scheme, rc);
+ if (rc)
+ return rc;
+ fuse_bits_2 = fuse_bits;
+ } else {
+ u32 temp_row;
- if (cpr_vreg->cpr_fuse_redundancy == 0) {
- fuse_bits = cpr_vreg->cpr_fuse_bits;
- ro_sel_shift[CPR_CORNER_SVS] = 54;
- ro_sel_shift[CPR_CORNER_NORMAL] = 38;
- ro_sel_shift[CPR_CORNER_TURBO] = 41;
+ /* Use original fuse if no optional property */
+ CPR_PROP_READ_U32(of_node, "cpr-fuse-bp-cpr-disable",
+ &bp_cpr_disable, rc);
+ CPR_PROP_READ_U32(of_node, "cpr-fuse-bp-scheme",
+ &bp_scheme, rc);
+ CPR_PROP_READ_U32(of_node, "cpr-fuse-row",
+ &temp_row, rc);
+ if (rc)
+ return rc;
+ fuse_bits_2 = readll_relaxed(cpr_vreg->efuse_base
+ + temp_row * BYTES_PER_FUSE_ROW);
+ pr_info("[original row:%d] = 0x%llx\n",
+ temp_row, fuse_bits_2);
+ }
} else {
- fuse_bits = cpr_vreg->cpr_fuse_bits_2;
- ro_sel_shift[CPR_CORNER_SVS] = 46;
- ro_sel_shift[CPR_CORNER_NORMAL] = 36;
- ro_sel_shift[CPR_CORNER_TURBO] = 39;
+ CPR_PROP_READ_U32(of_node, "cpr-fuse-bp-cpr-disable",
+ &bp_cpr_disable, rc);
+ CPR_PROP_READ_U32(of_node, "cpr-fuse-bp-scheme",
+ &bp_scheme, rc);
+ if (rc)
+ return rc;
+ fuse_bits_2 = fuse_bits;
}
- /* SVS */
- ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_SVS])
- & CPR_FUSE_RO_SEL_BITS_MASK;
- val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_SVS_SHIFT)
- & CPR_FUSE_TARGET_QUOT_BITS_MASK;
- cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_SVS] = val;
- cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_SVS] = ro_sel;
- pr_info("SVS: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+ cpr_vreg->cpr_fuse_disable = (fuse_bits_2 >> bp_cpr_disable) & 0x01;
+ cpr_vreg->cpr_fuse_local = (fuse_bits_2 >> bp_scheme) & 0x01;
- /* Nominal */
- ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_NORMAL])
- & CPR_FUSE_RO_SEL_BITS_MASK;
- val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_NOMINAL_SHIFT)
- & CPR_FUSE_TARGET_QUOT_BITS_MASK;
- cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_NORMAL] = val;
- cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_NORMAL] = ro_sel;
- pr_info("Nominal: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+ pr_info("disable = %d, local = %d\n",
+ cpr_vreg->cpr_fuse_disable, cpr_vreg->cpr_fuse_local);
- /* Turbo */
- ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_TURBO])
+ for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) {
+ ro_sel = (fuse_bits >> bp_ro_sel[i])
& CPR_FUSE_RO_SEL_BITS_MASK;
- val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_TURBO_SHIFT)
+ val = (fuse_bits >> bp_target_quot[i])
& CPR_FUSE_TARGET_QUOT_BITS_MASK;
- cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_TURBO] = val;
- cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_TURBO] = ro_sel;
- pr_info("Turbo: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+ cpr_vreg->cpr_fuse_target_quot[i] = val;
+ cpr_vreg->cpr_fuse_ro_sel[i] = ro_sel;
+ pr_info("Corner[%d]: ro_sel = %d, target quot = %d\n",
+ i, ro_sel, val);
+ }
+ cpr_vreg->cpr_fuse_bits = fuse_bits;
if (!cpr_vreg->cpr_fuse_bits) {
cpr_vreg->cpr_fuse_disable = 1;
pr_err("cpr_fuse_bits = 0: set cpr_fuse_disable = 1\n");
@@ -1138,7 +1199,7 @@
return 0;
}
-static int __init cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg)
+static int __devinit cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg)
{
int i;
@@ -1155,7 +1216,7 @@
return 0;
}
-static int __init cpr_init_cpr_parameters(struct platform_device *pdev,
+static int __devinit cpr_init_cpr_parameters(struct platform_device *pdev,
struct cpr_regulator *cpr_vreg)
{
struct device_node *of_node = pdev->dev.of_node;
@@ -1223,20 +1284,12 @@
return rc;
}
-static int __init cpr_init_cpr(struct platform_device *pdev,
+static int __devinit cpr_init_cpr(struct platform_device *pdev,
struct cpr_regulator *cpr_vreg)
{
struct resource *res;
int rc = 0;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "cpr_efuse");
- if (!res || !res->start) {
- pr_err("cpr_efuse missing: res=%p\n", res);
- return -EINVAL;
- }
- cpr_vreg->cpr_fuse_addr = res->start;
-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rbcpr_clk");
if (!res || !res->start) {
pr_err("missing rbcpr_clk address: res=%p\n", res);
@@ -1244,7 +1297,7 @@
}
cpr_vreg->rbcpr_clk_addr = res->start;
- rc = cpr_init_cpr_efuse(cpr_vreg);
+ rc = cpr_init_cpr_efuse(pdev, cpr_vreg);
if (rc)
return rc;
@@ -1287,44 +1340,41 @@
return 0;
}
-static int __init cpr_pvs_parse_dt(struct platform_device *pdev,
- struct cpr_regulator *cpr_vreg)
+static int __devinit cpr_efuse_init(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
+{
+ struct resource *res;
+ int len;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse_addr");
+ if (!res || !res->start) {
+ pr_err("efuse_addr missing: res=%p\n", res);
+ return -EINVAL;
+ }
+ cpr_vreg->efuse_addr = res->start;
+ len = res->end - res->start + 1;
+
+ pr_info("efuse_addr = 0x%x (len=0x%x)\n", res->start, len);
+
+ cpr_vreg->efuse_base = ioremap(cpr_vreg->efuse_addr, len);
+ if (!cpr_vreg->efuse_base) {
+ pr_err("Unable to map efuse_addr 0x%08x\n",
+ cpr_vreg->efuse_addr);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void cpr_efuse_free(struct cpr_regulator *cpr_vreg)
+{
+ iounmap(cpr_vreg->efuse_base);
+}
+
+static int __devinit cpr_voltage_plan_init(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
{
struct device_node *of_node = pdev->dev.of_node;
- struct resource *res;
- int rc;
- size_t pvs_bins;
-
- /* Parse process voltage parameters */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pvs_efuse");
- if (!res || !res->start) {
- pr_err("pvs_efuse missing: res=%p\n", res);
- return -EINVAL;
- }
- cpr_vreg->pvs_efuse = res->start;
-
- rc = of_property_read_u32(of_node, "qcom,num-efuse-bits",
- &cpr_vreg->num_efuse_bits);
- if (rc < 0) {
- pr_err("num-efuse-bits missing: rc=%d\n", rc);
- return rc;
- }
-
- if (cpr_vreg->num_efuse_bits == 0 ||
- cpr_vreg->num_efuse_bits > CPR_PVS_EFUSE_BITS_MAX) {
- pr_err("invalid num-efuse-bits : %d\n",
- cpr_vreg->num_efuse_bits);
- return -EINVAL;
- }
-
- pvs_bins = 1 << cpr_vreg->num_efuse_bits;
- rc = of_property_read_u32_array(of_node, "qcom,pvs-bin-process",
- cpr_vreg->pvs_bin_process,
- pvs_bins);
- if (rc < 0) {
- pr_err("pvs-bin-process missing: rc=%d\n", rc);
- return rc;
- }
+ int rc, i;
rc = of_property_read_u32_array(of_node,
"qcom,pvs-corner-ceiling-slow",
@@ -1353,6 +1403,17 @@
return rc;
}
+ /* Set ceiling max and use it for APC_PVS_NO */
+ cpr_vreg->ceiling_max =
+ cpr_vreg->pvs_corner_v[APC_PVS_SLOW][CPR_CORNER_TURBO];
+
+ for (i = APC_PVS_SLOW; i < NUM_APC_PVS; i++) {
+ pr_info("[%d] [%d %d %d] uV\n", i,
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_SVS],
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_NORMAL],
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_TURBO]);
+ }
+
return 0;
}
@@ -1386,31 +1447,39 @@
return -ENOMEM;
}
- rc = cpr_pvs_parse_dt(pdev, cpr_vreg);
+ rc = cpr_efuse_init(pdev, cpr_vreg);
if (rc) {
- pr_err("Wrong DT parameter specified: rc=%d\n", rc);
+ pr_err("Wrong eFuse address specified: rc=%d\n", rc);
return rc;
}
- rc = cpr_pvs_init(cpr_vreg);
+ rc = cpr_voltage_plan_init(pdev, cpr_vreg);
+ if (rc) {
+ pr_err("Wrong DT parameter specified: rc=%d\n", rc);
+ goto err_out;
+ }
+
+ rc = cpr_pvs_init(pdev, cpr_vreg);
if (rc) {
pr_err("Initialize PVS wrong: rc=%d\n", rc);
- return rc;
+ goto err_out;
}
rc = cpr_apc_init(pdev, cpr_vreg);
if (rc) {
if (rc != -EPROBE_DEFER)
pr_err("Initialize APC wrong: rc=%d\n", rc);
- return rc;
+ goto err_out;
}
rc = cpr_init_cpr(pdev, cpr_vreg);
if (rc) {
pr_err("Initialize CPR failed: rc=%d\n", rc);
- return rc;
+ goto err_out;
}
+ cpr_efuse_free(cpr_vreg);
+
mutex_init(&cpr_vreg->cpr_mutex);
rdesc = &cpr_vreg->rdesc;
@@ -1433,6 +1502,10 @@
the_cpr = cpr_vreg;
return 0;
+
+err_out:
+ cpr_efuse_free(cpr_vreg);
+ return rc;
}
static int __devexit cpr_regulator_remove(struct platform_device *pdev)
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index 2e70c83..231a7e0 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -402,8 +402,7 @@
per_cpu(cpufreq_suspend, cpu).device_suspended = 0;
}
- msm_cpufreq_wq = alloc_workqueue("msm-cpufreq",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
+ msm_cpufreq_wq = alloc_workqueue("msm-cpufreq", WQ_HIGHPRI, 0);
register_hotcpu_notifier(&msm_cpufreq_cpu_notifier);
return cpufreq_register_driver(&msm_cpufreq_driver);
diff --git a/arch/arm/mach-msm/hsic_sysmon.c b/arch/arm/mach-msm/hsic_sysmon.c
index 8270197..79fb1a3 100644
--- a/arch/arm/mach-msm/hsic_sysmon.c
+++ b/arch/arm/mach-msm/hsic_sysmon.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
@@ -239,7 +239,7 @@
if (!hs)
continue;
- ret += scnprintf(buf, DEBUG_BUF_SIZE,
+ ret += scnprintf(buf + ret, DEBUG_BUF_SIZE - ret,
"---HSIC Sysmon #%d---\n"
"epin:%d, epout:%d\n"
"bytes to host: %d\n"
@@ -324,15 +324,6 @@
struct usb_endpoint_descriptor *ep_desc;
int i;
int ret = -ENOMEM;
- __u8 ifc_num;
-
- pr_debug("id:%lu", id->driver_info);
-
- ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
-
- /* is this the interface we're looking for? */
- if (ifc_num != id->driver_info)
- return -ENODEV;
hs = kzalloc(sizeof(*hs), GFP_KERNEL);
if (!hs) {
@@ -367,12 +358,17 @@
goto error;
}
- hs->id = HSIC_SYSMON_DEV_EXT_MODEM;
- hsic_sysmon_devices[HSIC_SYSMON_DEV_EXT_MODEM] = hs;
+ hs->id = HSIC_SYSMON_DEV_EXT_MODEM + id->driver_info;
+ if (hs->id >= NUM_HSIC_SYSMON_DEVS) {
+ pr_warn("invalid dev id(%d)", hs->id);
+ hs->id = 0;
+ }
+
+ hsic_sysmon_devices[hs->id] = hs;
usb_set_intfdata(ifc, hs);
hs->pdev.name = "sys_mon";
- hs->pdev.id = SYSMON_SS_EXT_MODEM;
+ hs->pdev.id = SYSMON_SS_EXT_MODEM + hs->id;
hs->pdev.dev.release = hsic_sysmon_pdev_release;
platform_device_register(&hs->pdev);
@@ -406,11 +402,12 @@
return 0;
}
-/* driver_info maps to the interface number corresponding to sysmon */
+/* driver_info is the instance number when multiple devices are present */
static const struct usb_device_id hsic_sysmon_ids[] = {
- { USB_DEVICE(0x5c6, 0x9048), .driver_info = 1, },
- { USB_DEVICE(0x5c6, 0x904C), .driver_info = 1, },
- { USB_DEVICE(0x5c6, 0x9075), .driver_info = 1, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 1), .driver_info = 0, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904C, 1), .driver_info = 0, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 1), .driver_info = 0, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 1), .driver_info = 1, },
{} /* terminating entry */
};
MODULE_DEVICE_TABLE(usb, hsic_sysmon_ids);
diff --git a/arch/arm/mach-msm/hsic_sysmon.h b/arch/arm/mach-msm/hsic_sysmon.h
index 983f464..9655dc0 100644
--- a/arch/arm/mach-msm/hsic_sysmon.h
+++ b/arch/arm/mach-msm/hsic_sysmon.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
@@ -18,6 +18,7 @@
*/
enum hsic_sysmon_device_id {
HSIC_SYSMON_DEV_EXT_MODEM,
+ HSIC_SYSMON_DEV_EXT_MODEM_2,
NUM_HSIC_SYSMON_DEVS
};
diff --git a/arch/arm/mach-msm/hsic_sysmon_test.c b/arch/arm/mach-msm/hsic_sysmon_test.c
index bc60c6e..fac6575 100644
--- a/arch/arm/mach-msm/hsic_sysmon_test.c
+++ b/arch/arm/mach-msm/hsic_sysmon_test.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
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
@@ -36,13 +37,14 @@
size_t count, loff_t *ppos)
{
struct sysmon_test_dev *dev = sysmon_dev;
+ enum hsic_sysmon_device_id id =
+ (enum hsic_sysmon_device_id)file->private_data;
int ret;
if (!dev)
return -ENODEV;
- ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, dev->buf, RD_BUF_SIZE,
- &dev->buflen, 3000);
+ ret = hsic_sysmon_read(id, dev->buf, RD_BUF_SIZE, &dev->buflen, 3000);
if (!ret)
return simple_read_from_buffer(ubuf, count, ppos,
dev->buf, dev->buflen);
@@ -53,7 +55,9 @@
static ssize_t sysmon_test_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
- struct sysmon_test_dev *dev = sysmon_dev;
+ struct sysmon_test_dev *dev = sysmon_dev;
+ enum hsic_sysmon_device_id id =
+ (enum hsic_sysmon_device_id)file->private_data;
int ret;
if (!dev)
@@ -64,8 +68,7 @@
return 0;
}
- ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM,
- dev->buf, count, 1000);
+ ret = hsic_sysmon_write(id, dev->buf, count, 1000);
if (ret < 0) {
pr_err("error writing to hsic_sysmon");
return ret;
@@ -76,38 +79,44 @@
static int sysmon_test_open(struct inode *inode, struct file *file)
{
- return hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM);
+ file->private_data = inode->i_private;
+ return hsic_sysmon_open((enum hsic_sysmon_device_id)inode->i_private);
}
static int sysmon_test_release(struct inode *inode, struct file *file)
{
- hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
+ hsic_sysmon_close((enum hsic_sysmon_device_id)inode->i_private);
return 0;
}
-static struct dentry *dfile;
-const struct file_operations sysmon_test_ops = {
+static const struct file_operations sysmon_test_ops = {
.read = sysmon_test_read,
.write = sysmon_test_write,
.open = sysmon_test_open,
.release = sysmon_test_release
};
+static struct dentry *dfile0, *dfile1;
+
static int __init sysmon_test_init(void)
{
sysmon_dev = kzalloc(sizeof(*sysmon_dev), GFP_KERNEL);
if (!sysmon_dev)
return -ENOMEM;
- dfile = debugfs_create_file("hsic_sysmon_test", 0666, NULL,
- 0, &sysmon_test_ops);
+ dfile0 = debugfs_create_file("hsic_sysmon_test.0", 0666, NULL,
+ (void *)HSIC_SYSMON_DEV_EXT_MODEM, &sysmon_test_ops);
+ dfile1 = debugfs_create_file("hsic_sysmon_test.1", 0666, NULL,
+ (void *)HSIC_SYSMON_DEV_EXT_MODEM_2, &sysmon_test_ops);
return 0;
}
static void __exit sysmon_test_exit(void)
{
- if (dfile)
- debugfs_remove(dfile);
+ if (dfile0)
+ debugfs_remove(dfile0);
+ if (dfile1)
+ debugfs_remove(dfile1);
kfree(sysmon_dev);
}
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index 5d25134..f8a32b4 100644
--- a/arch/arm/mach-msm/idle-v7.S
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -25,6 +25,7 @@
#ifdef CONFIG_MSM_SCM
#define SCM_SVC_BOOT 0x1
#define SCM_CMD_TERMINATE_PC 0x2
+#define SCM_CMD_CORE_HOTPLUGGED 0x10
#endif
ENTRY(msm_arch_idle)
@@ -99,7 +100,7 @@
#ifdef CONFIG_MSM_SCM
ldr r0, =SCM_SVC_BOOT
ldr r1, =SCM_CMD_TERMINATE_PC
- mov r2, #0
+ ldr r2, =SCM_CMD_CORE_HOTPLUGGED
bl scm_call_atomic1
#else
mrc p15, 0, r3, c1, c0, 0 /* read current CR */
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 7b26bd6..6370bd4 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -183,7 +183,7 @@
};
struct msm_camera_gpio_num_info {
- uint16_t gpio_num[2];
+ uint16_t gpio_num[7];
};
struct msm_camera_gpio_conf {
diff --git a/arch/arm/mach-msm/include/mach/clk-provider.h b/arch/arm/mach-msm/include/mach/clk-provider.h
index 27c6df4..72b5cc1 100644
--- a/arch/arm/mach-msm/include/mach/clk-provider.h
+++ b/arch/arm/mach-msm/include/mach/clk-provider.h
@@ -108,11 +108,13 @@
int (*in_hwcg_mode)(struct clk *clk);
enum handoff (*handoff)(struct clk *clk);
int (*reset)(struct clk *clk, enum clk_reset_action action);
+ int (*pre_set_rate)(struct clk *clk, unsigned long new_rate);
int (*set_rate)(struct clk *clk, unsigned long rate);
+ void (*post_set_rate)(struct clk *clk, unsigned long old_rate);
int (*set_max_rate)(struct clk *clk, unsigned long rate);
int (*set_flags)(struct clk *clk, unsigned flags);
unsigned long (*get_rate)(struct clk *clk);
- int (*list_rate)(struct clk *clk, unsigned n);
+ long (*list_rate)(struct clk *clk, unsigned n);
int (*is_enabled)(struct clk *clk);
long (*round_rate)(struct clk *clk, unsigned long rate);
int (*set_parent)(struct clk *clk, struct clk *parent);
diff --git a/arch/arm/mach-msm/include/mach/clock-generic.h b/arch/arm/mach-msm/include/mach/clock-generic.h
index 0f689f1..f6feda0 100644
--- a/arch/arm/mach-msm/include/mach/clock-generic.h
+++ b/arch/arm/mach-msm/include/mach/clock-generic.h
@@ -72,8 +72,6 @@
struct clk_div_ops {
int (*set_div)(struct div_clk *clk, int div);
int (*get_div)(struct div_clk *clk);
-
- /* Optional */
bool (*is_enabled)(struct div_clk *clk);
int (*enable)(struct div_clk *clk);
void (*disable)(struct div_clk *clk);
@@ -84,6 +82,7 @@
unsigned int min_div;
unsigned int max_div;
unsigned long rate_margin;
+ /* Optional */
struct clk_div_ops *ops;
/* Fields not used by helper function. */
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index eeda7ce..c979e0c 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -189,22 +189,39 @@
};
enum dump_reg {
- DUMP_REG_FAR0,
+ DUMP_REG_FIRST,
+ DUMP_REG_FAR0 = DUMP_REG_FIRST,
DUMP_REG_FAR1,
DUMP_REG_PAR0,
DUMP_REG_PAR1,
DUMP_REG_FSR,
DUMP_REG_FSYNR0,
DUMP_REG_FSYNR1,
- DUMP_REG_TTBR0,
- DUMP_REG_TTBR1,
+ DUMP_REG_TTBR0_0,
+ DUMP_REG_TTBR0_1,
+ DUMP_REG_TTBR1_0,
+ DUMP_REG_TTBR1_1,
DUMP_REG_SCTLR,
DUMP_REG_ACTLR,
DUMP_REG_PRRR,
+ DUMP_REG_MAIR0 = DUMP_REG_PRRR,
DUMP_REG_NMRR,
+ DUMP_REG_MAIR1 = DUMP_REG_NMRR,
MAX_DUMP_REGS,
};
+struct dump_regs_tbl {
+ /*
+ * To keep things context-bank-agnostic, we only store the CB
+ * register offset in `key'
+ */
+ unsigned long key;
+ const char *name;
+ int offset;
+ int must_be_present;
+};
+extern struct dump_regs_tbl dump_regs_tbl[MAX_DUMP_REGS];
+
#define COMBINE_DUMP_REG(upper, lower) (((u64) upper << 32) | lower)
struct msm_iommu_context_reg {
diff --git a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
index 1c20d04..04cd441 100644
--- a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
+++ b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
@@ -19,6 +19,8 @@
#define GET_GLOBAL_REG(reg, base) (readl_relaxed((base) + (reg)))
#define GET_CTX_REG(reg, base, ctx) \
(readl_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
+#define GET_CTX_REG_L(reg, base, ctx) \
+ (readll_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
#define SET_GLOBAL_REG(reg, base, val) writel_relaxed((val), ((base) + (reg)))
@@ -196,7 +198,7 @@
#define GET_CONTEXTIDR(b, c) GET_CTX_REG(CB_CONTEXTIDR, (b), (c))
#define GET_PRRR(b, c) GET_CTX_REG(CB_PRRR, (b), (c))
#define GET_NMRR(b, c) GET_CTX_REG(CB_NMRR, (b), (c))
-#define GET_PAR(b, c) GET_CTX_REG(CB_PAR, (b), (c))
+#define GET_PAR(b, c) GET_CTX_REG_L(CB_PAR, (b), (c))
#define GET_FSR(b, c) GET_CTX_REG(CB_FSR, (b), (c))
#define GET_FSRRESTORE(b, c) GET_CTX_REG(CB_FSRRESTORE, (b), (c))
#define GET_FAR(b, c) GET_CTX_REG(CB_FAR, (b), (c))
@@ -1307,6 +1309,7 @@
#define CB_PAR_TF (CB_PAR_TF_MASK << CB_PAR_TF_SHIFT)
#define CB_PAR_AFF (CB_PAR_AFF_MASK << CB_PAR_AFF_SHIFT)
#define CB_PAR_PF (CB_PAR_PF_MASK << CB_PAR_PF_SHIFT)
+#define CB_PAR_EF (CB_PAR_EF_MASK << CB_PAR_EF_SHIFT)
#define CB_PAR_TLBMCF (CB_PAR_TLBMCF_MASK << CB_PAR_TLBMCF_SHIFT)
#define CB_PAR_TLBLKF (CB_PAR_TLBLKF_MASK << CB_PAR_TLBLKF_SHIFT)
#define CB_PAR_ATOT (CB_PAR_ATOT_MASK << CB_PAR_ATOT_SHIFT)
@@ -1682,11 +1685,12 @@
#define CB_PAR_TF_MASK 0x01
#define CB_PAR_AFF_MASK 0x01
#define CB_PAR_PF_MASK 0x01
+#define CB_PAR_EF_MASK 0x01
#define CB_PAR_TLBMCF_MASK 0x01
#define CB_PAR_TLBLKF_MASK 0x01
-#define CB_PAR_ATOT_MASK 0x01
-#define CB_PAR_PLVL_MASK 0x03
-#define CB_PAR_STAGE_MASK 0x01
+#define CB_PAR_ATOT_MASK 0x01ULL
+#define CB_PAR_PLVL_MASK 0x03ULL
+#define CB_PAR_STAGE_MASK 0x01ULL
/* Primary Region Remap Register: CB_PRRR */
#define CB_PRRR_TR0_MASK 0x03
@@ -2052,11 +2056,12 @@
#define CB_PAR_TF_SHIFT 1
#define CB_PAR_AFF_SHIFT 2
#define CB_PAR_PF_SHIFT 3
+#define CB_PAR_EF_SHIFT 4
#define CB_PAR_TLBMCF_SHIFT 5
#define CB_PAR_TLBLKF_SHIFT 6
#define CB_PAR_ATOT_SHIFT 31
-#define CB_PAR_PLVL_SHIFT 0
-#define CB_PAR_STAGE_SHIFT 3
+#define CB_PAR_PLVL_SHIFT 32
+#define CB_PAR_STAGE_SHIFT 35
/* Primary Region Remap Register: CB_PRRR */
#define CB_PRRR_TR0_SHIFT 0
diff --git a/arch/arm/mach-msm/include/mach/ipa.h b/arch/arm/mach-msm/include/mach/ipa.h
index 697de5e..b6acef2 100644
--- a/arch/arm/mach-msm/include/mach/ipa.h
+++ b/arch/arm/mach-msm/include/mach/ipa.h
@@ -648,6 +648,8 @@
int a2_mux_write(enum a2_mux_logical_channel_id lcid, struct sk_buff *skb);
+int a2_mux_is_ch_empty(enum a2_mux_logical_channel_id lcid);
+
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);
@@ -667,6 +669,8 @@
int teth_bridge_set_aggr_params(struct teth_aggr_params *aggr_params);
+void ipa_bam_reg_dump(void);
+
#else /* CONFIG_IPA */
static inline int a2_mux_open_channel(enum a2_mux_logical_channel_id lcid,
@@ -686,6 +690,11 @@
return -EPERM;
}
+static inline int a2_mux_is_ch_empty(enum a2_mux_logical_channel_id lcid)
+{
+ return -EPERM;
+}
+
static inline int a2_mux_is_ch_low(enum a2_mux_logical_channel_id lcid)
{
return -EPERM;
@@ -1099,6 +1108,11 @@
return -EPERM;
}
+static inline void ipa_bam_reg_dump(void)
+{
+ return;
+}
+
#endif /* CONFIG_IPA*/
#endif /* _IPA_H_ */
diff --git a/arch/arm/mach-msm/include/mach/jtag.h b/arch/arm/mach-msm/include/mach/jtag.h
index 2131be6..ae006be 100644
--- a/arch/arm/mach-msm/include/mach/jtag.h
+++ b/arch/arm/mach-msm/include/mach/jtag.h
@@ -16,9 +16,11 @@
#if defined(CONFIG_MSM_JTAG) || defined(CONFIG_MSM_JTAG_MM)
extern void msm_jtag_save_state(void);
extern void msm_jtag_restore_state(void);
+extern bool msm_jtag_fuse_apps_access_disabled(void);
#else
static inline void msm_jtag_save_state(void) {}
static inline void msm_jtag_restore_state(void) {}
+static inline bool msm_jtag_fuse_apps_access_disabled(void) { return false; }
#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_hdmi_audio_codec.h b/arch/arm/mach-msm/include/mach/msm_hdmi_audio_codec.h
index 95f33d5..ff3da11 100644
--- a/arch/arm/mach-msm/include/mach/msm_hdmi_audio_codec.h
+++ b/arch/arm/mach-msm/include/mach/msm_hdmi_audio_codec.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
@@ -25,7 +25,8 @@
struct msm_hdmi_audio_codec_ops {
int (*audio_info_setup)(struct platform_device *pdev,
- u32 num_of_channels, u32 channel_allocation, u32 level_shift,
+ u32 sample_rate, u32 num_of_channels,
+ u32 channel_allocation, u32 level_shift,
bool down_mix);
int (*get_audio_edid_blk) (struct platform_device *pdev,
struct msm_hdmi_audio_edid_blk *blk);
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
index 0a91719..b3bf07c 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
@@ -115,6 +115,20 @@
*/
int ipc_log_string(void *ilctxt, const char *fmt, ...) __printf(2, 3);
+/**
+ * ipc_log_extract - Reads and deserializes log
+ *
+ * @ilctxt: logging context
+ * @buff: buffer to receive the data
+ * @size: size of the buffer
+ * @returns: 0 if no data read; >0 number of bytes read; < 0 error
+ *
+ * If no data is available to be read, then the ilctxt::read_avail
+ * completion is reinitialized. This allows clients to block
+ * until new log data is save.
+ */
+int ipc_log_extract(void *ilctxt, char *buff, int size);
+
/*
* Print a string to decode context.
* @dctxt Decode context
@@ -220,6 +234,9 @@
static inline int ipc_log_string(void *ilctxt, const char *fmt, ...)
{ return -EINVAL; }
+static inline int ipc_log_extract(void *ilctxt, char *buff, int size)
+{ return -EINVAL; }
+
#define IPC_SPRINTF_DECODE(dctxt, args...) do { } while (0)
static inline void tsv_timestamp_read(struct encode_context *ectxt,
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_router.h b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
index a87380c..f5f1954 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_router.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
@@ -29,6 +29,7 @@
enum msm_ipc_router_event {
MSM_IPC_ROUTER_READ_CB = 0,
MSM_IPC_ROUTER_WRITE_DONE,
+ MSM_IPC_ROUTER_RESUME_TX,
};
struct comm_mode_info {
diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h
index 5c8f525..95c4fe3 100644
--- a/arch/arm/mach-msm/include/mach/msm_memtypes.h
+++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h
@@ -79,5 +79,4 @@
int __init dt_scan_for_memory_hole(unsigned long node, const char *uname,
int depth, void *data);
void adjust_meminfo(unsigned long start, unsigned long size);
-unsigned long __init reserve_memory_for_fmem(unsigned long, unsigned long);
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_qmi_interface.h b/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
index 11867f3..1641e8c 100644
--- a/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
+++ b/arch/arm/mach-msm/include/mach/msm_qmi_interface.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
@@ -20,6 +20,7 @@
#include <linux/socket.h>
#include <linux/gfp.h>
#include <linux/qmi_encdec.h>
+#include <linux/workqueue.h>
#define QMI_COMMON_TLV_TYPE 0
@@ -45,6 +46,8 @@
void *ind_cb_priv;
int handle_reset;
wait_queue_head_t reset_waitq;
+ struct list_head pending_txn_list;
+ struct delayed_work resume_tx_work;
};
enum qmi_result_type_v01 {
@@ -153,7 +156,8 @@
void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
- void *resp_cb_data),
+ void *resp_cb_data,
+ int stat),
void *resp_cb_data);
/**
diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h
index 2cc7b10..62fada1 100644
--- a/arch/arm/mach-msm/include/mach/msm_smd.h
+++ b/arch/arm/mach-msm/include/mach/msm_smd.h
@@ -134,25 +134,10 @@
int disable_smsm_reset_handshake;
};
-/*
- * Shared Memory Regions
- *
- * the array of these regions is expected to be in ascending order by phys_addr
- *
- * @phys_addr: physical base address of the region
- * @size: size of the region in bytes
- */
-struct smd_smem_regions {
- phys_addr_t phys_addr;
- resource_size_t size;
-};
-
struct smd_platform {
uint32_t num_ss_configs;
struct smd_subsystem_config *smd_ss_configs;
struct smd_subsystem_restart_config *smd_ssr_config;
- uint32_t num_smem_areas;
- struct smd_smem_regions *smd_smem_areas;
};
#ifdef CONFIG_MSM_SMD
@@ -322,24 +307,6 @@
*/
int smd_is_pkt_avail(smd_channel_t *ch);
-/**
- * smd_module_init_notifier_register() - Register a smd module
- * init notifier block
- * @nb: Notifier block to be registered
- *
- * In order to mark the dependency on SMD Driver module initialization
- * register a notifier using this API. Once the smd module_init is
- * done, notification will be passed to the registered module.
- */
-int smd_module_init_notifier_register(struct notifier_block *nb);
-
-/**
- * smd_module_init_notifier_register() - Unregister a smd module
- * init notifier block
- * @nb: Notifier block to be registered
- */
-int smd_module_init_notifier_unregister(struct notifier_block *nb);
-
/*
* SMD initialization function that registers for a SMD platform driver.
*
@@ -474,16 +441,6 @@
return -ENODEV;
}
-static inline int smd_module_init_notifier_register(struct notifier_block *nb)
-{
- return -ENODEV;
-}
-
-static inline int smd_module_init_notifier_unregister(struct notifier_block *nb)
-{
- return -ENODEV;
-}
-
static inline int __init msm_smd_init(void)
{
return 0;
diff --git a/arch/arm/mach-msm/include/mach/msm_smem.h b/arch/arm/mach-msm/include/mach/msm_smem.h
index 57f22cc..64ab6bf 100644
--- a/arch/arm/mach-msm/include/mach/msm_smem.h
+++ b/arch/arm/mach-msm/include/mach/msm_smem.h
@@ -144,6 +144,20 @@
void *smem_alloc2(unsigned id, unsigned size_in);
void *smem_get_entry(unsigned id, unsigned *size);
void *smem_find(unsigned id, unsigned size);
+
+/**
+ * smem_get_entry_no_rlock - Get existing item without using remote spinlock
+ *
+ * @id: ID of SMEM item
+ * @size_out: Pointer to size variable for storing the result
+ * @returns: Pointer to SMEM item or NULL if it doesn't exist
+ *
+ * This function does not lock the remote spinlock and should only be used in
+ * failure-recover cases such as retrieving the subsystem failure reason during
+ * subsystem restart.
+ */
+void *smem_get_entry_no_rlock(unsigned id, unsigned *size_out);
+
/**
* smem_virt_to_phys() - Convert SMEM address to physical address.
*
@@ -155,6 +169,13 @@
*/
phys_addr_t smem_virt_to_phys(void *smem_address);
+/**
+ * SMEM initialization function that registers for a SMEM platform driver.
+ *
+ * @returns: success on successful driver registration.
+ */
+int __init msm_smem_init(void);
+
#else
static inline void *smem_alloc(unsigned id, unsigned size)
{
@@ -172,9 +193,17 @@
{
return NULL;
}
+void *smem_get_entry_no_rlock(unsigned id, unsigned *size_out)
+{
+ return NULL;
+}
static inline phys_addr_t smem_virt_to_phys(void *smem_address)
{
return (phys_addr_t) NULL;
}
+static int __init msm_smem_init(void)
+{
+ return 0;
+}
#endif /* CONFIG_MSM_SMD */
#endif /* _ARCH_ARM_MACH_MSM_SMEM_H_ */
diff --git a/arch/arm/mach-msm/include/mach/msm_tspp.h b/arch/arm/mach-msm/include/mach/msm_tspp.h
index ddc99f3..2c40d83 100644
--- a/arch/arm/mach-msm/include/mach/msm_tspp.h
+++ b/arch/arm/mach-msm/include/mach/msm_tspp.h
@@ -25,7 +25,7 @@
struct tspp_data_descriptor {
void *virt_base; /* logical address of the actual data */
- u32 phys_base; /* physical address of the actual data */
+ phys_addr_t phys_base; /* physical address of the actual data */
u32 size; /* size of buffer in bytes */
int id; /* unique identifier */
void *user; /* user-defined data */
@@ -33,9 +33,9 @@
typedef void (tspp_notifier)(int channel_id, void *user);
typedef void* (tspp_allocator)(int channel_id, u32 size,
- u32 *phys_base, void *user);
+ phys_addr_t *phys_base, void *user);
typedef void (tspp_memfree)(int channel_id, u32 size,
- void *virt_base, u32 phys_base, void *user);
+ void *virt_base, phys_addr_t phys_base, void *user);
/* Kernel API functions */
int tspp_open_stream(u32 dev, u32 channel_id,
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 6f83c53..32d58d4 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -174,8 +174,8 @@
/* Request Power State */
unsigned power_state;
struct ocmem_eviction_data *edata;
- /* Request that triggered eviction */
- struct ocmem_req *e_handle;
+ /* Eviction data of the request being evicted */
+ struct ocmem_eviction_data *eviction_info;
};
struct ocmem_handle {
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
index 2cfb5ac..661d496 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.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
@@ -53,6 +53,12 @@
/* Encoder/decoder configuration block */
#define USM_PARAM_ID_ENCDEC_ENC_CFG_BLK 0x0001230D
+/* Max number of static located ports (bytes) */
+#define USM_MAX_PORT_NUMBER 8
+
+/* Max number of static located transparent data (bytes) */
+#define USM_MAX_CFG_DATA_SIZE 100
+
/* Parameter structures used in USM_STREAM_CMD_SET_ENCDEC_PARAM command */
/* common declarations */
struct usm_cfg_common {
@@ -60,26 +66,7 @@
u16 bits_per_sample;
u32 sample_rate;
u32 dev_id;
- u32 data_map;
-} __packed;
-
-/* Max number of static located transparent data (bytes) */
-#define USM_MAX_CFG_DATA_SIZE 100
-struct usm_encode_cfg_blk {
- u32 frames_per_buf;
- u32 format_id;
- /* <cfg_size> = sizeof(usm_cfg_common)+|tarnsp_data| */
- u32 cfg_size;
- struct usm_cfg_common cfg_common;
- /* Transparent configuration data for specific encoder */
- u8 transp_data[USM_MAX_CFG_DATA_SIZE];
-} __packed;
-
-struct usm_stream_cmd_encdec_cfg_blk {
- struct apr_hdr hdr;
- u32 param_id;
- u32 param_size;
- struct usm_encode_cfg_blk enc_blk;
+ u8 data_map[USM_MAX_PORT_NUMBER];
} __packed;
struct us_encdec_cfg {
@@ -89,16 +76,6 @@
u8 *params;
} __packed;
-struct usm_stream_media_format_update {
- struct apr_hdr hdr;
- u32 format_id;
- /* <cfg_size> = sizeof(usm_cfg_common)+|tarnsp_data| */
- u32 cfg_size;
- struct usm_cfg_common cfg_common;
- /* Transparent configuration data for specific encoder */
- u8 transp_data[USM_MAX_CFG_DATA_SIZE];
-} __packed;
-
/* Start/stop US signal detection */
#define USM_SESSION_CMD_SIGNAL_DETECT_MODE 0x00012719
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
index 4008698..782f3ae 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.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
@@ -56,4 +56,43 @@
#define USM_DATA_EVENT_WRITE_DONE 0x00011274
+/* Max number of static located ports (bytes) */
+#define USM_MAX_PORT_NUMBER_A 4
+
+/* Parameter structures used in USM_STREAM_CMD_SET_ENCDEC_PARAM command */
+/* common declarations */
+struct usm_cfg_common_a {
+ u16 ch_cfg;
+ u16 bits_per_sample;
+ u32 sample_rate;
+ u32 dev_id;
+ u8 data_map[USM_MAX_PORT_NUMBER_A];
+} __packed;
+
+struct usm_stream_media_format_update {
+ struct apr_hdr hdr;
+ u32 format_id;
+ /* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+ u32 cfg_size;
+ struct usm_cfg_common_a cfg_common;
+ /* Transparent configuration data for specific encoder */
+ u8 transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_encode_cfg_blk {
+ u32 frames_per_buf;
+ u32 format_id;
+ /* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+ u32 cfg_size;
+ struct usm_cfg_common_a cfg_common;
+ /* Transparent configuration data for specific encoder */
+ u8 transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_stream_cmd_encdec_cfg_blk {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct usm_encode_cfg_blk enc_blk;
+} __packed;
#endif /* __APR_US_A_H__ */
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
index 11de6ef..26e8369 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
@@ -67,4 +67,30 @@
#define USM_DATA_EVENT_WRITE_DONE 0x00012727
+struct usm_stream_media_format_update {
+ struct apr_hdr hdr;
+ u32 format_id;
+ /* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+ u32 cfg_size;
+ struct usm_cfg_common cfg_common;
+ /* Transparent configuration data for specific encoder */
+ u8 transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_encode_cfg_blk {
+ u32 frames_per_buf;
+ u32 format_id;
+ /* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+ u32 cfg_size;
+ struct usm_cfg_common cfg_common;
+ /* Transparent configuration data for specific encoder */
+ u8 transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_stream_cmd_encdec_cfg_blk {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct usm_encode_cfg_blk enc_blk;
+} __packed;
#endif /* __APR_US_B_H__ */
diff --git a/arch/arm/mach-msm/include/mach/smem_log.h b/arch/arm/mach-msm/include/mach/smem_log.h
index 992ab05..a429bc3 100644
--- a/arch/arm/mach-msm/include/mach/smem_log.h
+++ b/arch/arm/mach-msm/include/mach/smem_log.h
@@ -1,4 +1,4 @@
-/* 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
@@ -51,6 +51,9 @@
#define SMEM_LOG_DCVS_EVENT_BASE 0x00070000
#define SMEM_LOG_SLEEP_EVENT_BASE 0x00080000
#define SMEM_LOG_RPC_ROUTER_EVENT_BASE 0x00090000
+#define SMEM_LOG_IPC_ROUTER_EVENT_BASE 0x000D0000
+#define SMEM_LOG_QMI_CCI_EVENT_BASE 0x000E0000
+#define SMEM_LOG_QMI_CSI_EVENT_BASE 0x000F0000
#if defined(CONFIG_MSM_N_WAY_SMSM)
#define DEM_SMSM_ISR (SMEM_LOG_DEM_EVENT_BASE + 0x1)
#define DEM_STATE_CHANGE (SMEM_LOG_DEM_EVENT_BASE + 0x2)
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index d52686c..830992c 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -32,6 +32,7 @@
#define SOCINFO_VERSION_MINOR(ver) (ver & 0x0000ffff)
#ifdef CONFIG_OF
+#define of_board_is_cdp() of_machine_is_compatible("qcom,cdp")
#define of_board_is_sim() of_machine_is_compatible("qcom,sim")
#define of_board_is_rumi() of_machine_is_compatible("qcom,rumi")
#define of_board_is_fluid() of_machine_is_compatible("qcom,fluid")
@@ -115,6 +116,9 @@
MSM_CPU_7X27AA,
MSM_CPU_9615,
MSM_CPU_8974,
+ MSM_CPU_8974PRO_AA,
+ MSM_CPU_8974PRO_AB,
+ MSM_CPU_8974PRO_AC,
MSM_CPU_8627,
MSM_CPU_8625,
MSM_CPU_9625,
@@ -434,6 +438,42 @@
#endif
}
+static inline int cpu_is_msm8974pro_aa(void)
+{
+#ifdef CONFIG_ARCH_MSM8974
+ enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+ BUG_ON(cpu == MSM_CPU_UNKNOWN);
+ return cpu == MSM_CPU_8974PRO_AA;
+#else
+ return 0;
+#endif
+}
+
+static inline int cpu_is_msm8974pro_ab(void)
+{
+#ifdef CONFIG_ARCH_MSM8974
+ enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+ BUG_ON(cpu == MSM_CPU_UNKNOWN);
+ return cpu == MSM_CPU_8974PRO_AB;
+#else
+ return 0;
+#endif
+}
+
+static inline int cpu_is_msm8974pro_ac(void)
+{
+#ifdef CONFIG_ARCH_MSM8974
+ enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+ BUG_ON(cpu == MSM_CPU_UNKNOWN);
+ return cpu == MSM_CPU_8974PRO_AC;
+#else
+ return 0;
+#endif
+}
+
static inline int cpu_is_mpq8092(void)
{
#ifdef CONFIG_ARCH_MPQ8092
@@ -499,4 +539,10 @@
cpu_is_msm8627();
}
+static inline int soc_class_is_msm8974(void)
+{
+ return cpu_is_msm8974() || cpu_is_msm8974pro_aa() ||
+ cpu_is_msm8974pro_ab() || cpu_is_msm8974pro_ac();
+}
+
#endif
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index a3e993d..b2dd49c 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -198,14 +198,6 @@
struct usb_bam_connect_ipa_params *ipa_params);
/**
- * Wait for Consumer granted from Resource Manager.
- *
- * @ipa_params - in/out parameters
- *
- */
-void usb_bam_wait_for_cons_granted(
- struct usb_bam_connect_ipa_params *ipa_params);
-/**
* Register a wakeup callback from peer BAM.
*
* @idx - Connection index.
diff --git a/arch/arm/mach-msm/include/mach/usb_bridge.h b/arch/arm/mach-msm/include/mach/usb_bridge.h
index c62cf01..b3b7d71 100644
--- a/arch/arm/mach-msm/include/mach/usb_bridge.h
+++ b/arch/arm/mach-msm/include/mach/usb_bridge.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
@@ -17,10 +17,8 @@
#include <linux/netdevice.h>
#include <linux/usb.h>
-/* bridge device 0: DUN
- * bridge device 1 : Tethered RMNET
- */
-#define MAX_BRIDGE_DEVICES 2
+#define MAX_BRIDGE_DEVICES 4
+#define BRIDGE_NAME_MAX_LEN 20
struct bridge_ops {
int (*send_pkt)(void *, void *, size_t actual);
@@ -37,11 +35,12 @@
/* context of the gadget port using bridge driver */
void *ctx;
- /* bridge device array index mapped to the gadget port array index.
- * data bridge[ch_id] <-- bridge --> gadget port[ch_id]
- */
+ /*to maps bridge driver instance*/
unsigned int ch_id;
+ /*to match against bridge xport name to get bridge driver instance*/
+ char *name;
+
/* flow control bits */
unsigned long flags;
@@ -101,7 +100,10 @@
int data_bridge_unthrottle_rx(unsigned int);
/* defined in control bridge */
-int ctrl_bridge_probe(struct usb_interface *, struct usb_host_endpoint *, int);
+int ctrl_bridge_init(void);
+void ctrl_bridge_exit(void);
+int ctrl_bridge_probe(struct usb_interface *, struct usb_host_endpoint *,
+ char *, int);
void ctrl_bridge_disconnect(unsigned int);
int ctrl_bridge_resume(unsigned int);
int ctrl_bridge_suspend(unsigned int);
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
index a183f0e..634a4dc 100644
--- a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -89,8 +89,8 @@
USB_GADGET_RMNET,
};
-#define NUM_RMNET_HSIC_PORTS 1
-#define NUM_DUN_HSIC_PORTS 1
+#define NUM_RMNET_HSIC_PORTS 2
+#define NUM_DUN_HSIC_PORTS 2
#define NUM_PORTS (NUM_RMNET_HSIC_PORTS \
+ NUM_DUN_HSIC_PORTS)
@@ -102,9 +102,11 @@
int ghsic_ctrl_connect(void *, int);
void ghsic_ctrl_disconnect(void *, int);
int ghsic_ctrl_setup(unsigned int, enum gadget_type);
+void ghsic_ctrl_set_port_name(const char *, const char *);
int ghsic_data_connect(void *, int);
void ghsic_data_disconnect(void *, int);
int ghsic_data_setup(unsigned int, enum gadget_type);
+void ghsic_data_set_port_name(const char *, const char *);
int ghsuart_ctrl_connect(void *, int);
void ghsuart_ctrl_disconnect(void *, int);
diff --git a/arch/arm/mach-msm/ipc_logging.c b/arch/arm/mach-msm/ipc_logging.c
index 43214cc..bad861d 100644
--- a/arch/arm/mach-msm/ipc_logging.c
+++ b/arch/arm/mach-msm/ipc_logging.c
@@ -34,6 +34,8 @@
static LIST_HEAD(ipc_log_context_list);
DEFINE_RWLOCK(ipc_log_context_list_lock);
static atomic_t next_log_id = ATOMIC_INIT(0);
+static void *get_deserialization_func(struct ipc_log_context *ilctxt,
+ int type);
static struct ipc_log_page *get_first_page(struct ipc_log_context *ilctxt)
{
@@ -339,6 +341,59 @@
ipc_log_write(ilctxt, &ectxt);
return 0;
}
+EXPORT_SYMBOL(ipc_log_string);
+
+/**
+ * ipc_log_extract - Reads and deserializes log
+ *
+ * @ctxt: logging context
+ * @buff: buffer to receive the data
+ * @size: size of the buffer
+ * @returns: 0 if no data read; >0 number of bytes read; < 0 error
+ *
+ * If no data is available to be read, then the ilctxt::read_avail
+ * completion is reinitialized. This allows clients to block
+ * until new log data is save.
+ */
+int ipc_log_extract(void *ctxt, char *buff, int size)
+{
+ struct encode_context ectxt;
+ struct decode_context dctxt;
+ void (*deserialize_func)(struct encode_context *ectxt,
+ struct decode_context *dctxt);
+ struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt;
+ unsigned long flags;
+
+ if (size < MAX_MSG_DECODED_SIZE)
+ return -EINVAL;
+
+ dctxt.output_format = OUTPUT_DEBUGFS;
+ dctxt.buff = buff;
+ dctxt.size = size;
+ read_lock_irqsave(&ipc_log_context_list_lock, flags);
+ spin_lock(&ilctxt->ipc_log_context_lock);
+ while (dctxt.size >= MAX_MSG_DECODED_SIZE &&
+ !is_ilctxt_empty(ilctxt)) {
+ msg_read(ilctxt, &ectxt);
+ deserialize_func = get_deserialization_func(ilctxt,
+ ectxt.hdr.type);
+ spin_unlock(&ilctxt->ipc_log_context_lock);
+ read_unlock_irqrestore(&ipc_log_context_list_lock, flags);
+ if (deserialize_func)
+ deserialize_func(&ectxt, &dctxt);
+ else
+ pr_err("%s: unknown message 0x%x\n",
+ __func__, ectxt.hdr.type);
+ read_lock_irqsave(&ipc_log_context_list_lock, flags);
+ spin_lock(&ilctxt->ipc_log_context_lock);
+ }
+ if ((size - dctxt.size) == 0)
+ init_completion(&ilctxt->read_avail);
+ spin_unlock(&ilctxt->ipc_log_context_lock);
+ read_unlock_irqrestore(&ipc_log_context_list_lock, flags);
+ return size - dctxt.size;
+}
+EXPORT_SYMBOL(ipc_log_extract);
/*
* Helper funtion used to read data from a message context.
@@ -482,6 +537,21 @@
}
EXPORT_SYMBOL(add_deserialization_func);
+static void *get_deserialization_func(struct ipc_log_context *ilctxt,
+ int type)
+{
+ struct dfunc_info *df_info = NULL;
+
+ if (!ilctxt)
+ return NULL;
+
+ list_for_each_entry(df_info, &ilctxt->dfunc_info_list, list) {
+ if (df_info->type == type)
+ return df_info->dfunc;
+ }
+ return NULL;
+}
+
void *ipc_log_context_create(int max_num_pages,
const char *mod_name)
{
diff --git a/arch/arm/mach-msm/ipc_logging.h b/arch/arm/mach-msm/ipc_logging.h
index 36b4171..ddf194b 100644
--- a/arch/arm/mach-msm/ipc_logging.h
+++ b/arch/arm/mach-msm/ipc_logging.h
@@ -13,6 +13,8 @@
#ifndef _IPC_LOGGING_H
#define _IPC_LOGGING_H
+#include <mach/msm_ipc_logging.h>
+
struct ipc_log_page_header {
uint32_t magic;
uint32_t nmagic; /* inverse of magic number */
@@ -64,6 +66,7 @@
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define IS_MSG_TYPE(x) (((x) > TSV_TYPE_MSG_START) && \
((x) < TSV_TYPE_MSG_END))
+#define MAX_MSG_DECODED_SIZE (MAX_MSG_SIZE*4)
extern rwlock_t ipc_log_context_list_lock;
diff --git a/arch/arm/mach-msm/ipc_logging_debug.c b/arch/arm/mach-msm/ipc_logging_debug.c
index 246fb99..8a5957c 100644
--- a/arch/arm/mach-msm/ipc_logging_debug.c
+++ b/arch/arm/mach-msm/ipc_logging_debug.c
@@ -33,58 +33,6 @@
static DEFINE_MUTEX(ipc_log_debugfs_init_lock);
static struct dentry *root_dent;
-#define MAX_MSG_DECODED_SIZE (MAX_MSG_SIZE*4)
-
-static void *get_deserialization_func(struct ipc_log_context *ilctxt,
- int type)
-{
- struct dfunc_info *df_info = NULL;
-
- if (!ilctxt)
- return NULL;
-
- list_for_each_entry(df_info, &ilctxt->dfunc_info_list, list) {
- if (df_info->type == type)
- return df_info->dfunc;
- }
- return NULL;
-}
-
-static int deserialize_log(struct ipc_log_context *ilctxt,
- char *buff, int size)
-{
- struct encode_context ectxt;
- struct decode_context dctxt;
- void (*deserialize_func)(struct encode_context *ectxt,
- struct decode_context *dctxt);
- unsigned long flags;
-
- dctxt.output_format = OUTPUT_DEBUGFS;
- dctxt.buff = buff;
- dctxt.size = size;
- read_lock_irqsave(&ipc_log_context_list_lock, flags);
- spin_lock(&ilctxt->ipc_log_context_lock);
- while (dctxt.size >= MAX_MSG_DECODED_SIZE &&
- !is_ilctxt_empty(ilctxt)) {
- msg_read(ilctxt, &ectxt);
- deserialize_func = get_deserialization_func(ilctxt,
- ectxt.hdr.type);
- spin_unlock(&ilctxt->ipc_log_context_lock);
- read_unlock_irqrestore(&ipc_log_context_list_lock, flags);
- if (deserialize_func)
- deserialize_func(&ectxt, &dctxt);
- else
- pr_err("%s: unknown message 0x%x\n",
- __func__, ectxt.hdr.type);
- read_lock_irqsave(&ipc_log_context_list_lock, flags);
- spin_lock(&ilctxt->ipc_log_context_lock);
- }
- if ((size - dctxt.size) == 0)
- init_completion(&ilctxt->read_avail);
- spin_unlock(&ilctxt->ipc_log_context_lock);
- read_unlock_irqrestore(&ipc_log_context_list_lock, flags);
- return size - dctxt.size;
-}
static int debug_log(struct ipc_log_context *ilctxt,
char *buff, int size, int cont)
@@ -97,7 +45,7 @@
return -ENOMEM;
}
do {
- i = deserialize_log(ilctxt, buff, size - 1);
+ i = ipc_log_extract(ilctxt, buff, size - 1);
if (cont && i == 0) {
wait_for_completion_interruptible(&ilctxt->read_avail);
if (signal_pending(current))
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index 9ec8395..c0cca1d 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -95,9 +95,9 @@
#define NTFY(x...) do { } while (0)
#endif
-#define IPC_ROUTER_LOG_EVENT_ERROR 0x10
-#define IPC_ROUTER_LOG_EVENT_TX 0x11
-#define IPC_ROUTER_LOG_EVENT_RX 0x12
+#define IPC_ROUTER_LOG_EVENT_ERROR 0x00
+#define IPC_ROUTER_LOG_EVENT_TX 0x01
+#define IPC_ROUTER_LOG_EVENT_RX 0x02
static LIST_HEAD(control_ports);
static DECLARE_RWSEM(control_ports_lock_lha5);
@@ -735,8 +735,14 @@
&rport_ptr->resume_tx_port_list, list) {
local_port =
msm_ipc_router_lookup_local_port(rtx_port->port_id);
- if (local_port)
+ if (local_port && local_port->notify)
+ local_port->notify(MSM_IPC_ROUTER_RESUME_TX,
+ local_port->priv);
+ else if (local_port)
post_pkt_to_port(local_port, pkt, 1);
+ else
+ pr_err("%s: Local Port %d not Found",
+ __func__, rtx_port->port_id);
list_del(&rtx_port->list);
kfree(rtx_port);
}
@@ -1792,7 +1798,7 @@
#if defined(DEBUG)
if (msm_ipc_router_debug_mask & SMEM_LOG) {
smem_log_event((SMEM_LOG_PROC_ID_APPS |
- SMEM_LOG_RPC_ROUTER_EVENT_BASE |
+ SMEM_LOG_IPC_ROUTER_EVENT_BASE |
IPC_ROUTER_LOG_EVENT_RX),
(hdr->src_node_id << 24) |
(hdr->src_port_id & 0xffffff),
@@ -2111,7 +2117,7 @@
#if defined(DEBUG)
if (msm_ipc_router_debug_mask & SMEM_LOG) {
smem_log_event((SMEM_LOG_PROC_ID_APPS |
- SMEM_LOG_RPC_ROUTER_EVENT_BASE |
+ SMEM_LOG_IPC_ROUTER_EVENT_BASE |
IPC_ROUTER_LOG_EVENT_TX),
(hdr->src_node_id << 24) |
(hdr->src_port_id & 0xffffff),
@@ -2216,10 +2222,13 @@
}
ret = msm_ipc_router_send_to(src, out_skb_head, dest);
+ if (ret == -EAGAIN)
+ return ret;
if (ret < 0) {
pr_err("%s: msm_ipc_router_send_to failed - ret: %d\n",
__func__, ret);
msm_ipc_router_free_skb(out_skb_head);
+ return ret;
}
return 0;
}
@@ -2257,6 +2266,48 @@
}
/**
+ * msm_ipc_router_rx_data_wait() - Wait for new message destined to a local port.
+ * @port_ptr: Pointer to the local port
+ * @timeout: < 0 timeout indicates infinite wait till a message arrives.
+ * > 0 timeout indicates the wait time.
+ * 0 indicates that we do not wait.
+ * @return: 0 if there are pending messages to read,
+ * standard Linux error code otherwise.
+ *
+ * Checks for the availability of messages that are destined to a local port.
+ * If no messages are present then waits as per @timeout.
+ */
+int msm_ipc_router_rx_data_wait(struct msm_ipc_port *port_ptr, long timeout)
+{
+ int ret = 0;
+
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
+ while (list_empty(&port_ptr->port_rx_q)) {
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
+ if (timeout < 0) {
+ ret = wait_event_interruptible(
+ port_ptr->port_rx_wait_q,
+ !list_empty(&port_ptr->port_rx_q));
+ if (ret)
+ return ret;
+ } else if (timeout > 0) {
+ timeout = wait_event_interruptible_timeout(
+ port_ptr->port_rx_wait_q,
+ !list_empty(&port_ptr->port_rx_q),
+ timeout);
+ if (timeout < 0)
+ return -EFAULT;
+ }
+ if (timeout == 0)
+ return -ENOMSG;
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
+ }
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
+
+ return ret;
+}
+
+/**
* msm_ipc_router_recv_from() - Recieve messages destined to a local port.
* @port_ptr: Pointer to the local port
* @data : Pointer to the socket buffer head
@@ -2265,7 +2316,7 @@
* > 0 timeout indicates the wait time.
* 0 indicates that we do not wait.
* @return: = Number of bytes read(On successful read operation).
- * = 0 (If there are no pending messages and timeout is 0).
+ * = -ENOMSG (If there are no pending messages and timeout is 0).
* = -EINVAL (If either of the arguments, port_ptr or data is invalid)
* = -EFAULT (If there are no pending messages when timeout is > 0
* and the wait_event_interruptible_timeout has returned value > 0)
@@ -2294,28 +2345,10 @@
}
*data = NULL;
- mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
- while (list_empty(&port_ptr->port_rx_q)) {
- mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
- if (timeout < 0) {
- ret = wait_event_interruptible(
- port_ptr->port_rx_wait_q,
- !list_empty(&port_ptr->port_rx_q));
- if (ret)
- return ret;
- } else if (timeout > 0) {
- timeout = wait_event_interruptible_timeout(
- port_ptr->port_rx_wait_q,
- !list_empty(&port_ptr->port_rx_q),
- timeout);
- if (timeout < 0)
- return -EFAULT;
- }
- if (timeout == 0)
- return 0;
- mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
- }
- mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
+
+ ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
+ if (ret)
+ return ret;
ret = msm_ipc_router_read(port_ptr, data, 0);
if (ret <= 0 || !(*data))
@@ -2349,12 +2382,10 @@
ret = msm_ipc_router_recv_from(port_ptr, &in_skb_head, src, 0);
- if (ret == 0)
- return -ENOMSG;
-
if (ret < 0) {
- pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
- __func__, ret);
+ if (ret != -ENOMSG)
+ pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
+ __func__, ret);
return ret;
}
diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h
index 55aeade..7bfc52b 100644
--- a/arch/arm/mach-msm/ipc_router.h
+++ b/arch/arm/mach-msm/ipc_router.h
@@ -160,6 +160,8 @@
void msm_ipc_sync_default_sec_rule(void *rule);
+int msm_ipc_router_rx_data_wait(struct msm_ipc_port *port_ptr, long timeout);
+
#if defined CONFIG_MSM_IPC_ROUTER_SMD_XPRT
extern void *msm_ipc_load_default_node(void);
diff --git a/arch/arm/mach-msm/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c
index 515dc92..ea27c71 100644
--- a/arch/arm/mach-msm/ipc_socket.c
+++ b/arch/arm/mach-msm/ipc_socket.c
@@ -411,33 +411,14 @@
lock_sock(sk);
timeout = sk->sk_rcvtimeo;
- mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
- while (list_empty(&port_ptr->port_rx_q)) {
- mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
- release_sock(sk);
- if (timeout < 0) {
- ret = wait_event_interruptible(
- port_ptr->port_rx_wait_q,
- !list_empty(&port_ptr->port_rx_q));
- if (ret)
- return ret;
- } else if (timeout > 0) {
- timeout = wait_event_interruptible_timeout(
- port_ptr->port_rx_wait_q,
- !list_empty(&port_ptr->port_rx_q),
- timeout);
- if (timeout < 0)
- return -EFAULT;
- }
- if (timeout == 0) {
+ ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
+ if (ret) {
+ release_sock(sk);
+ if (ret == -ENOMSG)
m->msg_namelen = 0;
- return 0;
- }
- lock_sock(sk);
- mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
+ return ret;
}
- mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
ret = msm_ipc_router_read(port_ptr, &msg, buf_len);
if (ret <= 0 || !msg) {
diff --git a/arch/arm/mach-msm/jtag-fuse.c b/arch/arm/mach-msm/jtag-fuse.c
new file mode 100644
index 0000000..ccefc41
--- /dev/null
+++ b/arch/arm/mach-msm/jtag-fuse.c
@@ -0,0 +1,140 @@
+/* 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/kernel.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <mach/jtag.h>
+
+#define fuse_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
+#define fuse_readl(drvdata, off) __raw_readl(drvdata->base + off)
+
+#define OEM_CONFIG0 (0x000)
+#define OEM_CONFIG1 (0x004)
+
+#define ALL_DEBUG_DISABLE BIT(21)
+#define APPS_DBGEN_DISABLE BIT(0)
+#define APPS_NIDEN_DISABLE BIT(1)
+#define APPS_SPIDEN_DISABLE BIT(2)
+#define APPS_SPNIDEN_DISABLE BIT(3)
+#define DAP_DEVICEEN_DISABLE BIT(8)
+
+struct fuse_drvdata {
+ void __iomem *base;
+ struct device *dev;
+};
+
+static struct fuse_drvdata *fusedrvdata;
+
+bool msm_jtag_fuse_apps_access_disabled(void)
+{
+ struct fuse_drvdata *drvdata = fusedrvdata;
+ uint32_t config0, config1;
+ bool ret;
+
+ if (!drvdata)
+ return false;
+
+ config0 = fuse_readl(drvdata, OEM_CONFIG0);
+ config1 = fuse_readl(drvdata, OEM_CONFIG1);
+
+ dev_dbg(drvdata->dev, "apps config0: %lx\n", (unsigned long)config0);
+ dev_dbg(drvdata->dev, "apps config1: %lx\n", (unsigned long)config1);
+
+ if (config0 & ALL_DEBUG_DISABLE)
+ ret = true;
+ else if (config1 & APPS_DBGEN_DISABLE)
+ ret = true;
+ else if (config1 & APPS_NIDEN_DISABLE)
+ ret = true;
+ else if (config1 & APPS_SPIDEN_DISABLE)
+ ret = true;
+ else if (config1 & APPS_SPNIDEN_DISABLE)
+ ret = true;
+ else if (config1 & DAP_DEVICEEN_DISABLE)
+ ret = true;
+ else
+ ret = false;
+
+ if (ret)
+ dev_dbg(drvdata->dev, "apps fuse disabled\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(msm_jtag_fuse_apps_access_disabled);
+
+static int __devinit jtag_fuse_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fuse_drvdata *drvdata;
+ struct resource *res;
+
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ /* Store the driver data pointer for use in exported functions */
+ fusedrvdata = drvdata;
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fuse-base");
+ if (!res)
+ return -ENODEV;
+
+ drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!drvdata->base)
+ return -ENOMEM;
+
+ dev_info(dev, "JTag Fuse initialized\n");
+ return 0;
+}
+
+static int __devexit jtag_fuse_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct of_device_id jtag_fuse_match[] = {
+ {.compatible = "qcom,jtag-fuse"},
+ {}
+};
+
+static struct platform_driver jtag_fuse_driver = {
+ .probe = jtag_fuse_probe,
+ .remove = __devexit_p(jtag_fuse_remove),
+ .driver = {
+ .name = "msm-jtag-fuse",
+ .owner = THIS_MODULE,
+ .of_match_table = jtag_fuse_match,
+ },
+};
+
+static int __init jtag_fuse_init(void)
+{
+ return platform_driver_register(&jtag_fuse_driver);
+}
+arch_initcall(jtag_fuse_init);
+
+static void __exit jtag_fuse_exit(void)
+{
+ platform_driver_unregister(&jtag_fuse_driver);
+}
+module_exit(jtag_fuse_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("JTag Fuse driver");
diff --git a/arch/arm/mach-msm/jtag-mm.c b/arch/arm/mach-msm/jtag-mm.c
index 8a67614..55c5a7e 100644
--- a/arch/arm/mach-msm/jtag-mm.c
+++ b/arch/arm/mach-msm/jtag-mm.c
@@ -787,6 +787,9 @@
static uint32_t count;
struct device *dev = &pdev->dev;
+ if (msm_jtag_fuse_apps_access_disabled())
+ return -EPERM;
+
cpu = count;
count++;
diff --git a/arch/arm/mach-msm/jtag.c b/arch/arm/mach-msm/jtag.c
index 24e1c41..840449c 100644
--- a/arch/arm/mach-msm/jtag.c
+++ b/arch/arm/mach-msm/jtag.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
@@ -1105,6 +1105,9 @@
int ret;
uint32_t dbgdidr;
+ if (msm_jtag_fuse_apps_access_disabled())
+ return -EPERM;
+
/* This will run on core0 so use it to populate parameters */
/* Populate dbg_ctx data */
@@ -1146,6 +1149,9 @@
uint32_t etmidr;
uint32_t etmccr;
+ if (msm_jtag_fuse_apps_access_disabled())
+ return -EPERM;
+
/* Vote for ETM power/clock enable */
etm_clk_enable();
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 7c1b8d6..b30cba4 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -131,6 +131,15 @@
#define VREF_LDO_BIT_POS 0
#define VREF_LDO_MASK KRAIT_MASK(6, 0)
+#define PWR_GATE_SWITCH_MODE_POS 4
+#define PWR_GATE_SWITCH_MODE_MASK KRAIT_MASK(6, 4)
+
+#define PWR_GATE_SWITCH_MODE_PC 0
+#define PWR_GATE_SWITCH_MODE_LDO 1
+#define PWR_GATE_SWITCH_MODE_BHS 2
+#define PWR_GATE_SWITCH_MODE_DT 3
+#define PWR_GATE_SWITCH_MODE_RET 4
+
#define LDO_HDROOM_MIN 50000
#define LDO_HDROOM_MAX 250000
@@ -144,6 +153,10 @@
#define LDO_DELTA_MAX 100000
#define MSM_L2_SAW_PHYS 0xf9012000
+#define MSM_MDD_BASE_PHYS 0xf908a800
+
+#define KPSS_VERSION_2P0 0x20000000
+
/**
* struct pmic_gang_vreg -
* @name: the string used to represent the gang
@@ -580,35 +593,55 @@
if (kvreg->mode == HS_MODE)
return 0;
/* enable bhs */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_EN_MASK, BHS_EN_MASK);
- /* complete the above write before the delay */
- mb();
- /* wait for the bhs to settle */
- udelay(BHS_SETTLING_DELAY_US);
+ if (version > KPSS_VERSION_2P0) {
+ krait_masked_write(kvreg, APC_PWR_GATE_MODE,
+ PWR_GATE_SWITCH_MODE_MASK,
+ PWR_GATE_SWITCH_MODE_BHS << PWR_GATE_SWITCH_MODE_POS);
- /* Turn on BHS segments */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
- BHS_SEG_EN_MASK, BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
+ /* complete the writes before the delay */
+ mb();
- /* complete the above write before the delay */
- mb();
+ /* wait for the bhs to settle */
+ udelay(BHS_SETTLING_DELAY_US);
+ } else {
+ /* enable bhs */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ BHS_EN_MASK, BHS_EN_MASK);
- /*
- * wait for the bhs to settle - note that
- * after the voltage has settled both BHS and LDO are supplying power
- * to the krait. This avoids glitches during switching
- */
- udelay(BHS_SETTLING_DELAY_US);
+ /* complete the above write before the delay */
+ mb();
- /*
- * enable ldo bypass - the krait is powered still by LDO since
- * LDO is enabled
- */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, LDO_BYP_MASK, LDO_BYP_MASK);
+ /* wait for the bhs to settle */
+ udelay(BHS_SETTLING_DELAY_US);
- /* disable ldo - only the BHS provides voltage to the cpu after this */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ /* Turn on BHS segments */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_SEG_EN_MASK,
+ BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
+
+ /* complete the above write before the delay */
+ mb();
+
+ /*
+ * wait for the bhs to settle - note that
+ * after the voltage has settled both BHS and LDO are supplying
+ * power to the krait. This avoids glitches during switching
+ */
+ udelay(BHS_SETTLING_DELAY_US);
+
+ /*
+ * enable ldo bypass - the krait is powered still by LDO since
+ * LDO is enabled
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ LDO_BYP_MASK, LDO_BYP_MASK);
+
+ /*
+ * disable ldo - only the BHS provides voltage to
+ * the cpu after this
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
LDO_PWR_DWN_MASK, LDO_PWR_DWN_MASK);
+ }
kvreg->mode = HS_MODE;
pr_debug("%s using BHS\n", kvreg->name);
@@ -629,27 +662,39 @@
switch_to_using_hs(kvreg);
set_krait_ldo_uv(kvreg, kvreg->uV - kvreg->ldo_delta_uV);
+ if (version > KPSS_VERSION_2P0) {
+ krait_masked_write(kvreg, APC_PWR_GATE_MODE,
+ PWR_GATE_SWITCH_MODE_MASK,
+ PWR_GATE_SWITCH_MODE_LDO << PWR_GATE_SWITCH_MODE_POS);
- /*
- * enable ldo - note that both LDO and BHS are are supplying voltage to
- * the cpu after this. This avoids glitches during switching from BHS
- * to LDO.
- */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, LDO_PWR_DWN_MASK, 0);
+ /* complete the writes before the delay */
+ mb();
- /* complete the writes before the delay */
- mb();
+ /* wait for the ldo to settle */
+ udelay(LDO_SETTLING_DELAY_US);
+ } else {
+ /*
+ * enable ldo - note that both LDO and BHS are are supplying
+ * voltage to the cpu after this. This avoids glitches during
+ * switching from BHS to LDO.
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ LDO_PWR_DWN_MASK, 0);
- /* wait for the ldo to settle */
- udelay(LDO_SETTLING_DELAY_US);
+ /* complete the writes before the delay */
+ mb();
- /*
- * disable BHS and disable LDO bypass seperate from enabling
- * the LDO above.
- */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
- BHS_EN_MASK | LDO_BYP_MASK, 0);
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_SEG_EN_MASK, 0);
+ /* wait for the ldo to settle */
+ udelay(LDO_SETTLING_DELAY_US);
+
+ /*
+ * disable BHS and disable LDO bypass seperate from enabling
+ * the LDO above.
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ BHS_EN_MASK | LDO_BYP_MASK, 0);
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_SEG_EN_MASK, 0);
+ }
kvreg->mode = LDO_MODE;
pr_debug("%s using LDO\n", kvreg->name);
@@ -993,22 +1038,34 @@
DEFINE_SIMPLE_ATTRIBUTE(retention_fops,
get_retention_dbg_uV, set_retention_dbg_uV, "%llu\n");
+static void kvreg_ldo_voltage_init(struct krait_power_vreg *kvreg)
+{
+ set_krait_retention_uv(kvreg, kvreg->retention_uV);
+ set_krait_ldo_uv(kvreg, kvreg->ldo_default_uV);
+}
+
#define CPU_PWR_CTL_ONLINE_MASK 0x80
static void kvreg_hw_init(struct krait_power_vreg *kvreg)
{
- int online;
- /*
- * bhs_cnt value sets the ramp-up time from power collapse,
- * initialize the ramp up time
- */
- set_krait_retention_uv(kvreg, kvreg->retention_uV);
- set_krait_ldo_uv(kvreg, kvreg->ldo_default_uV);
-
/* setup the bandgap that configures the reference to the LDO */
writel_relaxed(0x00000190, kvreg->mdd_base + MDD_CONFIG_CTL);
/* Enable MDD */
writel_relaxed(0x00000002, kvreg->mdd_base + MDD_MODE);
mb();
+
+ if (version > KPSS_VERSION_2P0) {
+ /* Configure hardware sequencer delays. */
+ writel_relaxed(0x30430600, kvreg->reg_base + APC_PWR_GATE_DLY);
+
+ /* Enable the hardware sequencer in BHS mode. */
+ writel_relaxed(0x00000021, kvreg->reg_base + APC_PWR_GATE_MODE);
+ }
+}
+
+static void online_at_probe(struct krait_power_vreg *kvreg)
+{
+ int online;
+
online = CPU_PWR_CTL_ONLINE_MASK
& readl_relaxed(kvreg->reg_base + CPU_PWR_CTL);
kvreg->online_at_probe
@@ -1017,11 +1074,15 @@
static void glb_init(void __iomem *apcs_gcc_base)
{
- /* configure bi-modal switch */
- writel_relaxed(0x0008736E, apcs_gcc_base + PWR_GATE_CONFIG);
/* read kpss version */
version = readl_relaxed(apcs_gcc_base + VERSION);
pr_debug("version= 0x%x\n", version);
+
+ /* configure bi-modal switch */
+ if (version > KPSS_VERSION_2P0)
+ writel_relaxed(0x0308736E, apcs_gcc_base + PWR_GATE_CONFIG);
+ else
+ writel_relaxed(0x0008736E, apcs_gcc_base + PWR_GATE_CONFIG);
}
static int __devinit krait_power_probe(struct platform_device *pdev)
@@ -1179,6 +1240,14 @@
list_add_tail(&kvreg->link, &the_gang->krait_power_vregs);
mutex_unlock(&the_gang->krait_power_vregs_lock);
+ online_at_probe(kvreg);
+ kvreg_ldo_voltage_init(kvreg);
+
+ if (kvreg->cpu_num == 0)
+ kvreg_hw_init(kvreg);
+
+ per_cpu(krait_vregs, cpu_num) = kvreg;
+
kvreg->rdev = regulator_register(&kvreg->desc, &pdev->dev, init_data,
kvreg, pdev->dev.of_node);
if (IS_ERR(kvreg->rdev)) {
@@ -1187,8 +1256,6 @@
goto out;
}
- kvreg_hw_init(kvreg);
- per_cpu(krait_vregs, cpu_num) = kvreg;
dev_dbg(&pdev->dev, "id=%d, name=%s\n", pdev->id, kvreg->name);
return 0;
@@ -1426,10 +1493,29 @@
}
module_exit(krait_power_exit);
-void secondary_cpu_hs_init(void *base_ptr)
+#define GCC_BASE 0xF9011000
+
+/**
+ * secondary_cpu_hs_init - Initialize BHS and LDO registers
+ * for nonboot cpu
+ *
+ * @base_ptr: address pointer to APC registers of a cpu
+ * @cpu: the cpu being brought out of reset
+ *
+ * seconday_cpu_hs_init() is called when a secondary cpu
+ * is being brought online for the first time. It is not
+ * called for boot cpu. It initializes power related
+ * registers and makes the core run from BHS.
+ * It also ends up turning on MDD which is required when the
+ * core switches to LDO mode
+ */
+void secondary_cpu_hs_init(void *base_ptr, int cpu)
{
uint32_t reg_val;
void *l2_saw_base;
+ void *gcc_base_ptr;
+ void *mdd_base;
+ struct krait_power_vreg *kvreg;
/* Turn on the BHS, turn off LDO Bypass and power down LDO */
reg_val = BHS_CNT_DEFAULT << BHS_CNT_BIT_POS
@@ -1438,14 +1524,23 @@
| BHS_EN_MASK;
writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
- /* complete the above write before the delay */
- mb();
- /* wait for the bhs to settle */
- udelay(BHS_SETTLING_DELAY_US);
+ if (version == 0) {
+ gcc_base_ptr = ioremap_nocache(GCC_BASE, SZ_4K);
+ version = readl_relaxed(gcc_base_ptr + VERSION);
+ iounmap(gcc_base_ptr);
+ }
- /* Turn on BHS segments */
- reg_val |= BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS;
- writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
+ /* Turn on the BHS segments only for version < 2 */
+ if (version <= KPSS_VERSION_2P0) {
+ /* complete the above write before the delay */
+ mb();
+ /* wait for the bhs to settle */
+ udelay(BHS_SETTLING_DELAY_US);
+
+ /* Turn on BHS segments */
+ reg_val |= BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS;
+ writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
+ }
/* complete the above write before the delay */
mb();
@@ -1456,23 +1551,48 @@
reg_val |= LDO_BYP_MASK;
writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
- if (the_gang && the_gang->manage_phases)
- return;
+ kvreg = per_cpu(krait_vregs, cpu);
+ if (kvreg != NULL) {
+ kvreg_hw_init(kvreg);
+ } else {
+ /*
+ * This nonboot cpu has not been probed yet. This cpu was
+ * brought out of reset as a part of maxcpus >= 2. Initialize
+ * its MDD and APC_PWR_GATE_MODE register here
+ */
+ mdd_base = ioremap_nocache(MSM_MDD_BASE_PHYS + cpu * 0x10000,
+ SZ_4K);
+ /* setup the bandgap that configures the reference to the LDO */
+ writel_relaxed(0x00000190, mdd_base + MDD_CONFIG_CTL);
+ /* Enable MDD */
+ writel_relaxed(0x00000002, mdd_base + MDD_MODE);
+ mb();
+ iounmap(mdd_base);
- /*
- * If the driver has not yet started to manage phases then enable
- * max phases.
- */
- l2_saw_base = ioremap_nocache(MSM_L2_SAW_PHYS, SZ_4K);
- if (!l2_saw_base) {
- __WARN();
- return;
+ if (version > KPSS_VERSION_2P0) {
+ writel_relaxed(0x30430600, base_ptr + APC_PWR_GATE_DLY);
+ writel_relaxed(0x00000021,
+ base_ptr + APC_PWR_GATE_MODE);
+ }
+ mb();
}
- writel_relaxed(0x10003, l2_saw_base + 0x1c);
- mb();
- udelay(PHASE_SETTLING_TIME_US);
- iounmap(l2_saw_base);
+ if (!the_gang || !the_gang->manage_phases) {
+ /*
+ * If the driver has not yet started to manage phases then
+ * enable max phases.
+ */
+ l2_saw_base = ioremap_nocache(MSM_L2_SAW_PHYS, SZ_4K);
+ if (l2_saw_base) {
+ writel_relaxed(0x10003, l2_saw_base + 0x1c);
+ mb();
+ udelay(PHASE_SETTLING_TIME_US);
+
+ iounmap(l2_saw_base);
+ } else {
+ __WARN();
+ }
+ }
}
MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index 7a7fb99..2ce4fa0 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -43,7 +43,6 @@
/* fixme */
#include <asm/tlbflush.h>
#include <../../mm/mm.h>
-#include <linux/fmem.h>
#if defined(CONFIG_ARCH_MSM7X27)
static void *strongly_ordered_page;
@@ -251,16 +250,6 @@
: "=r" (msm_ttbr0));
}
-int request_fmem_c_region(void *unused)
-{
- return fmem_set_state(FMEM_C_STATE);
-}
-
-int release_fmem_c_region(void *unused)
-{
- return fmem_set_state(FMEM_T_STATE);
-}
-
static char * const memtype_names[] = {
[MEMTYPE_SMI_KERNEL] = "SMI_KERNEL",
[MEMTYPE_SMI] = "SMI",
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c
index 65834eb..3a996eb 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c
@@ -595,8 +595,8 @@
.ws = 10000,
.mas_hw_id = MAS_APPSS_PROC,
.prio_lvl = 0,
- .prio_rd = 2,
- .prio_wr = 2,
+ .prio_rd = 0,
+ .prio_wr = 0,
},
{
.id = MSM_BUS_MASTER_MSS_PROC,
diff --git a/arch/arm/mach-msm/msm_dcvs_scm.c b/arch/arm/mach-msm/msm_dcvs_scm.c
index 78d62ac..e03ac64 100644
--- a/arch/arm/mach-msm/msm_dcvs_scm.c
+++ b/arch/arm/mach-msm/msm_dcvs_scm.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
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/memory_alloc.h>
+#include <asm/cacheflush.h>
#include <mach/memory.h>
#include <mach/scm.h>
#include <mach/msm_dcvs_scm.h>
@@ -83,6 +84,12 @@
}
EXPORT_SYMBOL(msm_dcvs_scm_init);
+static void __msm_dcvs_flush_cache(void *v, size_t size)
+{
+ __cpuc_flush_dcache_area(v, size);
+ outer_flush_range(virt_to_phys(v), virt_to_phys(v) + size);
+}
+
int msm_dcvs_scm_register_core(uint32_t core_id,
struct msm_dcvs_core_param *param)
{
@@ -99,6 +106,8 @@
reg_data.core_id = core_id;
reg_data.core_param_phy = virt_to_phys(p);
+ __msm_dcvs_flush_cache(p, sizeof(struct msm_dcvs_core_param));
+
ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_REGISTER_CORE,
®_data, sizeof(reg_data), NULL, 0);
@@ -125,6 +134,8 @@
algo.core_id = core_id;
algo.algo_phy = virt_to_phys(p);
+ __msm_dcvs_flush_cache(p, sizeof(struct msm_algo_param));
+
ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM,
&algo, sizeof(algo), NULL, 0);
@@ -150,6 +161,8 @@
algo.core_id = 0;
algo.algo_phy = virt_to_phys(p);
+ __msm_dcvs_flush_cache(p, sizeof(struct msm_algo_param));
+
ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM,
&algo, sizeof(algo), NULL, 0);
@@ -197,6 +210,12 @@
sizeof(struct msm_dcvs_freq_entry)*pwr_param->num_freq);
memcpy(coefft, coeffs, sizeof(struct msm_dcvs_energy_curve_coeffs));
+ __msm_dcvs_flush_cache(pwrt, sizeof(struct msm_dcvs_power_params));
+ __msm_dcvs_flush_cache(freqt,
+ sizeof(struct msm_dcvs_freq_entry) * pwr_param->num_freq);
+ __msm_dcvs_flush_cache(coefft,
+ sizeof(struct msm_dcvs_energy_curve_coeffs));
+
pwr.core_id = core_id;
pwr.pwr_param_phy = virt_to_phys(pwrt);
pwr.freq_phy = virt_to_phys(freqt);
diff --git a/arch/arm/mach-msm/msm_qmi_interface.c b/arch/arm/mach-msm/msm_qmi_interface.c
index 9b3e500..a8fed52 100644
--- a/arch/arm/mach-msm/msm_qmi_interface.c
+++ b/arch/arm/mach-msm/msm_qmi_interface.c
@@ -25,6 +25,8 @@
#include <linux/socket.h>
#include <linux/gfp.h>
#include <linux/qmi_encdec.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
#include <mach/msm_qmi_interface.h>
#include <mach/msm_ipc_router.h>
@@ -33,6 +35,8 @@
static LIST_HEAD(svc_event_nb_list);
static DEFINE_MUTEX(svc_event_nb_list_lock);
+static DEFINE_MUTEX(msm_qmi_init_lock);
+static struct workqueue_struct *msm_qmi_pending_workqueue;
struct elem_info qmi_response_type_v01_ei[] = {
{
@@ -87,12 +91,98 @@
spin_unlock_irqrestore(&handle->notify_lock, flags);
break;
+ case MSM_IPC_ROUTER_RESUME_TX:
+ queue_delayed_work(msm_qmi_pending_workqueue,
+ &handle->resume_tx_work,
+ msecs_to_jiffies(0));
+ break;
default:
break;
}
mutex_unlock(&handle->handle_lock);
}
+/**
+ * init_msm_qmi() - Init function for kernel space QMI
+ *
+ * This function is implemented to initialize the QMI resources that are common
+ * across kernel space QMI users. As it is not necessary for this init function
+ * to be module_init function it is called when the first handle of kernel space
+ * QMI gets created.
+ */
+static void init_msm_qmi(void)
+{
+ static bool msm_qmi_inited;
+
+ if (likely(msm_qmi_inited))
+ return;
+
+ mutex_lock(&msm_qmi_init_lock);
+ if (likely(msm_qmi_inited && msm_qmi_pending_workqueue)) {
+ mutex_unlock(&msm_qmi_init_lock);
+ return;
+ }
+ msm_qmi_inited = 1;
+ msm_qmi_pending_workqueue =
+ create_singlethread_workqueue("msm_qmi_rtx_q");
+ mutex_unlock(&msm_qmi_init_lock);
+}
+
+/**
+ * handle_resume_tx() - Handle the Resume_Tx event
+ * @work : Pointer to the work strcuture.
+ *
+ * This function handles the resume_tx event for any QMI client that
+ * exists in the kernel space. This function parses the pending_txn_list of
+ * the handle and attempts a send for each transaction in that list.
+ */
+static void handle_resume_tx(struct work_struct *work)
+{
+ struct delayed_work *rtx_work = to_delayed_work(work);
+ struct qmi_handle *handle =
+ container_of(rtx_work, struct qmi_handle, resume_tx_work);
+ struct qmi_txn *pend_txn, *temp_txn;
+ int ret;
+ uint16_t msg_id;
+
+ mutex_lock(&handle->handle_lock);
+ list_for_each_entry_safe(pend_txn, temp_txn,
+ &handle->pending_txn_list, list) {
+ ret = msm_ipc_router_send_msg(
+ (struct msm_ipc_port *)handle->src_port,
+ (struct msm_ipc_addr *)handle->dest_info,
+ pend_txn->enc_data, pend_txn->enc_data_len);
+
+ if (ret == -EAGAIN) {
+ mutex_unlock(&handle->handle_lock);
+ return;
+ }
+ msg_id = ((struct qmi_header *)pend_txn->enc_data)->msg_id;
+ kfree(pend_txn->enc_data);
+ if (ret < 0) {
+ if (pend_txn->type == QMI_ASYNC_TXN) {
+ pend_txn->resp_cb(pend_txn->handle,
+ msg_id, pend_txn->resp,
+ pend_txn->resp_cb_data,
+ ret);
+ list_del(&pend_txn->list);
+ kfree(pend_txn);
+ } else if (pend_txn->type == QMI_SYNC_TXN) {
+ pend_txn->send_stat = ret;
+ wake_up(&pend_txn->wait_q);
+ }
+ pr_err("%s: Sending transaction %d from port %d failed",
+ __func__, pend_txn->txn_id,
+ ((struct msm_ipc_port *)handle->src_port)->
+ this_port.port_id);
+ } else {
+ list_del(&pend_txn->list);
+ list_add_tail(&pend_txn->list, &handle->txn_list);
+ }
+ }
+ mutex_unlock(&handle->handle_lock);
+}
+
struct qmi_handle *qmi_handle_create(
void (*notify)(struct qmi_handle *handle,
enum qmi_event_type event, void *notify_priv),
@@ -118,20 +208,39 @@
temp_handle->src_port = port_ptr;
temp_handle->next_txn_id = 1;
INIT_LIST_HEAD(&temp_handle->txn_list);
+ INIT_LIST_HEAD(&temp_handle->pending_txn_list);
mutex_init(&temp_handle->handle_lock);
spin_lock_init(&temp_handle->notify_lock);
temp_handle->notify = notify;
temp_handle->notify_priv = notify_priv;
temp_handle->handle_reset = 0;
init_waitqueue_head(&temp_handle->reset_waitq);
+ INIT_DELAYED_WORK(&temp_handle->resume_tx_work, handle_resume_tx);
+ init_msm_qmi();
return temp_handle;
}
EXPORT_SYMBOL(qmi_handle_create);
static void clean_txn_info(struct qmi_handle *handle)
{
- struct qmi_txn *txn_handle, *temp_txn_handle;
+ struct qmi_txn *txn_handle, *temp_txn_handle, *pend_txn;
+ list_for_each_entry_safe(pend_txn, temp_txn_handle,
+ &handle->pending_txn_list, list) {
+ if (pend_txn->type == QMI_ASYNC_TXN) {
+ list_del(&pend_txn->list);
+ pend_txn->resp_cb(pend_txn->handle,
+ ((struct qmi_header *)
+ pend_txn->enc_data)->msg_id,
+ pend_txn->resp, pend_txn->resp_cb_data,
+ -ENETRESET);
+ kfree(pend_txn->enc_data);
+ kfree(pend_txn);
+ } else if (pend_txn->type == QMI_SYNC_TXN) {
+ kfree(pend_txn->enc_data);
+ wake_up(&pend_txn->wait_q);
+ }
+ }
list_for_each_entry_safe(txn_handle, temp_txn_handle,
&handle->txn_list, list) {
if (txn_handle->type == QMI_ASYNC_TXN) {
@@ -154,7 +263,7 @@
handle->handle_reset = 1;
clean_txn_info(handle);
mutex_unlock(&handle->handle_lock);
-
+ flush_delayed_work(&handle->resume_tx_work);
rc = wait_event_interruptible(handle->reset_waitq,
list_empty(&handle->txn_list));
@@ -194,7 +303,7 @@
struct msg_desc *resp_desc, void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
- void *resp_cb_data),
+ void *resp_cb_data, int stat),
void *resp_cb_data)
{
struct qmi_txn *txn_handle;
@@ -230,6 +339,8 @@
txn_handle->resp_received = 0;
txn_handle->resp_cb = resp_cb;
txn_handle->resp_cb_data = resp_cb_data;
+ txn_handle->enc_data = NULL;
+ txn_handle->enc_data_len = 0;
/* Encode the request msg */
encoded_req_len = req_desc->max_msg_len + QMI_HEADER_SIZE;
@@ -256,12 +367,35 @@
txn_handle->txn_id, req_desc->msg_id,
encoded_req_len);
encoded_req_len += QMI_HEADER_SIZE;
- list_add_tail(&txn_handle->list, &handle->txn_list);
+ /*
+ * Check if this port has transactions queued to its pending list
+ * and if there are any pending transactions then add the current
+ * transaction to the pending list rather than sending it. This avoids
+ * out-of-order message transfers.
+ */
+ if (!list_empty(&handle->pending_txn_list)) {
+ rc = -EAGAIN;
+ goto append_pend_txn;
+ }
+
+ list_add_tail(&txn_handle->list, &handle->txn_list);
/* Send the request */
rc = msm_ipc_router_send_msg((struct msm_ipc_port *)(handle->src_port),
(struct msm_ipc_addr *)handle->dest_info,
encoded_req, encoded_req_len);
+append_pend_txn:
+ if (rc == -EAGAIN) {
+ txn_handle->enc_data = encoded_req;
+ txn_handle->enc_data_len = encoded_req_len;
+ if (list_empty(&handle->pending_txn_list))
+ list_del(&txn_handle->list);
+ list_add_tail(&txn_handle->list, &handle->pending_txn_list);
+ if (ret_txn_handle)
+ *ret_txn_handle = txn_handle;
+ mutex_unlock(&handle->handle_lock);
+ return 0;
+ }
if (rc < 0) {
pr_err("%s: send_msg failed %d\n", __func__, rc);
goto encode_and_send_req_err3;
@@ -305,14 +439,16 @@
/* Wait for the response */
if (!timeout_ms) {
- rc = wait_event_interruptible(txn_handle->wait_q,
- (txn_handle->resp_received ||
- handle->handle_reset));
+ wait_event(txn_handle->wait_q,
+ (txn_handle->resp_received ||
+ handle->handle_reset ||
+ (txn_handle->send_stat < 0)));
} else {
- rc = wait_event_interruptible_timeout(txn_handle->wait_q,
- (txn_handle->resp_received ||
- handle->handle_reset),
- msecs_to_jiffies(timeout_ms));
+ rc = wait_event_timeout(txn_handle->wait_q,
+ (txn_handle->resp_received ||
+ handle->handle_reset ||
+ (txn_handle->send_stat < 0)),
+ msecs_to_jiffies(timeout_ms));
if (rc == 0)
rc = -ETIMEDOUT;
}
@@ -324,6 +460,8 @@
rc = -ENETRESET;
if (rc >= 0)
rc = -EFAULT;
+ if (txn_handle->send_stat < 0)
+ rc = txn_handle->send_stat;
goto send_req_wait_err;
}
rc = 0;
@@ -344,7 +482,7 @@
void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
- void *resp_cb_data),
+ void *resp_cb_data, int stat),
void *resp_cb_data)
{
return qmi_encode_and_send_req(NULL, handle, QMI_ASYNC_TXN,
@@ -407,7 +545,7 @@
if (txn_handle->resp_cb)
txn_handle->resp_cb(txn_handle->handle, msg_id,
txn_handle->resp,
- txn_handle->resp_cb_data);
+ txn_handle->resp_cb_data, 0);
list_del(&txn_handle->list);
kfree(txn_handle);
rc = 0;
diff --git a/arch/arm/mach-msm/msm_qmi_interface_priv.h b/arch/arm/mach-msm/msm_qmi_interface_priv.h
index 58f1ce3..8eba0db 100644
--- a/arch/arm/mach-msm/msm_qmi_interface_priv.h
+++ b/arch/arm/mach-msm/msm_qmi_interface_priv.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
@@ -34,12 +34,15 @@
uint16_t txn_id;
enum txn_type type;
struct qmi_handle *handle;
+ void *enc_data;
+ unsigned int enc_data_len;
struct msg_desc *resp_desc;
void *resp;
unsigned int resp_len;
int resp_received;
+ int send_stat;
void (*resp_cb)(struct qmi_handle *handle, unsigned int msg_id,
- void *msg, void *resp_cb_data);
+ void *msg, void *resp_cb_data, int stat);
void *resp_cb_data;
wait_queue_head_t wait_q;
};
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index a3fd6b2..cbf7933 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -1192,19 +1192,23 @@
}
/* Remove the request from eviction lists */
-static void cancel_restore(struct ocmem_req *e_handle,
- struct ocmem_req *req)
+static void cancel_restore(struct ocmem_req *req)
{
- struct ocmem_eviction_data *edata = e_handle->edata;
+ struct ocmem_eviction_data *edata;
- if (!edata || !req)
+ if (!req)
+ return;
+
+ edata = req->eviction_info;
+
+ if (!edata)
return;
if (list_empty(&edata->req_list))
return;
list_del_init(&req->eviction_list);
- req->e_handle = NULL;
+ req->eviction_info = NULL;
return;
}
@@ -1499,8 +1503,8 @@
}
/* Remove the request from any restore lists */
- if (req->e_handle)
- cancel_restore(req->e_handle, req);
+ if (req->eviction_info)
+ cancel_restore(req);
/* Remove the request from any pending opreations */
if (TEST_STATE(req, R_ENQUEUED)) {
@@ -1747,12 +1751,7 @@
goto shrink_fail;
}
- if (!req->e_handle) {
- pr_err("Unable to find evicting request\n");
- goto shrink_fail;
- }
-
- edata = req->e_handle->edata;
+ edata = req->eviction_info;
if (!edata) {
pr_err("Unable to find eviction data\n");
@@ -1905,7 +1904,7 @@
&e_req->eviction_list,
&edata->req_list);
atomic_inc(&edata->pending);
- e_req->e_handle = req;
+ e_req->eviction_info = edata;
}
}
} else {
@@ -2037,7 +2036,7 @@
pr_debug("ocmem: restoring evicted request %p\n",
req);
req->edata = NULL;
- req->e_handle = NULL;
+ req->eviction_info = NULL;
req->op = SCHED_ALLOCATE;
inc_ocmem_stat(zone_of(req), NR_RESTORES);
sched_enqueue(req);
@@ -2076,8 +2075,11 @@
struct ocmem_eviction_data *edata = evictions[id];
int rc = 0;
- if (!edata)
+ if (!edata) {
+ pr_err("Client %s invoked restore without any eviction\n",
+ get_name(id));
return -EINVAL;
+ }
mutex_lock(&free_mutex);
rc = __restore_common(edata);
diff --git a/arch/arm/mach-msm/perf_debug.c b/arch/arm/mach-msm/perf_debug.c
index 70420db..0a799aa 100644
--- a/arch/arm/mach-msm/perf_debug.c
+++ b/arch/arm/mach-msm/perf_debug.c
@@ -32,6 +32,8 @@
"7 Perf: Add L1 counters to tracepoints\n"
"8 Perf: Add cortex A7 perf support\n"
"9 ARM: dts: msm: add perf-events support for msm8226\n"
+ "10 Perf: Fix counts across power collapse\n"
+ "11 ARM: dts: msm: add perf-events support for msm8x10, msm8x12\n"
;
static ssize_t desc_read(struct file *fp, char __user *buf,
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index c572291..ff1fd4f 100644
--- a/arch/arm/mach-msm/peripheral-loader.c
+++ b/arch/arm/mach-msm/peripheral-loader.c
@@ -771,7 +771,7 @@
ret = request_threaded_irq(desc->proxy_unvote_irq,
NULL,
proxy_unvote_intr_handler,
- IRQF_TRIGGER_RISING|IRQF_SHARED,
+ IRQF_TRIGGER_RISING,
desc->name, desc);
if (ret < 0) {
dev_err(desc->dev,
diff --git a/arch/arm/mach-msm/pil-dsps.c b/arch/arm/mach-msm/pil-dsps.c
index 73b58ab..f0a5ebf 100644
--- a/arch/arm/mach-msm/pil-dsps.c
+++ b/arch/arm/mach-msm/pil-dsps.c
@@ -23,6 +23,7 @@
#include <mach/msm_smsm.h>
#include <mach/ramdump.h>
#include <mach/msm_smem.h>
+#include <mach/msm_bus_board.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -317,6 +318,8 @@
goto err_subsys;
}
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
ret = smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
dsps_smsm_state_cb, drv);
if (ret)
diff --git a/arch/arm/mach-msm/pil-gss.c b/arch/arm/mach-msm/pil-gss.c
index 840c90f..b209a25 100644
--- a/arch/arm/mach-msm/pil-gss.c
+++ b/arch/arm/mach-msm/pil-gss.c
@@ -571,6 +571,8 @@
goto err_smem;
}
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
ret = devm_request_irq(&pdev->dev, drv->irq, gss_wdog_bite_irq,
IRQF_TRIGGER_RISING, "gss_a5_wdog", drv);
if (ret < 0)
diff --git a/arch/arm/mach-msm/pil-modem.c b/arch/arm/mach-msm/pil-modem.c
index 8398206..f6a853e 100644
--- a/arch/arm/mach-msm/pil-modem.c
+++ b/arch/arm/mach-msm/pil-modem.c
@@ -25,6 +25,7 @@
#include <mach/subsystem_restart.h>
#include <mach/msm_smsm.h>
#include <mach/ramdump.h>
+#include <mach/msm_bus_board.h>
#include "modem_notifier.h"
#include "peripheral-loader.h"
@@ -490,6 +491,8 @@
goto err_ramdump;
}
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
ret = devm_request_irq(&pdev->dev, drv->irq, modem_wdog_bite_irq,
IRQF_TRIGGER_RISING, "modem_watchdog", drv);
if (ret)
diff --git a/arch/arm/mach-msm/pil-msa.c b/arch/arm/mach-msm/pil-msa.c
new file mode 100644
index 0000000..3a26af9
--- /dev/null
+++ b/arch/arm/mach-msm/pil-msa.c
@@ -0,0 +1,386 @@
+/* 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/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/dma-mapping.h>
+
+#include "peripheral-loader.h"
+#include "pil-q6v5.h"
+#include "pil-msa.h"
+
+/* Q6 Register Offsets */
+#define QDSP6SS_RST_EVB 0x010
+
+/* AXI Halting Registers */
+#define MSS_Q6_HALT_BASE 0x180
+#define MSS_MODEM_HALT_BASE 0x200
+#define MSS_NC_HALT_BASE 0x280
+
+/* RMB Status Register Values */
+#define STATUS_PBL_SUCCESS 0x1
+#define STATUS_XPU_UNLOCKED 0x1
+#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2
+
+/* PBL/MBA interface registers */
+#define RMB_MBA_IMAGE 0x00
+#define RMB_PBL_STATUS 0x04
+#define RMB_MBA_COMMAND 0x08
+#define RMB_MBA_STATUS 0x0C
+#define RMB_PMI_META_DATA 0x10
+#define RMB_PMI_CODE_START 0x14
+#define RMB_PMI_CODE_LENGTH 0x18
+
+#define MAX_VDD_MX_UV 1150000
+
+#define POLL_INTERVAL_US 50
+
+#define CMD_META_DATA_READY 0x1
+#define CMD_LOAD_READY 0x2
+
+#define STATUS_META_DATA_AUTH_SUCCESS 0x3
+#define STATUS_AUTH_COMPLETE 0x4
+
+/* External BHS */
+#define EXTERNAL_BHS_ON BIT(0)
+#define EXTERNAL_BHS_STATUS BIT(4)
+#define BHS_TIMEOUT_US 50
+
+static int pbl_mba_boot_timeout_ms = 1000;
+module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
+
+static int modem_auth_timeout_ms = 10000;
+module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
+
+static int pil_msa_pbl_power_up(struct q6v5_data *drv)
+{
+ int ret = 0;
+ struct device *dev = drv->desc.dev;
+ u32 regval;
+
+ if (drv->vreg) {
+ ret = regulator_enable(drv->vreg);
+ if (ret)
+ dev_err(dev, "Failed to enable modem regulator.\n");
+ }
+
+ if (drv->cxrail_bhs) {
+ regval = readl_relaxed(drv->cxrail_bhs);
+ regval |= EXTERNAL_BHS_ON;
+ writel_relaxed(regval, drv->cxrail_bhs);
+
+ ret = readl_poll_timeout(drv->cxrail_bhs, regval,
+ regval & EXTERNAL_BHS_STATUS, 1, BHS_TIMEOUT_US);
+ }
+
+ return ret;
+}
+
+static int pil_msa_pbl_power_down(struct q6v5_data *drv)
+{
+ u32 regval;
+
+ if (drv->cxrail_bhs) {
+ regval = readl_relaxed(drv->cxrail_bhs);
+ regval &= ~EXTERNAL_BHS_ON;
+ writel_relaxed(regval, drv->cxrail_bhs);
+ }
+
+ if (drv->vreg)
+ return regulator_disable(drv->vreg);
+
+ return 0;
+}
+
+static int pil_msa_pbl_enable_clks(struct q6v5_data *drv)
+{
+ int ret;
+
+ ret = clk_prepare_enable(drv->ahb_clk);
+ if (ret)
+ goto err_ahb_clk;
+ ret = clk_prepare_enable(drv->axi_clk);
+ if (ret)
+ goto err_axi_clk;
+ ret = clk_prepare_enable(drv->rom_clk);
+ if (ret)
+ goto err_rom_clk;
+
+ return 0;
+
+err_rom_clk:
+ clk_disable_unprepare(drv->axi_clk);
+err_axi_clk:
+ clk_disable_unprepare(drv->ahb_clk);
+err_ahb_clk:
+ return ret;
+}
+
+static void pil_msa_pbl_disable_clks(struct q6v5_data *drv)
+{
+ clk_disable_unprepare(drv->rom_clk);
+ clk_disable_unprepare(drv->axi_clk);
+ clk_disable_unprepare(drv->ahb_clk);
+}
+
+static int pil_msa_wait_for_mba_ready(struct q6v5_data *drv)
+{
+ struct device *dev = drv->desc.dev;
+ int ret;
+ u32 status;
+
+ /* Wait for PBL completion. */
+ ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
+ status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
+ if (ret) {
+ dev_err(dev, "PBL boot timed out\n");
+ return ret;
+ }
+ if (status != STATUS_PBL_SUCCESS) {
+ dev_err(dev, "PBL returned unexpected status %d\n", status);
+ return -EINVAL;
+ }
+
+ /* Wait for MBA completion. */
+ ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
+ status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
+ if (ret) {
+ dev_err(dev, "MBA boot timed out\n");
+ return ret;
+ }
+ if (status != STATUS_XPU_UNLOCKED &&
+ status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
+ dev_err(dev, "MBA returned unexpected status %d\n", status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pil_msa_pbl_shutdown(struct pil_desc *pil)
+{
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+
+ pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
+ pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
+ pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
+
+ writel_relaxed(1, drv->restart_reg);
+
+ if (drv->is_booted) {
+ pil_msa_pbl_disable_clks(drv);
+ pil_msa_pbl_power_down(drv);
+ drv->is_booted = false;
+ }
+
+ return 0;
+}
+
+static int pil_msa_pbl_reset(struct pil_desc *pil)
+{
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
+ int ret;
+
+ /*
+ * Bring subsystem out of reset and enable required
+ * regulators and clocks.
+ */
+ ret = pil_msa_pbl_power_up(drv);
+ if (ret)
+ goto err_power;
+
+ /* Deassert reset to subsystem and wait for propagation */
+ writel_relaxed(0, drv->restart_reg);
+ mb();
+ udelay(2);
+
+ ret = pil_msa_pbl_enable_clks(drv);
+ if (ret)
+ goto err_clks;
+
+ /* Program Image Address */
+ if (drv->self_auth) {
+ writel_relaxed(start_addr, drv->rmb_base + RMB_MBA_IMAGE);
+ /* Ensure write to RMB base occurs before reset is released. */
+ mb();
+ } else {
+ writel_relaxed((start_addr >> 4) & 0x0FFFFFF0,
+ drv->reg_base + QDSP6SS_RST_EVB);
+ }
+
+ ret = pil_q6v5_reset(pil);
+ if (ret)
+ goto err_q6v5_reset;
+
+ /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
+ if (drv->self_auth) {
+ ret = pil_msa_wait_for_mba_ready(drv);
+ if (ret)
+ goto err_auth;
+ }
+
+ drv->is_booted = true;
+
+ return 0;
+
+err_auth:
+ pil_q6v5_shutdown(pil);
+err_q6v5_reset:
+ pil_msa_pbl_disable_clks(drv);
+err_clks:
+ pil_msa_pbl_power_down(drv);
+err_power:
+ return ret;
+}
+
+static int pil_msa_pbl_make_proxy_votes(struct pil_desc *pil)
+{
+ int ret;
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+
+ ret = regulator_set_voltage(drv->vreg_mx, VDD_MSS_UV, MAX_VDD_MX_UV);
+ if (ret) {
+ dev_err(pil->dev, "Failed to request vreg_mx voltage\n");
+ return ret;
+ }
+
+ ret = regulator_enable(drv->vreg_mx);
+ if (ret) {
+ dev_err(pil->dev, "Failed to enable vreg_mx\n");
+ regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
+ return ret;
+ }
+
+ ret = pil_q6v5_make_proxy_votes(pil);
+ if (ret) {
+ regulator_disable(drv->vreg_mx);
+ regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
+ }
+
+ return ret;
+}
+
+static void pil_msa_pbl_remove_proxy_votes(struct pil_desc *pil)
+{
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+ pil_q6v5_remove_proxy_votes(pil);
+ regulator_disable(drv->vreg_mx);
+ regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
+}
+
+struct pil_reset_ops pil_msa_pbl_ops = {
+ .proxy_vote = pil_msa_pbl_make_proxy_votes,
+ .proxy_unvote = pil_msa_pbl_remove_proxy_votes,
+ .auth_and_reset = pil_msa_pbl_reset,
+ .shutdown = pil_msa_pbl_shutdown,
+};
+
+static int pil_msa_mba_init_image(struct pil_desc *pil,
+ const u8 *metadata, size_t size)
+{
+ struct mba_data *drv = container_of(pil, struct mba_data, desc);
+ void *mdata_virt;
+ dma_addr_t mdata_phys;
+ s32 status;
+ int ret;
+
+ /* Make metadata physically contiguous and 4K aligned. */
+ mdata_virt = dma_alloc_coherent(pil->dev, size, &mdata_phys,
+ GFP_KERNEL);
+ if (!mdata_virt) {
+ dev_err(pil->dev, "MBA metadata buffer allocation failed\n");
+ return -ENOMEM;
+ }
+ memcpy(mdata_virt, metadata, size);
+ /* wmb() ensures copy completes prior to starting authentication. */
+ wmb();
+
+ /* Initialize length counter to 0 */
+ writel_relaxed(0, drv->rmb_base + RMB_PMI_CODE_LENGTH);
+
+ /* Pass address of meta-data to the MBA and perform authentication */
+ writel_relaxed(mdata_phys, drv->rmb_base + RMB_PMI_META_DATA);
+ writel_relaxed(CMD_META_DATA_READY, drv->rmb_base + RMB_MBA_COMMAND);
+ ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
+ status == STATUS_META_DATA_AUTH_SUCCESS || status < 0,
+ POLL_INTERVAL_US, modem_auth_timeout_ms * 1000);
+ if (ret) {
+ dev_err(pil->dev, "MBA authentication of headers timed out\n");
+ } else if (status < 0) {
+ dev_err(pil->dev, "MBA returned error %d for headers\n",
+ status);
+ ret = -EINVAL;
+ }
+
+ dma_free_coherent(pil->dev, size, mdata_virt, mdata_phys);
+
+ return ret;
+}
+
+static int pil_msa_mba_verify_blob(struct pil_desc *pil, phys_addr_t phy_addr,
+ size_t size)
+{
+ struct mba_data *drv = container_of(pil, struct mba_data, desc);
+ s32 status;
+ u32 img_length = readl_relaxed(drv->rmb_base + RMB_PMI_CODE_LENGTH);
+
+ /* Begin image authentication */
+ if (img_length == 0) {
+ writel_relaxed(phy_addr, drv->rmb_base + RMB_PMI_CODE_START);
+ writel_relaxed(CMD_LOAD_READY, drv->rmb_base + RMB_MBA_COMMAND);
+ }
+ /* Increment length counter */
+ img_length += size;
+ writel_relaxed(img_length, drv->rmb_base + RMB_PMI_CODE_LENGTH);
+
+ status = readl_relaxed(drv->rmb_base + RMB_MBA_STATUS);
+ if (status < 0) {
+ dev_err(pil->dev, "MBA returned error %d\n", status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pil_msa_mba_auth(struct pil_desc *pil)
+{
+ struct mba_data *drv = container_of(pil, struct mba_data, desc);
+ int ret;
+ s32 status;
+
+ /* Wait for all segments to be authenticated or an error to occur */
+ ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
+ status == STATUS_AUTH_COMPLETE || status < 0,
+ 50, modem_auth_timeout_ms * 1000);
+ if (ret) {
+ dev_err(pil->dev, "MBA authentication of image timed out\n");
+ } else if (status < 0) {
+ dev_err(pil->dev, "MBA returned error %d for image\n", status);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+struct pil_reset_ops pil_msa_mba_ops = {
+ .init_image = pil_msa_mba_init_image,
+ .verify_blob = pil_msa_mba_verify_blob,
+ .auth_and_reset = pil_msa_mba_auth,
+};
diff --git a/arch/arm/mach-msm/pil-msa.h b/arch/arm/mach-msm/pil-msa.h
new file mode 100644
index 0000000..9a7b56e
--- /dev/null
+++ b/arch/arm/mach-msm/pil-msa.h
@@ -0,0 +1,29 @@
+/* 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.
+ */
+
+#ifndef __MSM_PIL_MSA_H
+#define __MSM_PIL_MSA_H
+
+#include "peripheral-loader.h"
+
+#define VDD_MSS_UV 1050000
+
+struct mba_data {
+ void __iomem *rmb_base;
+ struct clk *xo;
+ struct pil_desc desc;
+};
+
+extern struct pil_reset_ops pil_msa_pbl_ops;
+extern struct pil_reset_ops pil_msa_mba_ops;
+
+#endif
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index 1085408..6e4aa02 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -31,6 +31,7 @@
#include <mach/msm_smsm.h>
#include <mach/ramdump.h>
#include <mach/msm_smem.h>
+#include <mach/msm_bus_board.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -336,6 +337,7 @@
pr_err("Ignoring wcnss bite irq, restart in progress\n");
return IRQ_HANDLED;
}
+ wcnss_pronto_log_debug_regs();
drv->restart_inprogress = true;
restart_wcnss(drv);
@@ -503,6 +505,8 @@
if (IS_ERR(drv->cxo))
return PTR_ERR(drv->cxo);
+ scm_pas_init(MSM_BUS_MASTER_CRYPTO_CORE0);
+
ret = pil_desc_init(desc);
if (ret)
return ret;
diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c
index a369878..58d4301 100644
--- a/arch/arm/mach-msm/pil-q6v3.c
+++ b/arch/arm/mach-msm/pil-q6v3.c
@@ -24,6 +24,7 @@
#include <mach/subsystem_restart.h>
#include <mach/scm.h>
#include <mach/ramdump.h>
+#include <mach/msm_bus_board.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -393,6 +394,8 @@
goto err_subsys;
}
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
ret = devm_request_irq(&pdev->dev, drv->irq, lpass_wdog_bite_irq,
IRQF_TRIGGER_RISING, "lpass_wdog", drv);
if (ret) {
diff --git a/arch/arm/mach-msm/pil-q6v4-lpass.c b/arch/arm/mach-msm/pil-q6v4-lpass.c
index 7acb599..6ec8430 100644
--- a/arch/arm/mach-msm/pil-q6v4-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v4-lpass.c
@@ -26,6 +26,7 @@
#include <mach/subsystem_notif.h>
#include <mach/ramdump.h>
#include <mach/msm_smem.h>
+#include <mach/msm_bus_board.h>
#include "smd_private.h"
#include "sysmon.h"
@@ -356,6 +357,8 @@
goto err_notif_riva;
}
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
if (IS_ERR(drv->modem_notif_hdle)) {
ret = PTR_ERR(drv->modem_notif_hdle);
diff --git a/arch/arm/mach-msm/pil-q6v4-mss.c b/arch/arm/mach-msm/pil-q6v4-mss.c
index c4b6038..6ee5965 100644
--- a/arch/arm/mach-msm/pil-q6v4-mss.c
+++ b/arch/arm/mach-msm/pil-q6v4-mss.c
@@ -24,6 +24,7 @@
#include <mach/msm_smsm.h>
#include <mach/ramdump.h>
#include <mach/msm_smem.h>
+#include <mach/msm_bus_board.h>
#include "smd_private.h"
#include "peripheral-loader.h"
@@ -462,6 +463,8 @@
if (ret)
goto err_irq;
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
smsm_state_cb, drv);
if (ret)
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index 6cd6ffe..3b88b10 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -30,6 +30,7 @@
#include <mach/scm.h>
#include <mach/ramdump.h>
#include <mach/msm_smem.h>
+#include <mach/msm_bus_board.h>
#include "peripheral-loader.h"
#include "pil-q6v5.h"
@@ -472,6 +473,8 @@
dev_info(&pdev->dev, "using non-secure boot\n");
}
+ scm_pas_init(MSM_BUS_MASTER_CRYPTO_CORE0);
+
ret = pil_desc_init(desc);
if (ret)
return ret;
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index b83202b..1f03fd32 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -35,64 +35,21 @@
#include "peripheral-loader.h"
#include "pil-q6v5.h"
+#include "pil-msa.h"
#include "sysmon.h"
-/* Q6 Register Offsets */
-#define QDSP6SS_RST_EVB 0x010
+#define MAX_VDD_MSS_UV 1150000
+#define PROXY_TIMEOUT_MS 10000
+#define MAX_SSR_REASON_LEN 81U
+#define STOP_ACK_TIMEOUT_MS 1000
-/* AXI Halting Registers */
-#define MSS_Q6_HALT_BASE 0x180
-#define MSS_MODEM_HALT_BASE 0x200
-#define MSS_NC_HALT_BASE 0x280
-
-/* RMB Status Register Values */
-#define STATUS_PBL_SUCCESS 0x1
-#define STATUS_XPU_UNLOCKED 0x1
-#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2
-
-/* PBL/MBA interface registers */
-#define RMB_MBA_IMAGE 0x00
-#define RMB_PBL_STATUS 0x04
-#define RMB_MBA_COMMAND 0x08
-#define RMB_MBA_STATUS 0x0C
-#define RMB_PMI_META_DATA 0x10
-#define RMB_PMI_CODE_START 0x14
-#define RMB_PMI_CODE_LENGTH 0x18
-
-#define VDD_MSS_UV 1050000
-#define MAX_VDD_MSS_UV 1150000
-#define MAX_VDD_MX_UV 1150000
-
-#define PROXY_TIMEOUT_MS 10000
-#define POLL_INTERVAL_US 50
-
-#define CMD_META_DATA_READY 0x1
-#define CMD_LOAD_READY 0x2
-
-#define STATUS_META_DATA_AUTH_SUCCESS 0x3
-#define STATUS_AUTH_COMPLETE 0x4
-
-#define MAX_SSR_REASON_LEN 81U
-
-/* External BHS */
-#define EXTERNAL_BHS_ON BIT(0)
-#define EXTERNAL_BHS_STATUS BIT(4)
-#define BHS_TIMEOUT_US 50
-
-#define STOP_ACK_TIMEOUT_MS 1000
-
-struct mba_data {
- void __iomem *rmb_base;
- void __iomem *io_clamp_reg;
- struct pil_desc desc;
+struct modem_data {
+ struct mba_data *mba;
+ struct q6v5_data *q6;
struct subsys_device *subsys;
struct subsys_desc subsys_desc;
void *adsp_state_notifier;
- u32 img_length;
- struct q6v5_data *q6;
- bool self_auth;
void *ramdump_dev;
- void *smem_ramdump_dev;
bool crash_shutdown;
bool ignore_errors;
int err_fatal_irq;
@@ -101,363 +58,16 @@
struct completion stop_ack;
};
-static int pbl_mba_boot_timeout_ms = 1000;
-module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
-
-static int modem_auth_timeout_ms = 10000;
-module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
-
-static int pil_mss_power_up(struct q6v5_data *drv)
-{
- int ret = 0;
- struct device *dev = drv->desc.dev;
- u32 regval;
-
- if (drv->vreg) {
- ret = regulator_enable(drv->vreg);
- if (ret)
- dev_err(dev, "Failed to enable modem regulator.\n");
- }
-
- if (drv->cxrail_bhs) {
- regval = readl_relaxed(drv->cxrail_bhs);
- regval |= EXTERNAL_BHS_ON;
- writel_relaxed(regval, drv->cxrail_bhs);
-
- ret = readl_poll_timeout(drv->cxrail_bhs, regval,
- regval & EXTERNAL_BHS_STATUS, 1, BHS_TIMEOUT_US);
- }
-
- return ret;
-}
-
-static int pil_mss_power_down(struct q6v5_data *drv)
-{
- u32 regval;
-
- if (drv->cxrail_bhs) {
- regval = readl_relaxed(drv->cxrail_bhs);
- regval &= ~EXTERNAL_BHS_ON;
- writel_relaxed(regval, drv->cxrail_bhs);
- }
-
- if (drv->vreg)
- return regulator_disable(drv->vreg);
-
- return 0;
-}
-
-static int pil_mss_enable_clks(struct q6v5_data *drv)
-{
- int ret;
-
- ret = clk_prepare_enable(drv->ahb_clk);
- if (ret)
- goto err_ahb_clk;
- ret = clk_prepare_enable(drv->axi_clk);
- if (ret)
- goto err_axi_clk;
- ret = clk_prepare_enable(drv->rom_clk);
- if (ret)
- goto err_rom_clk;
-
- return 0;
-
-err_rom_clk:
- clk_disable_unprepare(drv->axi_clk);
-err_axi_clk:
- clk_disable_unprepare(drv->ahb_clk);
-err_ahb_clk:
- return ret;
-}
-
-static void pil_mss_disable_clks(struct q6v5_data *drv)
-{
- clk_disable_unprepare(drv->rom_clk);
- clk_disable_unprepare(drv->axi_clk);
- clk_disable_unprepare(drv->ahb_clk);
-}
-
-static int wait_for_mba_ready(struct q6v5_data *drv)
-{
- struct device *dev = drv->desc.dev;
- struct mba_data *mba = platform_get_drvdata(to_platform_device(dev));
- int ret;
- u32 status;
-
- /* Wait for PBL completion. */
- ret = readl_poll_timeout(mba->rmb_base + RMB_PBL_STATUS, status,
- status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
- if (ret) {
- dev_err(dev, "PBL boot timed out\n");
- return ret;
- }
- if (status != STATUS_PBL_SUCCESS) {
- dev_err(dev, "PBL returned unexpected status %d\n", status);
- return -EINVAL;
- }
-
- /* Wait for MBA completion. */
- ret = readl_poll_timeout(mba->rmb_base + RMB_MBA_STATUS, status,
- status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
- if (ret) {
- dev_err(dev, "MBA boot timed out\n");
- return ret;
- }
- if (status != STATUS_XPU_UNLOCKED &&
- status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
- dev_err(dev, "MBA returned unexpected status %d\n", status);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int pil_mss_shutdown(struct pil_desc *pil)
-{
- struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
-
- pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
- pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
- pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
-
- writel_relaxed(1, drv->restart_reg);
-
- if (drv->is_booted) {
- pil_mss_disable_clks(drv);
- pil_mss_power_down(drv);
- drv->is_booted = false;
- }
-
- return 0;
-}
-
-static int pil_mss_reset(struct pil_desc *pil)
-{
- struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
- struct platform_device *pdev = to_platform_device(pil->dev);
- struct mba_data *mba = platform_get_drvdata(pdev);
- phys_addr_t start_addr = pil_get_entry_addr(pil);
- int ret;
-
- /*
- * Bring subsystem out of reset and enable required
- * regulators and clocks.
- */
- ret = pil_mss_power_up(drv);
- if (ret)
- goto err_power;
-
- /* Deassert reset to subsystem and wait for propagation */
- writel_relaxed(0, drv->restart_reg);
- mb();
- udelay(2);
-
- ret = pil_mss_enable_clks(drv);
- if (ret)
- goto err_clks;
-
- /* Program Image Address */
- if (mba->self_auth) {
- writel_relaxed(start_addr, mba->rmb_base + RMB_MBA_IMAGE);
- /* Ensure write to RMB base occurs before reset is released. */
- mb();
- } else {
- writel_relaxed((start_addr >> 4) & 0x0FFFFFF0,
- drv->reg_base + QDSP6SS_RST_EVB);
- }
-
- ret = pil_q6v5_reset(pil);
- if (ret)
- goto err_q6v5_reset;
-
- /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
- if (mba->self_auth) {
- ret = wait_for_mba_ready(drv);
- if (ret)
- goto err_auth;
- }
-
- drv->is_booted = true;
-
- return 0;
-
-err_auth:
- pil_q6v5_shutdown(pil);
-err_q6v5_reset:
- pil_mss_disable_clks(drv);
-err_clks:
- pil_mss_power_down(drv);
-err_power:
- return ret;
-}
-
-static int pil_q6v5_mss_make_proxy_votes(struct pil_desc *pil)
-{
- int ret;
- struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
-
- ret = regulator_set_voltage(drv->vreg_mx, VDD_MSS_UV, MAX_VDD_MX_UV);
- if (ret) {
- dev_err(pil->dev, "Failed to request vreg_mx voltage\n");
- return ret;
- }
-
- ret = regulator_enable(drv->vreg_mx);
- if (ret) {
- dev_err(pil->dev, "Failed to enable vreg_mx\n");
- regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
- return ret;
- }
-
- ret = pil_q6v5_make_proxy_votes(pil);
- if (ret) {
- regulator_disable(drv->vreg_mx);
- regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
- }
-
- return ret;
-}
-
-static void pil_q6v5_mss_remove_proxy_votes(struct pil_desc *pil)
-{
- struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
- pil_q6v5_remove_proxy_votes(pil);
- regulator_disable(drv->vreg_mx);
- regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
-}
-
-static struct pil_reset_ops pil_mss_ops = {
- .proxy_vote = pil_q6v5_mss_make_proxy_votes,
- .proxy_unvote = pil_q6v5_mss_remove_proxy_votes,
- .auth_and_reset = pil_mss_reset,
- .shutdown = pil_mss_shutdown,
-};
-
-static int pil_mba_make_proxy_votes(struct pil_desc *pil)
-{
- int ret;
- struct mba_data *drv = dev_get_drvdata(pil->dev);
-
- ret = clk_prepare_enable(drv->q6->xo);
- if (ret) {
- dev_err(pil->dev, "Failed to enable XO\n");
- return ret;
- }
- return 0;
-}
-
-static void pil_mba_remove_proxy_votes(struct pil_desc *pil)
-{
- struct mba_data *drv = dev_get_drvdata(pil->dev);
- clk_disable_unprepare(drv->q6->xo);
-}
-
-static int pil_mba_init_image(struct pil_desc *pil,
- const u8 *metadata, size_t size)
-{
- struct mba_data *drv = dev_get_drvdata(pil->dev);
- void *mdata_virt;
- dma_addr_t mdata_phys;
- s32 status;
- int ret;
-
- /* Make metadata physically contiguous and 4K aligned. */
- mdata_virt = dma_alloc_coherent(pil->dev, size, &mdata_phys,
- GFP_KERNEL);
- if (!mdata_virt) {
- dev_err(pil->dev, "MBA metadata buffer allocation failed\n");
- return -ENOMEM;
- }
- memcpy(mdata_virt, metadata, size);
- /* wmb() ensures copy completes prior to starting authentication. */
- wmb();
-
- /* Initialize length counter to 0 */
- writel_relaxed(0, drv->rmb_base + RMB_PMI_CODE_LENGTH);
- drv->img_length = 0;
-
- /* Pass address of meta-data to the MBA and perform authentication */
- writel_relaxed(mdata_phys, drv->rmb_base + RMB_PMI_META_DATA);
- writel_relaxed(CMD_META_DATA_READY, drv->rmb_base + RMB_MBA_COMMAND);
- ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
- status == STATUS_META_DATA_AUTH_SUCCESS || status < 0,
- POLL_INTERVAL_US, modem_auth_timeout_ms * 1000);
- if (ret) {
- dev_err(pil->dev, "MBA authentication of headers timed out\n");
- } else if (status < 0) {
- dev_err(pil->dev, "MBA returned error %d for headers\n",
- status);
- ret = -EINVAL;
- }
-
- dma_free_coherent(pil->dev, size, mdata_virt, mdata_phys);
-
- return ret;
-}
-
-static int pil_mba_verify_blob(struct pil_desc *pil, phys_addr_t phy_addr,
- size_t size)
-{
- struct mba_data *drv = dev_get_drvdata(pil->dev);
- s32 status;
-
- /* Begin image authentication */
- if (drv->img_length == 0) {
- writel_relaxed(phy_addr, drv->rmb_base + RMB_PMI_CODE_START);
- writel_relaxed(CMD_LOAD_READY, drv->rmb_base + RMB_MBA_COMMAND);
- }
- /* Increment length counter */
- drv->img_length += size;
- writel_relaxed(drv->img_length, drv->rmb_base + RMB_PMI_CODE_LENGTH);
-
- status = readl_relaxed(drv->rmb_base + RMB_MBA_STATUS);
- if (status < 0) {
- dev_err(pil->dev, "MBA returned error %d\n", status);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int pil_mba_auth(struct pil_desc *pil)
-{
- struct mba_data *drv = dev_get_drvdata(pil->dev);
- int ret;
- s32 status;
-
- /* Wait for all segments to be authenticated or an error to occur */
- ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
- status == STATUS_AUTH_COMPLETE || status < 0,
- 50, modem_auth_timeout_ms * 1000);
- if (ret) {
- dev_err(pil->dev, "MBA authentication of image timed out\n");
- } else if (status < 0) {
- dev_err(pil->dev, "MBA returned error %d for image\n", status);
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static struct pil_reset_ops pil_mba_ops = {
- .init_image = pil_mba_init_image,
- .proxy_vote = pil_mba_make_proxy_votes,
- .proxy_unvote = pil_mba_remove_proxy_votes,
- .verify_blob = pil_mba_verify_blob,
- .auth_and_reset = pil_mba_auth,
-};
-
-#define subsys_to_drv(d) container_of(d, struct mba_data, subsys_desc)
+#define subsys_to_drv(d) container_of(d, struct modem_data, subsys_desc)
static void log_modem_sfr(void)
{
u32 size;
char *smem_reason, reason[MAX_SSR_REASON_LEN];
- smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
+ smem_reason = smem_get_entry_no_rlock(SMEM_SSR_REASON_MSS0, &size);
if (!smem_reason || !size) {
- pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
+ pr_err("modem subsystem failure reason: (unknown, smem_get_entry_no_rlock failed).\n");
return;
}
if (!smem_reason[0]) {
@@ -472,7 +82,7 @@
wmb();
}
-static void restart_modem(struct mba_data *drv)
+static void restart_modem(struct modem_data *drv)
{
log_modem_sfr();
drv->ignore_errors = true;
@@ -481,7 +91,7 @@
static irqreturn_t modem_err_fatal_intr_handler(int irq, void *dev_id)
{
- struct mba_data *drv = dev_id;
+ struct modem_data *drv = dev_id;
/* Ignore if we're the one that set the force stop GPIO */
if (drv->crash_shutdown)
@@ -495,7 +105,7 @@
static irqreturn_t modem_stop_ack_intr_handler(int irq, void *dev_id)
{
- struct mba_data *drv = dev_id;
+ struct modem_data *drv = dev_id;
pr_info("Received stop ack interrupt from modem\n");
complete(&drv->stop_ack);
return IRQ_HANDLED;
@@ -503,7 +113,7 @@
static int modem_shutdown(const struct subsys_desc *subsys)
{
- struct mba_data *drv = subsys_to_drv(subsys);
+ struct modem_data *drv = subsys_to_drv(subsys);
unsigned long ret;
if (subsys->is_not_loadable)
@@ -518,14 +128,14 @@
gpio_set_value(drv->force_stop_gpio, 0);
}
- pil_shutdown(&drv->desc);
+ pil_shutdown(&drv->mba->desc);
pil_shutdown(&drv->q6->desc);
return 0;
}
static int modem_powerup(const struct subsys_desc *subsys)
{
- struct mba_data *drv = subsys_to_drv(subsys);
+ struct modem_data *drv = subsys_to_drv(subsys);
int ret;
if (subsys->is_not_loadable)
@@ -535,12 +145,12 @@
* run concurrently with either the watchdog bite error handler or the
* SMSM callback, making it safe to unset the flag below.
*/
- init_completion(&drv->stop_ack);
+ INIT_COMPLETION(drv->stop_ack);
drv->ignore_errors = false;
ret = pil_boot(&drv->q6->desc);
if (ret)
return ret;
- ret = pil_boot(&drv->desc);
+ ret = pil_boot(&drv->mba->desc);
if (ret)
pil_shutdown(&drv->q6->desc);
return ret;
@@ -548,7 +158,7 @@
static void modem_crash_shutdown(const struct subsys_desc *subsys)
{
- struct mba_data *drv = subsys_to_drv(subsys);
+ struct modem_data *drv = subsys_to_drv(subsys);
drv->crash_shutdown = true;
if (!subsys_get_crash_status(drv->subsys)) {
gpio_set_value(drv->force_stop_gpio, 1);
@@ -556,13 +166,9 @@
}
}
-static struct ramdump_segment smem_segments[] = {
- {0x0FA00000, 0x0FC00000 - 0x0FA00000},
-};
-
static int modem_ramdump(int enable, const struct subsys_desc *subsys)
{
- struct mba_data *drv = subsys_to_drv(subsys);
+ struct modem_data *drv = subsys_to_drv(subsys);
int ret;
if (!enable)
@@ -572,20 +178,10 @@
if (ret)
return ret;
- ret = pil_do_ramdump(&drv->desc, drv->ramdump_dev);
- if (ret < 0) {
+ ret = pil_do_ramdump(&drv->mba->desc, drv->ramdump_dev);
+ if (ret < 0)
pr_err("Unable to dump modem fw memory (rc = %d).\n", ret);
- goto out;
- }
- ret = do_elf_ramdump(drv->smem_ramdump_dev, smem_segments,
- ARRAY_SIZE(smem_segments));
- if (ret < 0) {
- pr_err("Unable to dump smem memory (rc = %d).\n", ret);
- goto out;
- }
-
-out:
pil_shutdown(&drv->q6->desc);
return ret;
}
@@ -606,7 +202,7 @@
static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
{
- struct mba_data *drv = dev_id;
+ struct modem_data *drv = dev_id;
if (drv->ignore_errors)
return IRQ_HANDLED;
pr_err("Watchdog bite received from modem software!\n");
@@ -618,16 +214,16 @@
static int mss_start(const struct subsys_desc *desc)
{
int ret;
- struct mba_data *drv = subsys_to_drv(desc);
+ struct modem_data *drv = subsys_to_drv(desc);
if (desc->is_not_loadable)
return 0;
- init_completion(&drv->stop_ack);
+ INIT_COMPLETION(drv->stop_ack);
ret = pil_boot(&drv->q6->desc);
if (ret)
return ret;
- ret = pil_boot(&drv->desc);
+ ret = pil_boot(&drv->mba->desc);
if (ret)
pil_shutdown(&drv->q6->desc);
return ret;
@@ -635,16 +231,16 @@
static void mss_stop(const struct subsys_desc *desc)
{
- struct mba_data *drv = subsys_to_drv(desc);
+ struct modem_data *drv = subsys_to_drv(desc);
if (desc->is_not_loadable)
return;
- pil_shutdown(&drv->desc);
+ pil_shutdown(&drv->mba->desc);
pil_shutdown(&drv->q6->desc);
}
-static int __devinit pil_subsys_init(struct mba_data *drv,
+static int __devinit pil_subsys_init(struct modem_data *drv,
struct platform_device *pdev)
{
int irq, ret;
@@ -688,14 +284,6 @@
goto err_ramdump;
}
- drv->smem_ramdump_dev = create_ramdump_device("smem-modem", &pdev->dev);
- if (!drv->smem_ramdump_dev) {
- pr_err("%s: Unable to create an smem ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto err_ramdump_smem;
- }
-
ret = devm_request_irq(&pdev->dev, irq, modem_wdog_bite_irq,
IRQF_TRIGGER_RISING, "modem_wdog", drv);
if (ret < 0) {
@@ -731,8 +319,6 @@
return 0;
err_irq:
- destroy_ramdump_device(drv->smem_ramdump_dev);
-err_ramdump_smem:
destroy_ramdump_device(drv->ramdump_dev);
err_ramdump:
subsys_unregister(drv->subsys);
@@ -740,10 +326,11 @@
return ret;
}
-static int __devinit pil_mss_loadable_init(struct mba_data *drv,
+static int __devinit pil_mss_loadable_init(struct modem_data *drv,
struct platform_device *pdev)
{
struct q6v5_data *q6;
+ struct mba_data *mba;
struct pil_desc *q6_desc, *mba_desc;
struct resource *res;
struct property *prop;
@@ -758,25 +345,32 @@
if (clk_ready < 0)
return clk_ready;
+ mba = devm_kzalloc(&pdev->dev, sizeof(*mba), GFP_KERNEL);
+ if (IS_ERR(mba))
+ return PTR_ERR(mba);
+ drv->mba = mba;
+
q6 = pil_q6v5_init(pdev);
if (IS_ERR(q6))
return PTR_ERR(q6);
drv->q6 = q6;
+ drv->mba->xo = q6->xo;
q6_desc = &q6->desc;
- q6_desc->ops = &pil_mss_ops;
+ q6_desc->ops = &pil_msa_pbl_ops;
q6_desc->owner = THIS_MODULE;
q6_desc->proxy_timeout = PROXY_TIMEOUT_MS;
q6_desc->proxy_unvote_irq = clk_ready;
- drv->self_auth = of_property_read_bool(pdev->dev.of_node,
+ q6->self_auth = of_property_read_bool(pdev->dev.of_node,
"qcom,pil-self-auth");
- if (drv->self_auth) {
+ if (q6->self_auth) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"rmb_base");
- drv->rmb_base = devm_request_and_ioremap(&pdev->dev, res);
- if (!drv->rmb_base)
+ q6->rmb_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!q6->rmb_base)
return -ENOMEM;
+ mba->rmb_base = q6->rmb_base;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg");
@@ -830,13 +424,11 @@
if (ret)
return ret;
- mba_desc = &drv->desc;
+ mba_desc = &mba->desc;
mba_desc->name = "modem";
mba_desc->dev = &pdev->dev;
- mba_desc->ops = &pil_mba_ops;
+ mba_desc->ops = &pil_msa_mba_ops;
mba_desc->owner = THIS_MODULE;
- mba_desc->proxy_timeout = PROXY_TIMEOUT_MS;
- mba_desc->proxy_unvote_irq = clk_ready;
ret = pil_desc_init(mba_desc);
if (ret)
@@ -852,7 +444,7 @@
static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
{
- struct mba_data *drv;
+ struct modem_data *drv;
int ret, err_fatal_gpio, is_not_loadable, stop_ack_gpio;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
@@ -870,6 +462,8 @@
return ret;
}
+ init_completion(&drv->stop_ack);
+
/* Get the IRQ from the GPIO for registering inbound handler */
err_fatal_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,gpio-err-fatal", 0);
@@ -901,14 +495,13 @@
static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
{
- struct mba_data *drv = platform_get_drvdata(pdev);
+ struct modem_data *drv = platform_get_drvdata(pdev);
subsys_notif_unregister_notifier(drv->adsp_state_notifier,
&adsp_state_notifier_block);
subsys_unregister(drv->subsys);
- destroy_ramdump_device(drv->smem_ramdump_dev);
destroy_ramdump_device(drv->ramdump_dev);
- pil_desc_release(&drv->desc);
+ pil_desc_release(&drv->mba->desc);
pil_desc_release(&drv->q6->desc);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-q6v5.h b/arch/arm/mach-msm/pil-q6v5.h
index 48d10df..b2fb7f7 100644
--- a/arch/arm/mach-msm/pil-q6v5.h
+++ b/arch/arm/mach-msm/pil-q6v5.h
@@ -22,6 +22,7 @@
struct q6v5_data {
void __iomem *reg_base;
+ void __iomem *rmb_base;
void __iomem *cxrail_bhs; /* External BHS register */
struct clk *xo; /* XO clock source */
struct clk *ahb_clk; /* PIL access to registers */
@@ -37,6 +38,7 @@
struct regulator *vreg_pll;
bool is_booted;
struct pil_desc desc;
+ bool self_auth;
};
int pil_q6v5_make_proxy_votes(struct pil_desc *pil);
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index bf7438b..9a364b7 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -25,6 +25,7 @@
#include <mach/subsystem_restart.h>
#include <mach/ramdump.h>
#include <mach/msm_smem.h>
+#include <mach/msm_bus_board.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -534,6 +535,8 @@
goto err_subsys;
}
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
ret = devm_request_irq(&pdev->dev, drv->irq, riva_wdog_bite_irq_hdlr,
IRQF_TRIGGER_RISING, "riva_wdog", drv);
if (ret < 0)
diff --git a/arch/arm/mach-msm/pil-tzapps.c b/arch/arm/mach-msm/pil-tzapps.c
index 1410117..d9f5700 100644
--- a/arch/arm/mach-msm/pil-tzapps.c
+++ b/arch/arm/mach-msm/pil-tzapps.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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 <linux/err.h>
#include <mach/subsystem_restart.h>
+#include <mach/msm_bus_board.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -97,6 +98,9 @@
pil_desc_release(desc);
return PTR_ERR(drv->subsys);
}
+
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
return 0;
}
diff --git a/arch/arm/mach-msm/pil-venus.c b/arch/arm/mach-msm/pil-venus.c
index 4e9e54b..a7ebbf7 100644
--- a/arch/arm/mach-msm/pil-venus.c
+++ b/arch/arm/mach-msm/pil-venus.c
@@ -605,6 +605,8 @@
if (!drv->ramdump_dev)
return -ENOMEM;
+ scm_pas_init(MSM_BUS_MASTER_CRYPTO_CORE0);
+
rc = pil_desc_init(desc);
if (rc)
goto err_ramdump;
diff --git a/arch/arm/mach-msm/pil-vidc.c b/arch/arm/mach-msm/pil-vidc.c
index 629907f..3dbddae 100644
--- a/arch/arm/mach-msm/pil-vidc.c
+++ b/arch/arm/mach-msm/pil-vidc.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
@@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <mach/subsystem_restart.h>
+#include <mach/msm_bus_board.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -122,6 +123,9 @@
pil_desc_release(desc);
return PTR_ERR(drv->subsys);
}
+
+ scm_pas_init(MSM_BUS_MASTER_SPS);
+
return 0;
}
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index f4ca4e3..796fba1 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -147,10 +147,11 @@
unsigned int cpu)
{
void *base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K);
+
if (!base_ptr)
return -ENODEV;
- secondary_cpu_hs_init(base_ptr);
+ secondary_cpu_hs_init(base_ptr, cpu);
writel_relaxed(0x021, base_ptr+0x04);
mb();
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 6799cbf..dd0599b 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -31,7 +31,6 @@
#include <linux/of_platform.h>
#include <linux/regulator/krait-regulator.h>
#include <linux/cpu.h>
-#include <linux/clk.h>
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
#include <mach/system.h>
@@ -134,7 +133,6 @@
static bool msm_no_ramp_down_pc;
static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
static bool msm_pm_pc_reset_timer;
-static struct clk *pnoc_clk;
static int msm_pm_get_pc_mode(struct device_node *node,
const char *key, uint32_t *pc_mode_val)
@@ -967,7 +965,7 @@
if (acc_sts & msm_pm_slp_sts[cpu].mask)
return 0;
udelay(100);
- WARN(++timeout == 10, "CPU%u didn't collape within 1ms\n",
+ WARN(++timeout == 20, "CPU%u didn't collape within 2ms\n",
cpu);
}
@@ -1134,24 +1132,9 @@
pm_sleep_ops = *ops;
}
-int msm_suspend_prepare(void)
-{
- if (pnoc_clk != NULL)
- clk_disable_unprepare(pnoc_clk);
- return 0;
-}
-
-void msm_suspend_wake(void)
-{
- if (pnoc_clk != NULL)
- clk_prepare_enable(pnoc_clk);
-}
-
static const struct platform_suspend_ops msm_pm_ops = {
.enter = msm_pm_enter,
.valid = suspend_valid_only_mem,
- .prepare_late = msm_suspend_prepare,
- .wake = msm_suspend_wake,
};
static int __devinit msm_pm_snoc_client_probe(struct platform_device *pdev)
@@ -1285,14 +1268,90 @@
},
};
+#ifdef CONFIG_ARM_LPAE
+static int msm_pm_idmap_add_pmd(pud_t *pud, unsigned long addr,
+ unsigned long end, unsigned long prot)
+{
+ pmd_t *pmd;
+ unsigned long next;
-static int __init msm_pm_setup_saved_state(void)
+ if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) {
+ pmd = pmd_alloc_one(&init_mm, addr);
+ if (!pmd)
+ return -ENOMEM;
+
+ pud_populate(&init_mm, pud, pmd);
+ pmd += pmd_index(addr);
+ } else {
+ pmd = pmd_offset(pud, addr);
+ }
+
+ do {
+ next = pmd_addr_end(addr, end);
+ *pmd = __pmd((addr & PMD_MASK) | prot);
+ flush_pmd_entry(pmd);
+ } while (pmd++, addr = next, addr != end);
+
+ return 0;
+}
+#else /* !CONFIG_ARM_LPAE */
+static int msm_pm_idmap_add_pmd(pud_t *pud, unsigned long addr,
+ unsigned long end, unsigned long prot)
+{
+ pmd_t *pmd = pmd_offset(pud, addr);
+
+ addr = (addr & PMD_MASK) | prot;
+ pmd[0] = __pmd(addr);
+ addr += SECTION_SIZE;
+ pmd[1] = __pmd(addr);
+ flush_pmd_entry(pmd);
+
+ return 0;
+}
+#endif /* CONFIG_ARM_LPAE */
+
+static int msm_pm_idmap_add_pud(pgd_t *pgd, unsigned long addr,
+ unsigned long end,
+ unsigned long prot)
+{
+ pud_t *pud = pud_offset(pgd, addr);
+ unsigned long next;
+ int ret;
+
+ do {
+ next = pud_addr_end(addr, end);
+ ret = msm_pm_idmap_add_pmd(pud, addr, next, prot);
+ if (ret)
+ return ret;
+ } while (pud++, addr = next, addr != end);
+
+ return 0;
+}
+
+static int msm_pm_add_idmap(pgd_t *pgd, unsigned long addr,
+ unsigned long end,
+ unsigned long prot)
+{
+ unsigned long next;
+ int ret;
+
+ pgd += pgd_index(addr);
+ do {
+ next = pgd_addr_end(addr, end);
+ ret = msm_pm_idmap_add_pud(pgd, addr, next, prot);
+ if (ret)
+ return ret;
+ } while (pgd++, addr = next, addr != end);
+
+ return 0;
+}
+
+static int msm_pm_setup_pagetable(void)
{
pgd_t *pc_pgd;
- pmd_t *pmd;
- unsigned long pmdval;
unsigned long exit_phys;
- dma_addr_t temp_phys;
+ unsigned long end;
+ int ret;
/* Page table for cores to come back up safely. */
pc_pgd = pgd_alloc(&init_mm);
@@ -1301,12 +1360,33 @@
exit_phys = virt_to_phys(msm_pm_collapse_exit);
- pmd = pmd_offset(pud_offset(pc_pgd + pgd_index(exit_phys),exit_phys),
- exit_phys);
- pmdval = (exit_phys & PGDIR_MASK) |
- PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
- pmd[0] = __pmd(pmdval);
- pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
+ /*
+ * Make the (hopefully) reasonable assumption that the code size of
+ * msm_pm_collapse_exit won't be more than a section in size
+ */
+ end = exit_phys + SECTION_SIZE;
+
+ ret = msm_pm_add_idmap(pc_pgd, exit_phys, end,
+ PMD_TYPE_SECT | PMD_SECT_AP_WRITE);
+
+ if (ret)
+ return ret;
+
+ msm_pm_pc_pgd = virt_to_phys(pc_pgd);
+ clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
+ virt_to_phys(&msm_pm_pc_pgd));
+
+ return 0;
+}
+
+static int __init msm_pm_setup_saved_state(void)
+{
+ int ret;
+ dma_addr_t temp_phys;
+
+ ret = msm_pm_setup_pagetable();
+ if (ret)
+ return ret;
msm_saved_state = dma_zalloc_coherent(NULL, CPU_SAVED_STATE_SIZE *
num_possible_cpus(),
@@ -1322,19 +1402,6 @@
*/
msm_saved_state_phys = (unsigned long)temp_phys;
-
- /* It is remotely possible that the code in msm_pm_collapse_exit()
- * which turns on the MMU with this mapping is in the
- * next even-numbered megabyte beyond the
- * start of msm_pm_collapse_exit().
- * Map this megabyte in as well.
- */
- pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
- flush_pmd_entry(pmd);
- msm_pm_pc_pgd = virt_to_phys(pc_pgd);
- clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
- virt_to_phys(&msm_pm_pc_pgd));
-
return 0;
}
arch_initcall(msm_pm_setup_saved_state);
@@ -1631,18 +1698,6 @@
return rc;
}
- pnoc_clk = clk_get_sys("pm_8x60", "bus_clk");
-
- if (IS_ERR(pnoc_clk))
- pnoc_clk = NULL;
- else {
- clk_set_rate(pnoc_clk, 19200000);
- rc = clk_prepare_enable(pnoc_clk);
-
- if (rc)
- pr_err("%s: PNOC clock enable failed\n", __func__);
- }
-
return platform_driver_register(&msm_pm_8x60_driver);
}
device_initcall(msm_pm_8x60_init);
diff --git a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
index 5303009..0c71659 100644
--- a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
+++ b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
@@ -48,6 +48,12 @@
{
int rc = 0;
+ if ((msm_audio_ion_data.smmu_enabled == true) &&
+ (msm_audio_ion_data.group == NULL)) {
+ pr_debug("%s:probe is not done, deferred\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
*client = msm_audio_ion_client_create(UINT_MAX, name);
if (IS_ERR_OR_NULL((void *)(*client))) {
pr_err("%s: ION create client for AUDIO failed\n", __func__);
@@ -96,8 +102,10 @@
err_ion_handle:
ion_free(*client, *handle);
+ *handle = NULL;
err_ion_client:
msm_audio_ion_client_destroy(*client);
+ *client = NULL;
err:
return -EINVAL;
}
@@ -143,8 +151,12 @@
goto err_ion_handle;
}
- if (bufsz != 0)
- memset((void *)*vaddr, 0, bufsz);
+ *vaddr = ion_map_kernel(*client, *handle);
+ if (IS_ERR_OR_NULL((void *)*vaddr)) {
+ pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+ goto err_ion_handle;
+ }
+ pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz);
return 0;
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
index d37a325..1ea213a 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
@@ -456,7 +456,7 @@
__func__, config->buf_num, config->stream_format,
config->port_cnt, config->params_data_size);
- pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d\n",
+ pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d,\n",
__func__,
config->port_id[0],
config->port_id[1],
@@ -464,6 +464,11 @@
config->port_id[3],
config->port_id[4]);
+ pr_debug("id[5]=%d, id[6]=%d, id[7]=%d\n",
+ config->port_id[5],
+ config->port_id[6],
+ config->port_id[7]);
+
/* q6usm allocation & configuration */
usf_xx->buffer_size = config->buf_size;
usf_xx->buffer_count = config->buf_num;
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
index 5d30eb1..80b1aaa 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.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
@@ -652,7 +652,7 @@
sizeof(struct usm_stream_cmd_encdec_cfg_blk);
uint32_t round_params_size = 0;
uint8_t is_allocated = 0;
-
+ size_t min_common_size;
if ((usc == NULL) || (us_cfg == NULL)) {
pr_err("%s: wrong input", __func__);
@@ -692,12 +692,13 @@
round_params_size;
enc_cfg->enc_blk.frames_per_buf = 1;
enc_cfg->enc_blk.format_id = int_format;
- enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+
+ min_common_size = min(sizeof(struct usm_cfg_common),
+ sizeof(struct usm_cfg_common_a));
+ enc_cfg->enc_blk.cfg_size = min_common_size +
USM_MAX_CFG_DATA_SIZE +
round_params_size;
memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common),
- sizeof(struct usm_cfg_common));
-
+ min_common_size);
/* Transparent data copy */
memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params,
us_cfg->params_size);
@@ -716,11 +717,15 @@
enc_cfg->enc_blk.transp_data[6],
enc_cfg->enc_blk.transp_data[7]
);
- pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:0x%x; dev_id=0x%x\n",
+ pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:[0x%x,0x%x,0x%x,0x%x];\n",
__func__, enc_cfg->enc_blk.cfg_common.sample_rate,
enc_cfg->enc_blk.cfg_common.ch_cfg,
enc_cfg->enc_blk.cfg_common.bits_per_sample,
- enc_cfg->enc_blk.cfg_common.data_map,
+ enc_cfg->enc_blk.cfg_common.data_map[0],
+ enc_cfg->enc_blk.cfg_common.data_map[1],
+ enc_cfg->enc_blk.cfg_common.data_map[2],
+ enc_cfg->enc_blk.cfg_common.data_map[3]);
+ pr_debug("dev_id=0x%x\n",
enc_cfg->enc_blk.cfg_common.dev_id);
rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg);
@@ -757,7 +762,7 @@
uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update);
uint32_t round_params_size = 0;
uint8_t is_allocated = 0;
-
+ size_t min_common_size;
if ((usc == NULL) || (us_cfg == NULL)) {
pr_err("%s: wrong input", __func__);
@@ -794,11 +799,13 @@
dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE;
dec_cfg->format_id = int_format;
- dec_cfg->cfg_size = sizeof(struct usm_cfg_common) +
+ min_common_size = min(sizeof(struct usm_cfg_common),
+ sizeof(struct usm_cfg_common_a));
+ dec_cfg->cfg_size = min_common_size +
USM_MAX_CFG_DATA_SIZE +
round_params_size;
memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common),
- sizeof(struct usm_cfg_common));
+ min_common_size);
/* Transparent data copy */
memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size);
pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n",
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
index a3af3e78..fe7c8c2 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
@@ -620,13 +620,13 @@
uint32_t int_format = INVALID_FORMAT;
switch (ext_format) {
case FORMAT_USPS_EPOS:
- int_format = US_POINT_EPOS_FORMAT;
+ int_format = US_POINT_EPOS_FORMAT_V2;
break;
case FORMAT_USRAW:
- int_format = US_RAW_FORMAT;
+ int_format = US_RAW_FORMAT_V2;
break;
case FORMAT_USPROX:
- int_format = US_PROX_FORMAT;
+ int_format = US_PROX_FORMAT_V2;
break;
default:
pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
@@ -757,11 +757,19 @@
enc_cfg->enc_blk.transp_data[6],
enc_cfg->enc_blk.transp_data[7]
);
- pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:0x%x; dev_id=0x%x\n",
+ pr_debug("%s: srate:%d, ch=%d, bps= %d;\n",
__func__, enc_cfg->enc_blk.cfg_common.sample_rate,
enc_cfg->enc_blk.cfg_common.ch_cfg,
- enc_cfg->enc_blk.cfg_common.bits_per_sample,
- enc_cfg->enc_blk.cfg_common.data_map,
+ enc_cfg->enc_blk.cfg_common.bits_per_sample);
+ pr_debug("dmap:[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x]; dev_id=0x%x\n",
+ enc_cfg->enc_blk.cfg_common.data_map[0],
+ enc_cfg->enc_blk.cfg_common.data_map[1],
+ enc_cfg->enc_blk.cfg_common.data_map[2],
+ enc_cfg->enc_blk.cfg_common.data_map[3],
+ enc_cfg->enc_blk.cfg_common.data_map[4],
+ enc_cfg->enc_blk.cfg_common.data_map[5],
+ enc_cfg->enc_blk.cfg_common.data_map[6],
+ enc_cfg->enc_blk.cfg_common.data_map[7],
enc_cfg->enc_blk.cfg_common.dev_id);
rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg);
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index 38ed867..2bae37a 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -60,12 +60,13 @@
spinlock_t smd_lock_write;
spinlock_t smd_lock_read;
struct completion smd_open;
+ struct completion remote_open;
};
#define DEFAULT_BUFFER_SIZE 256
#define DEBUG_PRINT_BUFFER_SIZE 512
#define MAX_SLEEP_BUFFER 128
-
+#define SMD_CHANNEL_NOTIF_TIMEOUT 5000
#define GFP_FLAG(noirq) (noirq ? GFP_ATOMIC : GFP_KERNEL)
#define INV_RSC "resource does not exist"
#define ERR "err\0"
@@ -1303,6 +1304,20 @@
}
EXPORT_SYMBOL(msm_rpm_exit_sleep);
+static int __devinit msm_rpm_smd_remote_probe(struct platform_device *pdev)
+{
+ if (pdev && pdev->id == msm_rpm_data.ch_type)
+ complete(&msm_rpm_data.remote_open);
+ return 0;
+}
+
+static struct platform_driver msm_rpm_smd_remote_driver = {
+ .probe = msm_rpm_smd_remote_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+};
+
static int __devinit msm_rpm_dev_probe(struct platform_device *pdev)
{
char *key = NULL;
@@ -1323,13 +1338,21 @@
key = "rpm-standalone";
standalone = of_property_read_bool(pdev->dev.of_node, key);
+ msm_rpm_smd_remote_driver.driver.name = msm_rpm_data.ch_name;
+ init_completion(&msm_rpm_data.remote_open);
init_completion(&msm_rpm_data.smd_open);
spin_lock_init(&msm_rpm_data.smd_lock_write);
spin_lock_init(&msm_rpm_data.smd_lock_read);
INIT_WORK(&msm_rpm_data.work, msm_rpm_smd_work);
- if (smd_named_open_on_edge(msm_rpm_data.ch_name, msm_rpm_data.ch_type,
- &msm_rpm_data.ch_info, &msm_rpm_data,
+ platform_driver_register(&msm_rpm_smd_remote_driver);
+ ret = wait_for_completion_timeout(&msm_rpm_data.remote_open,
+ msecs_to_jiffies(SMD_CHANNEL_NOTIF_TIMEOUT));
+
+ if (!ret || smd_named_open_on_edge(msm_rpm_data.ch_name,
+ msm_rpm_data.ch_type,
+ &msm_rpm_data.ch_info,
+ &msm_rpm_data,
msm_rpm_notify)) {
pr_info("Cannot open RPM channel %s %d\n", msm_rpm_data.ch_name,
msm_rpm_data.ch_type);
diff --git a/arch/arm/mach-msm/scm-pas.c b/arch/arm/mach-msm/scm-pas.c
index 4cb43b1..57acdc4 100644
--- a/arch/arm/mach-msm/scm-pas.c
+++ b/arch/arm/mach-msm/scm-pas.c
@@ -255,9 +255,14 @@
}
EXPORT_SYMBOL(pas_supported);
-static int __init scm_pas_init(void)
+void scm_pas_init(enum msm_bus_fabric_master_type id)
{
int i, rate;
+ static int is_inited;
+
+ if (is_inited)
+ return;
+
for (i = 0; i < NUM_CLKS; i++) {
scm_clocks[i] = clk_get_sys("scm", scm_clock_names[i]);
if (IS_ERR(scm_clocks[i]))
@@ -268,20 +273,14 @@
rate = clk_round_rate(scm_clocks[CORE_CLK_SRC], 1);
clk_set_rate(scm_clocks[CORE_CLK_SRC], rate);
- if (cpu_is_msm8974() || cpu_is_msm8226() || cpu_is_msm8610()) {
- scm_pas_bw_tbl[0].vectors[0].src = MSM_BUS_MASTER_CRYPTO_CORE0;
- scm_pas_bw_tbl[1].vectors[0].src = MSM_BUS_MASTER_CRYPTO_CORE0;
- } else {
- if (!IS_ERR(scm_clocks[BUS_CLK]))
- clk_set_rate(scm_clocks[BUS_CLK], 64000000);
- else
- pr_warn("unable to get bus clock\n");
- }
+ scm_pas_bw_tbl[0].vectors[0].src = id;
+ scm_pas_bw_tbl[1].vectors[0].src = id;
+
+ clk_set_rate(scm_clocks[BUS_CLK], 64000000);
scm_perf_client = msm_bus_scale_register_client(&scm_pas_bus_pdata);
if (!scm_perf_client)
pr_warn("unable to register bus client\n");
- return 0;
+ is_inited = 1;
}
-module_init(scm_pas_init);
diff --git a/arch/arm/mach-msm/scm-pas.h b/arch/arm/mach-msm/scm-pas.h
index f13757c..48bc558 100644
--- a/arch/arm/mach-msm/scm-pas.h
+++ b/arch/arm/mach-msm/scm-pas.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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
@@ -9,6 +9,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
+
+#include <mach/msm_bus_board.h>
+
#ifndef __MSM_SCM_PAS_H
#define __MSM_SCM_PAS_H
@@ -31,6 +34,7 @@
extern int pas_auth_and_reset(enum pas_id id);
extern int pas_shutdown(enum pas_id id);
extern int pas_supported(enum pas_id id);
+extern void scm_pas_init(enum msm_bus_fabric_master_type id);
#else
static inline int pas_init_image(enum pas_id id, const u8 *metadata,
size_t size)
@@ -53,6 +57,9 @@
{
return 0;
}
+static inline void scm_pas_init(enum msm_bus_fabric_master_type id)
+{
+}
#endif
#endif
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
index 6e05177..e9f44e3 100644
--- a/arch/arm/mach-msm/scm.c
+++ b/arch/arm/mach-msm/scm.c
@@ -191,12 +191,12 @@
u32 cmd_addr = virt_to_phys(cmd);
/*
- * Flush the entire cache here so callers don't have to remember
- * to flush the cache when passing physical addresses to the secure
- * side in the buffer.
+ * Flush the command buffer so that the secure world sees
+ * the correct data.
*/
- flush_cache_all();
- outer_flush_all();
+ __cpuc_flush_dcache_area((void *)cmd, cmd->len);
+ outer_flush_range(cmd_addr, cmd_addr + cmd->len);
+
ret = smc(cmd_addr);
if (ret < 0)
ret = scm_remap_error(ret);
@@ -233,6 +233,13 @@
* @resp_len: length of the response buffer
*
* Sends a command to the SCM and waits for the command to finish processing.
+ *
+ * A note on cache maintenance:
+ * Note that any buffers that are expected to be accessed by the secure world
+ * must be flushed before invoking scm_call and invalidated in the cache
+ * immediately after scm_call returns. Cache maintenance on the command and
+ * response buffers is taken care of by scm_call; however, callers are
+ * responsible for any other cached buffers passed over to the secure world.
*/
int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
void *resp_buf, size_t resp_len)
diff --git a/arch/arm/mach-msm/smcmod.c b/arch/arm/mach-msm/smcmod.c
index 705bab5..221a522 100644
--- a/arch/arm/mach-msm/smcmod.c
+++ b/arch/arm/mach-msm/smcmod.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
@@ -92,27 +92,6 @@
uint8_t verify;
} __packed;
-static void smcmod_inv_range(unsigned long start, unsigned long end)
-{
- uint32_t cacheline_size;
- uint32_t ctr;
-
- /* get cache line size */
- asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
- cacheline_size = 4 << ((ctr >> 16) & 0xf);
-
- /* invalidate the range */
- start = round_down(start, cacheline_size);
- end = round_up(end, cacheline_size);
- while (start < end) {
- asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
- : "memory");
- start += cacheline_size;
- }
- mb();
- isb();
-}
-
static int smcmod_ion_fd_to_phys(int32_t fd, struct ion_client *ion_clientp,
struct ion_handle **ion_handlep, uint32_t *phys_addrp, size_t *sizep)
{
@@ -234,10 +213,18 @@
}
}
+ /* No need to flush the cache lines for the command buffer here,
+ * because the buffer will be flushed by scm_call.
+ */
+
/* call scm function to switch to secure world */
reqp->return_val = scm_call(reqp->service_id, reqp->command_id,
cmd_vaddrp, reqp->cmd_len, resp_vaddrp, reqp->resp_len);
+ /* The cache lines for the response buffer have already been
+ * invalidated by scm_call before returning.
+ */
+
buf_cleanup:
/* if the client and handle(s) are valid, free them */
if (!IS_ERR_OR_NULL(ion_clientp)) {
@@ -355,45 +342,36 @@
goto buf_cleanup;
}
+ /* Only the scm_req structure will be flushed by scm_call,
+ * so we must flush the cache for the input ion buffers here.
+ */
+ msm_ion_do_cache_op(ion_clientp, ion_key_handlep, NULL,
+ scm_req.key_size, ION_IOC_CLEAN_CACHES);
+ msm_ion_do_cache_op(ion_clientp, ion_iv_handlep, NULL,
+ scm_req.init_vector_size, ION_IOC_CLEAN_CACHES);
+
+ /* For decrypt, cipher text is input, otherwise it's plain text. */
+ if (reqp->operation)
+ msm_ion_do_cache_op(ion_clientp, ion_cipher_handlep, NULL,
+ scm_req.cipher_text_size, ION_IOC_CLEAN_CACHES);
+ else
+ msm_ion_do_cache_op(ion_clientp, ion_plain_handlep, NULL,
+ scm_req.plain_text_size, ION_IOC_CLEAN_CACHES);
+
/* call scm function to switch to secure world */
reqp->return_val = scm_call(SMCMOD_SVC_CRYPTO,
SMCMOD_CRYPTO_CMD_CIPHER, &scm_req,
sizeof(scm_req), NULL, 0);
+ /* Invalidate the output buffer, since it's not done by scm_call */
+
/* for decrypt, plain text is the output, otherwise it's cipher text */
- if (reqp->operation) {
- void *vaddrp = NULL;
-
- /* map the plain text region to get the virtual address */
- vaddrp = ion_map_kernel(ion_clientp, ion_plain_handlep);
- if (IS_ERR_OR_NULL(vaddrp)) {
- ret = -EINVAL;
- goto buf_cleanup;
- }
-
- /* invalidate the range */
- smcmod_inv_range((unsigned long)vaddrp,
- (unsigned long)(vaddrp + scm_req.plain_text_size));
-
- /* unmap the mapped area */
- ion_unmap_kernel(ion_clientp, ion_plain_handlep);
- } else {
- void *vaddrp = NULL;
-
- /* map the cipher text region to get the virtual address */
- vaddrp = ion_map_kernel(ion_clientp, ion_cipher_handlep);
- if (IS_ERR_OR_NULL(vaddrp)) {
- ret = -EINVAL;
- goto buf_cleanup;
- }
-
- /* invalidate the range */
- smcmod_inv_range((unsigned long)vaddrp,
- (unsigned long)(vaddrp + scm_req.cipher_text_size));
-
- /* unmap the mapped area */
- ion_unmap_kernel(ion_clientp, ion_cipher_handlep);
- }
+ if (reqp->operation)
+ msm_ion_do_cache_op(ion_clientp, ion_plain_handlep, NULL,
+ scm_req.plain_text_size, ION_IOC_INV_CACHES);
+ else
+ msm_ion_do_cache_op(ion_clientp, ion_cipher_handlep, NULL,
+ scm_req.cipher_text_size, ION_IOC_INV_CACHES);
buf_cleanup:
/* if the client and handles are valid, free them */
@@ -424,7 +402,6 @@
struct ion_handle *ion_input_handlep = NULL;
struct ion_handle *ion_output_handlep = NULL;
size_t size = 0;
- void *vaddrp = NULL;
if (IS_ERR_OR_NULL(reqp))
return -EINVAL;
@@ -492,6 +469,14 @@
goto buf_cleanup;
}
+ /* Only the scm_req structure will be flushed by scm_call,
+ * so we must flush the cache for the input ion buffers here.
+ */
+ msm_ion_do_cache_op(ion_clientp, ion_key_handlep, NULL,
+ scm_req.key_size, ION_IOC_CLEAN_CACHES);
+ msm_ion_do_cache_op(ion_clientp, ion_input_handlep, NULL,
+ scm_req.input_size, ION_IOC_CLEAN_CACHES);
+
/* call scm function to switch to secure world */
if (reqp->fixed_block)
reqp->return_val = scm_call(SMCMOD_SVC_CRYPTO,
@@ -506,20 +491,9 @@
sizeof(scm_req),
NULL, 0);
-
- /* map the output region to get the virtual address */
- vaddrp = ion_map_kernel(ion_clientp, ion_output_handlep);
- if (IS_ERR_OR_NULL(vaddrp)) {
- ret = -EINVAL;
- goto buf_cleanup;
- }
-
- /* invalidate the range */
- smcmod_inv_range((unsigned long)vaddrp,
- (unsigned long)(vaddrp + scm_req.output_size));
-
- /* unmap the mapped area */
- ion_unmap_kernel(ion_clientp, ion_output_handlep);
+ /* Invalidate the output buffer, since it's not done by scm_call */
+ msm_ion_do_cache_op(ion_clientp, ion_output_handlep, NULL,
+ scm_req.output_size, ION_IOC_INV_CACHES);
buf_cleanup:
/* if the client and handles are valid, free them */
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 4649390..a6e3497 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -176,8 +176,6 @@
},
};
-static void *smd_dev;
-
struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
#define SMSM_STATE_ADDR(entry) (smsm_info.state + entry)
@@ -381,14 +379,6 @@
static DEFINE_MUTEX(smsm_lock);
static struct smsm_state_info *smsm_states;
-/**
- * Variables to indicate smd module initialization.
- * Dependents to register for smd module init notifier.
- */
-static int smd_module_inited;
-static RAW_NOTIFIER_HEAD(smd_module_init_notifier_list);
-static DEFINE_MUTEX(smd_module_init_notifier_lock);
-static void smd_module_init_notify(uint32_t state, void *data);
static int smd_stream_write_avail(struct smd_channel *ch);
static int smd_stream_read_avail(struct smd_channel *ch);
@@ -708,6 +698,7 @@
uint32_t local_pid;
uint32_t remote_pid;
char subsys_name[SMD_MAX_CH_NAME_LEN];
+ bool initialized;
};
/**
@@ -762,6 +753,11 @@
static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm);
+static bool smd_edge_inited(int edge)
+{
+ return edge_to_pids[edge].initialized;
+}
+
/* on smp systems, the probe might get called from multiple cores,
hence use a lock */
static DEFINE_MUTEX(smd_probe_lock);
@@ -795,6 +791,11 @@
if (!shared[n].name[0])
continue;
+ if (!smd_initialized && !smd_edge_inited(type)) {
+ SMD_INFO("Probe skipping ch %d, edge not inited\n", n);
+ continue;
+ }
+
if (!smd_alloc_channel(&shared[n]))
smd_ch_allocated[n] = 1;
else
@@ -1913,7 +1914,7 @@
struct smd_channel *ch;
unsigned long flags;
- if (smd_initialized == 0) {
+ if (smd_initialized == 0 && !smd_edge_inited(edge)) {
SMD_INFO("smd_open() before smd_init()\n");
return -ENODEV;
}
@@ -2436,16 +2437,23 @@
struct smsm_size_info_type *smsm_size_info;
unsigned long flags;
unsigned long j_start;
+ static int first = 1;
+ remote_spinlock_t *remote_spinlock;
+
+ if (!first)
+ return 0;
+ first = 0;
/* Verify that remote spinlock is not deadlocked */
+ remote_spinlock = smem_get_remote_spinlock();
j_start = jiffies;
- while (!remote_spin_trylock_irqsave(&remote_spinlock, flags)) {
+ while (!remote_spin_trylock_irqsave(remote_spinlock, flags)) {
if (jiffies_to_msecs(jiffies - j_start) > RSPIN_INIT_WAIT_MS) {
panic("%s: Remote processor %d will not release spinlock\n",
- __func__, remote_spin_owner(&remote_spinlock));
+ __func__, remote_spin_owner(remote_spinlock));
}
}
- remote_spin_unlock_irqrestore(&remote_spinlock, flags);
+ remote_spin_unlock_irqrestore(remote_spinlock, flags);
smsm_size_info = smem_alloc(SMEM_SMSM_SIZE_INFO,
sizeof(struct smsm_size_info_type));
@@ -2860,9 +2868,6 @@
int ret;
unsigned long flags;
- if (!smd_initialized)
- return;
-
while (kfifo_len(&smsm_snapshot_fifo) >= SMSM_SNAPSHOT_SIZE) {
mutex_lock(&smsm_lock);
for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
@@ -3084,42 +3089,6 @@
}
EXPORT_SYMBOL(smsm_state_cb_deregister);
-int smd_module_init_notifier_register(struct notifier_block *nb)
-{
- int ret;
- if (!nb)
- return -EINVAL;
- mutex_lock(&smd_module_init_notifier_lock);
- ret = raw_notifier_chain_register(&smd_module_init_notifier_list, nb);
- if (smd_module_inited)
- nb->notifier_call(nb, 0, NULL);
- mutex_unlock(&smd_module_init_notifier_lock);
- return ret;
-}
-EXPORT_SYMBOL(smd_module_init_notifier_register);
-
-int smd_module_init_notifier_unregister(struct notifier_block *nb)
-{
- int ret;
- if (!nb)
- return -EINVAL;
- mutex_lock(&smd_module_init_notifier_lock);
- ret = raw_notifier_chain_unregister(&smd_module_init_notifier_list,
- nb);
- mutex_unlock(&smd_module_init_notifier_lock);
- return ret;
-}
-EXPORT_SYMBOL(smd_module_init_notifier_unregister);
-
-static void smd_module_init_notify(uint32_t state, void *data)
-{
- mutex_lock(&smd_module_init_notifier_lock);
- smd_module_inited = 1;
- raw_notifier_call_chain(&smd_module_init_notifier_list,
- state, data);
- mutex_unlock(&smd_module_init_notifier_lock);
-}
-
int smd_core_init(void)
{
int r;
@@ -3315,9 +3284,6 @@
struct smd_subsystem_config *smd_ss_config_list;
struct smd_subsystem_config *cfg;
int err_ret = 0;
- struct smd_smem_regions *smd_smem_areas;
- struct smem_area *smem_areas_tmp = NULL;
- int smem_idx;
smd_platform_data = pdev->dev.platform_data;
num_ss = smd_platform_data->num_ss_configs;
@@ -3327,57 +3293,6 @@
disable_smsm_reset_handshake = smd_platform_data->
smd_ssr_config->disable_smsm_reset_handshake;
- smd_smem_areas = smd_platform_data->smd_smem_areas;
- num_smem_areas = smd_platform_data->num_smem_areas + 1;
-
- /* Initialize main SMEM region */
- smem_areas_tmp = kmalloc_array(num_smem_areas, sizeof(struct smem_area),
- GFP_KERNEL);
- if (!smem_areas_tmp) {
- pr_err("%s: smem_areas kmalloc failed\n", __func__);
- err_ret = -ENOMEM;
- goto smem_areas_alloc_fail;
- }
-
- smem_areas_tmp[0].phys_addr = msm_shared_ram_phys;
- smem_areas_tmp[0].size = MSM_SHARED_RAM_SIZE;
- smem_areas_tmp[0].virt_addr = MSM_SHARED_RAM_BASE;
-
- /* Configure auxiliary SMEM regions */
- for (smem_idx = 1; smem_idx < num_smem_areas; ++smem_idx) {
- smem_areas_tmp[smem_idx].phys_addr =
- smd_smem_areas[smem_idx].phys_addr;
- smem_areas_tmp[smem_idx].size =
- smd_smem_areas[smem_idx].size;
- smem_areas_tmp[smem_idx].virt_addr = ioremap_nocache(
- (unsigned long)(smem_areas_tmp[smem_idx].phys_addr),
- smem_areas_tmp[smem_idx].size);
- if (!smem_areas_tmp[smem_idx].virt_addr) {
- pr_err("%s: ioremap_nocache() of addr: %pa size: %pa\n",
- __func__,
- &smem_areas_tmp[smem_idx].phys_addr,
- &smem_areas_tmp[smem_idx].size);
- err_ret = -ENOMEM;
- goto smem_failed;
- }
-
- if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
- (uintptr_t)smem_areas_tmp[smem_idx].virt_addr,
- smem_areas_tmp[smem_idx].size)) {
- pr_err("%s: invalid virtual address block %i: %p:%pa\n",
- __func__, smem_idx,
- smem_areas_tmp[smem_idx].virt_addr,
- &smem_areas_tmp[smem_idx].size);
- ++smem_idx;
- err_ret = -EINVAL;
- goto smem_failed;
- }
-
- SMD_DBG("%s: %d = %pa %pa", __func__, smem_idx,
- &smd_smem_areas[smem_idx].phys_addr,
- &smd_smem_areas[smem_idx].size);
- }
-
for (i = 0; i < num_ss; i++) {
cfg = &smd_ss_config_list[i];
@@ -3421,7 +3336,6 @@
SMD_INFO("smd_core_platform_init() done\n");
- smem_areas = smem_areas_tmp;
return 0;
intr_failed:
@@ -3438,19 +3352,106 @@
(void *)cfg->smsm_int.dev_id
);
}
-smem_failed:
- for (smem_idx = smem_idx - 1; smem_idx >= 1; --smem_idx)
- iounmap(smem_areas_tmp[smem_idx].virt_addr);
-
- num_smem_areas = 0;
- kfree(smem_areas_tmp);
-
-smem_areas_alloc_fail:
return err_ret;
}
-static int __devinit parse_smd_devicetree(struct device_node *node,
- void *irq_out_base)
+static int msm_smsm_probe(struct platform_device *pdev)
+{
+ uint32_t edge;
+ char *key;
+ int ret;
+ uint32_t irq_offset;
+ uint32_t irq_bitmask;
+ uint32_t irq_line;
+ struct interrupt_config_item *private_irq;
+ struct device_node *node;
+ void *irq_out_base;
+ resource_size_t irq_out_size;
+ struct platform_device *parent_pdev;
+ struct resource *r;
+
+ disable_smsm_reset_handshake = 1;
+
+ node = pdev->dev.of_node;
+
+ if (!pdev->dev.parent) {
+ pr_err("%s: missing link to parent device\n", __func__);
+ return -ENODEV;
+ }
+
+ parent_pdev = to_platform_device(pdev->dev.parent);
+
+ key = "irq-reg-base";
+ r = platform_get_resource_byname(parent_pdev, IORESOURCE_MEM, key);
+ if (!r)
+ goto missing_key;
+ 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;
+ }
+ SMSM_DBG("%s: %s = %p", __func__, key, irq_out_base);
+
+ key = "qcom,smsm-edge";
+ ret = of_property_read_u32(node, key, &edge);
+ if (ret)
+ goto missing_key;
+ SMSM_DBG("%s: %s = %d", __func__, key, edge);
+
+ key = "qcom,smsm-irq-offset";
+ ret = of_property_read_u32(node, key, &irq_offset);
+ if (ret)
+ goto missing_key;
+ SMSM_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+ key = "qcom,smsm-irq-bitmask";
+ ret = of_property_read_u32(node, key, &irq_bitmask);
+ if (ret)
+ goto missing_key;
+ SMSM_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+ key = "interrupts";
+ irq_line = irq_of_parse_and_map(node, 0);
+ if (!irq_line)
+ goto missing_key;
+ SMSM_DBG("%s: %s = %d", __func__, key, irq_line);
+
+ private_irq = &private_intr_config[edge_to_pids[edge].remote_pid].smsm;
+ private_irq->out_bit_pos = irq_bitmask;
+ private_irq->out_offset = irq_offset;
+ private_irq->out_base = irq_out_base;
+ private_irq->irq_id = irq_line;
+
+ ret = request_irq(irq_line,
+ private_irq->irq_handler,
+ IRQF_TRIGGER_RISING,
+ "smsm_dev",
+ NULL);
+ if (ret < 0) {
+ pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+ return ret;
+ } else {
+ ret = enable_irq_wake(irq_line);
+ if (ret < 0)
+ pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+ irq_line);
+ }
+
+ if (smsm_init())
+ pr_err("smsm_init() failed\n");
+
+ smsm_irq_handler(0, 0);
+
+ return 0;
+
+missing_key:
+ pr_err("%s: missing key: %s", __func__, key);
+ return -ENODEV;
+}
+
+static int msm_smd_probe(struct platform_device *pdev)
{
uint32_t edge;
char *key;
@@ -3461,6 +3462,33 @@
unsigned long irq_flags = IRQF_TRIGGER_RISING;
const char *pilstr;
struct interrupt_config_item *private_irq;
+ struct device_node *node;
+ void *irq_out_base;
+ resource_size_t irq_out_size;
+ struct platform_device *parent_pdev;
+ struct resource *r;
+
+ node = pdev->dev.of_node;
+
+ if (!pdev->dev.parent) {
+ pr_err("%s: missing link to parent device\n", __func__);
+ return -ENODEV;
+ }
+
+ parent_pdev = to_platform_device(pdev->dev.parent);
+
+ key = "irq-reg-base";
+ r = platform_get_resource_byname(parent_pdev, IORESOURCE_MEM, key);
+ if (!r)
+ goto missing_key;
+ 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);
key = "qcom,smd-edge";
ret = of_property_read_u32(node, key, &edge);
@@ -3521,68 +3549,9 @@
strlcpy(edge_to_pids[edge].subsys_name, pilstr,
SMD_MAX_CH_NAME_LEN);
- return 0;
+ edge_to_pids[edge].initialized = true;
-missing_key:
- pr_err("%s: missing key: %s", __func__, key);
- return -ENODEV;
-}
-
-static int __devinit parse_smsm_devicetree(struct device_node *node,
- void *irq_out_base)
-{
- uint32_t edge;
- char *key;
- int ret;
- uint32_t irq_offset;
- uint32_t irq_bitmask;
- uint32_t irq_line;
- struct interrupt_config_item *private_irq;
-
- key = "qcom,smsm-edge";
- ret = of_property_read_u32(node, key, &edge);
- if (ret)
- goto missing_key;
- SMD_DBG("%s: %s = %d", __func__, key, edge);
-
- key = "qcom,smsm-irq-offset";
- ret = of_property_read_u32(node, key, &irq_offset);
- if (ret)
- goto missing_key;
- SMD_DBG("%s: %s = %x", __func__, key, irq_offset);
-
- key = "qcom,smsm-irq-bitmask";
- ret = of_property_read_u32(node, key, &irq_bitmask);
- if (ret)
- goto missing_key;
- SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask);
-
- key = "interrupts";
- irq_line = irq_of_parse_and_map(node, 0);
- if (!irq_line)
- goto missing_key;
- SMD_DBG("%s: %s = %d", __func__, key, irq_line);
-
- private_irq = &private_intr_config[edge_to_pids[edge].remote_pid].smsm;
- private_irq->out_bit_pos = irq_bitmask;
- private_irq->out_offset = irq_offset;
- private_irq->out_base = irq_out_base;
- private_irq->irq_id = irq_line;
-
- ret = request_irq(irq_line,
- private_irq->irq_handler,
- IRQF_TRIGGER_RISING,
- "smsm_dev",
- NULL);
- if (ret < 0) {
- pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
- return ret;
- } else {
- ret = enable_irq_wake(irq_line);
- if (ret < 0)
- pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
- irq_line);
- }
+ schedule_work(&probe_work);
return 0;
@@ -3591,213 +3560,7 @@
return -ENODEV;
}
-static void __devinit unparse_smd_devicetree(struct device_node *node)
-{
- uint32_t irq_line;
-
- irq_line = irq_of_parse_and_map(node, 0);
-
- free_irq(irq_line, NULL);
-}
-
-static void __devinit unparse_smsm_devicetree(struct device_node *node)
-{
- uint32_t irq_line;
-
- irq_line = irq_of_parse_and_map(node, 0);
-
- free_irq(irq_line, NULL);
-}
-
-static int __devinit smd_core_devicetree_init(struct platform_device *pdev)
-{
- char *key;
- struct resource *r;
- void *irq_out_base;
- 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];
- struct device_node *node;
- int ret;
- const char *compatible;
- struct ramdump_segment *ramdump_segments_tmp = NULL;
- struct smem_area *smem_areas_tmp = NULL;
- int smem_idx = 0;
- int subnode_num = 0;
- int i;
- resource_size_t irq_out_size;
-
- disable_smsm_reset_handshake = 1;
-
- key = "irq-reg-base";
- r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
- if (!r) {
- pr_err("%s: missing '%s'\n", __func__, key);
- return -ENODEV;
- }
- 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);
-
- num_smem_areas = 1;
- while (1) {
- scnprintf(temp_string, temp_string_size, "aux-mem%d",
- num_smem_areas);
- r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- temp_string);
- if (!r)
- break;
-
- ++num_smem_areas;
- if (num_smem_areas > 999) {
- pr_err("%s: max num aux mem regions reached\n",
- __func__);
- break;
- }
- }
-
- /* Initialize main SMEM region and SSR ramdump region */
- key = "smem";
- r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
- if (!r) {
- pr_err("%s: missing '%s'\n", __func__, key);
- return -ENODEV;
- }
-
- smem_areas_tmp = kmalloc_array(num_smem_areas, sizeof(struct smem_area),
- GFP_KERNEL);
- if (!smem_areas_tmp) {
- pr_err("%s: smem areas kmalloc failed\n", __func__);
- ret = -ENOMEM;
- goto free_smem_areas;
- }
-
- ramdump_segments_tmp = kmalloc_array(num_smem_areas,
- sizeof(struct ramdump_segment), GFP_KERNEL);
- if (!ramdump_segments_tmp) {
- pr_err("%s: ramdump segment kmalloc failed\n", __func__);
- ret = -ENOMEM;
- goto free_smem_areas;
- }
-
- smem_areas_tmp[smem_idx].phys_addr = r->start;
- smem_areas_tmp[smem_idx].size = resource_size(r);
- smem_areas_tmp[smem_idx].virt_addr = MSM_SHARED_RAM_BASE;
-
- ramdump_segments_tmp[smem_idx].address = r->start;
- ramdump_segments_tmp[smem_idx].size = resource_size(r);
- ++smem_idx;
-
- /* Configure auxiliary SMEM regions */
- while (1) {
- scnprintf(temp_string, temp_string_size, "aux-mem%d",
- smem_idx);
- r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- temp_string);
- if (!r)
- break;
- aux_mem_base = r->start;
- aux_mem_size = resource_size(r);
-
- ramdump_segments_tmp[smem_idx].address = aux_mem_base;
- ramdump_segments_tmp[smem_idx].size = aux_mem_size;
-
- smem_areas_tmp[smem_idx].phys_addr = aux_mem_base;
- smem_areas_tmp[smem_idx].size = aux_mem_size;
- smem_areas_tmp[smem_idx].virt_addr = ioremap_nocache(
- (unsigned long)(smem_areas_tmp[smem_idx].phys_addr),
- smem_areas_tmp[smem_idx].size);
- SMD_DBG("%s: %s = %pa %pa -> %p", __func__, temp_string,
- &aux_mem_base, &aux_mem_size,
- smem_areas_tmp[smem_idx].virt_addr);
-
- if (!smem_areas_tmp[smem_idx].virt_addr) {
- pr_err("%s: ioremap_nocache() of addr:%pa size: %pa\n",
- __func__,
- &smem_areas_tmp[smem_idx].phys_addr,
- &smem_areas_tmp[smem_idx].size);
- ret = -ENOMEM;
- goto free_smem_areas;
- }
-
- if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
- (uintptr_t)smem_areas_tmp[smem_idx].virt_addr,
- smem_areas_tmp[smem_idx].size)) {
- pr_err("%s: invalid virtual address block %i: %p:%pa\n",
- __func__, smem_idx,
- smem_areas_tmp[smem_idx].virt_addr,
- &smem_areas_tmp[smem_idx].size);
- ++smem_idx;
- ret = -EINVAL;
- goto free_smem_areas;
- }
-
- ++smem_idx;
- if (smem_idx > 999) {
- pr_err("%s: max num aux mem regions reached\n",
- __func__);
- break;
- }
- }
-
- for_each_child_of_node(pdev->dev.of_node, node) {
- compatible = of_get_property(node, "compatible", NULL);
- if (!compatible) {
- pr_err("%s: invalid child node: compatible null\n",
- __func__);
- ret = -ENODEV;
- goto rollback_subnodes;
- }
- if (!strcmp(compatible, "qcom,smd")) {
- ret = parse_smd_devicetree(node, irq_out_base);
- if (ret)
- goto rollback_subnodes;
- } else if (!strcmp(compatible, "qcom,smsm")) {
- ret = parse_smsm_devicetree(node, irq_out_base);
- if (ret)
- goto rollback_subnodes;
- } else {
- pr_err("%s: invalid child node named: %s\n", __func__,
- compatible);
- ret = -ENODEV;
- goto rollback_subnodes;
- }
- ++subnode_num;
- }
-
- smem_areas = smem_areas_tmp;
- smem_ramdump_segments = ramdump_segments_tmp;
- return 0;
-
-rollback_subnodes:
- i = 0;
- for_each_child_of_node(pdev->dev.of_node, node) {
- if (i >= subnode_num)
- break;
- ++i;
- compatible = of_get_property(node, "compatible", NULL);
- if (!strcmp(compatible, "qcom,smd"))
- unparse_smd_devicetree(node);
- else
- unparse_smsm_devicetree(node);
- }
-free_smem_areas:
- for (smem_idx = smem_idx - 1; smem_idx >= 1; --smem_idx)
- iounmap(smem_areas_tmp[smem_idx].virt_addr);
-
- num_smem_areas = 0;
- kfree(ramdump_segments_tmp);
- kfree(smem_areas_tmp);
- return ret;
-}
-
-static int __devinit msm_smd_probe(struct platform_device *pdev)
+static int msm_smd_probe_legacy(struct platform_device *pdev)
{
int ret;
@@ -3805,13 +3568,6 @@
return -ENODEV;
SMD_INFO("smd probe\n");
- INIT_WORK(&probe_work, smd_channel_probe_worker);
-
- channel_close_wq = create_singlethread_workqueue("smd_channel_close");
- if (IS_ERR(channel_close_wq)) {
- pr_err("%s: create_singlethread_workqueue ENOMEM\n", __func__);
- return -ENOMEM;
- }
if (smsm_init()) {
pr_err("smsm_init() failed\n");
@@ -3820,13 +3576,8 @@
if (pdev) {
if (pdev->dev.of_node) {
- ret = smd_core_devicetree_init(pdev);
- if (ret) {
- pr_err("%s: device tree init failed\n",
- __func__);
- return ret;
- }
- smd_dev = &pdev->dev;
+ pr_err("%s: invalid device tree init\n", __func__);
+ return -ENODEV;
} else if (pdev->dev.platform_data) {
ret = smd_core_platform_init(pdev);
if (ret) {
@@ -3872,6 +3623,8 @@
unsigned long code,
void *data)
{
+ remote_spinlock_t *remote_spinlock;
+
/*
* Some SMD or SMSM clients assume SMD/SMSM SSR handling will be
* done in the AFTER_SHUTDOWN level. If this ever changes, extra
@@ -3886,7 +3639,8 @@
__func__, notifier->processor,
notifier->name);
- remote_spin_release(&remote_spinlock, notifier->processor);
+ remote_spinlock = smem_get_remote_spinlock();
+ remote_spin_release(remote_spinlock, notifier->processor);
remote_spin_release_all(notifier->processor);
smd_channel_reset(notifier->processor);
@@ -3912,17 +3666,39 @@
}
late_initcall(modem_restart_late_init);
-static struct of_device_id msm_smem_match_table[] = {
- { .compatible = "qcom,smem" },
+static struct of_device_id msm_smd_match_table[] = {
+ { .compatible = "qcom,smd" },
{},
};
static struct platform_driver msm_smd_driver = {
.probe = msm_smd_probe,
.driver = {
+ .name = "msm_smd_dt",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smd_match_table,
+ },
+};
+
+static struct of_device_id msm_smsm_match_table[] = {
+ { .compatible = "qcom,smsm" },
+ {},
+};
+
+static struct platform_driver msm_smsm_driver = {
+ .probe = msm_smsm_probe,
+ .driver = {
+ .name = "msm_smsm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smsm_match_table,
+ },
+};
+
+static struct platform_driver msm_smd_driver_legacy = {
+ .probe = msm_smd_probe_legacy,
+ .driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
- .of_match_table = msm_smem_match_table,
},
};
@@ -3941,9 +3717,19 @@
}
registered = true;
- rc = init_smem_remote_spinlock();
+
+ INIT_WORK(&probe_work, smd_channel_probe_worker);
+
+ channel_close_wq = create_singlethread_workqueue("smd_channel_close");
+ if (IS_ERR(channel_close_wq)) {
+ pr_err("%s: create_singlethread_workqueue ENOMEM\n", __func__);
+ return -ENOMEM;
+ }
+
+ rc = platform_driver_register(&msm_smd_driver_legacy);
if (rc) {
- pr_err("%s: remote spinlock init failed %d\n", __func__, rc);
+ pr_err("%s: msm_smd_driver_legacy register failed %d\n",
+ __func__, rc);
return rc;
}
@@ -3954,7 +3740,12 @@
return rc;
}
- smd_module_init_notify(0, NULL);
+ rc = platform_driver_register(&msm_smsm_driver);
+ if (rc) {
+ pr_err("%s: msm_smsm_driver register failed %d\n",
+ __func__, rc);
+ return rc;
+ }
return 0;
}
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index 0b270b7..3461e49 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -175,8 +175,10 @@
if (dev == smd_tty[num_dev].device_ptr)
break;
}
- if (num_dev >= MAX_SMD_TTYS)
+ if (num_dev >= MAX_SMD_TTYS) {
SMD_TTY_ERR("[%s]: Device Not Found", __func__);
+ return -EINVAL;
+ }
return snprintf(buf, PAGE_SIZE, "%d\n",
smd_tty[num_dev].open_wait);
diff --git a/arch/arm/mach-msm/smem.c b/arch/arm/mach-msm/smem.c
index bbb6ce0..1ec2a78 100644
--- a/arch/arm/mach-msm/smem.c
+++ b/arch/arm/mach-msm/smem.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/printk.h>
+#include <linux/notifier.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
@@ -53,15 +54,20 @@
pr_debug(x); \
} while (0)
-remote_spinlock_t remote_spinlock;
-int spinlocks_initialized;
-uint32_t num_smem_areas;
-struct smem_area *smem_areas;
-struct ramdump_segment *smem_ramdump_segments;
+#define SMEM_SPINLOCK_SMEM_ALLOC "S:3"
+static remote_spinlock_t remote_spinlock;
+static uint32_t num_smem_areas;
+static struct smem_area *smem_areas;
+static struct ramdump_segment *smem_ramdump_segments;
+static int spinlocks_initialized;
static void *smem_ramdump_dev;
static DEFINE_MUTEX(spinlock_init_lock);
static DEFINE_SPINLOCK(smem_init_check_lock);
+static int smem_module_inited;
+static RAW_NOTIFIER_HEAD(smem_module_init_notifier_list);
+static DEFINE_MUTEX(smem_module_init_notifier_lock);
+
struct restart_notifier_block {
unsigned processor;
@@ -82,6 +88,8 @@
{SMEM_Q6, "adsp", .nb.notifier_call = restart_notifier_cb},
};
+static int init_smem_remote_spinlock(void);
+
/**
* smem_phys_to_virt() - Convert a physical base and offset to virtual address
*
@@ -192,11 +200,21 @@
}
EXPORT_SYMBOL(smem_alloc);
-static void *__smem_get_entry(unsigned id, unsigned *size, bool skip_init_check)
+/**
+ * __smem_get_entry - Get pointer and size of existing SMEM item
+ *
+ * @id: ID of SMEM item
+ * @size: Pointer to size variable for storing the result
+ * @skip_init_check: True means do not verify that SMEM has been initialized
+ * @use_rspinlock: True to use the remote spinlock
+ * @returns: Pointer to SMEM item or NULL if it doesn't exist
+ */
+static void *__smem_get_entry(unsigned id, unsigned *size,
+ bool skip_init_check, bool use_rspinlock)
{
struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
struct smem_heap_entry *toc = shared->heap_toc;
- int use_spinlocks = spinlocks_initialized;
+ int use_spinlocks = spinlocks_initialized && use_rspinlock;
void *ret = 0;
unsigned long flags = 0;
@@ -233,7 +251,7 @@
unsigned size;
void *ptr;
- ptr = __smem_get_entry(id, &size, skip_init_check);
+ ptr = __smem_get_entry(id, &size, skip_init_check, true);
if (!ptr)
return 0;
@@ -312,10 +330,26 @@
void *smem_get_entry(unsigned id, unsigned *size)
{
- return __smem_get_entry(id, size, false);
+ return __smem_get_entry(id, size, false, true);
}
EXPORT_SYMBOL(smem_get_entry);
+/**
+ * smem_get_entry_no_rlock - Get existing item without using remote spinlock
+ *
+ * @id: ID of SMEM item
+ * @size_out: Pointer to size variable for storing the result
+ * @returns: Pointer to SMEM item or NULL if it doesn't exist
+ *
+ * This function does not lock the remote spinlock and should only be used in
+ * failure-recover cases such as retrieving the subsystem failure reason during
+ * subsystem restart.
+ */
+void *smem_get_entry_no_rlock(unsigned id, unsigned *size_out)
+{
+ return __smem_get_entry(id, size_out, false, false);
+}
+EXPORT_SYMBOL(smem_get_entry_no_rlock);
/**
* smem_get_remote_spinlock - Remote spinlock pointer for unit testing.
@@ -324,6 +358,8 @@
*/
remote_spinlock_t *smem_get_remote_spinlock(void)
{
+ if (unlikely(!spinlocks_initialized))
+ init_smem_remote_spinlock();
return &remote_spinlock;
}
EXPORT_SYMBOL(smem_get_remote_spinlock);
@@ -333,7 +369,7 @@
*
* @returns: sucess or error code for failure
*/
-int init_smem_remote_spinlock(void)
+static int init_smem_remote_spinlock(void)
{
int rc = 0;
@@ -473,3 +509,223 @@
return 0;
}
late_initcall(modem_restart_late_init);
+
+int smem_module_init_notifier_register(struct notifier_block *nb)
+{
+ int ret;
+ if (!nb)
+ return -EINVAL;
+ mutex_lock(&smem_module_init_notifier_lock);
+ ret = raw_notifier_chain_register(&smem_module_init_notifier_list, nb);
+ if (smem_module_inited)
+ nb->notifier_call(nb, 0, NULL);
+ mutex_unlock(&smem_module_init_notifier_lock);
+ return ret;
+}
+EXPORT_SYMBOL(smem_module_init_notifier_register);
+
+int smem_module_init_notifier_unregister(struct notifier_block *nb)
+{
+ int ret;
+ if (!nb)
+ return -EINVAL;
+ mutex_lock(&smem_module_init_notifier_lock);
+ ret = raw_notifier_chain_unregister(&smem_module_init_notifier_list,
+ nb);
+ mutex_unlock(&smem_module_init_notifier_lock);
+ return ret;
+}
+EXPORT_SYMBOL(smem_module_init_notifier_unregister);
+
+static void smem_module_init_notify(uint32_t state, void *data)
+{
+ mutex_lock(&smem_module_init_notifier_lock);
+ smem_module_inited = 1;
+ raw_notifier_call_chain(&smem_module_init_notifier_list,
+ state, data);
+ mutex_unlock(&smem_module_init_notifier_lock);
+}
+
+static int msm_smem_probe(struct platform_device *pdev)
+{
+ char *key;
+ struct resource *r;
+ 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 ret;
+ struct ramdump_segment *ramdump_segments_tmp = NULL;
+ struct smem_area *smem_areas_tmp = NULL;
+ int smem_idx = 0;
+
+ if (!smem_initialized_check())
+ return -ENODEV;
+
+ key = "irq-reg-base";
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!r) {
+ pr_err("%s: missing '%s'\n", __func__, key);
+ return -ENODEV;
+ }
+
+ num_smem_areas = 1;
+ while (1) {
+ scnprintf(temp_string, temp_string_size, "aux-mem%d",
+ num_smem_areas);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ temp_string);
+ if (!r)
+ break;
+
+ ++num_smem_areas;
+ if (num_smem_areas > 999) {
+ pr_err("%s: max num aux mem regions reached\n",
+ __func__);
+ break;
+ }
+ }
+ /* Initialize main SMEM region and SSR ramdump region */
+ key = "smem";
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!r) {
+ pr_err("%s: missing '%s'\n", __func__, key);
+ return -ENODEV;
+ }
+
+ smem_areas_tmp = kmalloc_array(num_smem_areas, sizeof(struct smem_area),
+ GFP_KERNEL);
+ if (!smem_areas_tmp) {
+ pr_err("%s: smem areas kmalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+
+ ramdump_segments_tmp = kmalloc_array(num_smem_areas,
+ sizeof(struct ramdump_segment), GFP_KERNEL);
+ if (!ramdump_segments_tmp) {
+ pr_err("%s: ramdump segment kmalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+ smem_areas_tmp[smem_idx].phys_addr = r->start;
+ smem_areas_tmp[smem_idx].size = resource_size(r);
+ smem_areas_tmp[smem_idx].virt_addr = MSM_SHARED_RAM_BASE;
+
+ ramdump_segments_tmp[smem_idx].address = r->start;
+ ramdump_segments_tmp[smem_idx].size = resource_size(r);
+ ++smem_idx;
+
+ /* Configure auxiliary SMEM regions */
+ while (1) {
+ scnprintf(temp_string, temp_string_size, "aux-mem%d",
+ smem_idx);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ temp_string);
+ if (!r)
+ break;
+ aux_mem_base = r->start;
+ aux_mem_size = resource_size(r);
+
+ ramdump_segments_tmp[smem_idx].address = aux_mem_base;
+ ramdump_segments_tmp[smem_idx].size = aux_mem_size;
+
+ smem_areas_tmp[smem_idx].phys_addr = aux_mem_base;
+ smem_areas_tmp[smem_idx].size = aux_mem_size;
+ smem_areas_tmp[smem_idx].virt_addr = ioremap_nocache(
+ (unsigned long)(smem_areas_tmp[smem_idx].phys_addr),
+ smem_areas_tmp[smem_idx].size);
+ SMEM_DBG("%s: %s = %pa %pa -> %p", __func__, temp_string,
+ &aux_mem_base, &aux_mem_size,
+ smem_areas_tmp[smem_idx].virt_addr);
+
+ if (!smem_areas_tmp[smem_idx].virt_addr) {
+ pr_err("%s: ioremap_nocache() of addr:%pa size: %pa\n",
+ __func__,
+ &smem_areas_tmp[smem_idx].phys_addr,
+ &smem_areas_tmp[smem_idx].size);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+
+ if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
+ (uintptr_t)smem_areas_tmp[smem_idx].virt_addr,
+ smem_areas_tmp[smem_idx].size)) {
+ pr_err("%s: invalid virtual address block %i: %p:%pa\n",
+ __func__, smem_idx,
+ smem_areas_tmp[smem_idx].virt_addr,
+ &smem_areas_tmp[smem_idx].size);
+ ++smem_idx;
+ ret = -EINVAL;
+ goto free_smem_areas;
+ }
+
+ ++smem_idx;
+ if (smem_idx > 999) {
+ pr_err("%s: max num aux mem regions reached\n",
+ __func__);
+ break;
+ }
+ }
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret)
+ pr_err("%s: of_platform_populate failed %d\n", __func__, ret);
+
+ smem_areas = smem_areas_tmp;
+ smem_ramdump_segments = ramdump_segments_tmp;
+ return 0;
+
+free_smem_areas:
+ for (smem_idx = smem_idx - 1; smem_idx >= 1; --smem_idx)
+ iounmap(smem_areas_tmp[smem_idx].virt_addr);
+
+ num_smem_areas = 0;
+ kfree(ramdump_segments_tmp);
+ kfree(smem_areas_tmp);
+ return ret;
+}
+
+static struct of_device_id msm_smem_match_table[] = {
+ { .compatible = "qcom,smem" },
+ {},
+};
+
+static struct platform_driver msm_smem_driver = {
+ .probe = msm_smem_probe,
+ .driver = {
+ .name = "msm_smem",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smem_match_table,
+ },
+};
+
+int __init msm_smem_init(void)
+{
+ static bool registered;
+ int rc;
+
+ if (registered)
+ return 0;
+
+ registered = true;
+
+ rc = init_smem_remote_spinlock();
+ if (rc) {
+ pr_err("%s: remote spinlock init failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = platform_driver_register(&msm_smem_driver);
+ if (rc) {
+ pr_err("%s: msm_smem_driver register failed %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ smem_module_init_notify(0, NULL);
+
+ return 0;
+}
+
+module_init(msm_smem_init);
diff --git a/arch/arm/mach-msm/smem_log.c b/arch/arm/mach-msm/smem_log.c
index 87f141d2..35a6e15 100644
--- a/arch/arm/mach-msm/smem_log.c
+++ b/arch/arm/mach-msm/smem_log.c
@@ -39,6 +39,7 @@
#include "smd_private.h"
#include "smd_rpc_sym.h"
#include "modem_notifier.h"
+#include "smem_private.h"
#define DEBUG
#undef DEBUG
@@ -158,7 +159,10 @@
{ SMEM_LOG_ERROR_EVENT_BASE, "ERROR" },
{ SMEM_LOG_DCVS_EVENT_BASE, "DCVS" },
{ SMEM_LOG_SLEEP_EVENT_BASE, "SLEEP" },
- { SMEM_LOG_RPC_ROUTER_EVENT_BASE, "ROUTER" },
+ { SMEM_LOG_RPC_ROUTER_EVENT_BASE, "RPCROUTER" },
+ { SMEM_LOG_QMI_CCI_EVENT_BASE, "QCCI" },
+ { SMEM_LOG_QMI_CSI_EVENT_BASE, "QCSI" },
+ { SMEM_LOG_IPC_ROUTER_EVENT_BASE, "IPCROUTER" },
};
struct sym event_syms[] = {
@@ -2005,7 +2009,7 @@
return ret;
}
-static int smd_module_init_notifier(struct notifier_block *this,
+static int smem_module_init_notifier(struct notifier_block *this,
unsigned long code,
void *_cmd)
{
@@ -2016,12 +2020,12 @@
}
static struct notifier_block nb = {
- .notifier_call = smd_module_init_notifier,
+ .notifier_call = smem_module_init_notifier,
};
static int __init smem_log_init(void)
{
- return smd_module_init_notifier_register(&nb);
+ return smem_module_init_notifier_register(&nb);
}
diff --git a/arch/arm/mach-msm/smem_private.h b/arch/arm/mach-msm/smem_private.h
index c4f9a77..ceb8028 100644
--- a/arch/arm/mach-msm/smem_private.h
+++ b/arch/arm/mach-msm/smem_private.h
@@ -17,10 +17,6 @@
#include <mach/ramdump.h>
-#define SMEM_SPINLOCK_SMEM_ALLOC "S:3"
-extern remote_spinlock_t remote_spinlock;
-extern int spinlocks_initialized; /* only modify in init_smem_remote_spinlock */
-
#define SMD_HEAP_SIZE 512
struct smem_heap_info {
@@ -58,19 +54,26 @@
void __iomem *virt_addr;
};
-extern uint32_t num_smem_areas;
-extern struct smem_area *smem_areas;
-
-extern struct ramdump_segment *smem_ramdump_segments;
-
/* used for unit testing spinlocks */
remote_spinlock_t *smem_get_remote_spinlock(void);
-/*
- * used to ensure the remote spinlock is only inited once since local
- * spinlock init code appears non-reentrant
- */
-int init_smem_remote_spinlock(void);
-
bool smem_initialized_check(void);
+
+/**
+ * smem_module_init_notifier_register() - Register a smem module
+ * init notifier block
+ * @nb: Notifier block to be registered
+ *
+ * In order to mark the dependency on SMEM Driver module initialization
+ * register a notifier using this API. Once the smem module_init is
+ * done, notification will be passed to the registered module.
+ */
+int smem_module_init_notifier_register(struct notifier_block *nb);
+
+/**
+ * smem_module_init_notifier_register() - Unregister a smem module
+ * init notifier block
+ * @nb: Notifier block to be unregistered
+ */
+int smem_module_init_notifier_unregister(struct notifier_block *nb);
#endif /* _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_ */
diff --git a/arch/arm/mach-msm/smp2p.c b/arch/arm/mach-msm/smp2p.c
index ee262b0..4b69cf0 100644
--- a/arch/arm/mach-msm/smp2p.c
+++ b/arch/arm/mach-msm/smp2p.c
@@ -379,7 +379,7 @@
struct smp2p_out_list_item *out_item)
{
void *item_ptr = NULL;
- unsigned size;
+ unsigned size = 0;
if (!out_item)
return item_ptr;
@@ -1241,7 +1241,7 @@
{
unsigned long flags;
struct smp2p_out_list_item *out_item;
- uint32_t *entry_ptr;
+ uint32_t *entry_ptr = NULL;
if (remote_pid >= SMP2P_NUM_PROCS)
return -EINVAL;
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 575cb49..6cb04c7 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -71,6 +71,21 @@
};
enum {
+ PLATFORM_SUBTYPE_QRD = 0x0,
+ PLATFORM_SUBTYPE_SKUAA = 0x1,
+ PLATFORM_SUBTYPE_SKUF = 0x2,
+ PLATFORM_SUBTYPE_SKUAB = 0x3,
+ PLATFORM_SUBTYPE_QRD_INVALID,
+};
+
+const char *qrd_hw_platform_subtype[] = {
+ [PLATFORM_SUBTYPE_QRD] = "QRD",
+ [PLATFORM_SUBTYPE_SKUAA] = "SKUAA",
+ [PLATFORM_SUBTYPE_SKUF] = "SKUF",
+ [PLATFORM_SUBTYPE_SKUAB] = "SKUAB",
+};
+
+enum {
PLATFORM_SUBTYPE_UNKNOWN = 0x0,
PLATFORM_SUBTYPE_CHARM = 0x1,
PLATFORM_SUBTYPE_STRANGE = 0x2,
@@ -282,6 +297,24 @@
[185] = MSM_CPU_8974,
[186] = MSM_CPU_8974,
+ /* 8974AA IDs */
+ [208] = MSM_CPU_8974PRO_AA,
+ [211] = MSM_CPU_8974PRO_AA,
+ [214] = MSM_CPU_8974PRO_AA,
+ [217] = MSM_CPU_8974PRO_AA,
+
+ /* 8974AB IDs */
+ [209] = MSM_CPU_8974PRO_AB,
+ [212] = MSM_CPU_8974PRO_AB,
+ [215] = MSM_CPU_8974PRO_AB,
+ [218] = MSM_CPU_8974PRO_AB,
+
+ /* 8974AC IDs */
+ [194] = MSM_CPU_8974PRO_AC,
+ [210] = MSM_CPU_8974PRO_AC,
+ [213] = MSM_CPU_8974PRO_AC,
+ [216] = MSM_CPU_8974PRO_AC,
+
/* 8625 IDs */
[127] = MSM_CPU_8625,
[128] = MSM_CPU_8625,
@@ -629,9 +662,18 @@
}
hw_subtype = socinfo_get_platform_subtype();
+ if (HW_PLATFORM_QRD == socinfo_get_platform_type()) {
+ if (hw_subtype >= PLATFORM_SUBTYPE_QRD_INVALID) {
+ pr_err("%s: Invalid hardware platform sub type for qrd found\n",
+ __func__);
+ hw_subtype = PLATFORM_SUBTYPE_QRD;
+ }
+ return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+ qrd_hw_platform_subtype[hw_subtype]);
+ }
if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) {
pr_err("%s: Invalid hardware platform sub type found\n",
- __func__);
+ __func__);
hw_subtype = PLATFORM_SUBTYPE_UNKNOWN;
}
return snprintf(buf, PAGE_SIZE, "%-.32s\n",
diff --git a/arch/arm/mach-msm/test_qmi_client.c b/arch/arm/mach-msm/test_qmi_client.c
index d070e37..b701be8 100644
--- a/arch/arm/mach-msm/test_qmi_client.c
+++ b/arch/arm/mach-msm/test_qmi_client.c
@@ -61,6 +61,25 @@
/* Variable to hold the test result */
static int test_res;
+static unsigned int callback_count;
+static void test_async_resp_cb(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ void *resp_cb_data, int stat)
+{
+ callback_count++;
+ if (stat == 0)
+ D("%s invoked %d time(s): [RESP_LEN] = %d, [RESP_VALID] = %d",
+ __func__, callback_count,
+ ((struct test_data_resp_msg_v01 *)msg)->data_len,
+ ((struct test_data_resp_msg_v01 *)msg)->data_valid);
+ else if (stat < 0)
+ pr_err("%s: Request Failed [MSG_ID]: %d, [ERR_ID]: %d, [Callback_count]: %d",
+ __func__, msg_id, stat, callback_count);
+
+ kfree(msg);
+ kfree(resp_cb_data);
+}
+
static int test_qmi_ping_pong_send_sync_msg(void)
{
struct test_ping_req_msg_v01 req;
@@ -138,12 +157,68 @@
return rc;
}
+static int test_qmi_data_send_async_msg(unsigned int data_len)
+{
+ struct test_data_req_msg_v01 *req;
+ struct test_data_resp_msg_v01 *resp;
+ struct msg_desc req_desc, *resp_desc;
+ int rc, i;
+
+ req = kzalloc(sizeof(struct test_data_req_msg_v01), GFP_KERNEL);
+ if (!req) {
+ pr_err("%s: Data req msg alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ resp = kzalloc(sizeof(struct test_data_resp_msg_v01), GFP_KERNEL);
+ if (!resp) {
+ pr_err("%s: Data resp msg alloc failed\n", __func__);
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ resp_desc = kzalloc(sizeof(struct msg_desc), GFP_KERNEL);
+ if (!resp_desc) {
+ pr_err("%s: Resp_desc msg alloc failed\n", __func__);
+ kfree(req);
+ kfree(resp);
+ return -ENOMEM;
+ }
+
+ req->data_len = data_len;
+ for (i = 0; i < data_len; i = i + sizeof(int))
+ memcpy(req->data + i, (uint8_t *)&i, sizeof(int));
+ req->client_name_valid = 0;
+
+ req_desc.max_msg_len = TEST_DATA_REQ_MAX_MSG_LEN_V01;
+ req_desc.msg_id = TEST_DATA_REQ_MSG_ID_V01;
+ req_desc.ei_array = test_data_req_msg_v01_ei;
+
+ resp_desc->max_msg_len = TEST_DATA_REQ_MAX_MSG_LEN_V01;
+ resp_desc->msg_id = TEST_DATA_REQ_MSG_ID_V01;
+ resp_desc->ei_array = test_data_resp_msg_v01_ei;
+
+ rc = qmi_send_req_nowait(test_clnt, &req_desc, req, sizeof(*req),
+ resp_desc, resp, sizeof(*resp),
+ test_async_resp_cb, (void *)resp_desc);
+ if (rc < 0) {
+ pr_err("%s: send req failed\n", __func__);
+ kfree(resp);
+ kfree(resp_desc);
+ }
+ kfree(req);
+ return rc;
+}
+
static void test_clnt_recv_msg(struct work_struct *work)
{
int rc;
- rc = qmi_recv_msg(test_clnt);
- if (rc < 0)
+ do {
+ D("%s: Notified about a Receive Event", __func__);
+ } while ((rc = qmi_recv_msg(test_clnt)) == 0);
+
+ if (rc != -ENOMSG)
pr_err("%s: Error receiving message\n", __func__);
}
@@ -280,6 +355,32 @@
} while (test_clnt_reset);
}
}
+ } else if (!strncmp(cmd, "data_async", sizeof(cmd))) {
+ int i;
+ callback_count = 0;
+ for (i = 0; i < test_rep_cnt; i++) {
+ test_res = test_qmi_data_send_async_msg(test_data_sz);
+ if (test_res == -ENETRESET || test_clnt_reset) {
+ --i;
+ do {
+ msleep(50);
+ } while (test_clnt_reset);
+ } else if (test_res < 0) {
+ --i;
+ pr_err("%s: Error sending txn, aborting now",
+ __func__);
+ break;
+ }
+ }
+ while (callback_count < i) {
+ if (test_clnt_reset) {
+ pr_err("%s: Service Exited", __func__);
+ break;
+ }
+ msleep(50);
+ }
+ D("%s complete\n", __func__);
+ callback_count = 0;
} else {
test_res = -EINVAL;
}
diff --git a/block/blk-core.c b/block/blk-core.c
index 153240e..40d9b35 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -30,6 +30,7 @@
#include <linux/list_sort.h>
#include <linux/delay.h>
#include <linux/ratelimit.h>
+#include <linux/pm_runtime.h>
#define CREATE_TRACE_POINTS
#include <trace/events/block.h>
@@ -1195,6 +1196,16 @@
}
EXPORT_SYMBOL_GPL(part_round_stats);
+#ifdef CONFIG_PM_RUNTIME
+static void blk_pm_put_request(struct request *rq)
+{
+ if (rq->q->dev && !(rq->cmd_flags & REQ_PM) && !--rq->q->nr_pending)
+ pm_runtime_mark_last_busy(rq->q->dev);
+}
+#else
+static inline void blk_pm_put_request(struct request *rq) {}
+#endif
+
/*
* queue lock must be held
*/
@@ -1205,6 +1216,8 @@
if (unlikely(--req->ref_count))
return;
+ blk_pm_put_request(req);
+
elv_completed_request(q, req);
/* this is a bio leak */
@@ -1981,6 +1994,28 @@
}
}
+#ifdef CONFIG_PM_RUNTIME
+/*
+ * Don't process normal requests when queue is suspended
+ * or in the process of suspending/resuming
+ */
+static struct request *blk_pm_peek_request(struct request_queue *q,
+ struct request *rq)
+{
+ if (q->dev && (q->rpm_status == RPM_SUSPENDED ||
+ (q->rpm_status != RPM_ACTIVE && !(rq->cmd_flags & REQ_PM))))
+ return NULL;
+ else
+ return rq;
+}
+#else
+static inline struct request *blk_pm_peek_request(struct request_queue *q,
+ struct request *rq)
+{
+ return rq;
+}
+#endif
+
/**
* blk_peek_request - peek at the top of a request queue
* @q: request queue to peek at
@@ -2003,6 +2038,11 @@
int ret;
while ((rq = __elv_next_request(q)) != NULL) {
+
+ rq = blk_pm_peek_request(q, rq);
+ if (!rq)
+ break;
+
if (!(rq->cmd_flags & REQ_STARTED)) {
/*
* This is the first time the device driver
@@ -2018,10 +2058,8 @@
* not be passed by new incoming requests
*/
rq->cmd_flags |= REQ_STARTED;
- if (rq->cmd_flags & REQ_URGENT) {
- WARN_ON(q->dispatched_urgent);
+ if (rq->cmd_flags & REQ_URGENT)
q->dispatched_urgent = true;
- }
trace_block_rq_issue(q, rq);
}
@@ -2979,6 +3017,149 @@
}
EXPORT_SYMBOL(blk_finish_plug);
+#ifdef CONFIG_PM_RUNTIME
+/**
+ * blk_pm_runtime_init - Block layer runtime PM initialization routine
+ * @q: the queue of the device
+ * @dev: the device the queue belongs to
+ *
+ * Description:
+ * Initialize runtime-PM-related fields for @q and start auto suspend for
+ * @dev. Drivers that want to take advantage of request-based runtime PM
+ * should call this function after @dev has been initialized, and its
+ * request queue @q has been allocated, and runtime PM for it can not happen
+ * yet(either due to disabled/forbidden or its usage_count > 0). In most
+ * cases, driver should call this function before any I/O has taken place.
+ *
+ * This function takes care of setting up using auto suspend for the device,
+ * the autosuspend delay is set to -1 to make runtime suspend impossible
+ * until an updated value is either set by user or by driver. Drivers do
+ * not need to touch other autosuspend settings.
+ *
+ * The block layer runtime PM is request based, so only works for drivers
+ * that use request as their IO unit instead of those directly use bio's.
+ */
+void blk_pm_runtime_init(struct request_queue *q, struct device *dev)
+{
+ q->dev = dev;
+ q->rpm_status = RPM_ACTIVE;
+ pm_runtime_set_autosuspend_delay(q->dev, -1);
+ pm_runtime_use_autosuspend(q->dev);
+}
+EXPORT_SYMBOL(blk_pm_runtime_init);
+
+/**
+ * blk_pre_runtime_suspend - Pre runtime suspend check
+ * @q: the queue of the device
+ *
+ * Description:
+ * This function will check if runtime suspend is allowed for the device
+ * by examining if there are any requests pending in the queue. If there
+ * are requests pending, the device can not be runtime suspended; otherwise,
+ * the queue's status will be updated to SUSPENDING and the driver can
+ * proceed to suspend the device.
+ *
+ * For the not allowed case, we mark last busy for the device so that
+ * runtime PM core will try to autosuspend it some time later.
+ *
+ * This function should be called near the start of the device's
+ * runtime_suspend callback.
+ *
+ * Return:
+ * 0 - OK to runtime suspend the device
+ * -EBUSY - Device should not be runtime suspended
+ */
+int blk_pre_runtime_suspend(struct request_queue *q)
+{
+ int ret = 0;
+
+ spin_lock_irq(q->queue_lock);
+ if (q->nr_pending) {
+ ret = -EBUSY;
+ pm_runtime_mark_last_busy(q->dev);
+ } else {
+ q->rpm_status = RPM_SUSPENDING;
+ }
+ spin_unlock_irq(q->queue_lock);
+ return ret;
+}
+EXPORT_SYMBOL(blk_pre_runtime_suspend);
+
+/**
+ * blk_post_runtime_suspend - Post runtime suspend processing
+ * @q: the queue of the device
+ * @err: return value of the device's runtime_suspend function
+ *
+ * Description:
+ * Update the queue's runtime status according to the return value of the
+ * device's runtime suspend function and mark last busy for the device so
+ * that PM core will try to auto suspend the device at a later time.
+ *
+ * This function should be called near the end of the device's
+ * runtime_suspend callback.
+ */
+void blk_post_runtime_suspend(struct request_queue *q, int err)
+{
+ spin_lock_irq(q->queue_lock);
+ if (!err) {
+ q->rpm_status = RPM_SUSPENDED;
+ } else {
+ q->rpm_status = RPM_ACTIVE;
+ pm_runtime_mark_last_busy(q->dev);
+ }
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_post_runtime_suspend);
+
+/**
+ * blk_pre_runtime_resume - Pre runtime resume processing
+ * @q: the queue of the device
+ *
+ * Description:
+ * Update the queue's runtime status to RESUMING in preparation for the
+ * runtime resume of the device.
+ *
+ * This function should be called near the start of the device's
+ * runtime_resume callback.
+ */
+void blk_pre_runtime_resume(struct request_queue *q)
+{
+ spin_lock_irq(q->queue_lock);
+ q->rpm_status = RPM_RESUMING;
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_pre_runtime_resume);
+
+/**
+ * blk_post_runtime_resume - Post runtime resume processing
+ * @q: the queue of the device
+ * @err: return value of the device's runtime_resume function
+ *
+ * Description:
+ * Update the queue's runtime status according to the return value of the
+ * device's runtime_resume function. If it is successfully resumed, process
+ * the requests that are queued into the device's queue when it is resuming
+ * and then mark last busy and initiate autosuspend for it.
+ *
+ * This function should be called near the end of the device's
+ * runtime_resume callback.
+ */
+void blk_post_runtime_resume(struct request_queue *q, int err)
+{
+ spin_lock_irq(q->queue_lock);
+ if (!err) {
+ q->rpm_status = RPM_ACTIVE;
+ __blk_run_queue(q);
+ pm_runtime_mark_last_busy(q->dev);
+ pm_request_autosuspend(q->dev);
+ } else {
+ q->rpm_status = RPM_SUSPENDED;
+ }
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_post_runtime_resume);
+#endif
+
int __init blk_dev_init(void)
{
BUILD_BUG_ON(__REQ_NR_BITS > 8 *
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 32629e2..b4711cb 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -239,6 +239,9 @@
unsigned long workload_expires;
struct cfq_group *serving_group;
+ unsigned int nr_urgent_pending;
+ unsigned int nr_urgent_in_flight;
+
/*
* Each priority tree is sorted by next_request position. These
* trees are used when determining if two or more queues are
@@ -2091,6 +2094,14 @@
(RQ_CFQG(rq))->dispatched++;
elv_dispatch_sort(q, rq);
+ if (rq->cmd_flags & REQ_URGENT) {
+ if (!cfqd->nr_urgent_pending)
+ WARN_ON(1);
+ else
+ cfqd->nr_urgent_pending--;
+ cfqd->nr_urgent_in_flight++;
+ }
+
cfqd->rq_in_flight[cfq_cfqq_sync(cfqq)]++;
cfqq->nr_sectors += blk_rq_sectors(rq);
cfq_blkiocg_update_dispatch_stats(&cfqq->cfqg->blkg, blk_rq_bytes(rq),
@@ -3194,6 +3205,69 @@
}
}
+/*
+ * Called when a request (rq) is reinserted (to cfqq). Check if there's
+ * something we should do about it
+ */
+static void
+cfq_rq_requeued(struct cfq_data *cfqd, struct cfq_queue *cfqq,
+ struct request *rq)
+{
+ struct cfq_io_cq *cic = RQ_CIC(rq);
+
+ cfqd->rq_queued++;
+ if (rq->cmd_flags & REQ_PRIO)
+ cfqq->prio_pending++;
+
+ cfqq->dispatched--;
+ (RQ_CFQG(rq))->dispatched--;
+
+ cfqd->rq_in_flight[cfq_cfqq_sync(cfqq)]--;
+
+ cfq_update_io_thinktime(cfqd, cfqq, cic);
+ cfq_update_io_seektime(cfqd, cfqq, rq);
+ cfq_update_idle_window(cfqd, cfqq, cic);
+
+ cfqq->last_request_pos = blk_rq_pos(rq) + blk_rq_sectors(rq);
+
+ if (cfqq == cfqd->active_queue) {
+ if (cfq_cfqq_wait_request(cfqq)) {
+ if (blk_rq_bytes(rq) > PAGE_CACHE_SIZE ||
+ cfqd->busy_queues > 1) {
+ cfq_del_timer(cfqd, cfqq);
+ cfq_clear_cfqq_wait_request(cfqq);
+ } else {
+ cfq_blkiocg_update_idle_time_stats(
+ &cfqq->cfqg->blkg);
+ cfq_mark_cfqq_must_dispatch(cfqq);
+ }
+ }
+ } else if (cfq_should_preempt(cfqd, cfqq, rq)) {
+ cfq_preempt_queue(cfqd, cfqq);
+ }
+}
+
+static int cfq_reinsert_request(struct request_queue *q, struct request *rq)
+{
+ struct cfq_data *cfqd = q->elevator->elevator_data;
+ struct cfq_queue *cfqq = RQ_CFQQ(rq);
+
+ if (!cfqq || cfqq->cfqd != cfqd)
+ return -EIO;
+
+ cfq_log_cfqq(cfqd, cfqq, "re-insert_request");
+ list_add(&rq->queuelist, &cfqq->fifo);
+ cfq_add_rq_rb(rq);
+
+ cfq_rq_requeued(cfqd, cfqq, rq);
+ if (rq->cmd_flags & REQ_URGENT) {
+ if (cfqd->nr_urgent_in_flight)
+ cfqd->nr_urgent_in_flight--;
+ cfqd->nr_urgent_pending++;
+ }
+ return 0;
+}
+
static void cfq_insert_request(struct request_queue *q, struct request *rq)
{
struct cfq_data *cfqd = q->elevator->elevator_data;
@@ -3208,7 +3282,45 @@
cfq_blkiocg_update_io_add_stats(&(RQ_CFQG(rq))->blkg,
&cfqd->serving_group->blkg, rq_data_dir(rq),
rq_is_sync(rq));
+
cfq_rq_enqueued(cfqd, cfqq, rq);
+
+ if (rq->cmd_flags & REQ_URGENT) {
+ WARN_ON(1);
+ blk_dump_rq_flags(rq, "");
+ rq->cmd_flags &= ~REQ_URGENT;
+ }
+
+ /* Request is considered URGENT if:
+ * 1. The queue being served is of a lower IO priority then the new
+ * request
+ * OR:
+ * 2. The workload being performed is ASYNC
+ * Only READ requests may be considered as URGENT
+ */
+ if ((cfqd->active_queue &&
+ cfqq->ioprio_class < cfqd->active_queue->ioprio_class) ||
+ (cfqd->serving_type == ASYNC_WORKLOAD &&
+ rq_data_dir(rq) == READ)) {
+ rq->cmd_flags |= REQ_URGENT;
+ cfqd->nr_urgent_pending++;
+ }
+}
+
+
+/**
+ * cfq_urgent_pending() - Return TRUE if there is an urgent
+ * request on scheduler
+ * @q: requests queue
+ */
+static bool cfq_urgent_pending(struct request_queue *q)
+{
+ struct cfq_data *cfqd = q->elevator->elevator_data;
+
+ if (cfqd->nr_urgent_pending && !cfqd->nr_urgent_in_flight)
+ return true;
+
+ return false;
}
/*
@@ -3292,6 +3404,14 @@
const int sync = rq_is_sync(rq);
unsigned long now;
+ if (rq->cmd_flags & REQ_URGENT) {
+ if (!cfqd->nr_urgent_in_flight)
+ WARN_ON(1);
+ else
+ cfqd->nr_urgent_in_flight--;
+ rq->cmd_flags &= ~REQ_URGENT;
+ }
+
now = jiffies;
cfq_log_cfqq(cfqd, cfqq, "complete rqnoidle %d",
!!(rq->cmd_flags & REQ_NOIDLE));
@@ -3859,6 +3979,8 @@
.elevator_bio_merged_fn = cfq_bio_merged,
.elevator_dispatch_fn = cfq_dispatch_requests,
.elevator_add_req_fn = cfq_insert_request,
+ .elevator_reinsert_req_fn = cfq_reinsert_request,
+ .elevator_is_urgent_fn = cfq_urgent_pending,
.elevator_activate_req_fn = cfq_activate_request,
.elevator_deactivate_req_fn = cfq_deactivate_request,
.elevator_completed_req_fn = cfq_completed_request,
diff --git a/block/elevator.c b/block/elevator.c
index 27adf7c..55f1f1e 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -34,6 +34,7 @@
#include <linux/blktrace_api.h>
#include <linux/hash.h>
#include <linux/uaccess.h>
+#include <linux/pm_runtime.h>
#include <trace/events/block.h>
@@ -568,6 +569,27 @@
e->type->ops.elevator_bio_merged_fn(q, rq, bio);
}
+#ifdef CONFIG_PM_RUNTIME
+static void blk_pm_requeue_request(struct request *rq)
+{
+ if (rq->q->dev && !(rq->cmd_flags & REQ_PM))
+ rq->q->nr_pending--;
+}
+
+static void blk_pm_add_request(struct request_queue *q, struct request *rq)
+{
+ if (q->dev && !(rq->cmd_flags & REQ_PM) && q->nr_pending++ == 0 &&
+ (q->rpm_status == RPM_SUSPENDED || q->rpm_status == RPM_SUSPENDING))
+ pm_request_resume(q->dev);
+}
+#else
+static inline void blk_pm_requeue_request(struct request *rq) {}
+static inline void blk_pm_add_request(struct request_queue *q,
+ struct request *rq)
+{
+}
+#endif
+
void elv_requeue_request(struct request_queue *q, struct request *rq)
{
/*
@@ -582,6 +604,8 @@
rq->cmd_flags &= ~REQ_STARTED;
+ blk_pm_requeue_request(rq);
+
__elv_add_request(q, rq, ELEVATOR_INSERT_REQUEUE);
}
@@ -658,6 +682,8 @@
{
trace_block_rq_insert(q, rq);
+ blk_pm_add_request(q, rq);
+
rq->q = q;
if (rq->cmd_flags & REQ_SOFTBARRIER) {
@@ -816,7 +842,6 @@
if (rq->cmd_flags & REQ_URGENT) {
q->notified_urgent = false;
- WARN_ON(!q->dispatched_urgent);
q->dispatched_urgent = false;
}
/*
diff --git a/block/row-iosched.c b/block/row-iosched.c
index e71f6af..8e19c94 100644
--- a/block/row-iosched.c
+++ b/block/row-iosched.c
@@ -97,7 +97,7 @@
/* Default values for idling on read queues (in msec) */
#define ROW_IDLE_TIME_MSEC 5
-#define ROW_READ_FREQ_MSEC 20
+#define ROW_READ_FREQ_MSEC 5
/**
* struct rowq_idling_data - parameters for idling on the queue
@@ -331,6 +331,10 @@
struct row_queue *rqueue = RQ_ROWQ(rq);
s64 diff_ms;
bool queue_was_empty = list_empty(&rqueue->fifo);
+ unsigned long bv_page_flags = 0;
+
+ if (rq->bio && rq->bio->bi_io_vec && rq->bio->bi_io_vec->bv_page)
+ bv_page_flags = rq->bio->bi_io_vec->bv_page->flags;
list_add_tail(&rq->queuelist, &rqueue->fifo);
rd->nr_reqs[rq_data_dir(rq)]++;
@@ -346,11 +350,14 @@
if (row_queues_def[rqueue->prio].idling_enabled) {
if (rd->rd_idle_data.idling_queue_idx == rqueue->prio &&
hrtimer_active(&rd->rd_idle_data.hr_timer)) {
- (void)hrtimer_cancel(&rd->rd_idle_data.hr_timer);
- row_log_rowq(rd, rqueue->prio,
- "Canceled delayed work on %d",
- rd->rd_idle_data.idling_queue_idx);
- rd->rd_idle_data.idling_queue_idx = ROWQ_MAX_PRIO;
+ if (hrtimer_try_to_cancel(
+ &rd->rd_idle_data.hr_timer) >= 0) {
+ row_log_rowq(rd, rqueue->prio,
+ "Canceled delayed work on %d",
+ rd->rd_idle_data.idling_queue_idx);
+ rd->rd_idle_data.idling_queue_idx =
+ ROWQ_MAX_PRIO;
+ }
}
diff_ms = ktime_to_ms(ktime_sub(ktime_get(),
rqueue->idle_data.last_insert_time));
@@ -360,7 +367,9 @@
rqueue->idle_data.begin_idling = false;
return;
}
- if (diff_ms < rd->rd_idle_data.freq_ms) {
+
+ if ((bv_page_flags & (1L << PG_readahead)) ||
+ (diff_ms < rd->rd_idle_data.freq_ms)) {
rqueue->idle_data.begin_idling = true;
row_log_rowq(rd, rqueue->prio, "Enable idling");
} else {
@@ -577,14 +586,14 @@
for (i = 0; i < ROWQ_REG_PRIO_IDX; i++) {
if (!list_empty(&rd->row_queues[i].fifo)) {
if (hrtimer_active(&rd->rd_idle_data.hr_timer)) {
- (void)hrtimer_cancel(
- &rd->rd_idle_data.hr_timer);
- row_log_rowq(rd,
- rd->rd_idle_data.idling_queue_idx,
+ if (hrtimer_try_to_cancel(
+ &rd->rd_idle_data.hr_timer) >= 0) {
+ row_log(rd->dispatch_queue,
"Canceling delayed work on %d. RT pending",
- rd->rd_idle_data.idling_queue_idx);
- rd->rd_idle_data.idling_queue_idx =
- ROWQ_MAX_PRIO;
+ rd->rd_idle_data.idling_queue_idx);
+ rd->rd_idle_data.idling_queue_idx =
+ ROWQ_MAX_PRIO;
+ }
}
if (row_regular_req_pending(rd) &&
@@ -720,11 +729,12 @@
int ret = 0, currq, ioprio_class_to_serve, start_idx, end_idx;
if (force && hrtimer_active(&rd->rd_idle_data.hr_timer)) {
- (void)hrtimer_cancel(&rd->rd_idle_data.hr_timer);
- row_log_rowq(rd, rd->rd_idle_data.idling_queue_idx,
- "Canceled delayed work on %d - forced dispatch",
- rd->rd_idle_data.idling_queue_idx);
- rd->rd_idle_data.idling_queue_idx = ROWQ_MAX_PRIO;
+ if (hrtimer_try_to_cancel(&rd->rd_idle_data.hr_timer) >= 0) {
+ row_log(rd->dispatch_queue,
+ "Canceled delayed work on %d - forced dispatch",
+ rd->rd_idle_data.idling_queue_idx);
+ rd->rd_idle_data.idling_queue_idx = ROWQ_MAX_PRIO;
+ }
}
if (rd->pending_urgent_rq) {
diff --git a/block/test-iosched.c b/block/test-iosched.c
index b1e5492..07b36b8 100644
--- a/block/test-iosched.c
+++ b/block/test-iosched.c
@@ -101,8 +101,8 @@
goto exit;
}
- ptd->test_info.test_duration = jiffies -
- ptd->test_info.test_duration;
+ ptd->test_info.test_duration = ktime_sub(ktime_get(),
+ ptd->test_info.test_duration);
test_pr_info("%s: Test is completed, test_count=%d, reinsert_count=%d,",
__func__, ptd->test_count, ptd->reinsert_count);
@@ -774,7 +774,7 @@
goto error;
}
- ptd->test_info.test_duration = jiffies;
+ ptd->test_info.test_duration = ktime_get();
ret = run_test(ptd);
if (ret) {
test_pr_err("%s: failed to run the test\n", __func__);
@@ -784,10 +784,10 @@
test_pr_info("%s: Waiting for the test completion", __func__);
wait_event(ptd->wait_q, ptd->test_state == TEST_COMPLETED);
- t_info->test_duration = ptd->test_info.test_duration;
- t_info->test_byte_count = ptd->test_info.test_byte_count;
del_timer_sync(&ptd->timeout_timer);
+ memcpy(t_info, &ptd->test_info, sizeof(struct test_info));
+
ret = check_test_result(ptd);
if (ret) {
test_pr_err("%s: check_test_result failed\n",
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index cb4528f..7a0d23b 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -22,6 +22,7 @@
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
obj-$(CONFIG_REGMAP) += regmap/
obj-$(CONFIG_SOC_BUS) += soc.o
+obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_SYNC) += sync.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 1b1cbb5..b3a7d6e 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -24,6 +24,7 @@
#include <linux/wait.h>
#include <linux/async.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/devinfo.h>
#include "base.h"
#include "power/power.h"
@@ -257,6 +258,12 @@
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
+
+ /* If using pinctrl, bind pins now before probing */
+ ret = pinctrl_bind_pins(dev);
+ if (ret)
+ goto probe_failed;
+
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c
index 0de37c9..58b0513 100644
--- a/drivers/base/genlock.c
+++ b/drivers/base/genlock.c
@@ -742,6 +742,16 @@
fd_install(ret, lock->file);
+ /*
+ * Taking a reference for lock file.
+ * This is required as now we have two file descriptor
+ * pointing to same file. If one FD is closed, lock file
+ * will be closed. Taking this reference will make sure
+ * that file doesn't get close. This refrence will go
+ * when client will call close on this FD.
+ */
+ fget(ret);
+
return ret;
}
EXPORT_SYMBOL(genlock_get_fd_handle);
diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c
new file mode 100644
index 0000000..67a274e
--- /dev/null
+++ b/drivers/base/pinctrl.c
@@ -0,0 +1,69 @@
+/*
+ * Driver core interface to the pinctrl subsystem.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ * Based on bits of regulator core, gpio core and clk core
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/device.h>
+#include <linux/pinctrl/devinfo.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/slab.h>
+
+/**
+ * pinctrl_bind_pins() - called by the device core before probe
+ * @dev: the device that is just about to probe
+ */
+int pinctrl_bind_pins(struct device *dev)
+{
+ int ret;
+
+ dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
+ if (!dev->pins)
+ return -ENOMEM;
+
+ dev->pins->p = devm_pinctrl_get(dev);
+ if (IS_ERR(dev->pins->p)) {
+ dev_dbg(dev, "no pinctrl handle\n");
+ ret = PTR_ERR(dev->pins->p);
+ goto cleanup_alloc;
+ }
+
+ dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(dev->pins->default_state)) {
+ dev_dbg(dev, "no default pinctrl state\n");
+ ret = 0;
+ goto cleanup_get;
+ }
+
+ ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
+ if (ret) {
+ dev_dbg(dev, "failed to activate default pinctrl state\n");
+ goto cleanup_get;
+ }
+
+ return 0;
+
+ /*
+ * If no pinctrl handle or default state was found for this device,
+ * let's explicitly free the pin container in the device, there is
+ * no point in keeping it around.
+ */
+cleanup_get:
+ devm_pinctrl_put(dev->pins->p);
+cleanup_alloc:
+ devm_kfree(dev, dev->pins);
+ dev->pins = NULL;
+
+ /* Only return deferrals */
+ if (ret != -EPROBE_DEFER)
+ ret = 0;
+
+ return ret;
+}
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 94c3554..0c67ed8 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -20,6 +20,8 @@
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeup.h>
+#include <linux/spinlock.h>
#include <asm/current.h>
#ifdef CONFIG_DIAG_OVER_USB
#include <mach/usbdiag.h>
@@ -39,9 +41,41 @@
struct mutex dci_event_mask_mutex;
struct mutex dci_health_mutex;
+spinlock_t ws_lock;
+unsigned long ws_lock_flags;
+
+/* Number of milliseconds anticipated to process the DCI data */
+#define DCI_WAKEUP_TIMEOUT 1
+
#define DCI_CHK_CAPACITY(entry, new_data_len) \
((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0) \
+#ifdef CONFIG_DEBUG_FS
+struct diag_dci_data_info *dci_data_smd;
+struct mutex dci_stat_mutex;
+
+void diag_dci_smd_record_info(int read_bytes)
+{
+ static int curr_dci_data_smd;
+ static unsigned long iteration;
+ struct diag_dci_data_info *temp_data = dci_data_smd;
+ if (!temp_data)
+ return;
+ mutex_lock(&dci_stat_mutex);
+ if (curr_dci_data_smd == DIAG_DCI_DEBUG_CNT)
+ curr_dci_data_smd = 0;
+ temp_data += curr_dci_data_smd;
+ temp_data->iteration = iteration + 1;
+ temp_data->data_size = read_bytes;
+ diag_get_timestamp(temp_data->time_stamp);
+ curr_dci_data_smd++;
+ iteration++;
+ mutex_unlock(&dci_stat_mutex);
+}
+#else
+void diag_dci_smd_record_info(int read_bytes) { }
+#endif
+
/* Process the data read from the smd dci channel */
int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
int recd_bytes)
@@ -49,6 +83,7 @@
int read_bytes, dci_pkt_len, i;
uint8_t recv_pkt_cmd_code;
+ diag_dci_smd_record_info(recd_bytes);
/* Each SMD read can have multiple DCI packets */
read_bytes = 0;
while (read_bytes < recd_bytes) {
@@ -69,6 +104,11 @@
read_bytes += 5 + dci_pkt_len;
buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
}
+ /* Release wakeup source when there are no more clients to
+ process DCI data */
+ if (driver->num_dci_client == 0)
+ diag_dci_try_deactivate_wakeup_source(smd_info->ch);
+
/* wake up all sleeping DCI clients which have some data */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
if (driver->dci_client_tbl[i].client &&
@@ -488,6 +528,7 @@
if (!temp) {
pr_err("diag: Invalid buffer in %s\n", __func__);
+ return -ENOMEM;
}
/* This is Pkt request/response transaction */
@@ -1073,6 +1114,9 @@
diag_smd_notify);
driver->smd_dci[index].ch_save =
driver->smd_dci[index].ch;
+ driver->dci_device = &pdev->dev;
+ driver->dci_device->power.wakeup = wakeup_source_register
+ ("DIAG_DCI_WS");
if (err)
pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
__func__, pdev->id, err);
@@ -1095,6 +1139,9 @@
diag_smd_notify);
driver->smd_dci_cmd[index].ch_save =
driver->smd_dci_cmd[index].ch;
+ driver->dci_cmd_device = &pdev->dev;
+ driver->dci_cmd_device->power.wakeup = wakeup_source_register
+ ("DIAG_DCI_CMD_WS");
if (err)
pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
__func__, pdev->id, err);
@@ -1146,10 +1193,13 @@
driver->dci_tag = 0;
driver->dci_client_id = 0;
driver->num_dci_client = 0;
+ driver->dci_device = NULL;
+ driver->dci_cmd_device = NULL;
mutex_init(&driver->dci_mutex);
mutex_init(&dci_log_mask_mutex);
mutex_init(&dci_event_mask_mutex);
mutex_init(&dci_health_mutex);
+ spin_lock_init(&ws_lock);
for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
success = diag_smd_constructor(&driver->smd_dci[i], i,
@@ -1212,6 +1262,10 @@
if (driver->diag_dci_wq)
destroy_workqueue(driver->diag_dci_wq);
+ mutex_destroy(&driver->dci_mutex);
+ mutex_destroy(&dci_log_mask_mutex);
+ mutex_destroy(&dci_event_mask_mutex);
+ mutex_destroy(&dci_health_mutex);
return DIAG_DCI_NO_REG;
}
@@ -1354,4 +1408,48 @@
return 0;
}
+uint8_t diag_dci_get_cumulative_real_time()
+{
+ uint8_t real_time = MODE_NONREALTIME, i;
+ for (i = 0; i < MAX_DCI_CLIENTS; i++)
+ if (driver->dci_client_tbl[i].client &&
+ driver->dci_client_tbl[i].real_time ==
+ MODE_REALTIME) {
+ real_time = 1;
+ break;
+ }
+ return real_time;
+}
+int diag_dci_set_real_time(int client_id, uint8_t real_time)
+{
+ int i = DCI_CLIENT_INDEX_INVALID;
+ i = diag_dci_find_client_index(client_id);
+
+ if (i != DCI_CLIENT_INDEX_INVALID)
+ driver->dci_client_tbl[i].real_time = real_time;
+ return i;
+}
+
+void diag_dci_try_activate_wakeup_source(smd_channel_t *channel)
+{
+ spin_lock_irqsave(&ws_lock, ws_lock_flags);
+ if (channel == driver->smd_dci[MODEM_DATA].ch) {
+ pm_wakeup_event(driver->dci_device, DCI_WAKEUP_TIMEOUT);
+ pm_stay_awake(driver->dci_device);
+ } else if (channel == driver->smd_dci_cmd[MODEM_DATA].ch) {
+ pm_wakeup_event(driver->dci_cmd_device, DCI_WAKEUP_TIMEOUT);
+ pm_stay_awake(driver->dci_cmd_device);
+ }
+ spin_unlock_irqrestore(&ws_lock, ws_lock_flags);
+}
+
+void diag_dci_try_deactivate_wakeup_source(smd_channel_t *channel)
+{
+ spin_lock_irqsave(&ws_lock, ws_lock_flags);
+ if (channel == driver->smd_dci[MODEM_DATA].ch)
+ pm_relax(driver->dci_device);
+ else if (channel == driver->smd_dci_cmd[MODEM_DATA].ch)
+ pm_relax(driver->dci_cmd_device);
+ spin_unlock_irqrestore(&ws_lock, ws_lock_flags);
+}
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index cc7f93d..520995b 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -28,6 +28,10 @@
#define DCI_LOG_CON_MIN_LEN 14
#define DCI_EVENT_CON_MIN_LEN 16
+#ifdef CONFIG_DEBUG_FS
+#define DIAG_DCI_DEBUG_CNT 100
+#define DIAG_DCI_DEBUG_LEN 100
+#endif
/* 16 log code categories, each has:
* 1 bytes equip id + 1 dirty byte + 512 byte max log mask
@@ -62,6 +66,7 @@
int received_logs;
int received_events;
struct mutex data_mutex;
+ uint8_t real_time;
};
/* This is used for DCI health stats */
@@ -90,6 +95,18 @@
DIAG_DCI_TABLE_ERR /* Error dealing with registration tables */
};
+#ifdef CONFIG_DEBUG_FS
+/* To collect debug information during each smd read */
+struct diag_dci_data_info {
+ unsigned long iteration;
+ int data_size;
+ char time_stamp[DIAG_TS_SIZE];
+};
+
+extern struct diag_dci_data_info *dci_data_smd;
+extern struct mutex dci_stat_mutex;
+#endif
+
int diag_dci_init(void);
void diag_dci_exit(void);
void diag_update_smd_dci_work_fn(struct work_struct *);
@@ -118,4 +135,10 @@
void create_dci_event_mask_tbl(unsigned char *tbl_buf);
int diag_dci_clear_event_mask(void);
int diag_dci_query_event_mask(uint16_t event_id);
+void diag_dci_smd_record_info(int read_bytes);
+uint8_t diag_dci_get_cumulative_real_time(void);
+int diag_dci_set_real_time(int client_id, uint8_t real_time);
+/* Functions related to DCI wakeup sources */
+void diag_dci_try_activate_wakeup_source(smd_channel_t *channel);
+void diag_dci_try_deactivate_wakeup_source(smd_channel_t *channel);
#endif
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 71eda68..3d1a6cd 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -18,11 +18,15 @@
#include "diagfwd.h"
#include "diagfwd_bridge.h"
#include "diagfwd_hsic.h"
+#include "diagmem.h"
+#include "diag_dci.h"
#define DEBUG_BUF_SIZE 4096
static struct dentry *diag_dbgfs_dent;
static int diag_dbgfs_table_index;
static int diag_dbgfs_finished;
+static int diag_dbgfs_dci_data_index;
+static int diag_dbgfs_dci_finished;
static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
@@ -66,7 +70,19 @@
"Modem CMD in_busy_1: %d\n"
"Modem CMD in_busy_2: %d\n"
"DCI CMD Modem in_busy_1: %d\n"
- "logging_mode: %d\n",
+ "Modem supports STM: %d\n"
+ "LPASS supports STM: %d\n"
+ "RIVA supports STM: %d\n"
+ "Modem STM state: %d\n"
+ "LPASS STM state: %d\n"
+ "RIVA STM state: %d\n"
+ "APPS STM state: %d\n"
+ "Modem STM requested state: %d\n"
+ "LPASS STM requested state: %d\n"
+ "RIVA STM requested state: %d\n"
+ "APPS STM requested state: %d\n"
+ "logging_mode: %d\n"
+ "real_time_mode: %d\n",
(unsigned int)driver->smd_data[MODEM_DATA].ch,
(unsigned int)driver->smd_data[LPASS_DATA].ch,
(unsigned int)driver->smd_data[WCNSS_DATA].ch,
@@ -96,7 +112,19 @@
driver->smd_cmd[MODEM_DATA].in_busy_1,
driver->smd_cmd[MODEM_DATA].in_busy_2,
driver->smd_dci_cmd[MODEM_DATA].in_busy_1,
- driver->logging_mode);
+ driver->peripheral_supports_stm[MODEM_DATA],
+ driver->peripheral_supports_stm[LPASS_DATA],
+ driver->peripheral_supports_stm[WCNSS_DATA],
+ driver->stm_state[MODEM_DATA],
+ driver->stm_state[LPASS_DATA],
+ driver->stm_state[WCNSS_DATA],
+ driver->stm_state[APPS_DATA],
+ driver->stm_state_requested[MODEM_DATA],
+ driver->stm_state_requested[LPASS_DATA],
+ driver->stm_state_requested[WCNSS_DATA],
+ driver->stm_state_requested[APPS_DATA],
+ driver->logging_mode,
+ driver->real_time_mode);
#ifdef CONFIG_DIAG_OVER_USB
ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
@@ -109,6 +137,94 @@
return ret;
}
+static ssize_t diag_dbgfs_read_dcistats(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ char *buf = NULL;
+ int bytes_remaining, bytes_written = 0, bytes_in_buf = 0, i = 0;
+ struct diag_dci_data_info *temp_data = dci_data_smd;
+ int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
+
+ if (diag_dbgfs_dci_finished) {
+ diag_dbgfs_dci_finished = 0;
+ return 0;
+ }
+
+ buf = kzalloc(sizeof(char) * buf_size, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(buf)) {
+ pr_err("diag: %s, Error allocating memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ bytes_remaining = buf_size;
+
+ if (diag_dbgfs_dci_data_index == 0) {
+ bytes_written =
+ scnprintf(buf, buf_size,
+ "number of clients: %d\n"
+ "dci proc active: %d\n"
+ "dci real time vote: %d\n",
+ driver->num_dci_client,
+ (driver->proc_active_mask & DIAG_PROC_DCI) ? 1 : 0,
+ (driver->proc_rt_vote_mask & DIAG_PROC_DCI) ? 1 : 0);
+ bytes_in_buf += bytes_written;
+ bytes_remaining -= bytes_written;
+#ifdef CONFIG_DIAG_OVER_USB
+ bytes_written = scnprintf(buf+bytes_in_buf, bytes_remaining,
+ "usb_connected: %d\n",
+ driver->usb_connected);
+ bytes_in_buf += bytes_written;
+ bytes_remaining -= bytes_written;
+#endif
+ if (driver->dci_device) {
+ bytes_written = scnprintf(buf+bytes_in_buf,
+ bytes_remaining,
+ "dci power active, relax: %lu, %lu\n",
+ driver->dci_device->power.wakeup->active_count,
+ driver->dci_device->power.wakeup->relax_count);
+ bytes_in_buf += bytes_written;
+ bytes_remaining -= bytes_written;
+ }
+ if (driver->dci_cmd_device) {
+ bytes_written = scnprintf(buf+bytes_in_buf,
+ bytes_remaining,
+ "dci cmd power active, relax: %lu, %lu\n",
+ driver->dci_cmd_device->power.wakeup->
+ active_count,
+ driver->dci_cmd_device->power.wakeup->
+ relax_count);
+ bytes_in_buf += bytes_written;
+ bytes_remaining -= bytes_written;
+ }
+ }
+ temp_data += diag_dbgfs_dci_data_index;
+ for (i = diag_dbgfs_dci_data_index; i < DIAG_DCI_DEBUG_CNT; i++) {
+ if (temp_data->iteration != 0) {
+ bytes_written = scnprintf(
+ buf + bytes_in_buf, bytes_remaining,
+ "i %-20ld\t"
+ "s %-20d\t"
+ "t %-20s\n",
+ temp_data->iteration,
+ temp_data->data_size,
+ temp_data->time_stamp);
+ bytes_in_buf += bytes_written;
+ bytes_remaining -= bytes_written;
+ /* Check if there is room for another entry */
+ if (bytes_remaining < bytes_written)
+ break;
+ }
+ temp_data++;
+ }
+
+ diag_dbgfs_dci_data_index = (i >= DIAG_DCI_DEBUG_CNT) ? 0 : i + 1;
+ bytes_written = simple_read_from_buffer(ubuf, count, ppos, buf,
+ bytes_in_buf);
+ kfree(buf);
+ diag_dbgfs_dci_finished = 1;
+ return bytes_written;
+}
+
static ssize_t diag_dbgfs_read_workpending(struct file *file,
char __user *ubuf, size_t count, loff_t *ppos)
{
@@ -248,6 +364,102 @@
}
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+static ssize_t diag_dbgfs_read_mempool(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf = NULL;
+ int ret = 0, i = 0;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(buf)) {
+ pr_err("diag: %s, Error allocating memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ ret = scnprintf(buf, DEBUG_BUF_SIZE,
+ "POOL_TYPE_COPY: [0x%p : 0x%p] count = %d\n"
+ "POOL_TYPE_HDLC: [0x%p : 0x%p] count = %d\n"
+ "POOL_TYPE_USER: [0x%p : 0x%p] count = %d\n"
+ "POOL_TYPE_WRITE_STRUCT: [0x%p : 0x%p] count = %d\n",
+ driver->diagpool,
+ diag_pools_array[POOL_COPY_IDX],
+ driver->count,
+ driver->diag_hdlc_pool,
+ diag_pools_array[POOL_HDLC_IDX],
+ driver->count_hdlc_pool,
+ driver->diag_user_pool,
+ diag_pools_array[POOL_USER_IDX],
+ driver->count_user_pool,
+ driver->diag_write_struct_pool,
+ diag_pools_array[POOL_WRITE_STRUCT_IDX],
+ driver->count_write_struct_pool);
+
+ for (i = 0; i < MAX_HSIC_CH; i++) {
+ if (!diag_hsic[i].hsic_inited)
+ continue;
+ ret += scnprintf(buf+ret, DEBUG_BUF_SIZE-ret,
+ "POOL_TYPE_HSIC_%d: [0x%p : 0x%p] count = %d\n",
+ i+1,
+ diag_hsic[i].diag_hsic_pool,
+ diag_pools_array[POOL_HSIC_IDX + i],
+ diag_hsic[i].count_hsic_pool);
+ }
+
+ for (i = 0; i < MAX_HSIC_CH; i++) {
+ if (!diag_hsic[i].hsic_inited)
+ continue;
+ ret += scnprintf(buf+ret, DEBUG_BUF_SIZE-ret,
+ "POOL_TYPE_HSIC_%d_WRITE: [0x%p : 0x%p] count = %d\n",
+ i+1,
+ diag_hsic[i].diag_hsic_write_pool,
+ diag_pools_array[POOL_HSIC_WRITE_IDX + i],
+ diag_hsic[i].count_hsic_write_pool);
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+ kfree(buf);
+ return ret;
+}
+#else
+static ssize_t diag_dbgfs_read_mempool(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf = NULL;
+ int ret = 0;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(buf)) {
+ pr_err("diag: %s, Error allocating memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ ret = scnprintf(buf, DEBUG_BUF_SIZE,
+ "POOL_TYPE_COPY: [0x%p : 0x%p] count = %d\n"
+ "POOL_TYPE_HDLC: [0x%p : 0x%p] count = %d\n"
+ "POOL_TYPE_USER: [0x%p : 0x%p] count = %d\n"
+ "POOL_TYPE_WRITE_STRUCT: [0x%p : 0x%p] count = %d\n",
+ driver->diagpool,
+ diag_pools_array[POOL_COPY_IDX],
+ driver->count,
+ driver->diag_hdlc_pool,
+ diag_pools_array[POOL_HDLC_IDX],
+ driver->count_hdlc_pool,
+ driver->diag_user_pool,
+ diag_pools_array[POOL_USER_IDX],
+ driver->count_user_pool,
+ driver->diag_write_struct_pool,
+ diag_pools_array[POOL_WRITE_STRUCT_IDX],
+ driver->count_write_struct_pool);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+ kfree(buf);
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
static ssize_t diag_dbgfs_read_bridge(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
@@ -383,6 +595,14 @@
.read = diag_dbgfs_read_workpending,
};
+const struct file_operations diag_dbgfs_mempool_ops = {
+ .read = diag_dbgfs_read_mempool,
+};
+
+const struct file_operations diag_dbgfs_dcistats_ops = {
+ .read = diag_dbgfs_read_dcistats,
+};
+
void diag_debugfs_init(void)
{
diag_dbgfs_dent = debugfs_create_dir("diag", 0);
@@ -398,6 +618,12 @@
debugfs_create_file("work_pending", 0444, diag_dbgfs_dent, 0,
&diag_dbgfs_workpending_ops);
+ debugfs_create_file("mempool", 0444, diag_dbgfs_dent, 0,
+ &diag_dbgfs_mempool_ops);
+
+ debugfs_create_file("dci_stats", 0444, diag_dbgfs_dent, 0,
+ &diag_dbgfs_dcistats_ops);
+
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
debugfs_create_file("bridge", 0444, diag_dbgfs_dent, 0,
&diag_dbgfs_bridge_ops);
@@ -405,6 +631,16 @@
diag_dbgfs_table_index = 0;
diag_dbgfs_finished = 0;
+ diag_dbgfs_dci_data_index = 0;
+ diag_dbgfs_dci_finished = 0;
+
+ /* DCI related structures */
+ dci_data_smd = kzalloc(sizeof(struct diag_dci_data_info) *
+ DIAG_DCI_DEBUG_CNT, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(dci_data_smd))
+ pr_warn("diag: could not allocate memory for dci debug info\n");
+
+ mutex_init(&dci_stat_mutex);
}
void diag_debugfs_cleanup(void)
@@ -413,6 +649,9 @@
debugfs_remove_recursive(diag_dbgfs_dent);
diag_dbgfs_dent = NULL;
}
+
+ kfree(dci_data_smd);
+ mutex_destroy(&dci_stat_mutex);
}
#else
void diag_debugfs_init(void) { }
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index 5aed793..aa1d847 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -26,7 +26,7 @@
#define ALL_SSID -1
#define MAX_SSID_PER_RANGE 100
-#define FEATURE_MASK_LEN_BYTES 1
+#define FEATURE_MASK_LEN_BYTES 2
struct mask_info {
int equip_id;
@@ -307,7 +307,8 @@
diag_send_feature_mask_update(smd_info);
if (smd_info->notify_context == SMD_EVENT_OPEN)
- diag_send_diag_mode_update_by_smd(smd_info, MODE_REALTIME);
+ diag_send_diag_mode_update_by_smd(smd_info,
+ driver->real_time_mode);
smd_info->notify_context = 0;
}
@@ -465,7 +466,7 @@
void *buf = driver->buf_feature_mask_update;
int header_size = sizeof(struct diag_ctrl_feature_mask);
int wr_size = -ENOMEM, retry_count = 0;
- uint8_t feature_byte = 0;
+ uint8_t feature_bytes[FEATURE_MASK_LEN_BYTES] = {0, 0};
int total_len = 0;
if (!smd_info) {
@@ -486,11 +487,12 @@
driver->feature_mask->ctrl_pkt_data_len = 4 + FEATURE_MASK_LEN_BYTES;
driver->feature_mask->feature_mask_len = FEATURE_MASK_LEN_BYTES;
memcpy(buf, driver->feature_mask, header_size);
- feature_byte |= F_DIAG_INT_FEATURE_MASK;
- feature_byte |= F_DIAG_LOG_ON_DEMAND_RSP_ON_MASTER;
- feature_byte |= driver->supports_separate_cmdrsp ?
+ feature_bytes[0] |= F_DIAG_INT_FEATURE_MASK;
+ feature_bytes[0] |= F_DIAG_LOG_ON_DEMAND_RSP_ON_MASTER;
+ feature_bytes[0] |= driver->supports_separate_cmdrsp ?
F_DIAG_REQ_RSP_CHANNEL : 0;
- memcpy(buf+header_size, &feature_byte, FEATURE_MASK_LEN_BYTES);
+ feature_bytes[1] |= F_DIAG_OVER_STM;
+ memcpy(buf+header_size, &feature_bytes, FEATURE_MASK_LEN_BYTES);
total_len = header_size + FEATURE_MASK_LEN_BYTES;
while (retry_count < 3) {
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index b05bcef..3ae56c5 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -44,6 +44,26 @@
#define POOL_TYPE_HSIC_WRITE 11
#define POOL_TYPE_HSIC_2_WRITE 12
#define POOL_TYPE_ALL 10
+
+#define POOL_COPY_IDX 0
+#define POOL_HDLC_IDX 1
+#define POOL_USER_IDX 2
+#define POOL_WRITE_STRUCT_IDX 3
+#define POOL_HSIC_IDX 4
+#define POOL_HSIC_2_IDX 5
+#define POOL_HSIC_3_IDX 6
+#define POOL_HSIC_4_IDX 7
+#define POOL_HSIC_WRITE_IDX 8
+#define POOL_HSIC_2_WRITE_IDX 9
+#define POOL_HSIC_3_WRITE_IDX 10
+#define POOL_HSIC_4_WRITE_IDX 11
+
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+#define NUM_MEMORY_POOLS 12
+#else
+#define NUM_MEMORY_POOLS 4
+#endif
+
#define MODEM_DATA 0
#define LPASS_DATA 1
#define WCNSS_DATA 2
@@ -69,6 +89,13 @@
#define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */
#define DIAG_CON_WCNSS (0x0008) /* Bit mask for WCNSS */
+#define NUM_STM_PROCESSORS 4
+
+#define DIAG_STM_MODEM 0x01
+#define DIAG_STM_LPASS 0x02
+#define DIAG_STM_WCNSS 0x04
+#define DIAG_STM_APPS 0x08
+
/*
* The status bit masks when received in a signal handler are to be
* used in conjunction with the peripheral list bit mask to determine the
@@ -93,6 +120,16 @@
#define SMD_CMD_TYPE 3
#define SMD_DCI_CMD_TYPE 4
+#define DIAG_PROC_DCI 1
+#define DIAG_PROC_MEMORY_DEVICE 2
+
+/* Flags to vote the DCI or Memory device process up or down
+ when it becomes active or inactive */
+#define VOTE_DOWN 0
+#define VOTE_UP 1
+
+#define DIAG_TS_SIZE 50
+
/* Maximum number of pkt reg supported at initialization*/
extern int diag_max_reg;
extern int diag_threshold_reg;
@@ -160,6 +197,11 @@
spinlock_t read_spinlock;
};
+struct real_time_vote_t {
+ uint16_t proc;
+ uint8_t real_time_vote;
+};
+
/* This structure is defined in USB header file */
#ifndef CONFIG_DIAG_OVER_USB
struct diag_request {
@@ -195,6 +237,8 @@
struct work_struct diag_read_smd_work;
struct work_struct diag_notify_update_smd_work;
int notify_context;
+ struct work_struct diag_general_smd_work;
+ int general_context;
/*
* Function ptr for function to call to process the data that
@@ -226,6 +270,12 @@
unsigned int buf_tbl_size;
int use_device_tree;
int supports_separate_cmdrsp;
+ /* The state requested in the STM command */
+ int stm_state_requested[NUM_STM_PROCESSORS];
+ /* The current STM state */
+ int stm_state[NUM_STM_PROCESSORS];
+ /* Whether or not the peripheral supports STM */
+ int peripheral_supports_stm[NUM_SMD_CONTROL_CHANNELS];
/* DCI related variables */
struct dci_pkt_req_tracking_tbl *req_tracking_tbl;
struct diag_dci_client_tbl *dci_client_tbl;
@@ -264,7 +314,6 @@
struct diag_ctrl_msg_mask *msg_mask;
struct diag_ctrl_feature_mask *feature_mask;
/* State for diag forwarding */
- int real_time_mode;
struct diag_smd_info smd_data[NUM_SMD_DATA_CHANNELS];
struct diag_smd_info smd_cntl[NUM_SMD_CONTROL_CHANNELS];
struct diag_smd_info smd_dci[NUM_SMD_DCI_CHANNELS];
@@ -284,6 +333,16 @@
unsigned hdlc_count;
unsigned hdlc_escape;
int in_busy_pktdata;
+ struct device *dci_device;
+ struct device *dci_cmd_device;
+ /* Variables for non real time mode */
+ int real_time_mode;
+ int real_time_update_busy;
+ uint16_t proc_active_mask;
+ uint16_t proc_rt_vote_mask;
+ struct mutex real_time_mutex;
+ struct work_struct diag_real_time_work;
+ struct workqueue_struct *diag_real_time_wq;
#ifdef CONFIG_DIAG_OVER_USB
int usb_connected;
struct usb_diag_ch *legacy_ch;
@@ -346,4 +405,6 @@
extern int wrap_enabled;
extern uint16_t wrap_count;
+void diag_get_timestamp(char *time_str);
+
#endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 71f3cb4..5e06f19 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -16,6 +16,7 @@
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
+#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/diagchar.h>
#include <linux/platform_device.h>
@@ -43,6 +44,9 @@
#include "diag_masks.h"
#include "diagfwd_bridge.h"
+#include <linux/coresight-stm.h>
+#include <linux/kernel.h>
+
MODULE_DESCRIPTION("Diag Char Driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
@@ -269,20 +273,24 @@
DCI_CLIENT_INDEX_INVALID)
diagchar_ioctl(NULL, DIAG_IOCTL_DCI_DEINIT, 0);
/* If the exiting process is the socket process */
+ mutex_lock(&driver->diagchar_mutex);
if (driver->socket_process &&
(driver->socket_process->tgid == current->tgid)) {
driver->socket_process = NULL;
+ diag_update_proc_vote(DIAG_PROC_MEMORY_DEVICE, VOTE_DOWN);
}
if (driver->callback_process &&
(driver->callback_process->tgid == current->tgid)) {
driver->callback_process = NULL;
+ diag_update_proc_vote(DIAG_PROC_MEMORY_DEVICE, VOTE_DOWN);
}
+ mutex_unlock(&driver->diagchar_mutex);
#ifdef CONFIG_DIAG_OVER_USB
/* If the SD logging process exits, change logging to USB mode */
if (driver->logging_process_id == current->tgid) {
driver->logging_mode = USB_MODE;
- diag_send_diag_mode_update(MODE_REALTIME);
+ diag_update_proc_vote(DIAG_PROC_MEMORY_DEVICE, VOTE_DOWN);
diagfwd_connect();
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diag_clear_hsic_tbl();
@@ -298,9 +306,10 @@
mutex_lock(&driver->diagchar_mutex);
driver->ref_count--;
- /* On Client exit, try to destroy all 3 pools */
+ /* On Client exit, try to destroy all 4 pools */
diagmem_exit(driver, POOL_TYPE_COPY);
diagmem_exit(driver, POOL_TYPE_HDLC);
+ diagmem_exit(driver, POOL_TYPE_USER);
diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT);
for (i = 0; i < driver->num_clients; i++) {
if (NULL != diagpriv_data && diagpriv_data->pid ==
@@ -382,6 +391,18 @@
(*count_entries)++;
}
+void diag_get_timestamp(char *time_str)
+{
+ struct timeval t;
+ struct tm broken_tm;
+ do_gettimeofday(&t);
+ if (!time_str)
+ return;
+ time_to_tm(t.tv_sec, 0, &broken_tm);
+ scnprintf(time_str, DIAG_TS_SIZE, "%d:%d:%d:%ld", broken_tm.tm_hour,
+ broken_tm.tm_min, broken_tm.tm_sec, t.tv_usec);
+}
+
static int diag_get_remote(int remote_info)
{
int val = (remote_info < 0) ? -remote_info : remote_info;
@@ -783,7 +804,6 @@
int diag_switch_logging(unsigned long ioarg)
{
int temp = 0, success = -EINVAL, status = 0;
- int temp_realtime_mode = driver->real_time_mode;
int requested_mode = (int)ioarg;
switch (requested_mode) {
@@ -793,7 +813,6 @@
case UART_MODE:
case SOCKET_MODE:
case CALLBACK_MODE:
- case MEMORY_DEVICE_MODE_NRT:
break;
default:
pr_err("diag: In %s, request to switch to invalid mode: %d\n",
@@ -801,26 +820,28 @@
return -EINVAL;
}
- mutex_lock(&driver->diagchar_mutex);
- temp = driver->logging_mode;
- driver->logging_mode = requested_mode;
-
- if (driver->logging_mode == MEMORY_DEVICE_MODE_NRT) {
- diag_send_diag_mode_update(MODE_NONREALTIME);
- driver->logging_mode = MEMORY_DEVICE_MODE;
- } else {
- diag_send_diag_mode_update(MODE_REALTIME);
- }
-
- if (temp == driver->logging_mode) {
- mutex_unlock(&driver->diagchar_mutex);
- if (driver->logging_mode != MEMORY_DEVICE_MODE ||
- temp_realtime_mode)
+ if (requested_mode == driver->logging_mode) {
+ if (requested_mode != MEMORY_DEVICE_MODE ||
+ driver->real_time_mode)
pr_info_ratelimited("diag: Already in logging mode change requested, mode: %d\n",
driver->logging_mode);
return 0;
}
+ diag_update_proc_vote(DIAG_PROC_MEMORY_DEVICE, VOTE_UP);
+ if (requested_mode != MEMORY_DEVICE_MODE)
+ diag_update_real_time_vote(DIAG_PROC_MEMORY_DEVICE,
+ MODE_REALTIME);
+
+ if (!(requested_mode == MEMORY_DEVICE_MODE &&
+ driver->logging_mode == USB_MODE))
+ queue_work(driver->diag_real_time_wq,
+ &driver->diag_real_time_work);
+
+ mutex_lock(&driver->diagchar_mutex);
+ temp = driver->logging_mode;
+ driver->logging_mode = requested_mode;
+
if (driver->logging_mode == MEMORY_DEVICE_MODE) {
diag_clear_hsic_tbl();
driver->mask_check = 1;
@@ -898,12 +919,14 @@
long diagchar_ioctl(struct file *filp,
unsigned int iocmd, unsigned long ioarg)
{
- int i, result = -EINVAL, interim_size = 0, client_id = 0;
+ int i, result = -EINVAL, interim_size = 0, client_id = 0, real_time = 0;
+ int retry_count = 0, timer = 0;
uint16_t support_list = 0, interim_rsp_id, remote_dev;
struct diag_dci_client_tbl *dci_params;
struct diag_dci_health_stats stats;
struct diag_log_event_stats le_stats;
struct diagpkt_delay_params delay_params;
+ struct real_time_vote_t rt_vote;
switch (iocmd) {
case DIAG_IOCTL_COMMAND_REG:
@@ -953,6 +976,10 @@
driver->smd_dci_cmd[i].in_busy_1 = 0;
}
driver->num_dci_client++;
+ if (driver->num_dci_client == 1)
+ diag_update_proc_vote(DIAG_PROC_DCI, VOTE_UP);
+ queue_work(driver->diag_real_time_wq,
+ &driver->diag_real_time_work);
pr_debug("diag: In %s, id = %d\n",
__func__, driver->dci_client_id);
driver->dci_client_id++;
@@ -976,6 +1003,7 @@
driver->dci_client_tbl[i].dropped_events = 0;
driver->dci_client_tbl[i].received_logs = 0;
driver->dci_client_tbl[i].received_events = 0;
+ driver->dci_client_tbl[i].real_time = 1;
mutex_init(&driver->dci_client_tbl[i].
data_mutex);
break;
@@ -1024,6 +1052,15 @@
mutex_destroy(&driver->dci_client_tbl[result].
data_mutex);
driver->num_dci_client--;
+ if (driver->num_dci_client == 0) {
+ diag_update_proc_vote(DIAG_PROC_DCI, VOTE_DOWN);
+ } else {
+ real_time = diag_dci_get_cumulative_real_time();
+ diag_update_real_time_vote(DIAG_PROC_DCI,
+ real_time);
+ }
+ queue_work(driver->diag_real_time_wq,
+ &driver->diag_real_time_work);
}
mutex_unlock(&driver->dci_mutex);
break;
@@ -1118,6 +1155,44 @@
else
result = 1;
break;
+ case DIAG_IOCTL_VOTE_REAL_TIME:
+ if (copy_from_user(&rt_vote, (void *)ioarg, sizeof(struct
+ real_time_vote_t)))
+ result = -EFAULT;
+ driver->real_time_update_busy++;
+ if (rt_vote.proc == DIAG_PROC_DCI) {
+ diag_dci_set_real_time(current->tgid,
+ rt_vote.real_time_vote);
+ real_time = diag_dci_get_cumulative_real_time();
+ } else {
+ real_time = rt_vote.real_time_vote;
+ }
+ diag_update_real_time_vote(rt_vote.proc, real_time);
+ queue_work(driver->diag_real_time_wq,
+ &driver->diag_real_time_work);
+ result = 0;
+ break;
+ case DIAG_IOCTL_GET_REAL_TIME:
+ if (copy_from_user(&real_time, (void *)ioarg, sizeof(int)))
+ return -EFAULT;
+ while (retry_count < 3) {
+ if (driver->real_time_update_busy > 0) {
+ retry_count++;
+ /* The value 10000 was chosen empirically as an
+ optimum value in order to give the work in
+ diag_real_time_wq to complete processing.*/
+ for (timer = 0; timer < 5; timer++)
+ usleep_range(10000, 10100);
+ } else {
+ real_time = driver->real_time_mode;
+ if (copy_to_user((void *)ioarg, &real_time,
+ sizeof(int)))
+ return -EFAULT;
+ result = 0;
+ break;
+ }
+ }
+ break;
}
return result;
}
@@ -1378,19 +1453,25 @@
driver->data_ready[index] ^= DCI_DATA_TYPE;
for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
driver->smd_dci[i].in_busy_1 = 0;
- if (driver->smd_dci[i].ch)
+ if (driver->smd_dci[i].ch) {
+ diag_dci_try_deactivate_wakeup_source(
+ driver->smd_dci[i].ch);
queue_work(driver->diag_dci_wq,
&(driver->smd_dci[i].diag_read_smd_work));
+ }
}
if (driver->supports_separate_cmdrsp) {
for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++) {
if (!driver->separate_cmdrsp[i])
continue;
driver->smd_dci_cmd[i].in_busy_1 = 0;
- if (driver->smd_dci_cmd[i].ch)
+ if (driver->smd_dci_cmd[i].ch) {
+ diag_dci_try_deactivate_wakeup_source(
+ driver->smd_dci_cmd[i].ch);
queue_work(driver->diag_dci_wq,
&(driver->smd_dci_cmd[i].
diag_read_smd_work));
+ }
}
}
goto exit;
@@ -1409,7 +1490,8 @@
size_t count, loff_t *ppos)
{
int err, ret = 0, pkt_type, token_offset = 0;
- int remote_proc = 0, index;
+ int remote_proc = 0;
+ uint8_t index;
#ifdef DIAG_DEBUG
int length = 0, i;
#endif
@@ -1452,11 +1534,14 @@
err = copy_from_user(user_space_data, buf + 4, payload_size);
if (err) {
pr_alert("diag: copy failed for DCI data\n");
+ diagmem_free(driver, user_space_data, POOL_TYPE_USER);
+ user_space_data = NULL;
return DIAG_DCI_SEND_DATA_FAIL;
}
err = diag_process_dci_transaction(user_space_data,
payload_size);
diagmem_free(driver, user_space_data, POOL_TYPE_USER);
+ user_space_data = NULL;
return err;
}
if (pkt_type == CALLBACK_DATA_TYPE) {
@@ -1467,16 +1552,19 @@
return -EBADMSG;
}
+ mutex_lock(&driver->diagchar_mutex);
buf_copy = diagmem_alloc(driver, payload_size, POOL_TYPE_COPY);
if (!buf_copy) {
driver->dropped_count++;
+ mutex_unlock(&driver->diagchar_mutex);
return -ENOMEM;
}
err = copy_from_user(buf_copy, buf + 4, payload_size);
if (err) {
pr_err("diag: copy failed for user space data\n");
- return -EIO;
+ ret = -EIO;
+ goto fail_free_copy;
}
/* Check for proc_type */
remote_proc = diag_get_remote(*(int *)buf_copy);
@@ -1485,8 +1573,7 @@
wait_event_interruptible(driver->wait_q,
(driver->in_busy_pktdata == 0));
ret = diag_process_apps_pkt(buf_copy, payload_size);
- diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
- return ret;
+ goto fail_free_copy;
}
/* The packet is for the remote processor */
token_offset = 4;
@@ -1499,20 +1586,20 @@
1 + payload_size);
send.terminate = 1;
- mutex_lock(&driver->diagchar_mutex);
+
if (!buf_hdlc)
buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
POOL_TYPE_HDLC);
if (!buf_hdlc) {
ret = -ENOMEM;
- goto fail_free_hdlc;
+ driver->used = 0;
+ goto fail_free_copy;
}
if (HDLC_OUT_BUF_SIZE < (2 * payload_size) + 3) {
pr_err("diag: Dropping packet, HDLC encoded packet payload size crosses buffer limit. Current payload size %d\n",
((2*payload_size) + 3));
driver->dropped_count++;
ret = -EBADMSG;
- diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
goto fail_free_hdlc;
}
enc.dest = buf_hdlc + driver->used;
@@ -1577,13 +1664,7 @@
}
}
#endif
- diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
- diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
- buf_copy = NULL;
- buf_hdlc = NULL;
- driver->used = 0;
- mutex_unlock(&driver->diagchar_mutex);
- return ret;
+ goto fail_free_hdlc;
}
if (pkt_type == USER_SPACE_DATA_TYPE) {
user_space_data = diagmem_alloc(driver, payload_size,
@@ -1597,6 +1678,7 @@
if (err) {
pr_err("diag: copy failed for user space data\n");
diagmem_free(driver, user_space_data, POOL_TYPE_USER);
+ user_space_data = NULL;
return -EIO;
}
/* Check for proc_type */
@@ -1615,6 +1697,7 @@
pr_alert("diag: mask request Invalid\n");
diagmem_free(driver, user_space_data,
POOL_TYPE_USER);
+ user_space_data = NULL;
return -EFAULT;
}
}
@@ -1692,6 +1775,7 @@
err);
diagmem_free(driver, user_space_data,
POOL_TYPE_USER);
+ user_space_data = NULL;
return err;
}
}
@@ -1702,6 +1786,7 @@
diag_process_hdlc((void *)
(user_space_data + token_offset), payload_size);
diagmem_free(driver, user_space_data, POOL_TYPE_USER);
+ user_space_data = NULL;
return 0;
}
@@ -1713,9 +1798,11 @@
return -EBADMSG;
}
+ mutex_lock(&driver->diagchar_mutex);
buf_copy = diagmem_alloc(driver, payload_size, POOL_TYPE_COPY);
if (!buf_copy) {
driver->dropped_count++;
+ mutex_unlock(&driver->diagchar_mutex);
return -ENOMEM;
}
@@ -1725,6 +1812,21 @@
ret = -EFAULT;
goto fail_free_copy;
}
+ if (driver->stm_state[APPS_DATA] &&
+ (pkt_type >= DATA_TYPE_EVENT && pkt_type <= DATA_TYPE_LOG)) {
+ int stm_size = 0;
+
+ stm_size = stm_log_inv_ts(OST_ENTITY_DIAG, 0, buf_copy,
+ payload_size);
+
+ if (stm_size == 0)
+ pr_debug("diag: In %s, stm_log_inv_ts returned size of 0\n",
+ __func__);
+
+ diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+ return 0;
+ }
+
#ifdef DIAG_DEBUG
printk(KERN_DEBUG "data is -->\n");
for (i = 0; i < payload_size; i++)
@@ -1744,13 +1846,13 @@
length++;
}
#endif
- mutex_lock(&driver->diagchar_mutex);
if (!buf_hdlc)
buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
POOL_TYPE_HDLC);
if (!buf_hdlc) {
ret = -ENOMEM;
- goto fail_free_hdlc;
+ driver->used = 0;
+ goto fail_free_copy;
}
if (HDLC_OUT_BUF_SIZE < (2*payload_size) + 3) {
pr_err("diag: Dropping packet, HDLC encoded packet payload size crosses buffer limit. Current payload size %d\n",
@@ -1763,7 +1865,6 @@
err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
if (err) {
/*Free the buffer right away if write failed */
- diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
if (driver->logging_mode == USB_MODE)
diagmem_free(driver, (unsigned char *)driver->
write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
@@ -1776,7 +1877,7 @@
POOL_TYPE_HDLC);
if (!buf_hdlc) {
ret = -ENOMEM;
- goto fail_free_hdlc;
+ goto fail_free_copy;
}
}
@@ -1792,7 +1893,6 @@
err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
if (err) {
/*Free the buffer right away if write failed */
- diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
if (driver->logging_mode == USB_MODE)
diagmem_free(driver, (unsigned char *)driver->
write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
@@ -1805,7 +1905,7 @@
POOL_TYPE_HDLC);
if (!buf_hdlc) {
ret = -ENOMEM;
- goto fail_free_hdlc;
+ goto fail_free_copy;
}
enc.dest = buf_hdlc + driver->used;
enc.dest_last = (void *)(buf_hdlc + driver->used +
@@ -1818,7 +1918,6 @@
err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
if (err) {
/*Free the buffer right away if write failed */
- diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
if (driver->logging_mode == USB_MODE)
diagmem_free(driver, (unsigned char *)driver->
write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
@@ -1830,6 +1929,7 @@
}
diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+ buf_copy = NULL;
mutex_unlock(&driver->diagchar_mutex);
if (!timer_in_progress) {
timer_in_progress = 1;
@@ -1838,17 +1938,36 @@
return 0;
fail_free_hdlc:
+ diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
buf_hdlc = NULL;
driver->used = 0;
diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+ buf_copy = NULL;
mutex_unlock(&driver->diagchar_mutex);
return ret;
fail_free_copy:
diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+ buf_copy = NULL;
+ mutex_unlock(&driver->diagchar_mutex);
return ret;
}
+static void diag_real_time_info_init(void)
+{
+ if (!driver)
+ return;
+ driver->real_time_mode = 1;
+ driver->real_time_update_busy = 0;
+ driver->proc_active_mask = 0;
+ driver->proc_rt_vote_mask |= DIAG_PROC_DCI;
+ driver->proc_rt_vote_mask |= DIAG_PROC_MEMORY_DEVICE;
+ driver->diag_real_time_wq = create_singlethread_workqueue(
+ "diag_real_time_wq");
+ INIT_WORK(&(driver->diag_real_time_work), diag_real_time_work_fn);
+ mutex_init(&driver->real_time_mutex);
+}
+
int mask_request_validate(unsigned char mask_buf[])
{
uint8_t packet_id;
@@ -2050,6 +2169,7 @@
init_waitqueue_head(&driver->wait_q);
init_waitqueue_head(&driver->smd_wait_q);
INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
+ diag_real_time_info_init();
diag_debugfs_init();
diag_masks_init();
diagfwd_init();
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 6851fd8..de4433b 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -46,6 +46,16 @@
#define MODE_CMD 41
#define RESET_ID 2
+#define STM_CMD_VERSION_OFFSET 4
+#define STM_CMD_MASK_OFFSET 5
+#define STM_CMD_DATA_OFFSET 6
+#define STM_CMD_NUM_BYTES 7
+
+#define STM_RSP_VALID_INDEX 7
+#define STM_RSP_SUPPORTED_INDEX 8
+#define STM_RSP_SMD_COMPLY_INDEX 9
+#define STM_RSP_NUM_BYTES 10
+
int diag_debug_buf_idx;
unsigned char diag_debug_buf[1024];
/* Number of entries in table of buffers */
@@ -380,10 +390,18 @@
(smd_info->type == SMD_DATA_TYPE))
buf = smd_info->buf_in_2;
+ if (!buf && (smd_info->type == SMD_DCI_TYPE ||
+ smd_info->type == SMD_DCI_CMD_TYPE))
+ diag_dci_try_deactivate_wakeup_source(smd_info->ch);
+
if (smd_info->ch && buf) {
temp_buf = buf;
pkt_len = smd_cur_packet_size(smd_info->ch);
+ if (pkt_len == 0 && (smd_info->type == SMD_DCI_TYPE ||
+ smd_info->type == SMD_DCI_CMD_TYPE))
+ diag_dci_try_deactivate_wakeup_source(smd_info->ch);
+
while (pkt_len && (pkt_len != total_recd)) {
loop_count++;
r = smd_read_avail(smd_info->ch);
@@ -402,7 +420,7 @@
} else {
pr_debug("diag: In %s, return from wait_event ch closed\n",
__func__);
- return;
+ goto fail_return;
}
}
total_recd += r;
@@ -415,13 +433,13 @@
} else {
pr_err("diag: In %s, SMD sending in packets more than %d bytes\n",
__func__, MAX_IN_BUF_SIZE);
- return;
+ goto fail_return;
}
}
if (pkt_len < r) {
pr_err("diag: In %s, SMD sending incorrect pkt\n",
__func__);
- return;
+ goto fail_return;
}
if (pkt_len > r) {
pr_debug("diag: In %s, SMD sending partial pkt %d %d %d %d %d %d\n",
@@ -452,6 +470,13 @@
(driver->logging_mode == MEMORY_DEVICE_MODE)) {
chk_logging_wakeup();
}
+ return;
+
+fail_return:
+ if (smd_info->type == SMD_DCI_TYPE ||
+ smd_info->type == SMD_DCI_CMD_TYPE)
+ diag_dci_try_deactivate_wakeup_source(smd_info->ch);
+ return;
}
void diag_read_smd_work_fn(struct work_struct *work)
@@ -763,6 +788,77 @@
}
}
+void diag_process_stm_mask(uint8_t cmd, uint8_t data_mask, int data_type,
+ uint8_t *rsp_supported, uint8_t *rsp_smd_comply)
+{
+ int status = 0;
+ if (data_type >= MODEM_DATA && data_type <= WCNSS_DATA) {
+ if (driver->peripheral_supports_stm[data_type]) {
+ status = diag_send_stm_state(
+ &driver->smd_cntl[data_type], cmd);
+ if (status == 1)
+ *rsp_smd_comply |= data_mask;
+ *rsp_supported |= data_mask;
+ } else if (driver->smd_cntl[data_type].ch) {
+ *rsp_smd_comply |= data_mask;
+ }
+ if ((*rsp_smd_comply & data_mask) &&
+ (*rsp_supported & data_mask))
+ driver->stm_state[data_type] = cmd;
+
+ driver->stm_state_requested[data_type] = cmd;
+ } else if (data_type == APPS_DATA) {
+ *rsp_supported |= data_mask;
+ *rsp_smd_comply |= data_mask;
+ driver->stm_state[data_type] = cmd;
+ driver->stm_state_requested[data_type] = cmd;
+ }
+}
+
+int diag_process_stm_cmd(unsigned char *buf)
+{
+ uint8_t version = *(buf+STM_CMD_VERSION_OFFSET);
+ uint8_t mask = *(buf+STM_CMD_MASK_OFFSET);
+ uint8_t cmd = *(buf+STM_CMD_DATA_OFFSET);
+ uint8_t rsp_supported = 0;
+ uint8_t rsp_smd_comply = 0;
+ int valid_command = 1;
+ int i;
+
+ /* Check if command is valid */
+ if ((version != 1) || (mask == 0) || (0 != (mask >> 4)) ||
+ (cmd != ENABLE_STM && cmd != DISABLE_STM)) {
+ valid_command = 0;
+ } else {
+ if (mask & DIAG_STM_MODEM)
+ diag_process_stm_mask(cmd, DIAG_STM_MODEM, MODEM_DATA,
+ &rsp_supported, &rsp_smd_comply);
+
+ if (mask & DIAG_STM_LPASS)
+ diag_process_stm_mask(cmd, DIAG_STM_LPASS, LPASS_DATA,
+ &rsp_supported, &rsp_smd_comply);
+
+ if (mask & DIAG_STM_WCNSS)
+ diag_process_stm_mask(cmd, DIAG_STM_WCNSS, WCNSS_DATA,
+ &rsp_supported, &rsp_smd_comply);
+
+ if (mask & DIAG_STM_APPS)
+ diag_process_stm_mask(cmd, DIAG_STM_APPS, APPS_DATA,
+ &rsp_supported, &rsp_smd_comply);
+ }
+
+ for (i = 0; i < STM_CMD_NUM_BYTES; i++)
+ driver->apps_rsp_buf[i] = *(buf+i);
+
+ driver->apps_rsp_buf[STM_RSP_VALID_INDEX] = valid_command;
+ driver->apps_rsp_buf[STM_RSP_SUPPORTED_INDEX] = rsp_supported;
+ driver->apps_rsp_buf[STM_RSP_SMD_COMPLY_INDEX] = rsp_smd_comply;
+
+ encode_rsp_and_send(STM_RSP_NUM_BYTES-1);
+
+ return 0;
+}
+
int diag_process_apps_pkt(unsigned char *buf, int len)
{
uint16_t subsys_cmd_code;
@@ -838,6 +934,9 @@
*(uint32_t *)(driver->apps_rsp_buf+4) = PKT_SIZE;
encode_rsp_and_send(7);
return 0;
+ } else if ((*buf == 0x4b) && (*(buf+1) == 0x12) &&
+ (*(uint16_t *)(buf+2) == 0x020E)) {
+ return diag_process_stm_cmd(buf);
}
/* Check for Apps Only & get event mask request */
else if (!(driver->smd_data[MODEM_DATA].ch) && chk_apps_only() &&
@@ -1384,6 +1483,8 @@
/* Poll SMD CNTL channels to check for data */
diag_smd_notify(&(driver->smd_cntl[i]), SMD_EVENT_DATA);
}
+ queue_work(driver->diag_real_time_wq,
+ &driver->diag_real_time_work);
/* Poll USB channel to check for data*/
queue_work(driver->diag_wq, &(driver->diag_read_work));
@@ -1419,6 +1520,8 @@
}
}
}
+ queue_work(driver->diag_real_time_wq,
+ &driver->diag_real_time_work);
#ifdef CONFIG_DIAG_SDIO_PIPE
if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())
if (driver->mdm_ch && !IS_ERR(driver->mdm_ch))
@@ -1588,6 +1691,9 @@
/* Notify the clients of the close */
diag_dci_notify_client(smd_info->peripheral_mask,
DIAG_STATUS_CLOSED);
+ } else if (smd_info->type == SMD_CNTL_TYPE) {
+ diag_cntl_stm_notify(smd_info,
+ CLEAR_PERIPHERAL_STM_STATE);
}
return;
} else if (event == SMD_EVENT_OPEN) {
@@ -1614,10 +1720,12 @@
wake_up(&driver->smd_wait_q);
if (smd_info->type == SMD_DCI_TYPE ||
- smd_info->type == SMD_DCI_CMD_TYPE)
+ smd_info->type == SMD_DCI_CMD_TYPE) {
+ if (event == SMD_EVENT_DATA)
+ diag_dci_try_activate_wakeup_source(smd_info->ch);
queue_work(driver->diag_dci_wq,
&(smd_info->diag_read_smd_work));
- else
+ } else
queue_work(driver->diag_wq, &(smd_info->diag_read_smd_work));
}
@@ -1829,20 +1937,27 @@
* information to the update function.
*/
smd_info->notify_context = 0;
+ smd_info->general_context = 0;
switch (type) {
case SMD_DATA_TYPE:
case SMD_CMD_TYPE:
INIT_WORK(&(smd_info->diag_notify_update_smd_work),
diag_clean_reg_fn);
+ INIT_WORK(&(smd_info->diag_general_smd_work),
+ diag_cntl_smd_work_fn);
break;
case SMD_CNTL_TYPE:
INIT_WORK(&(smd_info->diag_notify_update_smd_work),
diag_mask_update_fn);
+ INIT_WORK(&(smd_info->diag_general_smd_work),
+ diag_cntl_smd_work_fn);
break;
case SMD_DCI_TYPE:
case SMD_DCI_CMD_TYPE:
INIT_WORK(&(smd_info->diag_notify_update_smd_work),
diag_update_smd_dci_work_fn);
+ INIT_WORK(&(smd_info->diag_general_smd_work),
+ diag_cntl_smd_work_fn);
break;
default:
pr_err("diag: In %s, unknown type, type: %d\n", __func__, type);
@@ -1927,8 +2042,15 @@
mutex_init(&driver->diag_hdlc_mutex);
mutex_init(&driver->diag_cntl_mutex);
- for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
driver->separate_cmdrsp[i] = 0;
+ driver->peripheral_supports_stm[i] = DISABLE_STM;
+ }
+
+ for (i = 0; i < NUM_STM_PROCESSORS; i++) {
+ driver->stm_state_requested[i] = DISABLE_STM;
+ driver->stm_state[i] = DISABLE_STM;
+ }
for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
success = diag_smd_constructor(&driver->smd_data[i], i,
diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c
index 8c07219b..e5a6734 100644
--- a/drivers/char/diag/diagfwd_bridge.c
+++ b/drivers/char/diag/diagfwd_bridge.c
@@ -35,7 +35,7 @@
/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
int diagfwd_connect_bridge(int process_cable)
{
- int i;
+ uint8_t i;
pr_debug("diag: in %s\n", __func__);
@@ -45,7 +45,7 @@
return 0;
}
-void connect_bridge(int process_cable, int index)
+void connect_bridge(int process_cable, uint8_t index)
{
int err;
@@ -65,6 +65,12 @@
driver->in_busy_smux = 0;
diagfwd_connect_smux();
} else {
+ if (index >= MAX_HSIC_CH) {
+ pr_err("diag: Invalid hsic channel index %d in %s\n",
+ index, __func__);
+ mutex_unlock(&diag_bridge[index].bridge_mutex);
+ return;
+ }
if (diag_hsic[index].hsic_device_enabled &&
(driver->logging_mode != MEMORY_DEVICE_MODE ||
diag_hsic[index].hsic_data_requested)) {
@@ -74,7 +80,7 @@
* device is not open */
if (!diag_hsic[index].hsic_device_opened) {
hsic_diag_bridge_ops[index].ctxt =
- (void *)(index);
+ (void *)(int)(index);
err = diag_bridge_open(index,
&hsic_diag_bridge_ops[index]);
if (err) {
diff --git a/drivers/char/diag/diagfwd_bridge.h b/drivers/char/diag/diagfwd_bridge.h
index 88c96ab..ae1259b 100644
--- a/drivers/char/diag/diagfwd_bridge.h
+++ b/drivers/char/diag/diagfwd_bridge.h
@@ -21,7 +21,7 @@
#define SMUX 4
int diagfwd_connect_bridge(int);
-void connect_bridge(int, int);
+void connect_bridge(int, uint8_t);
int diagfwd_disconnect_bridge(int);
void diagfwd_bridge_init(int index);
void diagfwd_bridge_exit(void);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 13cbc8f..a832cb3 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -40,6 +40,55 @@
smd_info->notify_context = 0;
}
+void diag_cntl_smd_work_fn(struct work_struct *work)
+{
+ struct diag_smd_info *smd_info = container_of(work,
+ struct diag_smd_info,
+ diag_general_smd_work);
+
+ if (!smd_info || smd_info->type != SMD_CNTL_TYPE)
+ return;
+
+ if (smd_info->general_context == UPDATE_PERIPHERAL_STM_STATE) {
+ if (driver->peripheral_supports_stm[smd_info->peripheral] ==
+ ENABLE_STM) {
+ int status = 0;
+ int index = smd_info->peripheral;
+ status = diag_send_stm_state(smd_info,
+ (uint8_t)(driver->stm_state_requested[index]));
+ if (status == 1)
+ driver->stm_state[index] =
+ driver->stm_state_requested[index];
+ }
+ }
+ smd_info->general_context = 0;
+}
+
+void diag_cntl_stm_notify(struct diag_smd_info *smd_info, int action)
+{
+ if (!smd_info || smd_info->type != SMD_CNTL_TYPE)
+ return;
+
+ if (action == CLEAR_PERIPHERAL_STM_STATE)
+ driver->peripheral_supports_stm[smd_info->peripheral] =
+ DISABLE_STM;
+}
+
+static void process_stm_feature(struct diag_smd_info *smd_info,
+ uint8_t feature_mask)
+{
+ if (feature_mask & F_DIAG_OVER_STM) {
+ driver->peripheral_supports_stm[smd_info->peripheral] =
+ ENABLE_STM;
+ smd_info->general_context = UPDATE_PERIPHERAL_STM_STATE;
+ queue_work(driver->diag_cntl_wq,
+ &(smd_info->diag_general_smd_work));
+ } else {
+ driver->peripheral_supports_stm[smd_info->peripheral] =
+ DISABLE_STM;
+ }
+}
+
/* Process the data read from the smd control channel */
int diag_process_smd_cntl_read_data(struct diag_smd_info *smd_info, void *buf,
int total_recd)
@@ -120,8 +169,9 @@
uint8_t feature_mask = 0;
int feature_mask_len = *(int *)(buf+8);
if (feature_mask_len > 0) {
+ int periph = smd_info->peripheral;
feature_mask = *(uint8_t *)(buf+12);
- if (smd_info->peripheral == MODEM_DATA)
+ if (periph == MODEM_DATA)
driver->log_on_demand_support =
feature_mask &
F_DIAG_LOG_ON_DEMAND_RSP_ON_MASTER;
@@ -132,13 +182,16 @@
*/
if (driver->supports_separate_cmdrsp &&
(feature_mask & F_DIAG_REQ_RSP_CHANNEL))
- driver->separate_cmdrsp
- [smd_info->peripheral] =
+ driver->separate_cmdrsp[periph] =
ENABLE_SEPARATE_CMDRSP;
else
- driver->separate_cmdrsp
- [smd_info->peripheral] =
+ driver->separate_cmdrsp[periph] =
DISABLE_SEPARATE_CMDRSP;
+ if (feature_mask_len > 1) {
+ feature_mask = *(uint8_t *)(buf+13);
+ process_stm_feature(smd_info,
+ feature_mask);
+ }
}
flag = 1;
} else if (type != DIAG_CTRL_MSG_REG) {
@@ -151,15 +204,85 @@
return flag;
}
-void diag_send_diag_mode_update(int real_time)
+void diag_update_proc_vote(uint16_t proc, uint8_t vote)
{
- int i;
-
- for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
- diag_send_diag_mode_update_by_smd(&driver->smd_cntl[i],
- real_time);
+ mutex_lock(&driver->real_time_mutex);
+ if (vote)
+ driver->proc_active_mask |= proc;
+ else {
+ driver->proc_active_mask &= ~proc;
+ driver->proc_rt_vote_mask |= proc;
+ }
+ mutex_unlock(&driver->real_time_mutex);
}
+void diag_update_real_time_vote(uint16_t proc, uint8_t real_time)
+{
+ mutex_lock(&driver->real_time_mutex);
+ if (real_time)
+ driver->proc_rt_vote_mask |= proc;
+ else
+ driver->proc_rt_vote_mask &= ~proc;
+ mutex_unlock(&driver->real_time_mutex);
+}
+
+#ifdef CONFIG_DIAG_OVER_USB
+void diag_real_time_work_fn(struct work_struct *work)
+{
+ int temp_real_time = MODE_REALTIME, i;
+
+ /* If any of the process is voting for Real time, then Diag
+ should be in real time mode irrespective of other clauses. If
+ USB is connected, check what the memory device process is
+ voting for. If it is voting for Non real time, the final mode
+ should be Non real time, real time otherwise. If USB is
+ disconncted and no process is voting for real time, the
+ resultant mode should be Non Real Time.
+ */
+ if ((driver->proc_rt_vote_mask & driver->proc_active_mask) &&
+ (driver->proc_active_mask != 0))
+ temp_real_time = MODE_REALTIME;
+ else if (driver->usb_connected)
+ if ((driver->proc_rt_vote_mask & DIAG_PROC_MEMORY_DEVICE) == 0)
+ temp_real_time = MODE_NONREALTIME;
+ else
+ temp_real_time = MODE_REALTIME;
+ else
+ temp_real_time = MODE_NONREALTIME;
+
+ if (temp_real_time != driver->real_time_mode) {
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+ diag_send_diag_mode_update_by_smd(&driver->smd_cntl[i],
+ temp_real_time);
+ } else {
+ pr_info("diag: did not update real time mode, already in the req mode %d",
+ temp_real_time);
+ }
+ if (driver->real_time_update_busy > 0)
+ driver->real_time_update_busy--;
+}
+#else
+void diag_real_time_work_fn(struct work_struct *work)
+{
+ int temp_real_time = MODE_REALTIME, i;
+
+ if (!(driver->proc_rt_vote_mask & driver->proc_active_mask) &&
+ (driver->proc_active_mask != 0))
+ temp_real_time = MODE_NONREALTIME;
+
+ if (temp_real_time != driver->real_time_mode) {
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+ diag_send_diag_mode_update_by_smd(&driver->smd_cntl[i],
+ temp_real_time);
+ } else {
+ pr_warn("diag: did not update real time mode, already in the req mode %d",
+ temp_real_time);
+ }
+ if (driver->real_time_update_busy > 0)
+ driver->real_time_update_busy--;
+}
+#endif
+
void diag_send_diag_mode_update_by_smd(struct diag_smd_info *smd_info,
int real_time)
{
@@ -228,6 +351,56 @@
mutex_unlock(&driver->diag_cntl_mutex);
}
+int diag_send_stm_state(struct diag_smd_info *smd_info,
+ uint8_t stm_control_data)
+{
+ struct diag_ctrl_msg_stm stm_msg;
+ int msg_size = sizeof(struct diag_ctrl_msg_stm);
+ int retry_count = 0;
+ int wr_size = 0;
+ int success = 0;
+
+ if (!smd_info || (smd_info->type != SMD_CNTL_TYPE) ||
+ (driver->peripheral_supports_stm[smd_info->peripheral] ==
+ DISABLE_STM)) {
+ return -EINVAL;
+ }
+
+ if (smd_info->ch) {
+ stm_msg.ctrl_pkt_id = 21;
+ stm_msg.ctrl_pkt_data_len = 5;
+ stm_msg.version = 1;
+ stm_msg.control_data = stm_control_data;
+ while (retry_count < 3) {
+ wr_size = smd_write(smd_info->ch, &stm_msg, msg_size);
+ if (wr_size == -ENOMEM) {
+ /*
+ * The smd channel is full. Delay while
+ * smd processes existing data and smd
+ * has memory become available. The delay
+ * of 10000 was determined empirically as
+ * best value to use.
+ */
+ retry_count++;
+ usleep_range(10000, 10000);
+ } else {
+ success = 1;
+ break;
+ }
+ }
+ if (wr_size != msg_size) {
+ pr_err("diag: In %s, proc %d fail STM update %d, tried %d",
+ __func__, smd_info->peripheral, wr_size,
+ msg_size);
+ success = 0;
+ }
+ } else {
+ pr_err("diag: In %s, ch invalid, STM update on proc %d\n",
+ __func__, smd_info->peripheral);
+ }
+ return success;
+}
+
static int diag_smd_cntl_probe(struct platform_device *pdev)
{
int r = 0;
@@ -343,6 +516,7 @@
diag_smd_destructor(&driver->smd_cntl[i]);
destroy_workqueue(driver->diag_cntl_wq);
+ destroy_workqueue(driver->diag_real_time_wq);
platform_driver_unregister(&msm_smd_ch1_cntl_driver);
platform_driver_unregister(&diag_smd_lite_cntl_driver);
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 02b9757..c90c132 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -45,10 +45,18 @@
* new Data Rx and DCI Rx channels
*/
#define F_DIAG_REQ_RSP_CHANNEL 0x10
+/* Denotes we support diag over stm */
+#define F_DIAG_OVER_STM 0x02
#define ENABLE_SEPARATE_CMDRSP 1
#define DISABLE_SEPARATE_CMDRSP 0
+#define ENABLE_STM 1
+#define DISABLE_STM 0
+
+#define UPDATE_PERIPHERAL_STM_STATE 1
+#define CLEAR_PERIPHERAL_STM_STATE 2
+
struct cmd_code_range {
uint16_t cmd_code_lo;
uint16_t cmd_code_hi;
@@ -117,15 +125,28 @@
uint32_t event_stale_timer_val;
} __packed;
+struct diag_ctrl_msg_stm {
+ uint32_t ctrl_pkt_id;
+ uint32_t ctrl_pkt_data_len;
+ uint32_t version;
+ uint8_t control_data;
+} __packed;
+
void diagfwd_cntl_init(void);
void diagfwd_cntl_exit(void);
void diag_read_smd_cntl_work_fn(struct work_struct *);
void diag_notify_ctrl_update_fn(struct work_struct *work);
void diag_clean_reg_fn(struct work_struct *work);
+void diag_cntl_smd_work_fn(struct work_struct *work);
int diag_process_smd_cntl_read_data(struct diag_smd_info *smd_info, void *buf,
int total_recd);
-void diag_send_diag_mode_update(int real_time);
void diag_send_diag_mode_update_by_smd(struct diag_smd_info *smd_info,
int real_time);
+void diag_update_proc_vote(uint16_t proc, uint8_t vote);
+void diag_update_real_time_vote(uint16_t proc, uint8_t real_time);
+void diag_real_time_work_fn(struct work_struct *work);
+int diag_send_stm_state(struct diag_smd_info *smd_info,
+ uint8_t stm_control_data);
+void diag_cntl_stm_notify(struct diag_smd_info *smd_info, int action);
#endif
diff --git a/drivers/char/diag/diagmem.c b/drivers/char/diag/diagmem.c
index bd339e2..a6ef3ca 100644
--- a/drivers/char/diag/diagmem.c
+++ b/drivers/char/diag/diagmem.c
@@ -20,6 +20,8 @@
#include "diagfwd_bridge.h"
#include "diagfwd_hsic.h"
+mempool_t *diag_pools_array[NUM_MEMORY_POOLS];
+
void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
{
void *buf = NULL;
@@ -149,8 +151,9 @@
if (diag_hsic[index].diag_hsic_pool &&
(diag_hsic[index].hsic_inited == 0)) {
if (diag_hsic[index].count_hsic_pool == 0) {
- mempool_destroy(driver->diag_hdlc_pool);
- driver->diag_hdlc_pool = NULL;
+ mempool_destroy(
+ diag_hsic[index].diag_hsic_pool);
+ diag_hsic[index].diag_hsic_pool = NULL;
} else if (pool_type == POOL_TYPE_ALL)
pr_err("Unable to destroy HDLC mempool for ch %d"
, index);
@@ -251,21 +254,30 @@
{
mutex_init(&driver->diagmem_mutex);
- if (driver->count == 0)
+ if (driver->count == 0) {
driver->diagpool = mempool_create_kmalloc_pool(
driver->poolsize, driver->itemsize);
+ diag_pools_array[POOL_COPY_IDX] = driver->diagpool;
+ }
- if (driver->count_hdlc_pool == 0)
+ if (driver->count_hdlc_pool == 0) {
driver->diag_hdlc_pool = mempool_create_kmalloc_pool(
driver->poolsize_hdlc, driver->itemsize_hdlc);
+ diag_pools_array[POOL_HDLC_IDX] = driver->diag_hdlc_pool;
+ }
- if (driver->count_user_pool == 0)
+ if (driver->count_user_pool == 0) {
driver->diag_user_pool = mempool_create_kmalloc_pool(
driver->poolsize_user, driver->itemsize_user);
+ diag_pools_array[POOL_USER_IDX] = driver->diag_user_pool;
+ }
- if (driver->count_write_struct_pool == 0)
+ if (driver->count_write_struct_pool == 0) {
driver->diag_write_struct_pool = mempool_create_kmalloc_pool(
driver->poolsize_write_struct, driver->itemsize_write_struct);
+ diag_pools_array[POOL_WRITE_STRUCT_IDX] =
+ driver->diag_write_struct_pool;
+ }
if (!driver->diagpool)
pr_err("diag: Cannot allocate diag mempool\n");
@@ -283,16 +295,27 @@
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
void diagmem_hsic_init(int index)
{
- if (diag_hsic[index].count_hsic_pool == 0)
+ if (index < 0 || index >= MAX_HSIC_CH) {
+ pr_err("diag: Invalid hsic index in %s\n", __func__);
+ return;
+ }
+
+ if (diag_hsic[index].count_hsic_pool == 0) {
diag_hsic[index].diag_hsic_pool = mempool_create_kmalloc_pool(
diag_hsic[index].poolsize_hsic,
diag_hsic[index].itemsize_hsic);
+ diag_pools_array[POOL_HSIC_IDX + index] =
+ diag_hsic[index].diag_hsic_pool;
+ }
- if (diag_hsic[index].count_hsic_write_pool == 0)
+ if (diag_hsic[index].count_hsic_write_pool == 0) {
diag_hsic[index].diag_hsic_write_pool =
mempool_create_kmalloc_pool(
diag_hsic[index].poolsize_hsic_write,
diag_hsic[index].itemsize_hsic_write);
+ diag_pools_array[POOL_HSIC_WRITE_IDX + index] =
+ diag_hsic[index].diag_hsic_write_pool;
+ }
if (!diag_hsic[index].diag_hsic_pool)
pr_err("Cannot allocate diag HSIC mempool for ch %d\n", index);
diff --git a/drivers/char/diag/diagmem.h b/drivers/char/diag/diagmem.h
index f478263..6861790 100644
--- a/drivers/char/diag/diagmem.h
+++ b/drivers/char/diag/diagmem.h
@@ -14,6 +14,8 @@
#define DIAGMEM_H
#include "diagchar.h"
+extern mempool_t *diag_pools_array[NUM_MEMORY_POOLS];
+
void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type);
void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type);
void diagmem_init(struct diagchar_dev *driver);
diff --git a/drivers/coresight/Kconfig b/drivers/coresight/Kconfig
index 7ec83dd..5e2acfb 100644
--- a/drivers/coresight/Kconfig
+++ b/drivers/coresight/Kconfig
@@ -24,6 +24,15 @@
config HAVE_CORESIGHT_SINK
bool
+config CORESIGHT_FUSE
+ bool "CoreSight Fuse driver"
+ help
+ This driver provides support for CoreSight Fuse state checks that
+ other CoreSight drivers can query to determine existence of
+ Hardware functionality they support. Drivers can then take necessary
+ actions like failing the probe if the Hardware they manage is
+ functionally disabled.
+
config CORESIGHT_CTI
bool "CoreSight Cross Trigger Interface driver"
help
diff --git a/drivers/coresight/Makefile b/drivers/coresight/Makefile
index 2b14f86..23352a7 100644
--- a/drivers/coresight/Makefile
+++ b/drivers/coresight/Makefile
@@ -2,6 +2,7 @@
# Makefile for CoreSight drivers.
#
obj-$(CONFIG_CORESIGHT) += coresight.o
+obj-$(CONFIG_CORESIGHT_FUSE) += coresight-fuse.o
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
obj-$(CONFIG_CORESIGHT_TMC) += coresight-tmc.o
diff --git a/drivers/coresight/coresight-csr.c b/drivers/coresight/coresight-csr.c
index 8195184..132df90 100644
--- a/drivers/coresight/coresight-csr.c
+++ b/drivers/coresight/coresight-csr.c
@@ -168,6 +168,9 @@
struct resource *res;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/coresight/coresight-cti.c b/drivers/coresight/coresight-cti.c
index 6a8d412..d0900d1 100644
--- a/drivers/coresight/coresight-cti.c
+++ b/drivers/coresight/coresight-cti.c
@@ -388,6 +388,9 @@
struct resource *res;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/coresight/coresight-etb.c b/drivers/coresight/coresight-etb.c
index 31f85dc..473b4cb 100644
--- a/drivers/coresight/coresight-etb.c
+++ b/drivers/coresight/coresight-etb.c
@@ -362,6 +362,9 @@
struct resource *res;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/coresight/coresight-etm.c b/drivers/coresight/coresight-etm.c
index 5a5c0cf..52f3027 100644
--- a/drivers/coresight/coresight-etm.c
+++ b/drivers/coresight/coresight-etm.c
@@ -2113,6 +2113,10 @@
struct msm_client_dump dump;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled() ||
+ coresight_fuse_apps_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
@@ -2154,14 +2158,20 @@
drvdata->cpu = count++;
- get_online_cpus();
etmdrvdata[drvdata->cpu] = drvdata;
+ /*
+ * This is safe wrt CPU_UP_PREPARE and CPU_STARTING hotplug callbacks
+ * on the secondary cores that may enable the clock and perform
+ * etm_os_unlock since they occur before the cpu online mask is updated
+ * for the cpu which is checked by this smp call.
+ */
if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1))
drvdata->os_unlock = true;
+
/*
- * Use CPU0 to populate read-only configuration data for ETM0. For
- * other ETMs copy it over from ETM0.
+ * OS unlock must have happened on cpu0 so use it to populate read-only
+ * configuration data for ETM0. For other ETMs copy it over from ETM0.
*/
if (drvdata->cpu == 0) {
register_hotcpu_notifier(&etm_cpu_notifier);
@@ -2172,8 +2182,6 @@
etm_copy_arch_data(drvdata);
}
- put_online_cpus();
-
if (etm_arch_supported(drvdata->arch) == false) {
ret = -EINVAL;
goto err1;
diff --git a/drivers/coresight/coresight-funnel.c b/drivers/coresight/coresight-funnel.c
index 625f481..771321d 100644
--- a/drivers/coresight/coresight-funnel.c
+++ b/drivers/coresight/coresight-funnel.c
@@ -174,6 +174,9 @@
struct resource *res;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/coresight/coresight-fuse.c b/drivers/coresight/coresight-fuse.c
new file mode 100644
index 0000000..7af5c6c8
--- /dev/null
+++ b/drivers/coresight/coresight-fuse.c
@@ -0,0 +1,202 @@
+/* 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_coresight.h>
+#include <linux/coresight.h>
+
+#include "coresight-priv.h"
+
+#define fuse_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
+#define fuse_readl(drvdata, off) __raw_readl(drvdata->base + off)
+
+#define OEM_CONFIG0 (0x000)
+#define OEM_CONFIG1 (0x004)
+
+#define ALL_DEBUG_DISABLE BIT(21)
+#define APPS_DBGEN_DISABLE BIT(0)
+#define APPS_NIDEN_DISABLE BIT(1)
+#define APPS_SPIDEN_DISABLE BIT(2)
+#define APPS_SPNIDEN_DISABLE BIT(3)
+#define DAP_DBGEN_DISABLE BIT(4)
+#define DAP_NIDEN_DISABLE BIT(5)
+#define DAP_SPIDEN_DISABLE BIT(6)
+#define DAP_SPNIDEN_DISABLE BIT(7)
+#define DAP_DEVICEEN_DISABLE BIT(8)
+
+struct fuse_drvdata {
+ void __iomem *base;
+ struct device *dev;
+ struct coresight_device *csdev;
+};
+
+static struct fuse_drvdata *fusedrvdata;
+
+bool coresight_fuse_access_disabled(void)
+{
+ struct fuse_drvdata *drvdata = fusedrvdata;
+ uint32_t config0, config1;
+ bool ret;
+
+ config0 = fuse_readl(drvdata, OEM_CONFIG0);
+ config1 = fuse_readl(drvdata, OEM_CONFIG1);
+
+ dev_dbg(drvdata->dev, "config0: %lx\n", (unsigned long)config0);
+ dev_dbg(drvdata->dev, "config1: %lx\n", (unsigned long)config1);
+
+ if (config0 & ALL_DEBUG_DISABLE)
+ ret = true;
+ else if (config1 & DAP_DBGEN_DISABLE)
+ ret = true;
+ else if (config1 & DAP_NIDEN_DISABLE)
+ ret = true;
+ else if (config1 & DAP_SPIDEN_DISABLE)
+ ret = true;
+ else if (config1 & DAP_SPNIDEN_DISABLE)
+ ret = true;
+ else if (config1 & DAP_DEVICEEN_DISABLE)
+ ret = true;
+ else
+ ret = false;
+
+ if (ret)
+ dev_dbg(drvdata->dev, "coresight fuse disabled\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(coresight_fuse_access_disabled);
+
+bool coresight_fuse_apps_access_disabled(void)
+{
+ struct fuse_drvdata *drvdata = fusedrvdata;
+ uint32_t config0, config1;
+ bool ret;
+
+ config0 = fuse_readl(drvdata, OEM_CONFIG0);
+ config1 = fuse_readl(drvdata, OEM_CONFIG1);
+
+ dev_dbg(drvdata->dev, "apps config0: %lx\n", (unsigned long)config0);
+ dev_dbg(drvdata->dev, "apps config1: %lx\n", (unsigned long)config1);
+
+ if (config0 & ALL_DEBUG_DISABLE)
+ ret = true;
+ else if (config1 & APPS_DBGEN_DISABLE)
+ ret = true;
+ else if (config1 & APPS_NIDEN_DISABLE)
+ ret = true;
+ else if (config1 & APPS_SPIDEN_DISABLE)
+ ret = true;
+ else if (config1 & APPS_SPNIDEN_DISABLE)
+ ret = true;
+ else if (config1 & DAP_DEVICEEN_DISABLE)
+ ret = true;
+ else
+ ret = false;
+
+ if (ret)
+ dev_dbg(drvdata->dev, "apps fuse disabled\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(coresight_fuse_apps_access_disabled);
+
+static int __devinit fuse_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct coresight_platform_data *pdata;
+ struct fuse_drvdata *drvdata;
+ struct resource *res;
+ struct coresight_desc *desc;
+
+ if (pdev->dev.of_node) {
+ pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ pdev->dev.platform_data = pdata;
+ }
+
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ /* Store the driver data pointer for use in exported functions */
+ fusedrvdata = drvdata;
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fuse-base");
+ if (!res)
+ return -ENODEV;
+
+ drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!drvdata->base)
+ return -ENOMEM;
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+ desc->type = CORESIGHT_DEV_TYPE_NONE;
+ desc->pdata = pdev->dev.platform_data;
+ desc->dev = &pdev->dev;
+ desc->owner = THIS_MODULE;
+ drvdata->csdev = coresight_register(desc);
+ if (IS_ERR(drvdata->csdev))
+ return PTR_ERR(drvdata->csdev);
+
+ dev_info(dev, "Fuse initialized\n");
+ return 0;
+}
+
+static int __devexit fuse_remove(struct platform_device *pdev)
+{
+ struct fuse_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ coresight_unregister(drvdata->csdev);
+ return 0;
+}
+
+static struct of_device_id fuse_match[] = {
+ {.compatible = "arm,coresight-fuse"},
+ {}
+};
+
+static struct platform_driver fuse_driver = {
+ .probe = fuse_probe,
+ .remove = __devexit_p(fuse_remove),
+ .driver = {
+ .name = "coresight-fuse",
+ .owner = THIS_MODULE,
+ .of_match_table = fuse_match,
+ },
+};
+
+static int __init fuse_init(void)
+{
+ return platform_driver_register(&fuse_driver);
+}
+module_init(fuse_init);
+
+static void __exit fuse_exit(void)
+{
+ platform_driver_unregister(&fuse_driver);
+}
+module_exit(fuse_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight Fuse driver");
diff --git a/drivers/coresight/coresight-hwevent.c b/drivers/coresight/coresight-hwevent.c
index 777484d..269d56e 100644
--- a/drivers/coresight/coresight-hwevent.c
+++ b/drivers/coresight/coresight-hwevent.c
@@ -211,6 +211,9 @@
int ret, i;
const char *hmux_name, *hclk_name;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/coresight/coresight-priv.h b/drivers/coresight/coresight-priv.h
index f208185..b570252 100644
--- a/drivers/coresight/coresight-priv.h
+++ b/drivers/coresight/coresight-priv.h
@@ -36,6 +36,13 @@
#define BMVAL(val, lsb, msb) ((val & BM(lsb, msb)) >> lsb)
#define BVAL(val, n) ((val & BIT(n)) >> n)
+#ifdef CONFIG_CORESIGHT_FUSE
+extern bool coresight_fuse_access_disabled(void);
+extern bool coresight_fuse_apps_access_disabled(void);
+#else
+static inline bool coresight_fuse_access_disabled(void) { return false; }
+static inline bool coresight_fuse_apps_access_disabled(void) { return false; }
+#endif
#ifdef CONFIG_CORESIGHT_CSR
extern void msm_qdss_csr_enable_bam_to_usb(void);
extern void msm_qdss_csr_disable_bam_to_usb(void);
diff --git a/drivers/coresight/coresight-replicator.c b/drivers/coresight/coresight-replicator.c
index d4afa42..9f70dc7 100644
--- a/drivers/coresight/coresight-replicator.c
+++ b/drivers/coresight/coresight-replicator.c
@@ -126,6 +126,9 @@
struct resource *res;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/coresight/coresight-stm.c b/drivers/coresight/coresight-stm.c
index 7d4dabe..81672ea 100644
--- a/drivers/coresight/coresight-stm.c
+++ b/drivers/coresight/coresight-stm.c
@@ -450,6 +450,8 @@
static int stm_send(void *addr, const void *data, uint32_t size)
{
+ uint32_t len = size;
+
if (((unsigned long)data & 0x1) && (size >= 1)) {
stm_data_writeb(*(uint8_t *)data, addr);
data++;
@@ -479,7 +481,7 @@
size--;
}
- return size;
+ return len;
}
static int stm_trace_ost_header(unsigned long ch_addr, uint32_t options,
@@ -791,6 +793,9 @@
size_t res_size, bitmap_size;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index f39334a..4186abe 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -1134,6 +1134,9 @@
struct coresight_cti_data *ctidata;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/coresight/coresight-tpiu.c
index 53df0f9..032327c 100644
--- a/drivers/coresight/coresight-tpiu.c
+++ b/drivers/coresight/coresight-tpiu.c
@@ -717,6 +717,9 @@
struct resource *res;
struct coresight_desc *desc;
+ if (coresight_fuse_access_disabled())
+ return -EPERM;
+
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
if (IS_ERR(pdata))
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index fda64e5..96e759b 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -22,6 +22,7 @@
#include <linux/hrtimer.h>
#include <linux/tick.h>
#include <linux/ktime.h>
+#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/input.h>
#include <linux/workqueue.h>
@@ -103,6 +104,11 @@
* when user is changing the governor or limits.
*/
struct mutex timer_mutex;
+
+ struct task_struct *sync_thread;
+ wait_queue_head_t sync_wq;
+ atomic_t src_sync_cpu;
+ atomic_t sync_enabled;
};
static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info);
@@ -125,14 +131,6 @@
static DEFINE_PER_CPU(struct dbs_work_struct, dbs_refresh_work);
-struct dbs_sync_work_struct {
- struct work_struct work;
- unsigned int src_cpu;
- unsigned int targ_cpu;
-};
-
-static DEFINE_PER_CPU(struct dbs_sync_work_struct, dbs_sync_work);
-
static struct dbs_tuners {
unsigned int sampling_rate;
unsigned int up_threshold;
@@ -612,6 +610,9 @@
if (dbs_info->cur_policy) {
/* restart dbs timer */
dbs_timer_init(dbs_info);
+ /* Enable frequency synchronization
+ * of CPUs */
+ atomic_set(&dbs_info->sync_enabled, 1);
}
skip_this_cpu:
unlock_policy_rwsem_write(cpu);
@@ -641,15 +642,19 @@
if (dbs_info->cur_policy) {
/* cpu using ondemand, cancel dbs timer */
- mutex_lock(&dbs_info->timer_mutex);
dbs_timer_exit(dbs_info);
+ /* Disable frequency synchronization of
+ * CPUs to avoid re-queueing of work from
+ * sync_thread */
+ atomic_set(&dbs_info->sync_enabled, 0);
+ mutex_lock(&dbs_info->timer_mutex);
ondemand_powersave_bias_setspeed(
dbs_info->cur_policy,
NULL,
input);
-
mutex_unlock(&dbs_info->timer_mutex);
+
}
skip_this_cpu_bypass:
unlock_policy_rwsem_write(cpu);
@@ -1039,12 +1044,11 @@
static int dbs_migration_notify(struct notifier_block *nb,
unsigned long target_cpu, void *arg)
{
- struct dbs_sync_work_struct *sync_work =
- &per_cpu(dbs_sync_work, target_cpu);
- sync_work->src_cpu = (unsigned int)arg;
+ struct cpu_dbs_info_s *target_dbs_info =
+ &per_cpu(od_cpu_dbs_info, target_cpu);
- queue_work_on(target_cpu, dbs_wq,
- &per_cpu(dbs_sync_work, target_cpu).work);
+ atomic_set(&target_dbs_info->src_sync_cpu, (int)arg);
+ wake_up(&target_dbs_info->sync_wq);
return NOTIFY_OK;
}
@@ -1053,73 +1057,92 @@
.notifier_call = dbs_migration_notify,
};
-void dbs_synchronize(struct work_struct *work)
+static int sync_pending(struct cpu_dbs_info_s *this_dbs_info)
{
- struct cpufreq_policy *policy;
- struct cpu_dbs_info_s *this_dbs_info, *src_dbs_info;
- struct dbs_sync_work_struct *dbs_work;
- unsigned int cpu, src_cpu;
+ return atomic_read(&this_dbs_info->src_sync_cpu) >= 0;
+}
+
+static int dbs_sync_thread(void *data)
+{
+ int src_cpu, cpu = (int)data;
unsigned int src_freq, src_max_load;
+ struct cpu_dbs_info_s *this_dbs_info, *src_dbs_info;
+ struct cpufreq_policy *policy;
int delay;
- dbs_work = container_of(work, struct dbs_sync_work_struct, work);
- cpu = dbs_work->targ_cpu;
- src_cpu = dbs_work->src_cpu;
-
- get_online_cpus();
-
- /* Getting source cpu info */
- src_dbs_info = &per_cpu(od_cpu_dbs_info, src_cpu);
- if (src_dbs_info != NULL && src_dbs_info->cur_policy != NULL) {
- src_freq = src_dbs_info->cur_policy->cur;
- src_max_load = src_dbs_info->max_load;
- } else {
- src_freq = dbs_tuners_ins.sync_freq;
- src_max_load = 0;
- }
-
- if (lock_policy_rwsem_write(cpu) < 0)
- goto bail_acq_sema_failed;
-
this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
- policy = this_dbs_info->cur_policy;
- if (!policy) {
- /* CPU not using ondemand governor */
- goto bail_incorrect_governor;
- }
- delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+ while (1) {
+ wait_event(this_dbs_info->sync_wq,
+ sync_pending(this_dbs_info) ||
+ kthread_should_stop());
- if (policy->cur < src_freq) {
+ if (kthread_should_stop())
+ break;
- /* Cancelling the next ondemand sample */
- cancel_delayed_work_sync(&this_dbs_info->work);
+ get_online_cpus();
- /*
- * Arch specific cpufreq driver may fail.
- * Don't update governor frequency upon failure.
- */
- if (__cpufreq_driver_target(policy, src_freq,
- CPUFREQ_RELATION_L) >= 0) {
- policy->cur = src_freq;
- if (src_max_load > this_dbs_info->max_load) {
- this_dbs_info->max_load = src_max_load;
- this_dbs_info->prev_load = src_max_load;
- }
+ src_cpu = atomic_read(&this_dbs_info->src_sync_cpu);
+ src_dbs_info = &per_cpu(od_cpu_dbs_info, src_cpu);
+ if (src_dbs_info != NULL &&
+ src_dbs_info->cur_policy != NULL) {
+ src_freq = src_dbs_info->cur_policy->cur;
+ src_max_load = src_dbs_info->max_load;
+ } else {
+ src_freq = dbs_tuners_ins.sync_freq;
+ src_max_load = 0;
}
- /* Rescheduling the next ondemand sample */
- mutex_lock(&this_dbs_info->timer_mutex);
- schedule_delayed_work_on(cpu, &this_dbs_info->work,
- delay);
- mutex_unlock(&this_dbs_info->timer_mutex);
- }
-bail_incorrect_governor:
- unlock_policy_rwsem_write(cpu);
+ if (lock_policy_rwsem_write(cpu) < 0)
+ goto bail_acq_sema_failed;
+ if (!atomic_read(&this_dbs_info->sync_enabled)) {
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ put_online_cpus();
+ unlock_policy_rwsem_write(cpu);
+ continue;
+ }
+
+ policy = this_dbs_info->cur_policy;
+ if (!policy) {
+ /* CPU not using ondemand governor */
+ goto bail_incorrect_governor;
+ }
+ delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+
+
+ if (policy->cur < src_freq) {
+ /* cancel the next ondemand sample */
+ cancel_delayed_work_sync(&this_dbs_info->work);
+
+ /*
+ * Arch specific cpufreq driver may fail.
+ * Don't update governor frequency upon failure.
+ */
+ if (__cpufreq_driver_target(policy, src_freq,
+ CPUFREQ_RELATION_L) >= 0) {
+ policy->cur = src_freq;
+ if (src_max_load > this_dbs_info->max_load) {
+ this_dbs_info->max_load = src_max_load;
+ this_dbs_info->prev_load = src_max_load;
+ }
+ }
+
+ /* reschedule the next ondemand sample */
+ mutex_lock(&this_dbs_info->timer_mutex);
+ queue_delayed_work_on(cpu, dbs_wq,
+ &this_dbs_info->work, delay);
+ mutex_unlock(&this_dbs_info->timer_mutex);
+ }
+
+bail_incorrect_governor:
+ unlock_policy_rwsem_write(cpu);
bail_acq_sema_failed:
- put_online_cpus();
- return;
+ put_online_cpus();
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ }
+
+ return 0;
}
static void dbs_input_event(struct input_handle *handle, unsigned int type,
@@ -1215,6 +1238,10 @@
if (dbs_tuners_ins.ignore_nice)
j_dbs_info->prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ set_cpus_allowed(j_dbs_info->sync_thread,
+ *cpumask_of(j));
+ if (!dbs_tuners_ins.powersave_bias)
+ atomic_set(&j_dbs_info->sync_enabled, 1);
}
this_dbs_info->cpu = cpu;
this_dbs_info->rate_mult = 1;
@@ -1271,6 +1298,13 @@
mutex_lock(&dbs_mutex);
dbs_enable--;
+
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_info_s *j_dbs_info;
+ j_dbs_info = &per_cpu(od_cpu_dbs_info, j);
+ atomic_set(&j_dbs_info->sync_enabled, 0);
+ }
+
/* If device is being removed, policy is no longer
* valid. */
this_dbs_info->cur_policy = NULL;
@@ -1342,17 +1376,17 @@
&per_cpu(od_cpu_dbs_info, i);
struct dbs_work_struct *dbs_work =
&per_cpu(dbs_refresh_work, i);
- struct dbs_sync_work_struct *dbs_sync =
- &per_cpu(dbs_sync_work, i);
mutex_init(&this_dbs_info->timer_mutex);
INIT_WORK(&dbs_work->work, dbs_refresh_callback);
dbs_work->cpu = i;
- INIT_WORK(&dbs_sync->work, dbs_synchronize);
- dbs_sync->src_cpu = 0;
- dbs_sync->targ_cpu = i;
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ init_waitqueue_head(&this_dbs_info->sync_wq);
+ this_dbs_info->sync_thread = kthread_run(dbs_sync_thread,
+ (void *)i,
+ "dbs_sync/%d", i);
}
return cpufreq_register_governor(&cpufreq_gov_ondemand);
@@ -1367,6 +1401,7 @@
struct cpu_dbs_info_s *this_dbs_info =
&per_cpu(od_cpu_dbs_info, i);
mutex_destroy(&this_dbs_info->timer_mutex);
+ kthread_stop(this_dbs_info->sync_thread);
}
destroy_workqueue(dbs_wq);
}
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index d069f2e..0e4b309 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -1937,7 +1937,7 @@
rc = misc_register(&podev->miscdevice);
qce_hw_support(podev->qce, &podev->ce_support);
if (podev->ce_support.bam) {
- podev->platform_support.ce_shared = podev->ce_support.is_shared;
+ podev->platform_support.ce_shared = 0;
podev->platform_support.shared_ce_resource = 0;
podev->platform_support.hw_key_support =
podev->ce_support.hw_key;
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index f8185df..dadf87c 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -69,6 +69,29 @@
}
/**
+ * devfreq_set_freq_limits() - Set min and max frequency from freq_table
+ * @devfreq: the devfreq instance
+ */
+static void devfreq_set_freq_limits(struct devfreq *devfreq)
+{
+ int idx;
+ unsigned long min = ~0, max = 0;
+
+ if (!devfreq->profile->freq_table)
+ return;
+
+ for (idx = 0; idx < devfreq->profile->max_state; idx++) {
+ if (min > devfreq->profile->freq_table[idx])
+ min = devfreq->profile->freq_table[idx];
+ if (max < devfreq->profile->freq_table[idx])
+ max = devfreq->profile->freq_table[idx];
+ }
+
+ devfreq->min_freq = min;
+ devfreq->max_freq = max;
+}
+
+/**
* devfreq_get_freq_level() - Lookup freq_table for the frequency
* @devfreq: the devfreq instance
* @freq: the target frequency
@@ -506,6 +529,7 @@
devfreq->profile->max_state,
GFP_KERNEL);
devfreq->last_stat_updated = jiffies;
+ devfreq_set_freq_limits(devfreq);
dev_set_name(&devfreq->dev, dev_name(dev));
err = device_register(&devfreq->dev);
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
index c72f942..bc7da1e 100644
--- a/drivers/devfreq/governor_performance.c
+++ b/drivers/devfreq/governor_performance.c
@@ -32,7 +32,7 @@
{
int ret = 0;
- if (event == DEVFREQ_GOV_START) {
+ if (event == DEVFREQ_GOV_START || event == DEVFREQ_GOV_RESUME) {
mutex_lock(&devfreq->lock);
ret = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock);
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
index 0c6bed5..6d43685 100644
--- a/drivers/devfreq/governor_powersave.c
+++ b/drivers/devfreq/governor_powersave.c
@@ -29,7 +29,7 @@
{
int ret = 0;
- if (event == DEVFREQ_GOV_START) {
+ if (event == DEVFREQ_GOV_START || event == DEVFREQ_GOV_RESUME) {
mutex_lock(&devfreq->lock);
ret = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 5a75510..7db56a3 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1081,6 +1081,10 @@
}
}
+#ifdef CONFIG_PINCTRL
+ INIT_LIST_HEAD(&chip->pin_ranges);
+#endif
+
of_gpiochip_add(chip);
unlock:
@@ -1178,6 +1182,47 @@
}
EXPORT_SYMBOL_GPL(gpiochip_find);
+#ifdef CONFIG_PINCTRL
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins)
+{
+ struct gpio_pin_range *pin_range;
+
+ pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base;
+ pin_range->range.pin_base = pin_base;
+ pin_range->range.npins = npins;
+ pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
+ &pin_range->range);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+ struct gpio_pin_range *pin_range, *tmp;
+
+ list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
+ list_del(&pin_range->node);
+ pinctrl_remove_gpio_range(pin_range->pctldev,
+ &pin_range->range);
+ }
+}
+#else
+void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins) {}
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {}
+#endif
+
/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
diff --git a/drivers/gpio/qpnp-pin.c b/drivers/gpio/qpnp-pin.c
index 64341e9..ed001f0 100644
--- a/drivers/gpio/qpnp-pin.c
+++ b/drivers/gpio/qpnp-pin.c
@@ -589,6 +589,9 @@
}
mutex_unlock(&qpnp_pin_chips_lock);
+ if (!q_spec)
+ return -ENODEV;
+
rc = _qpnp_pin_config(q_chip, q_spec, param);
return rc;
@@ -1225,6 +1228,8 @@
if (!res) {
dev_err(&spmi->dev, "%s: node %s is missing has no base address definition\n",
__func__, d_node->of_node->full_name);
+ rc = -EINVAL;
+ goto err_probe;
}
rc = of_property_read_u32(d_node->of_node,
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 250b387..6777dae 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -1688,9 +1688,7 @@
heap->dev = dev;
down_write(&dev->lock);
- /* use negative heap->id to reverse the priority -- when traversing
- the list later attempt higher id numbers first */
- plist_node_init(&heap->node, -heap->id);
+ plist_node_init(&heap->node, heap->id);
plist_add(&heap->node, &dev->heaps);
debugfs_create_file(heap->name, 0664, dev->debug_root, heap,
&debug_heap_fops);
diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c
index e7f7836..b24b2bd 100644
--- a/drivers/gpu/ion/ion_cma_heap.c
+++ b/drivers/gpu/ion/ion_cma_heap.c
@@ -76,10 +76,10 @@
if (!ION_IS_CACHED(flags))
info->cpu_addr = dma_alloc_writecombine(dev, len,
- &(info->handle), 0);
+ &(info->handle), GFP_KERNEL);
else
info->cpu_addr = dma_alloc_nonconsistent(dev, len,
- &(info->handle), 0);
+ &(info->handle), GFP_KERNEL);
if (!info->cpu_addr) {
dev_err(dev, "Fail to allocate buffer\n");
diff --git a/drivers/gpu/ion/ion_cma_secure_heap.c b/drivers/gpu/ion/ion_cma_secure_heap.c
index b3960b2..415c73e 100644
--- a/drivers/gpu/ion/ion_cma_secure_heap.c
+++ b/drivers/gpu/ion/ion_cma_secure_heap.c
@@ -83,7 +83,8 @@
return ION_CMA_ALLOCATE_FAILED;
}
- info->cpu_addr = dma_alloc_attrs(dev, len, &(info->handle), 0, &attrs);
+ info->cpu_addr = dma_alloc_attrs(dev, len, &(info->handle), GFP_KERNEL,
+ &attrs);
if (!info->cpu_addr) {
dev_err(dev, "Fail to allocate buffer\n");
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index f1868a8..f2f4fad 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -203,8 +203,7 @@
}
/**
- * Protects memory if heap is unsecured heap. Also ensures that we are in
- * the correct FMEM state if this heap is a reusable heap.
+ * Protects memory if heap is unsecured heap.
* Must be called with heap->lock locked.
*/
static int ion_cp_protect(struct ion_heap *heap, int version, void *data)
@@ -244,8 +243,7 @@
}
/**
- * Unprotects memory if heap is secure heap. Also ensures that we are in
- * the correct FMEM state if this heap is a reusable heap.
+ * Unprotects memory if heap is secure heap.
* Must be called with heap->lock locked.
*/
static void ion_cp_unprotect(struct ion_heap *heap, int version, void *data)
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index b1c1c5d..a80b0c6 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -94,8 +94,10 @@
}
info = kmalloc(sizeof(struct page_info), GFP_KERNEL);
- info->page = page;
- info->order = orders[i];
+ if (info) {
+ info->page = page;
+ info->order = orders[i];
+ }
return info;
}
return NULL;
diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c
index 2bab7c4..44bb86f 100644
--- a/drivers/gpu/ion/ion_system_heap.c
+++ b/drivers/gpu/ion/ion_system_heap.c
@@ -139,8 +139,10 @@
continue;
info = kmalloc(sizeof(struct page_info), GFP_KERNEL);
- info->page = page;
- info->order = orders[i];
+ if (info) {
+ info->page = page;
+ info->order = orders[i];
+ }
return info;
}
return NULL;
diff --git a/drivers/gpu/ion/msm/ion_iommu_map.c b/drivers/gpu/ion/msm/ion_iommu_map.c
index 0a4fe1f..3e1a7ee 100644
--- a/drivers/gpu/ion/msm/ion_iommu_map.c
+++ b/drivers/gpu/ion/msm/ion_iommu_map.c
@@ -441,6 +441,7 @@
BUG_ON(iommu_meta->size != size);
mutex_unlock(&msm_iommu_map_mutex);
+ mutex_lock(&iommu_meta->lock);
iommu_map = ion_iommu_lookup(iommu_meta, domain_num, partition_num);
if (!iommu_map) {
iommu_map = __ion_iommu_map(iommu_meta, domain_num,
@@ -451,7 +452,7 @@
ret = 0;
} else {
ret = PTR_ERR(iommu_map);
- goto out;
+ goto out_unlock;
}
} else {
if (iommu_map->flags != iommu_flags) {
@@ -459,21 +460,24 @@
__func__, handle,
iommu_map->flags, iommu_flags);
ret = -EINVAL;
- goto out;
+ goto out_unlock;
} else if (iommu_map->mapped_size != iova_length) {
pr_err("%s: handle %p is already mapped with length %x, trying to map with length %lx\n",
__func__, handle, iommu_map->mapped_size,
iova_length);
ret = -EINVAL;
- goto out;
+ goto out_unlock;
} else {
kref_get(&iommu_map->ref);
*iova = iommu_map->iova_addr;
}
}
+ mutex_unlock(&iommu_meta->lock);
*buffer_size = size;
return ret;
+out_unlock:
+ mutex_unlock(&iommu_meta->lock);
out:
ion_iommu_meta_put(iommu_meta);
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index 4a45313..118c39a 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -17,7 +17,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/memory_alloc.h>
-#include <linux/fmem.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mm.h>
@@ -265,6 +264,9 @@
}
}
+ if (!outer_cache_op)
+ return -EINVAL;
+
outer_cache_op(buff_phys_start + offset,
buff_phys_start + offset + length);
@@ -365,6 +367,9 @@
if (!ION_IS_CACHED(flags))
return 0;
+ if (flags & ION_FLAG_SECURE)
+ return 0;
+
table = ion_sg_table(client, handle);
if (IS_ERR_OR_NULL(table))
@@ -419,22 +424,9 @@
struct ion_co_heap_pdata *co_heap_data,
struct ion_cp_heap_pdata *cp_data)
{
- if (cp_data->reusable) {
- const struct fmem_data *fmem_info = fmem_get_info();
-
- if (!fmem_info) {
- pr_err("fmem info pointer NULL!\n");
- BUG();
- }
-
- heap->base = fmem_info->phys - fmem_info->reserved_size_low;
- cp_data->virt_addr = fmem_info->virt;
- pr_info("ION heap %s using FMEM\n", shared_heap->name);
- } else {
- heap->base = msm_ion_get_base(heap->size + shared_heap->size,
- shared_heap->memory_type,
- co_heap_data->align);
- }
+ heap->base = msm_ion_get_base(heap->size + shared_heap->size,
+ shared_heap->memory_type,
+ co_heap_data->align);
if (heap->base) {
shared_heap->base = heap->base + heap->size;
cp_data->secure_base = heap->base;
@@ -460,15 +452,6 @@
struct ion_cp_heap_pdata *cp_data =
(struct ion_cp_heap_pdata *) shared_heap->extra_data;
if (cp_data->fixed_position == FIXED_MIDDLE) {
- const struct fmem_data *fmem_info =
- fmem_get_info();
-
- if (!fmem_info) {
- pr_err("fmem info pointer NULL!\n");
- BUG();
- }
-
- cp_data->virt_addr = fmem_info->virt;
if (!cp_data->secure_base) {
cp_data->secure_base = heap->base;
cp_data->secure_size =
@@ -520,17 +503,6 @@
struct ion_cp_heap_pdata *data =
(struct ion_cp_heap_pdata *)
heap->extra_data;
- if (data->reusable) {
- const struct fmem_data *fmem_info =
- fmem_get_info();
- heap->base = fmem_info->phys;
- data->virt_addr = fmem_info->virt;
- pr_info("ION heap %s using FMEM\n", heap->name);
- } else if (data->mem_is_fmem) {
- const struct fmem_data *fmem_info =
- fmem_get_info();
- heap->base = fmem_info->phys + fmem_info->size;
- }
align = data->align;
break;
}
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index ecd3c0d..32a667f 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -109,6 +109,10 @@
.pm4_fw = NULL,
.wait_timeout = 0, /* in milliseconds, 0 means disabled */
.ib_check_level = 0,
+ .ft_policy = KGSL_FT_DEFAULT_POLICY,
+ .ft_pf_policy = KGSL_FT_PAGEFAULT_DEFAULT_POLICY,
+ .fast_hang_detect = 1,
+ .long_ib_detect = 1,
};
/* This set of registers are used for Hang detection
@@ -123,20 +127,7 @@
#define LONG_IB_DETECT_REG_INDEX_START 1
#define LONG_IB_DETECT_REG_INDEX_END 5
-unsigned int ft_detect_regs[FT_DETECT_REGS_COUNT] = {
- A3XX_RBBM_STATUS,
- REG_CP_RB_RPTR, /* LONG_IB_DETECT_REG_INDEX_START */
- REG_CP_IB1_BASE,
- REG_CP_IB1_BUFSZ,
- REG_CP_IB2_BASE,
- REG_CP_IB2_BUFSZ, /* LONG_IB_DETECT_REG_INDEX_END */
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
-};
+unsigned int ft_detect_regs[FT_DETECT_REGS_COUNT];
/*
* This is the master list of all GPU cores that are supported by this
@@ -214,11 +205,18 @@
"a330_pm4.fw", "a330_pfp.fw", &adreno_a3xx_gpudev,
512, 0, 2, SZ_128K, NO_VER, NO_VER, 0x8AD, 0x2E4,
0x201, 0x200 },
+ /* 8226v2 */
+ { ADRENO_REV_A305B, 3, 0, 5, 0x12,
+ "a330_pm4.fw", "a330_pfp.fw", &adreno_a3xx_gpudev,
+ 512, 0, 2, SZ_128K, NO_VER, NO_VER, 0x8AD, 0x2E4,
+ 0x201, 0x200 },
{ ADRENO_REV_A305C, 3, 0, 5, 0x20,
"a300_pm4.fw", "a300_pfp.fw", &adreno_a3xx_gpudev,
512, 0, 2, SZ_128K, 0x3FF037, 0x3FF016 },
};
+static unsigned int adreno_isidle(struct kgsl_device *device);
+
/**
* adreno_perfcounter_init: Reserve kernel performance counters
* @device: device to configure
@@ -253,6 +251,10 @@
struct adreno_perfcount_group *group;
unsigned int i, j;
+ /* perfcounter start does nothing on a2xx */
+ if (adreno_is_a2xx(adreno_dev))
+ return;
+
/* group id iter */
for (i = 0; i < counters->group_count; i++) {
group = &(counters->groups[i]);
@@ -260,7 +262,9 @@
/* countable iter */
for (j = 0; j < group->reg_count; j++) {
if (group->regs[j].countable ==
- KGSL_PERFCOUNTER_NOT_USED)
+ KGSL_PERFCOUNTER_NOT_USED ||
+ group->regs[j].countable ==
+ KGSL_PERFCOUNTER_BROKEN)
continue;
if (adreno_dev->gpudev->perfcounter_enable)
@@ -721,6 +725,7 @@
phys_addr_t pt_val,
int num_iommu_units, uint32_t flags)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
phys_addr_t ttbr0_val;
unsigned int reg_pt_val;
unsigned int *cmds = cmds_orig;
@@ -750,7 +755,9 @@
* WAIT_FOR_ME
*/
cmds += adreno_wait_reg_eq(cmds,
- A3XX_CP_WFI_PEND_CTR, 1, 0xFFFFFFFF, 0xF);
+ adreno_getreg(adreno_dev,
+ ADRENO_REG_CP_WFI_PEND_CTR),
+ 1, 0xFFFFFFFF, 0xF);
/* set the iommu lock bit */
*cmds++ = cp_type3_packet(CP_REG_RMW, 3);
@@ -817,6 +824,27 @@
return cmds - cmds_orig;
}
+/**
+ * adreno_use_default_setstate() - Use CPU instead of the GPU to manage the mmu?
+ * @adreno_dev: the device
+ *
+ * In many cases it is preferable to poke the iommu or gpummu directly rather
+ * than using the GPU command stream. If we are idle or trying to go to a low
+ * power state, using the command stream will be slower and asynchronous, which
+ * needlessly complicates the power state transitions. Additionally,
+ * the hardware simulators do not support command stream MMU operations so
+ * the command stream can never be used if we are capturing CFF data.
+ *
+ */
+static bool adreno_use_default_setstate(struct adreno_device *adreno_dev)
+{
+ return (adreno_isidle(&adreno_dev->dev) ||
+ adreno_dev->drawctxt_active == NULL ||
+ KGSL_STATE_ACTIVE != adreno_dev->dev.state ||
+ atomic_read(&adreno_dev->dev.active_cnt) == 0 ||
+ adreno_dev->dev.cff_dump_enable);
+}
+
static void adreno_iommu_setstate(struct kgsl_device *device,
unsigned int context_id,
uint32_t flags)
@@ -831,22 +859,17 @@
struct adreno_context *adreno_ctx = NULL;
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- if (!adreno_dev->drawctxt_active ||
- KGSL_STATE_ACTIVE != device->state ||
- !atomic_read(&device->active_cnt) ||
- device->cff_dump_enable) {
+ if (adreno_use_default_setstate(adreno_dev)) {
kgsl_mmu_device_setstate(&device->mmu, flags);
return;
}
num_iommu_units = kgsl_mmu_get_num_iommu_units(&device->mmu);
- context = idr_find(&device->context_idr, context_id);
+ context = kgsl_context_get(device, context_id);
if (context == NULL)
return;
- kgsl_context_get(context);
-
- adreno_ctx = context->devctxt;
+ adreno_ctx = ADRENO_CONTEXT(context);
if (kgsl_mmu_enable_clk(&device->mmu,
KGSL_IOMMU_CONTEXT_USER))
@@ -887,9 +910,7 @@
adreno_ringbuffer_issuecmds(device, adreno_ctx, KGSL_CMD_FLAGS_PMODE,
&link[0], sizedwords);
- kgsl_mmu_disable_clk_on_ts(&device->mmu,
- rb->timestamp[KGSL_MEMSTORE_GLOBAL], true);
-
+ kgsl_mmu_disable_clk_on_ts(&device->mmu, rb->global_ts, true);
kgsl_context_put(context);
}
@@ -917,11 +938,11 @@
* writes For CFF dump we must idle and use the registers so that it is
* easier to filter out the mmu accesses from the dump
*/
- if (!device->cff_dump_enable && adreno_dev->drawctxt_active) {
- context = idr_find(&device->context_idr, context_id);
+ if (!adreno_use_default_setstate(adreno_dev)) {
+ context = kgsl_context_get(device, context_id);
if (context == NULL)
return;
- adreno_ctx = context->devctxt;
+ adreno_ctx = ADRENO_CONTEXT(context);
if (flags & KGSL_MMUFLAGS_PTUPDATE) {
/* wait for graphics pipe to be idle */
@@ -998,6 +1019,8 @@
adreno_ringbuffer_issuecmds(device, adreno_ctx,
KGSL_CMD_FLAGS_PMODE,
&link[0], sizedwords);
+
+ kgsl_context_put(context);
} else {
kgsl_mmu_device_setstate(&device->mmu, flags);
}
@@ -1042,9 +1065,9 @@
if (pdata->chipid != 0)
return pdata->chipid;
- adreno_regread(device, REG_RBBM_PERIPHID1, &coreid);
- adreno_regread(device, REG_RBBM_PERIPHID2, &majorid);
- adreno_regread(device, REG_RBBM_PATCH_RELEASE, &revid);
+ kgsl_regread(device, REG_RBBM_PERIPHID1, &coreid);
+ kgsl_regread(device, REG_RBBM_PERIPHID2, &majorid);
+ kgsl_regread(device, REG_RBBM_PATCH_RELEASE, &revid);
/*
* adreno 22x gpus are indicated by coreid 2,
@@ -1133,6 +1156,17 @@
adreno_dev->pfp_jt_idx = adreno_gpulist[i].pfp_jt_idx;
adreno_dev->pfp_jt_addr = adreno_gpulist[i].pfp_jt_addr;
adreno_dev->gpulist_index = i;
+ /*
+ * Initialize uninitialzed gpu registers, only needs to be done once
+ * Make all offsets that are not initialized to ADRENO_REG_UNUSED
+ */
+ for (i = 0; i < ADRENO_REG_REGISTER_MAX; i++) {
+ if (adreno_dev->gpudev->reg_offsets->offset_0 != i &&
+ !adreno_dev->gpudev->reg_offsets->offsets[i]) {
+ adreno_dev->gpudev->reg_offsets->offsets[i] =
+ ADRENO_REG_UNUSED;
+ }
+ }
}
static struct platform_device_id adreno_id_table[] = {
@@ -1652,6 +1686,8 @@
adreno_debugfs_init(device);
+ adreno_ft_init_sysfs(device);
+
kgsl_pwrscale_init(device);
kgsl_pwrscale_attach_policy(device, ADRENO_DEFAULT_PWRSCALE_POLICY);
@@ -1692,7 +1728,7 @@
static int adreno_init(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+ int i;
if (KGSL_STATE_DUMP_AND_FT != device->state)
kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
@@ -1732,11 +1768,21 @@
adreno_gpulist[adreno_dev->gpulist_index].sync_lock_pfp_ver))
device->mmu.flags |= KGSL_MMU_FLAGS_IOMMU_SYNC;
- rb->timestamp[KGSL_MEMSTORE_GLOBAL] = 0;
-
- /* Assign correct RBBM status register to hang detect regs
- */
- ft_detect_regs[0] = adreno_dev->gpudev->reg_rbbm_status;
+ /* Initialize ft detection register offsets */
+ ft_detect_regs[0] = adreno_getreg(adreno_dev,
+ ADRENO_REG_RBBM_STATUS);
+ ft_detect_regs[1] = adreno_getreg(adreno_dev,
+ ADRENO_REG_CP_RB_RPTR);
+ ft_detect_regs[2] = adreno_getreg(adreno_dev,
+ ADRENO_REG_CP_IB1_BASE);
+ ft_detect_regs[3] = adreno_getreg(adreno_dev,
+ ADRENO_REG_CP_IB1_BUFSZ);
+ ft_detect_regs[4] = adreno_getreg(adreno_dev,
+ ADRENO_REG_CP_IB2_BASE);
+ ft_detect_regs[5] = adreno_getreg(adreno_dev,
+ ADRENO_REG_CP_IB2_BUFSZ);
+ for (i = 6; i < FT_DETECT_REGS_COUNT; i++)
+ ft_detect_regs[i] = 0;
adreno_perfcounter_init(device);
@@ -1751,12 +1797,17 @@
int status = -EINVAL;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
unsigned int state = device->state;
+ unsigned int regulator_left_on = 0;
kgsl_cffdump_open(device);
if (KGSL_STATE_DUMP_AND_FT != device->state)
kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
+ regulator_left_on = (regulator_is_enabled(device->pwrctrl.gpu_reg) ||
+ (device->pwrctrl.gpu_cx &&
+ regulator_is_enabled(device->pwrctrl.gpu_cx)));
+
/* Power up the device */
kgsl_pwrctrl_enable(device);
@@ -1784,6 +1835,14 @@
goto error_mmu_off;
}
+ if (regulator_left_on && adreno_dev->gpudev->soft_reset) {
+ /*
+ * Reset the GPU for A3xx. A2xx does a soft reset in
+ * the start function.
+ */
+ adreno_dev->gpudev->soft_reset(adreno_dev);
+ }
+
/* Start the GPU */
adreno_dev->gpudev->start(adreno_dev);
@@ -1823,6 +1882,9 @@
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ if (adreno_dev->drawctxt_active)
+ kgsl_context_put(&adreno_dev->drawctxt_active->base);
+
adreno_dev->drawctxt_active = NULL;
adreno_ringbuffer_stop(&adreno_dev->ringbuffer);
@@ -1844,59 +1906,75 @@
return 0;
}
+/*
+ * Set the reset status of all contexts to
+ * INNOCENT_CONTEXT_RESET_EXT except for the bad context
+ * since thats the guilty party, if fault tolerance failed then
+ * mark all as guilty
+ */
+
+static int _mark_context_status(int id, void *ptr, void *data)
+{
+ unsigned int ft_status = *((unsigned int *) data);
+ struct kgsl_context *context = ptr;
+ struct adreno_context *adreno_context = ADRENO_CONTEXT(context);
+
+ if (ft_status) {
+ context->reset_status =
+ KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT;
+ adreno_context->flags |= CTXT_FLAGS_GPU_HANG;
+ } else if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT !=
+ context->reset_status) {
+ if (adreno_context->flags & (CTXT_FLAGS_GPU_HANG |
+ CTXT_FLAGS_GPU_HANG_FT))
+ context->reset_status =
+ KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT;
+ else
+ context->reset_status =
+ KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT;
+ }
+
+ return 0;
+}
+
static void adreno_mark_context_status(struct kgsl_device *device,
int ft_status)
{
- struct kgsl_context *context;
- int next = 0;
- /*
- * Set the reset status of all contexts to
- * INNOCENT_CONTEXT_RESET_EXT except for the bad context
- * since thats the guilty party, if fault tolerance failed then
- * mark all as guilty
- */
- while ((context = idr_get_next(&device->context_idr, &next))) {
- struct adreno_context *adreno_context = context->devctxt;
- if (ft_status) {
- context->reset_status =
- KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT;
- adreno_context->flags |= CTXT_FLAGS_GPU_HANG;
- } else if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT !=
- context->reset_status) {
- if (adreno_context->flags & (CTXT_FLAGS_GPU_HANG |
- CTXT_FLAGS_GPU_HANG_FT))
- context->reset_status =
- KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT;
- else
- context->reset_status =
- KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT;
- }
- next = next + 1;
+ /* Mark the status for all the contexts in the device */
+
+ read_lock(&device->context_lock);
+ idr_for_each(&device->context_idr, _mark_context_status, &ft_status);
+ read_unlock(&device->context_lock);
+}
+
+/*
+ * For hung contexts set the current memstore value to the most recent issued
+ * timestamp - this resets the status and lets the system continue on
+ */
+
+static int _set_max_ts(int id, void *ptr, void *data)
+{
+ struct kgsl_device *device = data;
+ struct kgsl_context *context = ptr;
+ struct adreno_context *drawctxt = ADRENO_CONTEXT(context);
+
+ if (drawctxt && drawctxt->flags & CTXT_FLAGS_GPU_HANG) {
+ kgsl_sharedmem_writel(device, &device->memstore,
+ KGSL_MEMSTORE_OFFSET(context->id,
+ soptimestamp), drawctxt->timestamp);
+ kgsl_sharedmem_writel(device, &device->memstore,
+ KGSL_MEMSTORE_OFFSET(context->id,
+ eoptimestamp), drawctxt->timestamp);
}
+
+ return 0;
}
static void adreno_set_max_ts_for_bad_ctxs(struct kgsl_device *device)
{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- struct kgsl_context *context;
- struct adreno_context *temp_adreno_context;
- int next = 0;
-
- while ((context = idr_get_next(&device->context_idr, &next))) {
- temp_adreno_context = context->devctxt;
- if (temp_adreno_context->flags & CTXT_FLAGS_GPU_HANG) {
- kgsl_sharedmem_writel(device, &device->memstore,
- KGSL_MEMSTORE_OFFSET(context->id,
- soptimestamp),
- rb->timestamp[context->id]);
- kgsl_sharedmem_writel(device, &device->memstore,
- KGSL_MEMSTORE_OFFSET(context->id,
- eoptimestamp),
- rb->timestamp[context->id]);
- }
- next = next + 1;
- }
+ read_lock(&device->context_lock);
+ idr_for_each(&device->context_idr, _set_max_ts, device);
+ read_unlock(&device->context_lock);
}
static void adreno_destroy_ft_data(struct adreno_ft_data *ft_data)
@@ -2086,7 +2164,7 @@
ft_data->start_of_replay_cmds = 0xFFFFFFFF;
ft_data->replay_for_snapshot = 0xFFFFFFFF;
- adreno_regread(device, REG_CP_IB1_BASE, &ft_data->ib1);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB1_BASE, &ft_data->ib1);
kgsl_sharedmem_readl(&device->memstore, &ft_data->context_id,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
@@ -2123,7 +2201,7 @@
ft_data->status = 0;
/* find the start of bad command sequence in rb */
- context = idr_find(&device->context_idr, ft_data->context_id);
+ context = kgsl_context_get(device, ft_data->context_id);
ft_data->ft_policy = adreno_dev->ft_policy;
@@ -2135,14 +2213,14 @@
ft_data->global_eop + 1, false);
if (ret) {
ft_data->ft_policy |= KGSL_FT_TEMP_DISABLE;
- return;
+ goto done;
} else {
ft_data->start_of_replay_cmds = rb_rptr;
ft_data->ft_policy &= ~KGSL_FT_TEMP_DISABLE;
}
if (context) {
- adreno_context = context->devctxt;
+ adreno_context = ADRENO_CONTEXT(context);
if (adreno_context->flags & CTXT_FLAGS_PREAMBLE) {
if (ft_data->ib1) {
ret = _find_hanging_ib_sequence(rb,
@@ -2150,14 +2228,16 @@
if (ret) {
KGSL_FT_ERR(device,
"Start not found for replay IB seq\n");
- ret = 0;
- return;
+ goto done;
}
ft_data->start_of_replay_cmds = rb_rptr;
ft_data->replay_for_snapshot = rb_rptr;
}
}
}
+
+done:
+ kgsl_context_put(context);
}
static int
@@ -2207,6 +2287,9 @@
return -EINVAL;
}
+ if (adreno_dev->drawctxt_active)
+ kgsl_context_put(&adreno_dev->drawctxt_active->base);
+
adreno_dev->drawctxt_active = NULL;
/* Stop the ringbuffer */
@@ -2269,11 +2352,9 @@
}
reset_done:
- if (context) {
- struct adreno_context *adreno_context = context->devctxt;
- kgsl_mmu_setstate(&device->mmu, adreno_context->pagetable,
- KGSL_MEMSTORE_GLOBAL);
- }
+ if (context)
+ kgsl_mmu_setstate(&device->mmu, context->pagetable,
+ KGSL_MEMSTORE_GLOBAL);
/* If iommu is used then we need to make sure that the iommu clocks
* are on since there could be commands in pipeline that touch iommu */
@@ -2377,7 +2458,8 @@
static int no_context_ft;
struct kgsl_mmu *mmu = &device->mmu;
- context = idr_find(&device->context_idr, ft_data->context_id);
+ context = kgsl_context_get(device, ft_data->context_id);
+
if (context == NULL) {
KGSL_FT_ERR(device, "Last context unknown id:%d\n",
ft_data->context_id);
@@ -2391,7 +2473,7 @@
}
} else {
no_context_ft = 0;
- adreno_context = context->devctxt;
+ adreno_context = ADRENO_CONTEXT(context);
adreno_context->flags |= CTXT_FLAGS_GPU_HANG;
/*
* set the invalid ts flag to 0 for this context since we have
@@ -2447,16 +2529,16 @@
goto play_good_cmds;
}
- /* Do not try the reply if hang is due to a pagefault */
- if (adreno_context && adreno_context->pagefault) {
+ /* Do not try to replay if hang is due to a pagefault */
+ if (context && test_bit(KGSL_CONTEXT_PAGEFAULT, &context->priv)) {
/* Resume MMU */
mmu->mmu_ops->mmu_pagefault_resume(mmu);
- if ((ft_data->context_id == adreno_context->id) &&
- (ft_data->global_eop == adreno_context->pagefault_ts)) {
+ if ((ft_data->context_id == context->id) &&
+ (ft_data->global_eop == context->pagefault_ts)) {
ft_data->ft_policy &= ~KGSL_FT_REPLAY;
KGSL_FT_ERR(device, "MMU fault skipping replay\n");
}
- adreno_context->pagefault = 0;
+ clear_bit(KGSL_CONTEXT_PAGEFAULT, &context->priv);
}
if (ft_data->ft_policy & KGSL_FT_REPLAY) {
@@ -2544,6 +2626,10 @@
adreno_context->flags = (adreno_context->flags &
~CTXT_FLAGS_GPU_HANG) | CTXT_FLAGS_GPU_HANG_FT;
}
+
+ if (last_active_ctx)
+ _kgsl_context_get(&last_active_ctx->base);
+
adreno_dev->drawctxt_active = last_active_ctx;
}
@@ -2568,17 +2654,18 @@
/* ringbuffer now has data from the last valid context id,
* so restore the active_ctx to the last valid context */
if (ft_data->last_valid_ctx_id) {
- struct kgsl_context *last_ctx =
- idr_find(&device->context_idr,
- ft_data->last_valid_ctx_id);
- if (last_ctx)
- adreno_dev->drawctxt_active = last_ctx->devctxt;
+ struct kgsl_context *last_ctx = kgsl_context_get(device,
+ ft_data->last_valid_ctx_id);
+
+ adreno_dev->drawctxt_active = ADRENO_CONTEXT(last_ctx);
}
done:
/* Turn off iommu clocks */
if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype())
kgsl_mmu_disable_clk_on_ts(&device->mmu, 0, false);
+
+ kgsl_context_put(context);
return ret;
}
@@ -2589,7 +2676,6 @@
int ret = 0;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- unsigned int timestamp;
/*
* If GPU FT is turned off do not run FT.
@@ -2606,8 +2692,8 @@
"Bad context_id: %u, global_eop: 0x%x\n",
ft_data->ib1, ft_data->context_id, ft_data->global_eop);
- timestamp = rb->timestamp[KGSL_MEMSTORE_GLOBAL];
- KGSL_FT_INFO(device, "Last issued global timestamp: %x\n", timestamp);
+ KGSL_FT_INFO(device, "Last issued global timestamp: %x\n",
+ rb->global_ts);
/* We may need to replay commands multiple times based on whether
* multiple contexts hang the GPU */
@@ -2636,14 +2722,12 @@
/* Restore correct states after fault tolerance */
if (adreno_dev->drawctxt_active)
device->mmu.hwpagetable =
- adreno_dev->drawctxt_active->pagetable;
+ adreno_dev->drawctxt_active->base.pagetable;
else
device->mmu.hwpagetable = device->mmu.defaultpagetable;
- rb->timestamp[KGSL_MEMSTORE_GLOBAL] = timestamp;
kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
- eoptimestamp),
- rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
+ eoptimestamp), rb->global_ts);
/* switch to NULL ctxt */
if (adreno_dev->drawctxt_active != NULL)
@@ -2734,6 +2818,264 @@
}
EXPORT_SYMBOL(adreno_dump_and_exec_ft);
+/**
+ * _ft_sysfs_store() - Common routine to write to FT sysfs files
+ * @buf: value to write
+ * @count: size of the value to write
+ * @sysfs_cfg: KGSL FT sysfs config to write
+ *
+ * This is a common routine to write to FT sysfs files.
+ */
+static int _ft_sysfs_store(const char *buf, size_t count, unsigned int *ptr)
+{
+ char temp[20];
+ unsigned long val;
+ int rc;
+
+ snprintf(temp, sizeof(temp), "%.*s",
+ (int)min(count, sizeof(temp) - 1), buf);
+ rc = kstrtoul(temp, 0, &val);
+ if (rc)
+ return rc;
+
+ *ptr = val;
+
+ return count;
+}
+
+/**
+ * _get_adreno_dev() - Routine to get a pointer to adreno dev
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value to write
+ * @count: size of the value to write
+ */
+struct adreno_device *_get_adreno_dev(struct device *dev)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ return device ? ADRENO_DEVICE(device) : NULL;
+}
+
+/**
+ * _ft_policy_store() - Routine to configure FT policy
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value to write
+ * @count: size of the value to write
+ *
+ * FT policy can be set to any of the options below.
+ * KGSL_FT_DISABLE -> BIT(0) Set to disable FT
+ * KGSL_FT_REPLAY -> BIT(1) Set to enable replay
+ * KGSL_FT_SKIPIB -> BIT(2) Set to skip IB
+ * KGSL_FT_SKIPFRAME -> BIT(3) Set to skip frame
+ * by default set FT policy to KGSL_FT_DEFAULT_POLICY
+ */
+static int _ft_policy_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ int ret;
+ if (adreno_dev == NULL)
+ return 0;
+
+ mutex_lock(&adreno_dev->dev.mutex);
+ ret = _ft_sysfs_store(buf, count, &adreno_dev->ft_policy);
+ mutex_unlock(&adreno_dev->dev.mutex);
+
+ return ret;
+}
+
+/**
+ * _ft_policy_show() - Routine to read FT policy
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value read
+ *
+ * This is a routine to read current FT policy
+ */
+static int _ft_policy_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ if (adreno_dev == NULL)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "0x%X\n", adreno_dev->ft_policy);
+}
+
+/**
+ * _ft_pagefault_policy_store() - Routine to configure FT
+ * pagefault policy
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value to write
+ * @count: size of the value to write
+ *
+ * FT pagefault policy can be set to any of the options below.
+ * KGSL_FT_PAGEFAULT_INT_ENABLE -> BIT(0) set to enable pagefault INT
+ * KGSL_FT_PAGEFAULT_GPUHALT_ENABLE -> BIT(1) Set to enable GPU HALT on
+ * pagefaults. This stalls the GPU on a pagefault on IOMMU v1 HW.
+ * KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE -> BIT(2) Set to log only one
+ * pagefault per page.
+ * KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT -> BIT(3) Set to log only one
+ * pagefault per INT.
+ */
+static int _ft_pagefault_policy_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ int ret;
+ if (adreno_dev == NULL)
+ return 0;
+
+ mutex_lock(&adreno_dev->dev.mutex);
+ ret = _ft_sysfs_store(buf, count, &adreno_dev->ft_pf_policy);
+ mutex_unlock(&adreno_dev->dev.mutex);
+
+ return ret;
+}
+
+/**
+ * _ft_pagefault_policy_show() - Routine to read FT pagefault
+ * policy
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value read
+ *
+ * This is a routine to read current FT pagefault policy
+ */
+static int _ft_pagefault_policy_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ if (adreno_dev == NULL)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "0x%X\n", adreno_dev->ft_pf_policy);
+}
+
+/**
+ * _ft_fast_hang_detect_store() - Routine to configure FT fast
+ * hang detect policy
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value to write
+ * @count: size of the value to write
+ *
+ * 0x1 - Enable fast hang detection
+ * 0x0 - Disable fast hang detection
+ */
+static int _ft_fast_hang_detect_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ int ret;
+ if (adreno_dev == NULL)
+ return 0;
+
+ mutex_lock(&adreno_dev->dev.mutex);
+ ret = _ft_sysfs_store(buf, count, &adreno_dev->fast_hang_detect);
+ mutex_unlock(&adreno_dev->dev.mutex);
+
+ return ret;
+
+}
+
+/**
+ * _ft_fast_hang_detect_show() - Routine to read FT fast
+ * hang detect policy
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value read
+ */
+static int _ft_fast_hang_detect_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ if (adreno_dev == NULL)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ (adreno_dev->fast_hang_detect ? 1 : 0));
+}
+
+/**
+ * _ft_long_ib_detect_store() - Routine to configure FT long IB
+ * detect policy
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value to write
+ * @count: size of the value to write
+ *
+ * 0x0 - Enable long IB detection
+ * 0x1 - Disable long IB detection
+ */
+static int _ft_long_ib_detect_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ int ret;
+ if (adreno_dev == NULL)
+ return 0;
+
+ mutex_lock(&adreno_dev->dev.mutex);
+ ret = _ft_sysfs_store(buf, count, &adreno_dev->long_ib_detect);
+ mutex_unlock(&adreno_dev->dev.mutex);
+
+ return ret;
+
+}
+
+/**
+ * _ft_long_ib_detect_show() - Routine to read FT long IB
+ * detect policy
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value read
+ */
+static int _ft_long_ib_detect_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ if (adreno_dev == NULL)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ (adreno_dev->long_ib_detect ? 1 : 0));
+}
+
+
+#define FT_DEVICE_ATTR(name) \
+ DEVICE_ATTR(name, 0644, _ ## name ## _show, _ ## name ## _store);
+
+FT_DEVICE_ATTR(ft_policy);
+FT_DEVICE_ATTR(ft_pagefault_policy);
+FT_DEVICE_ATTR(ft_fast_hang_detect);
+FT_DEVICE_ATTR(ft_long_ib_detect);
+
+
+const struct device_attribute *ft_attr_list[] = {
+ &dev_attr_ft_policy,
+ &dev_attr_ft_pagefault_policy,
+ &dev_attr_ft_fast_hang_detect,
+ &dev_attr_ft_long_ib_detect,
+ NULL,
+};
+
+int adreno_ft_init_sysfs(struct kgsl_device *device)
+{
+ return kgsl_create_device_sysfs_files(device->dev, ft_attr_list);
+}
+
+void adreno_ft_uninit_sysfs(struct kgsl_device *device)
+{
+ kgsl_remove_device_sysfs_files(device->dev, ft_attr_list);
+}
+
static int adreno_getproperty(struct kgsl_device *device,
enum kgsl_property_type type,
void *value,
@@ -2878,20 +3220,15 @@
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- unsigned long wait;
+ unsigned long wait = jiffies;
unsigned long timeout = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
unsigned int rptr;
- /*
- * The first time into the loop, wait for 100 msecs and kick wptr again
- * to ensure that the hardware has updated correctly. After that, kick
- * it periodically every KGSL_TIMEOUT_PART msecs until the timeout
- * expires
- */
-
- wait = jiffies + msecs_to_jiffies(100);
-
do {
+ /*
+ * Wait is "jiffies" first time in the loop to start
+ * GPU stall detection immediately.
+ */
if (time_after(jiffies, wait)) {
/* Check to see if the core is hung */
if (adreno_ft_detect(device, regs))
@@ -2913,16 +3250,15 @@
/* Caller must hold the device mutex. */
int adreno_idle(struct kgsl_device *device)
{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- unsigned int rbbm_status;
unsigned long wait_time;
unsigned long wait_time_part;
unsigned int prev_reg_val[FT_DETECT_REGS_COUNT];
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
memset(prev_reg_val, 0, sizeof(prev_reg_val));
kgsl_cffdump_regpoll(device,
- adreno_dev->gpudev->reg_rbbm_status << 2,
+ adreno_getreg(adreno_dev, ADRENO_REG_RBBM_STATUS) << 2,
0x00000000, 0x80000000);
retry:
@@ -2935,23 +3271,15 @@
wait_time_part = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART);
while (time_before(jiffies, wait_time)) {
- adreno_regread(device, adreno_dev->gpudev->reg_rbbm_status,
- &rbbm_status);
- if (adreno_is_a2xx(adreno_dev)) {
- if (rbbm_status == 0x110)
- return 0;
- } else {
- if (!(rbbm_status & 0x80000000))
- return 0;
- }
+ if (adreno_isidle(device))
+ return 0;
- /* Dont wait for timeout, detect hang faster.
- */
+ /* Dont wait for timeout, detect hang faster. */
if (time_after(jiffies, wait_time_part)) {
- wait_time_part = jiffies +
- msecs_to_jiffies(KGSL_TIMEOUT_PART);
- if ((adreno_ft_detect(device, prev_reg_val)))
- goto err;
+ wait_time_part = jiffies +
+ msecs_to_jiffies(KGSL_TIMEOUT_PART);
+ if ((adreno_ft_detect(device, prev_reg_val)))
+ goto err;
}
}
@@ -2981,9 +3309,8 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
/* Is the core idle? */
- adreno_regread(device,
- adreno_dev->gpudev->reg_rbbm_status,
- ®_rbbm_status);
+ adreno_readreg(adreno_dev, ADRENO_REG_RBBM_STATUS,
+ ®_rbbm_status);
if (adreno_is_a2xx(adreno_dev)) {
if (reg_rbbm_status == 0x110)
@@ -3001,9 +3328,8 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- WARN_ON(device->state == KGSL_STATE_INIT);
/* If the device isn't active, don't force it on. */
- if (device->state == KGSL_STATE_ACTIVE) {
+ if (kgsl_pwrctrl_isenabled(device)) {
/* Is the ring buffer is empty? */
unsigned int rptr = adreno_get_rptr(rb);
if (rptr == rb->wptr) {
@@ -3046,32 +3372,33 @@
phys_addr_t pt_base, unsigned int gpuaddr, unsigned int size)
{
struct kgsl_context *context;
- struct adreno_context *adreno_context = NULL;
int next = 0;
+ struct kgsl_memdesc *desc = NULL;
+ read_lock(&device->context_lock);
while (1) {
context = idr_get_next(&device->context_idr, &next);
if (context == NULL)
break;
- adreno_context = (struct adreno_context *)context->devctxt;
-
- if (kgsl_mmu_pt_equal(&device->mmu, adreno_context->pagetable,
+ if (kgsl_mmu_pt_equal(&device->mmu, context->pagetable,
pt_base)) {
- struct kgsl_memdesc *desc;
+ struct adreno_context *adreno_context;
+ adreno_context = ADRENO_CONTEXT(context);
desc = &adreno_context->gpustate;
if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size))
- return desc;
+ break;
desc = &adreno_context->context_gmem_shadow.gmemshadow;
if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size))
- return desc;
+ break;
}
next = next + 1;
+ desc = NULL;
}
-
- return NULL;
+ read_unlock(&device->context_lock);
+ return desc;
}
struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device,
@@ -3114,6 +3441,7 @@
return memdesc ? kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr) : NULL;
}
+
/**
* adreno_read - General read function to read adreno device memory
* @device - Pointer to the GPU device struct (for adreno device)
@@ -3146,7 +3474,7 @@
* @offsetwords - Word (4 Bytes) offset to the register to be read
* @value - Value read from device register
*/
-void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
+static void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
unsigned int *value)
{
adreno_read(device, device->reg_virt, offsetwords, value,
@@ -3166,7 +3494,8 @@
device->shader_mem_len);
}
-void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords,
+static void adreno_regwrite(struct kgsl_device *device,
+ unsigned int offsetwords,
unsigned int value)
{
unsigned int *reg;
@@ -3190,9 +3519,10 @@
static unsigned int _get_context_id(struct kgsl_context *k_ctxt)
{
unsigned int context_id = KGSL_MEMSTORE_GLOBAL;
+
if (k_ctxt != NULL) {
- struct adreno_context *a_ctxt = k_ctxt->devctxt;
- if (k_ctxt->id == KGSL_CONTEXT_INVALID || a_ctxt == NULL)
+ struct adreno_context *a_ctxt = ADRENO_CONTEXT(k_ctxt);
+ if (kgsl_context_detached(k_ctxt))
context_id = KGSL_CONTEXT_INVALID;
else if (a_ctxt->flags & CTXT_FLAGS_PER_CONTEXT_TS)
context_id = k_ctxt->id;
@@ -3263,7 +3593,8 @@
*/
if (context && device->state != KGSL_STATE_SLUMBER) {
- adreno_ringbuffer_issuecmds(device, context->devctxt,
+ adreno_ringbuffer_issuecmds(device,
+ ADRENO_CONTEXT(context),
KGSL_CMD_FLAGS_GET_INT, NULL, 0);
}
}
@@ -3323,6 +3654,7 @@
unsigned int curr_context_id = 0;
static struct adreno_context *curr_context;
static struct kgsl_context *context;
+ static char pid_name[TASK_COMM_LEN] = "unknown";
if (!adreno_dev->fast_hang_detect)
fast_hang_detected = 0;
@@ -3335,7 +3667,7 @@
if (is_adreno_rbbm_status_idle(device) &&
(kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED)
- == rb->timestamp[KGSL_MEMSTORE_GLOBAL])) {
+ == rb->global_ts)) {
/*
* On A2XX if the RPTR != WPTR and the device is idle, then
@@ -3345,9 +3677,11 @@
if (adreno_is_a2xx(adreno_dev)) {
unsigned int rptr;
- adreno_regread(device, REG_CP_RB_RPTR, &rptr);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR,
+ &rptr);
if (rptr != adreno_dev->ringbuffer.wptr)
- adreno_regwrite(device, REG_CP_RB_WPTR,
+ adreno_writereg(adreno_dev,
+ ADRENO_REG_CP_RB_WPTR,
adreno_dev->ringbuffer.wptr);
}
@@ -3370,7 +3704,7 @@
for (i = 0; i < FT_DETECT_REGS_COUNT; i++) {
if (ft_detect_regs[i] == 0)
continue;
- adreno_regread(device, ft_detect_regs[i],
+ kgsl_regread(device, ft_detect_regs[i],
&curr_reg_val[i]);
}
@@ -3384,19 +3718,28 @@
if (curr_global_ts == prev_global_ts) {
- /* Get the current context here */
- if (context == NULL) {
+ /* If we don't already have a good context, get it. */
+ if (kgsl_context_detached(context)) {
+ kgsl_context_put(context);
+ context = NULL;
+ curr_context = NULL;
+ strlcpy(pid_name, "unknown", sizeof(pid_name));
+
kgsl_sharedmem_readl(&device->memstore,
&curr_context_id,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
current_context));
/* Make sure the memstore read has posted */
mb();
- context = idr_find(&device->context_idr,
- curr_context_id);
+
+ context = kgsl_context_get(device, curr_context_id);
if (context != NULL) {
- curr_context = context->devctxt;
+ struct task_struct *task;
+ curr_context = ADRENO_CONTEXT(context);
curr_context->ib_gpu_time_used = 0;
+ task = find_task_by_vpid(context->pid);
+ if (task)
+ get_task_comm(pid_name, task);
} else {
KGSL_DRV_ERR(device,
"Fault tolerance no context found\n");
@@ -3420,8 +3763,7 @@
KGSL_FT_ERR(device,
"Proc %s, ctxt_id %d ts %d triggered fault tolerance"
" on global ts %d\n",
- curr_context ? curr_context->pid_name : "",
- curr_context ? curr_context->id : 0,
+ pid_name, context ? context->id : 0,
(kgsl_readtimestamp(device, context,
KGSL_TIMESTAMP_RETIRED) + 1),
curr_global_ts + 1);
@@ -3433,7 +3775,7 @@
curr_context->ib_gpu_time_used += KGSL_TIMEOUT_PART;
KGSL_FT_INFO(device,
"Proc %s used GPU Time %d ms on timestamp 0x%X\n",
- curr_context->pid_name, curr_context->ib_gpu_time_used,
+ pid_name, curr_context->ib_gpu_time_used,
curr_global_ts+1);
if ((long_ib_detected) &&
@@ -3449,8 +3791,7 @@
"Proc %s, ctxt_id %d ts %d"
"used GPU for %d ms long ib "
"detected on global ts %d\n",
- curr_context->pid_name,
- curr_context->id,
+ pid_name, context->id,
(kgsl_readtimestamp(device,
context,
KGSL_TIMESTAMP_RETIRED)+1),
@@ -3469,8 +3810,10 @@
} else {
/* GPU is moving forward */
prev_global_ts = curr_global_ts;
+ kgsl_context_put(context);
context = NULL;
curr_context = NULL;
+ strlcpy(pid_name, "unknown", sizeof(pid_name));
adreno_dev->long_ib = 0;
adreno_dev->long_ib_ts = 0;
}
@@ -3493,7 +3836,7 @@
if (context_id == KGSL_CONTEXT_INVALID)
return -EINVAL;
- ts_issued = adreno_dev->ringbuffer.timestamp[context_id];
+ ts_issued = adreno_context_timestamp(context, &adreno_dev->ringbuffer);
if (timestamp_cmp(timestamp, ts_issued) <= 0)
return 0;
@@ -3528,7 +3871,8 @@
unsigned int msecs)
{
static unsigned int io_cnt;
- struct adreno_context *adreno_ctx = context ? context->devctxt : NULL;
+ struct adreno_context *adreno_ctx = context ? ADRENO_CONTEXT(context) :
+ NULL;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
unsigned int context_id = _get_context_id(context);
unsigned int time_elapsed = 0;
@@ -3536,8 +3880,6 @@
int ts_compare = 1;
int io, ret = -ETIMEDOUT;
- /* Get out early if the context has already been destroyed */
-
if (context_id == KGSL_CONTEXT_INVALID) {
KGSL_DRV_WARN(device, "context was detached");
return -EINVAL;
@@ -3675,9 +4017,9 @@
switch (type) {
case KGSL_TIMESTAMP_QUEUED: {
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- timestamp = rb->timestamp[context_id];
+ timestamp = adreno_context_timestamp(context,
+ &adreno_dev->ringbuffer);
break;
}
case KGSL_TIMESTAMP_CONSUMED:
@@ -3709,7 +4051,8 @@
binbase = data;
- context = kgsl_find_context(dev_priv, binbase->drawctxt_id);
+ context = kgsl_context_get_owner(dev_priv,
+ binbase->drawctxt_id);
if (context) {
adreno_drawctxt_set_bin_base_offset(
device, context, binbase->offset);
@@ -3720,6 +4063,8 @@
"device_id=%d\n",
binbase->drawctxt_id, device->id);
}
+
+ kgsl_context_put(context);
break;
}
case IOCTL_KGSL_PERFCOUNTER_GET: {
@@ -3846,6 +4191,7 @@
/* Optional functions */
.setstate = adreno_setstate,
.drawctxt_create = adreno_drawctxt_create,
+ .drawctxt_detach = adreno_drawctxt_detach,
.drawctxt_destroy = adreno_drawctxt_destroy,
.setproperty = adreno_setproperty,
.postmortem_dump = adreno_dump,
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 70b07c4..cb75b34 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -25,6 +25,9 @@
#define ADRENO_DEVICE(device) \
KGSL_CONTAINER_OF(device, struct adreno_device, dev)
+#define ADRENO_CONTEXT(device) \
+ KGSL_CONTAINER_OF(device, struct adreno_context, base)
+
#define ADRENO_CHIPID_CORE(_id) (((_id) >> 24) & 0xFF)
#define ADRENO_CHIPID_MAJOR(_id) (((_id) >> 16) & 0xFF)
#define ADRENO_CHIPID_MINOR(_id) (((_id) >> 8) & 0xFF)
@@ -168,14 +171,86 @@
unsigned int group_count;
};
+/**
+ * adreno_regs: List of registers that are used in kgsl driver for all
+ * 3D devices. Each device type has different offset value for the same
+ * register, so an array of register offsets are declared for every device
+ * and are indexed by the enumeration values defined in this enum
+ */
+enum adreno_regs {
+ ADRENO_REG_CP_DEBUG,
+ ADRENO_REG_CP_ME_RAM_WADDR,
+ ADRENO_REG_CP_ME_RAM_DATA,
+ ADRENO_REG_CP_PFP_UCODE_DATA,
+ ADRENO_REG_CP_PFP_UCODE_ADDR,
+ ADRENO_REG_CP_WFI_PEND_CTR,
+ ADRENO_REG_CP_RB_BASE,
+ ADRENO_REG_CP_RB_RPTR_ADDR,
+ ADRENO_REG_CP_RB_RPTR,
+ ADRENO_REG_CP_RB_WPTR,
+ ADRENO_REG_CP_PROTECT_CTRL,
+ ADRENO_REG_CP_ME_CNTL,
+ ADRENO_REG_CP_RB_CNTL,
+ ADRENO_REG_CP_IB1_BASE,
+ ADRENO_REG_CP_IB1_BUFSZ,
+ ADRENO_REG_CP_IB2_BASE,
+ ADRENO_REG_CP_IB2_BUFSZ,
+ ADRENO_REG_CP_TIMESTAMP,
+ ADRENO_REG_SCRATCH_ADDR,
+ ADRENO_REG_SCRATCH_UMSK,
+ ADRENO_REG_SCRATCH_REG2,
+ ADRENO_REG_RBBM_STATUS,
+ ADRENO_REG_RBBM_PERFCTR_CTL,
+ ADRENO_REG_RBBM_PERFCTR_LOAD_CMD0,
+ ADRENO_REG_RBBM_PERFCTR_LOAD_CMD1,
+ ADRENO_REG_RBBM_PERFCTR_LOAD_CMD2,
+ ADRENO_REG_RBBM_PERFCTR_PWR_1_LO,
+ ADRENO_REG_RBBM_INT_0_MASK,
+ ADRENO_REG_RBBM_INT_0_STATUS,
+ ADRENO_REG_RBBM_AHB_ERROR_STATUS,
+ ADRENO_REG_RBBM_PM_OVERRIDE2,
+ ADRENO_REG_VPC_VPC_DEBUG_RAM_SEL,
+ ADRENO_REG_VPC_VPC_DEBUG_RAM_READ,
+ ADRENO_REG_VSC_PIPE_DATA_ADDRESS_0,
+ ADRENO_REG_VSC_PIPE_DATA_LENGTH_7,
+ ADRENO_REG_VSC_SIZE_ADDRESS,
+ ADRENO_REG_VFD_CONTROL_0,
+ ADRENO_REG_VFD_FETCH_INSTR_0_0,
+ ADRENO_REG_VFD_FETCH_INSTR_1_F,
+ ADRENO_REG_VFD_INDEX_MAX,
+ ADRENO_REG_SP_VS_PVT_MEM_ADDR_REG,
+ ADRENO_REG_SP_FS_PVT_MEM_ADDR_REG,
+ ADRENO_REG_SP_VS_OBJ_START_REG,
+ ADRENO_REG_SP_FS_OBJ_START_REG,
+ ADRENO_REG_PA_SC_AA_CONFIG,
+ ADRENO_REG_SQ_GPR_MANAGEMENT,
+ ADRENO_REG_SQ_INST_STORE_MANAGMENT,
+ ADRENO_REG_TC_CNTL_STATUS,
+ ADRENO_REG_TP0_CHICKEN,
+ ADRENO_REG_REGISTER_MAX,
+};
+
+/**
+ * adreno_reg_offsets: Holds array of register offsets
+ * @offsets: Offset array of size defined by enum adreno_regs
+ * @offset_0: This is the index of the register in offset array whose value
+ * is 0. 0 is a valid register offset and during initialization of the
+ * offset array we need to know if an offset value is correctly defined to 0
+ */
+struct adreno_reg_offsets {
+ unsigned int *offsets;
+ enum adreno_regs offset_0;
+};
+
+#define ADRENO_REG_UNUSED 0xFFFFFFFF
+#define ADRENO_REG_DEFINE(_offset, _reg) [_offset] = _reg
+
struct adreno_gpudev {
/*
- * These registers are in a different location on A3XX, so define
- * them in the structure and use them as variables.
+ * These registers are in a different location on different devices,
+ * so define them in the structure and use them as variables.
*/
- unsigned int reg_rbbm_status;
- unsigned int reg_cp_pfp_ucode_data;
- unsigned int reg_cp_pfp_ucode_addr;
+ struct adreno_reg_offsets *reg_offsets;
/* keeps track of when we need to execute the draw workaround code */
int ctx_switches_since_last_draw;
@@ -205,6 +280,7 @@
void (*coresight_config_debug_reg) (struct kgsl_device *device,
int debug_reg, unsigned int val);
void (*soft_reset)(struct adreno_device *device);
+ void (*postmortem_dump)(struct adreno_device *adreno_dev);
};
/*
@@ -249,6 +325,11 @@
#define FT_DETECT_REGS_COUNT 12
+struct log_field {
+ bool show;
+ const char *display;
+};
+
/* Fault Tolerance policy flags */
#define KGSL_FT_OFF BIT(0)
#define KGSL_FT_REPLAY BIT(1)
@@ -259,10 +340,10 @@
#define KGSL_FT_DEFAULT_POLICY (KGSL_FT_REPLAY + KGSL_FT_SKIPIB)
/* Pagefault policy flags */
-#define KGSL_FT_PAGEFAULT_INT_ENABLE 0x00000001
-#define KGSL_FT_PAGEFAULT_GPUHALT_ENABLE 0x00000002
-#define KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE 0x00000004
-#define KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT 0x00000008
+#define KGSL_FT_PAGEFAULT_INT_ENABLE BIT(0)
+#define KGSL_FT_PAGEFAULT_GPUHALT_ENABLE BIT(1)
+#define KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE BIT(2)
+#define KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT BIT(3)
#define KGSL_FT_PAGEFAULT_DEFAULT_POLICY (KGSL_FT_PAGEFAULT_INT_ENABLE + \
KGSL_FT_PAGEFAULT_GPUHALT_ENABLE)
@@ -295,16 +376,15 @@
int adreno_coresight_init(struct platform_device *pdev);
int adreno_idle(struct kgsl_device *device);
-void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
- unsigned int *value);
-void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords,
- unsigned int value);
void adreno_shadermem_regread(struct kgsl_device *device,
unsigned int offsetwords,
unsigned int *value);
int adreno_dump(struct kgsl_device *device, int manual);
+void adreno_dump_fields(struct kgsl_device *device,
+ const char *start, const struct log_field *lines,
+ int num);
unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device
*adreno_dev);
@@ -330,6 +410,9 @@
unsigned int adreno_ft_detect(struct kgsl_device *device,
unsigned int *prev_reg_val);
+int adreno_ft_init_sysfs(struct kgsl_device *device);
+void adreno_ft_uninit_sysfs(struct kgsl_device *device);
+
int adreno_perfcounter_get(struct adreno_device *adreno_dev,
unsigned int groupid, unsigned int countable, unsigned int *offset,
unsigned int flags);
@@ -424,6 +507,26 @@
}
/**
+ * adreno_context_timestamp() - Return the last queued timestamp for the context
+ * @k_ctxt: Pointer to the KGSL context to query
+ * @rb: Pointer to the ringbuffer structure for the GPU
+ *
+ * Return the last queued context for the given context. This is used to verify
+ * that incoming requests are not using an invalid (unsubmitted) timestamp
+ */
+static inline int adreno_context_timestamp(struct kgsl_context *k_ctxt,
+ struct adreno_ringbuffer *rb)
+{
+ if (k_ctxt) {
+ struct adreno_context *a_ctxt = ADRENO_CONTEXT(k_ctxt);
+
+ if (a_ctxt->flags & CTXT_FLAGS_PER_CONTEXT_TS)
+ return a_ctxt->timestamp;
+ }
+ return rb->global_ts;
+}
+
+/**
* adreno_encode_istore_size - encode istore size in CP format
* @adreno_dev - The 3D device.
*
@@ -555,4 +658,66 @@
return cmds - start;
}
+/*
+ * adreno_checkreg_off() - Checks the validity of a register enum
+ * @adreno_dev: Pointer to adreno device
+ * @offset_name: The register enum that is checked
+ */
+static inline bool adreno_checkreg_off(struct adreno_device *adreno_dev,
+ enum adreno_regs offset_name)
+{
+ if (offset_name >= ADRENO_REG_REGISTER_MAX ||
+ ADRENO_REG_UNUSED ==
+ adreno_dev->gpudev->reg_offsets->offsets[offset_name]) {
+ BUG_ON(1);
+ }
+ return true;
+}
+
+/*
+ * adreno_readreg() - Read a register by getting its offset from the
+ * offset array defined in gpudev node
+ * @adreno_dev: Pointer to the the adreno device
+ * @offset_name: The register enum that is to be read
+ * @val: Register value read is placed here
+ */
+static inline void adreno_readreg(struct adreno_device *adreno_dev,
+ enum adreno_regs offset_name, unsigned int *val)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ if (adreno_checkreg_off(adreno_dev, offset_name))
+ kgsl_regread(device,
+ adreno_dev->gpudev->reg_offsets->offsets[offset_name],
+ val);
+}
+
+/*
+ * adreno_writereg() - Write a register by getting its offset from the
+ * offset array defined in gpudev node
+ * @adreno_dev: Pointer to the the adreno device
+ * @offset_name: The register enum that is to be written
+ * @val: Value to write
+ */
+static inline void adreno_writereg(struct adreno_device *adreno_dev,
+ enum adreno_regs offset_name, unsigned int val)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ if (adreno_checkreg_off(adreno_dev, offset_name))
+ kgsl_regwrite(device,
+ adreno_dev->gpudev->reg_offsets->offsets[offset_name], val);
+}
+
+/*
+ * adreno_getreg() - Returns the offset value of a register from the
+ * register offset array in the gpudev node
+ * @adreno_dev: Pointer to the the adreno device
+ * @offset_name: The register enum whore offset is returned
+ */
+static inline unsigned int adreno_getreg(struct adreno_device *adreno_dev,
+ enum adreno_regs offset_name)
+{
+ if (!adreno_checkreg_off(adreno_dev, offset_name))
+ return ADRENO_REG_REGISTER_MAX;
+ return adreno_dev->gpudev->reg_offsets->offsets[offset_name];
+}
#endif /*__ADRENO_H */
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index b0a1fe8..3d72c5c 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1355,7 +1355,7 @@
tmp_ctx.gmem_base = adreno_dev->gmem_base;
result = kgsl_allocate(&drawctxt->context_gmem_shadow.gmemshadow,
- drawctxt->pagetable, drawctxt->context_gmem_shadow.size);
+ drawctxt->base.pagetable, drawctxt->context_gmem_shadow.size);
if (result)
return result;
@@ -1364,7 +1364,7 @@
drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW;
/* blank out gmem shadow. */
- kgsl_sharedmem_set(drawctxt->dev_priv->device,
+ kgsl_sharedmem_set(drawctxt->base.device,
&drawctxt->context_gmem_shadow.gmemshadow, 0, 0,
drawctxt->context_gmem_shadow.size);
@@ -1389,7 +1389,7 @@
kgsl_cache_range_op(&drawctxt->context_gmem_shadow.gmemshadow,
KGSL_CACHE_OP_FLUSH);
- kgsl_cffdump_syncmem(drawctxt->dev_priv,
+ kgsl_cffdump_syncmem(drawctxt->base.device,
&drawctxt->context_gmem_shadow.gmemshadow,
drawctxt->context_gmem_shadow.gmemshadow.gpuaddr,
drawctxt->context_gmem_shadow.gmemshadow.size, false);
@@ -1410,13 +1410,13 @@
*/
ret = kgsl_allocate(&drawctxt->gpustate,
- drawctxt->pagetable, _context_size(adreno_dev));
+ drawctxt->base.pagetable, _context_size(adreno_dev));
if (ret)
return ret;
- kgsl_sharedmem_set(drawctxt->dev_priv->device, &drawctxt->gpustate, 0,
- 0, _context_size(adreno_dev));
+ kgsl_sharedmem_set(drawctxt->base.device, &drawctxt->gpustate,
+ 0, 0, _context_size(adreno_dev));
tmp_ctx.cmd = tmp_ctx.start
= (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET);
@@ -1440,8 +1440,8 @@
kgsl_cache_range_op(&drawctxt->gpustate,
KGSL_CACHE_OP_FLUSH);
- kgsl_cffdump_syncmem(drawctxt->dev_priv, &drawctxt->gpustate,
- drawctxt->gpustate.gpuaddr,
+ kgsl_cffdump_syncmem(drawctxt->base.device,
+ &drawctxt->gpustate, drawctxt->gpustate.gpuaddr,
drawctxt->gpustate.size, false);
done:
@@ -1516,7 +1516,7 @@
"Current active context has caused gpu hang\n");
if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
- kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
+ kgsl_cffdump_syncmem(context->base.device, &context->gpustate,
context->reg_save[1],
context->reg_save[2] << 2, true);
/* save registers and constants. */
@@ -1525,7 +1525,7 @@
context->reg_save, 3);
if (context->flags & CTXT_FLAGS_SHADER_SAVE) {
- kgsl_cffdump_syncmem(context->dev_priv,
+ kgsl_cffdump_syncmem(context->base.device,
&context->gpustate,
context->shader_save[1],
context->shader_save[2] << 2, true);
@@ -1534,7 +1534,7 @@
KGSL_CMD_FLAGS_PMODE,
context->shader_save, 3);
- kgsl_cffdump_syncmem(context->dev_priv,
+ kgsl_cffdump_syncmem(context->base.device,
&context->gpustate,
context->shader_fixup[1],
context->shader_fixup[2] << 2, true);
@@ -1552,7 +1552,7 @@
if ((context->flags & CTXT_FLAGS_GMEM_SAVE) &&
(context->flags & CTXT_FLAGS_GMEM_SHADOW)) {
- kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
+ kgsl_cffdump_syncmem(context->base.device, &context->gpustate,
context->context_gmem_shadow.gmem_save[1],
context->context_gmem_shadow.gmem_save[2] << 2, true);
/* save gmem.
@@ -1562,7 +1562,7 @@
KGSL_CMD_FLAGS_PMODE,
context->context_gmem_shadow.gmem_save, 3);
- kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
+ kgsl_cffdump_syncmem(context->base.device, &context->gpustate,
context->chicken_restore[1],
context->chicken_restore[2] << 2, true);
@@ -1586,9 +1586,18 @@
unsigned int cmds[5];
if (context == NULL) {
- /* No context - set the default apgetable and thats it */
+ /* No context - set the default pagetable and thats it */
+ unsigned int id;
+ /*
+ * If there isn't a current context, the kgsl_mmu_setstate
+ * will use the CPU path so we don't need to give
+ * it a valid context id.
+ */
+ id = (adreno_dev->drawctxt_active != NULL)
+ ? adreno_dev->drawctxt_active->base.id
+ : KGSL_CONTEXT_INVALID;
kgsl_mmu_setstate(&device->mmu, device->mmu.defaultpagetable,
- adreno_dev->drawctxt_active->id);
+ id);
return;
}
@@ -1597,16 +1606,17 @@
cmds[2] = cp_type3_packet(CP_MEM_WRITE, 2);
cmds[3] = device->memstore.gpuaddr +
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context);
- cmds[4] = context->id;
+ cmds[4] = context->base.id;
adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE,
cmds, 5);
- kgsl_mmu_setstate(&device->mmu, context->pagetable, context->id);
+ kgsl_mmu_setstate(&device->mmu, context->base.pagetable,
+ context->base.id);
/* restore gmem.
* (note: changes shader. shader must not already be restored.)
*/
if (context->flags & CTXT_FLAGS_GMEM_RESTORE) {
- kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
+ kgsl_cffdump_syncmem(context->base.device, &context->gpustate,
context->context_gmem_shadow.gmem_restore[1],
context->context_gmem_shadow.gmem_restore[2] << 2,
true);
@@ -1616,7 +1626,7 @@
context->context_gmem_shadow.gmem_restore, 3);
if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
- kgsl_cffdump_syncmem(context->dev_priv,
+ kgsl_cffdump_syncmem(context->base.device,
&context->gpustate,
context->chicken_restore[1],
context->chicken_restore[2] << 2, true);
@@ -1631,7 +1641,7 @@
}
if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
- kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
+ kgsl_cffdump_syncmem(context->base.device, &context->gpustate,
context->reg_restore[1],
context->reg_restore[2] << 2, true);
@@ -1641,7 +1651,7 @@
/* restore shader instructions & partitioning. */
if (context->flags & CTXT_FLAGS_SHADER_RESTORE) {
- kgsl_cffdump_syncmem(context->dev_priv,
+ kgsl_cffdump_syncmem(context->base.device,
&context->gpustate,
context->shader_restore[1],
context->shader_restore[2] << 2, true);
@@ -1707,11 +1717,11 @@
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
int i;
- adreno_regread(device, REG_MASTER_INT_SIGNAL, &master_status);
+ kgsl_regread(device, REG_MASTER_INT_SIGNAL, &master_status);
while (!status && (num_reads < VALID_STATUS_COUNT_MAX) &&
(master_status & MASTER_INT_SIGNAL__CP_INT_STAT)) {
- adreno_regread(device, REG_CP_INT_STATUS, &status);
- adreno_regread(device, REG_MASTER_INT_SIGNAL,
+ kgsl_regread(device, REG_CP_INT_STATUS, &status);
+ kgsl_regread(device, REG_MASTER_INT_SIGNAL,
&master_status);
num_reads++;
}
@@ -1752,7 +1762,7 @@
/* only ack bits we understand */
status &= CP_INT_MASK;
- adreno_regwrite(device, REG_CP_INT_ACK, status);
+ kgsl_regwrite(device, REG_CP_INT_ACK, status);
if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) {
queue_work(device->work_queue, &device->ts_expired_ws);
@@ -1767,10 +1777,10 @@
unsigned int addr = 0;
const char *source;
- adreno_regread(device, REG_RBBM_INT_STATUS, &status);
+ kgsl_regread(device, REG_RBBM_INT_STATUS, &status);
if (status & RBBM_INT_CNTL__RDERR_INT_MASK) {
- adreno_regread(device, REG_RBBM_READ_ERROR, &rderr);
+ kgsl_regread(device, REG_RBBM_READ_ERROR, &rderr);
source = (rderr & RBBM_READ_ERROR_REQUESTER)
? "host" : "cp";
/* convert to dword address */
@@ -1794,7 +1804,7 @@
}
status &= RBBM_INT_MASK;
- adreno_regwrite(device, REG_RBBM_INT_ACK, status);
+ kgsl_regwrite(device, REG_RBBM_INT_ACK, status);
}
irqreturn_t a2xx_irq_handler(struct adreno_device *adreno_dev)
@@ -1803,7 +1813,7 @@
irqreturn_t result = IRQ_NONE;
unsigned int status;
- adreno_regread(device, REG_MASTER_INT_SIGNAL, &status);
+ kgsl_regread(device, REG_MASTER_INT_SIGNAL, &status);
if (status & MASTER_INT_SIGNAL__MH_INT_STAT) {
kgsl_mh_intrcallback(device);
@@ -1828,14 +1838,14 @@
struct kgsl_device *device = &adreno_dev->dev;
if (state) {
- adreno_regwrite(device, REG_RBBM_INT_CNTL, RBBM_INT_MASK);
- adreno_regwrite(device, REG_CP_INT_CNTL, CP_INT_MASK);
- adreno_regwrite(device, MH_INTERRUPT_MASK,
+ kgsl_regwrite(device, REG_RBBM_INT_CNTL, RBBM_INT_MASK);
+ kgsl_regwrite(device, REG_CP_INT_CNTL, CP_INT_MASK);
+ kgsl_regwrite(device, MH_INTERRUPT_MASK,
kgsl_mmu_get_int_mask());
} else {
- adreno_regwrite(device, REG_RBBM_INT_CNTL, 0);
- adreno_regwrite(device, REG_CP_INT_CNTL, 0);
- adreno_regwrite(device, MH_INTERRUPT_MASK, 0);
+ kgsl_regwrite(device, REG_RBBM_INT_CNTL, 0);
+ kgsl_regwrite(device, REG_CP_INT_CNTL, 0);
+ kgsl_regwrite(device, MH_INTERRUPT_MASK, 0);
}
/* Force the writes to post before touching the IRQ line */
@@ -1847,7 +1857,7 @@
struct kgsl_device *device = &adreno_dev->dev;
unsigned int status;
- adreno_regread(device, REG_MASTER_INT_SIGNAL, &status);
+ kgsl_regread(device, REG_MASTER_INT_SIGNAL, &status);
return (status &
(MASTER_INT_SIGNAL__MH_INT_STAT |
@@ -1929,21 +1939,21 @@
unsigned int reg, val;
/* Freeze the counter */
- adreno_regwrite(device, REG_CP_PERFMON_CNTL,
+ kgsl_regwrite(device, REG_CP_PERFMON_CNTL,
REG_PERF_MODE_CNT | REG_PERF_STATE_FREEZE);
/* Get the value */
- adreno_regread(device, REG_RBBM_PERFCOUNTER1_LO, &val);
+ kgsl_regread(device, REG_RBBM_PERFCOUNTER1_LO, &val);
/* Reset the counter */
- adreno_regwrite(device, REG_CP_PERFMON_CNTL,
+ kgsl_regwrite(device, REG_CP_PERFMON_CNTL,
REG_PERF_MODE_CNT | REG_PERF_STATE_RESET);
/* Re-Enable the performance monitors */
- adreno_regread(device, REG_RBBM_PM_OVERRIDE2, ®);
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, (reg | 0x40));
- adreno_regwrite(device, REG_RBBM_PERFCOUNTER1_SELECT, 0x1);
- adreno_regwrite(device, REG_CP_PERFMON_CNTL,
+ kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, ®);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, (reg | 0x40));
+ kgsl_regwrite(device, REG_RBBM_PERFCOUNTER1_SELECT, 0x1);
+ kgsl_regwrite(device, REG_CP_PERFMON_CNTL,
REG_PERF_MODE_CNT | REG_PERF_STATE_ENABLE);
return val;
@@ -1969,7 +1979,7 @@
/* must be aligned to size */
rb_edram_info.f.edram_range = (adreno_dev->gmem_base >> 14);
- adreno_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val);
+ kgsl_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val);
}
static void a2xx_start(struct adreno_device *adreno_dev)
@@ -1981,8 +1991,8 @@
* before issuing a soft reset. The overrides will then be
* turned off (set to 0)
*/
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe);
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff);
/*
* Only reset CP block if all blocks have previously been
@@ -1990,11 +2000,11 @@
*/
if (!(device->flags & KGSL_FLAGS_SOFT_RESET) ||
!adreno_is_a22x(adreno_dev)) {
- adreno_regwrite(device, REG_RBBM_SOFT_RESET,
+ kgsl_regwrite(device, REG_RBBM_SOFT_RESET,
0xFFFFFFFF);
device->flags |= KGSL_FLAGS_SOFT_RESET;
} else {
- adreno_regwrite(device, REG_RBBM_SOFT_RESET,
+ kgsl_regwrite(device, REG_RBBM_SOFT_RESET,
0x00000001);
}
/*
@@ -2003,11 +2013,11 @@
*/
msleep(30);
- adreno_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000);
+ kgsl_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000);
if (adreno_is_a225(adreno_dev)) {
/* Enable large instruction store for A225 */
- adreno_regwrite(device, REG_SQ_FLOW_CONTROL,
+ kgsl_regwrite(device, REG_SQ_FLOW_CONTROL,
0x18000000);
}
@@ -2015,41 +2025,242 @@
/* For A20X based targets increase number of clocks
* that RBBM will wait before de-asserting Register
* Clock Active signal */
- adreno_regwrite(device, REG_RBBM_CNTL, 0x0000FFFF);
+ kgsl_regwrite(device, REG_RBBM_CNTL, 0x0000FFFF);
else
- adreno_regwrite(device, REG_RBBM_CNTL, 0x00004442);
+ kgsl_regwrite(device, REG_RBBM_CNTL, 0x00004442);
- adreno_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000);
- adreno_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000);
+ kgsl_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000);
+ kgsl_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000);
if (cpu_is_msm8960())
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0x200);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0x200);
else
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0);
if (!adreno_is_a22x(adreno_dev))
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0);
else
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x80);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x80);
- adreno_regwrite(device, REG_RBBM_DEBUG, 0x00080000);
+ kgsl_regwrite(device, REG_RBBM_DEBUG, 0x00080000);
/* Make sure interrupts are disabled */
- adreno_regwrite(device, REG_RBBM_INT_CNTL, 0);
- adreno_regwrite(device, REG_CP_INT_CNTL, 0);
- adreno_regwrite(device, REG_SQ_INT_CNTL, 0);
+ kgsl_regwrite(device, REG_RBBM_INT_CNTL, 0);
+ kgsl_regwrite(device, REG_CP_INT_CNTL, 0);
+ kgsl_regwrite(device, REG_SQ_INT_CNTL, 0);
a2xx_gmeminit(adreno_dev);
}
+static void a2xx_postmortem_dump(struct adreno_device *adreno_dev)
+{
+ unsigned int r1, r2, r3, rbbm_status;
+ unsigned int cp_stat, rb_count;
+ struct kgsl_device *device = &adreno_dev->dev;
+
+ kgsl_regread(device, REG_RBBM_STATUS, &rbbm_status);
+
+ kgsl_regread(device, REG_RBBM_PM_OVERRIDE1, &r2);
+ kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, &r3);
+ KGSL_LOG_DUMP(device,
+ "RBBM: STATUS = %08X | PM_OVERRIDE1 = %08X | PM_OVERRIDE2 = %08X\n",
+ rbbm_status, r2, r3);
+
+ kgsl_regread(device, REG_RBBM_INT_CNTL, &r1);
+ kgsl_regread(device, REG_RBBM_INT_STATUS, &r2);
+ kgsl_regread(device, REG_RBBM_READ_ERROR, &r3);
+ KGSL_LOG_DUMP(device,
+ "INT_CNTL = %08X | INT_STATUS = %08X | READ_ERROR = %08X\n",
+ r1, r2, r3);
+
+ {
+ char cmdFifo[16];
+ struct log_field lines[] = {
+ {rbbm_status & 0x001F, cmdFifo},
+ {rbbm_status & BIT(5), "TC busy "},
+ {rbbm_status & BIT(8), "HIRQ pending"},
+ {rbbm_status & BIT(9), "CPRQ pending"},
+ {rbbm_status & BIT(10), "CFRQ pending"},
+ {rbbm_status & BIT(11), "PFRQ pending"},
+ {rbbm_status & BIT(12), "VGT 0DMA bsy"},
+ {rbbm_status & BIT(14), "RBBM WU busy"},
+ {rbbm_status & BIT(16), "CP NRT busy "},
+ {rbbm_status & BIT(18), "MH busy "},
+ {rbbm_status & BIT(19), "MH chncy bsy"},
+ {rbbm_status & BIT(21), "SX busy "},
+ {rbbm_status & BIT(22), "TPC busy "},
+ {rbbm_status & BIT(24), "SC CNTX busy"},
+ {rbbm_status & BIT(25), "PA busy "},
+ {rbbm_status & BIT(26), "VGT busy "},
+ {rbbm_status & BIT(27), "SQ cntx1 bsy"},
+ {rbbm_status & BIT(28), "SQ cntx0 bsy"},
+ {rbbm_status & BIT(30), "RB busy "},
+ {rbbm_status & BIT(31), "Grphs pp bsy"},
+ };
+ snprintf(cmdFifo, sizeof(cmdFifo), "CMD FIFO=%01X ",
+ rbbm_status & 0xf);
+ adreno_dump_fields(device, " STATUS=", lines,
+ ARRAY_SIZE(lines));
+ }
+
+ kgsl_regread(device, REG_CP_RB_BASE, &r1);
+ kgsl_regread(device, REG_CP_RB_CNTL, &r2);
+ rb_count = 2 << (r2 & (BIT(6)-1));
+ kgsl_regread(device, REG_CP_RB_RPTR_ADDR, &r3);
+ KGSL_LOG_DUMP(device,
+ " RPTR = %08X | WPTR = %08X | RPTR_WR = %08X"
+ "\n", r1, r2, r3);
+
+ kgsl_regread(device, REG_CP_IB1_BASE, &r1);
+ kgsl_regread(device, REG_CP_IB1_BUFSZ, &r2);
+ KGSL_LOG_DUMP(device, "CP_IB1: BASE = %08X | BUFSZ = %d\n", r1, r2);
+
+ kgsl_regread(device, REG_CP_IB2_BASE, &r1);
+ kgsl_regread(device, REG_CP_IB2_BUFSZ, &r2);
+ KGSL_LOG_DUMP(device, "CP_IB2: BASE = %08X | BUFSZ = %d\n", r1, r2);
+
+ kgsl_regread(device, REG_CP_INT_CNTL, &r1);
+ kgsl_regread(device, REG_CP_INT_STATUS, &r2);
+ KGSL_LOG_DUMP(device, "CP_INT: CNTL = %08X | STATUS = %08X\n", r1, r2);
+
+ kgsl_regread(device, REG_CP_ME_CNTL, &r1);
+ kgsl_regread(device, REG_CP_ME_STATUS, &r2);
+ kgsl_regread(device, REG_MASTER_INT_SIGNAL, &r3);
+ KGSL_LOG_DUMP(device,
+ "CP_ME: CNTL = %08X | STATUS = %08X | MSTR_INT_SGNL = "
+ "%08X\n", r1, r2, r3);
+
+ kgsl_regread(device, REG_CP_STAT, &cp_stat);
+ KGSL_LOG_DUMP(device, "CP_STAT = %08X\n", cp_stat);
+#ifndef CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL
+ {
+ struct log_field lns[] = {
+ {cp_stat & BIT(0), "WR_BSY 0"},
+ {cp_stat & BIT(1), "RD_RQ_BSY 1"},
+ {cp_stat & BIT(2), "RD_RTN_BSY 2"},
+ };
+ adreno_dump_fields(device, " MIU=", lns, ARRAY_SIZE(lns));
+ }
+ {
+ struct log_field lns[] = {
+ {cp_stat & BIT(5), "RING_BUSY 5"},
+ {cp_stat & BIT(6), "NDRCTS_BSY 6"},
+ {cp_stat & BIT(7), "NDRCT2_BSY 7"},
+ {cp_stat & BIT(9), "ST_BUSY 9"},
+ {cp_stat & BIT(10), "BUSY 10"},
+ };
+ adreno_dump_fields(device, " CSF=", lns, ARRAY_SIZE(lns));
+ }
+ {
+ struct log_field lns[] = {
+ {cp_stat & BIT(11), "RNG_Q_BSY 11"},
+ {cp_stat & BIT(12), "NDRCTS_Q_B12"},
+ {cp_stat & BIT(13), "NDRCT2_Q_B13"},
+ {cp_stat & BIT(16), "ST_QUEUE_B16"},
+ {cp_stat & BIT(17), "PFP_BUSY 17"},
+ };
+ adreno_dump_fields(device, " RING=", lns, ARRAY_SIZE(lns));
+ }
+ {
+ struct log_field lns[] = {
+ {cp_stat & BIT(3), "RBIU_BUSY 3"},
+ {cp_stat & BIT(4), "RCIU_BUSY 4"},
+ {cp_stat & BIT(18), "MQ_RG_BSY 18"},
+ {cp_stat & BIT(19), "MQ_NDRS_BS19"},
+ {cp_stat & BIT(20), "MQ_NDR2_BS20"},
+ {cp_stat & BIT(21), "MIU_WC_STL21"},
+ {cp_stat & BIT(22), "CP_NRT_BSY22"},
+ {cp_stat & BIT(23), "3D_BUSY 23"},
+ {cp_stat & BIT(26), "ME_BUSY 26"},
+ {cp_stat & BIT(29), "ME_WC_BSY 29"},
+ {cp_stat & BIT(30), "MIU_FF EM 30"},
+ {cp_stat & BIT(31), "CP_BUSY 31"},
+ };
+ adreno_dump_fields(device, " CP_STT=", lns, ARRAY_SIZE(lns));
+ }
+#endif
+
+ kgsl_regread(device, REG_SCRATCH_REG0, &r1);
+ KGSL_LOG_DUMP(device, "SCRATCH_REG0 = %08X\n", r1);
+
+ kgsl_regread(device, REG_COHER_SIZE_PM4, &r1);
+ kgsl_regread(device, REG_COHER_BASE_PM4, &r2);
+ kgsl_regread(device, REG_COHER_STATUS_PM4, &r3);
+ KGSL_LOG_DUMP(device,
+ "COHER: SIZE_PM4 = %08X | BASE_PM4 = %08X | STATUS_PM4"
+ " = %08X\n", r1, r2, r3);
+
+ kgsl_regread(device, MH_AXI_ERROR, &r1);
+ KGSL_LOG_DUMP(device, "MH: AXI_ERROR = %08X\n", r1);
+
+ kgsl_regread(device, MH_MMU_PAGE_FAULT, &r1);
+ kgsl_regread(device, MH_MMU_CONFIG, &r2);
+ kgsl_regread(device, MH_MMU_MPU_BASE, &r3);
+ KGSL_LOG_DUMP(device,
+ "MH_MMU: PAGE_FAULT = %08X | CONFIG = %08X | MPU_BASE ="
+ " %08X\n", r1, r2, r3);
+
+ kgsl_regread(device, MH_MMU_MPU_END, &r1);
+ kgsl_regread(device, MH_MMU_VA_RANGE, &r2);
+ r3 = kgsl_mmu_get_current_ptbase(&device->mmu);
+ KGSL_LOG_DUMP(device,
+ " MPU_END = %08X | VA_RANGE = %08X | PT_BASE ="
+ " %08X\n", r1, r2, r3);
+
+ KGSL_LOG_DUMP(device, "PAGETABLE SIZE: %08X ",
+ kgsl_mmu_get_ptsize(&device->mmu));
+
+ kgsl_regread(device, MH_MMU_TRAN_ERROR, &r1);
+ KGSL_LOG_DUMP(device, " TRAN_ERROR = %08X\n", r1);
+
+ kgsl_regread(device, MH_INTERRUPT_MASK, &r1);
+ kgsl_regread(device, MH_INTERRUPT_STATUS, &r2);
+ KGSL_LOG_DUMP(device,
+ "MH_INTERRUPT: MASK = %08X | STATUS = %08X\n", r1, r2);
+}
+
+/* Register offset defines for A2XX */
+static unsigned int a2xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_DEBUG, REG_CP_DEBUG),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_WADDR, REG_CP_ME_RAM_WADDR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_DATA, REG_CP_ME_RAM_DATA),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_PFP_UCODE_DATA, REG_CP_PFP_UCODE_DATA),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_PFP_UCODE_ADDR, REG_CP_PFP_UCODE_ADDR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE, REG_CP_RB_BASE),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR, REG_CP_RB_RPTR_ADDR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR, REG_CP_RB_RPTR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_WPTR, REG_CP_RB_WPTR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_CNTL, REG_CP_ME_CNTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_CNTL, REG_CP_RB_CNTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_IB1_BASE, REG_CP_IB1_BASE),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_IB1_BUFSZ, REG_CP_IB1_BUFSZ),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_IB2_BASE, REG_CP_IB2_BASE),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_IB2_BUFSZ, REG_CP_IB2_BUFSZ),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_TIMESTAMP, REG_CP_TIMESTAMP),
+ ADRENO_REG_DEFINE(ADRENO_REG_SCRATCH_ADDR, REG_SCRATCH_ADDR),
+ ADRENO_REG_DEFINE(ADRENO_REG_SCRATCH_UMSK, REG_SCRATCH_UMSK),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_STATUS, REG_RBBM_STATUS),
+ ADRENO_REG_DEFINE(ADRENO_REG_PA_SC_AA_CONFIG, REG_PA_SC_AA_CONFIG),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PM_OVERRIDE2, REG_RBBM_PM_OVERRIDE2),
+ ADRENO_REG_DEFINE(ADRENO_REG_SCRATCH_REG2, REG_SCRATCH_REG2),
+ ADRENO_REG_DEFINE(ADRENO_REG_SQ_GPR_MANAGEMENT, REG_SQ_GPR_MANAGEMENT),
+ ADRENO_REG_DEFINE(ADRENO_REG_SQ_INST_STORE_MANAGMENT,
+ REG_SQ_INST_STORE_MANAGMENT),
+ ADRENO_REG_DEFINE(ADRENO_REG_TC_CNTL_STATUS, REG_TC_CNTL_STATUS),
+ ADRENO_REG_DEFINE(ADRENO_REG_TP0_CHICKEN, REG_TP0_CHICKEN),
+};
+
+struct adreno_reg_offsets a2xx_reg_offsets = {
+ .offsets = a2xx_register_offsets,
+ .offset_0 = ADRENO_REG_REGISTER_MAX,
+};
+
/* Defined in adreno_a2xx_snapshot.c */
void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
int *remain, int hang);
struct adreno_gpudev adreno_a2xx_gpudev = {
- .reg_rbbm_status = REG_RBBM_STATUS,
- .reg_cp_pfp_ucode_addr = REG_CP_PFP_UCODE_ADDR,
- .reg_cp_pfp_ucode_data = REG_CP_PFP_UCODE_DATA,
+ .reg_offsets = &a2xx_reg_offsets,
.ctxt_create = a2xx_drawctxt_create,
.ctxt_save = a2xx_drawctxt_save,
@@ -2062,4 +2273,5 @@
.rb_init = a2xx_rb_init,
.busy_cycles = a2xx_busy_cycles,
.start = a2xx_start,
+ .postmortem_dump = a2xx_postmortem_dump,
};
diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c
index 2c86f82..5134ed6 100644
--- a/drivers/gpu/msm/adreno_a2xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a2xx_snapshot.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
@@ -38,11 +38,11 @@
header->size = SXDEBUG_COUNT;
for (i = 0; i < SXDEBUG_COUNT; i++) {
- adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1B00 | i);
- adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1B00 | i);
+ kgsl_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
}
- adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
return DEBUG_SECTION_SZ(SXDEBUG_COUNT);
}
@@ -65,11 +65,11 @@
header->size = CPDEBUG_COUNT;
for (i = 0; i < CPDEBUG_COUNT; i++) {
- adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1628);
- adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1628);
+ kgsl_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
}
- adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
return DEBUG_SECTION_SZ(CPDEBUG_COUNT);
}
@@ -82,7 +82,8 @@
#define SQ_DEBUG_WRITE(_device, _reg, _data, _offset) \
do { _data[(_offset)++] = (_reg); \
- adreno_regread(_device, (_reg), &_data[(_offset)++]); } while (0)
+ kgsl_regread(_device, (_reg), &_data[(_offset)++]); \
+ } while (0)
#define SQ_DEBUG_BANK_SIZE 23
@@ -175,7 +176,7 @@
header->size = size;
for (i = 0; i < 16; i++) {
- adreno_regwrite(device, REG_SQ_DEBUG_TB_STATUS_SEL,
+ kgsl_regwrite(device, REG_SQ_DEBUG_TB_STATUS_SEL,
i | (6<<4) | (i<<7) | (1<<11) | (1<<12)
| (i<<16) | (6<<20) | (i<<23));
SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM,
@@ -215,11 +216,11 @@
header->size = MIUDEBUG_COUNT;
for (i = 0; i < MIUDEBUG_COUNT; i++) {
- adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1600 | i);
- adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1600 | i);
+ kgsl_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
}
- adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
return DEBUG_SECTION_SZ(MIUDEBUG_COUNT);
}
@@ -297,8 +298,8 @@
* work
*/
- adreno_regread(device, REG_RBBM_PM_OVERRIDE2, &pmoverride);
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xFF);
+ kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, &pmoverride);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xFF);
/* SX debug registers */
snapshot = kgsl_snapshot_add_section(device,
@@ -376,7 +377,7 @@
/* Reset the clock gating */
- adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride);
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride);
return snapshot;
}
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index a757a22..670f0cf 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2321,7 +2321,7 @@
tmp_ctx.gmem_base = adreno_dev->gmem_base;
result = kgsl_allocate(&drawctxt->context_gmem_shadow.gmemshadow,
- drawctxt->pagetable, drawctxt->context_gmem_shadow.size);
+ drawctxt->base.pagetable, drawctxt->context_gmem_shadow.size);
if (result)
return result;
@@ -2355,7 +2355,7 @@
*/
ret = kgsl_allocate(&drawctxt->gpustate,
- drawctxt->pagetable, CONTEXT_SIZE);
+ drawctxt->base.pagetable, CONTEXT_SIZE);
if (ret)
return ret;
@@ -2420,7 +2420,7 @@
* already be saved.)
*/
- kgsl_cffdump_syncmem(context->dev_priv,
+ kgsl_cffdump_syncmem(context->base.device,
&context->gpustate,
context->context_gmem_shadow.gmem_save[1],
context->context_gmem_shadow.gmem_save[2] << 2, true);
@@ -2441,8 +2441,17 @@
if (context == NULL) {
/* No context - set the default pagetable and thats it */
+ unsigned int id;
+ /*
+ * If there isn't a current context, the kgsl_mmu_setstate
+ * will use the CPU path so we don't need to give
+ * it a valid context id.
+ */
+ id = (adreno_dev->drawctxt_active != NULL)
+ ? adreno_dev->drawctxt_active->base.id
+ : KGSL_CONTEXT_INVALID;
kgsl_mmu_setstate(&device->mmu, device->mmu.defaultpagetable,
- adreno_dev->drawctxt_active->id);
+ id);
return;
}
@@ -2451,10 +2460,11 @@
cmds[2] = cp_type3_packet(CP_MEM_WRITE, 2);
cmds[3] = device->memstore.gpuaddr +
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context);
- cmds[4] = context->id;
+ cmds[4] = context->base.id;
adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE,
cmds, 5);
- kgsl_mmu_setstate(&device->mmu, context->pagetable, context->id);
+ kgsl_mmu_setstate(&device->mmu, context->base.pagetable,
+ context->base.id);
/*
* Restore GMEM. (note: changes shader.
@@ -2462,7 +2472,7 @@
*/
if (context->flags & CTXT_FLAGS_GMEM_RESTORE) {
- kgsl_cffdump_syncmem(context->dev_priv,
+ kgsl_cffdump_syncmem(context->base.device,
&context->gpustate,
context->context_gmem_shadow.gmem_restore[1],
context->context_gmem_shadow.gmem_restore[2] << 2,
@@ -2545,7 +2555,7 @@
case A3XX_INT_RBBM_AHB_ERROR: {
unsigned int reg;
- adreno_regread(device, A3XX_RBBM_AHB_ERROR_STATUS, ®);
+ kgsl_regread(device, A3XX_RBBM_AHB_ERROR_STATUS, ®);
/*
* Return the word address of the erroring register so that it
@@ -2559,7 +2569,7 @@
(reg >> 24) & 0x3);
/* Clear the error */
- adreno_regwrite(device, A3XX_RBBM_AHB_CMD, (1 << 3));
+ kgsl_regwrite(device, A3XX_RBBM_AHB_CMD, (1 << 3));
return;
}
case A3XX_INT_RBBM_REG_TIMEOUT:
@@ -2738,21 +2748,21 @@
if (countable > 1)
return;
- adreno_regread(device, A3XX_RBBM_RBBM_CTL, &in);
+ kgsl_regread(device, A3XX_RBBM_RBBM_CTL, &in);
if (countable == 0)
out = in | RBBM_RBBM_CTL_RESET_PWR_CTR0;
else
out = in | RBBM_RBBM_CTL_RESET_PWR_CTR1;
- adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, out);
+ kgsl_regwrite(device, A3XX_RBBM_RBBM_CTL, out);
if (countable == 0)
out = in | RBBM_RBBM_CTL_ENABLE_PWR_CTR0;
else
out = in | RBBM_RBBM_CTL_ENABLE_PWR_CTR1;
- adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, out);
+ kgsl_regwrite(device, A3XX_RBBM_RBBM_CTL, out);
return;
}
@@ -2766,8 +2776,8 @@
if (counter > 1 || countable > 0x7f)
return;
- adreno_regread(device, A3XX_VBIF_PERF_CNT_EN, &in);
- adreno_regread(device, A3XX_VBIF_PERF_CNT_SEL, &sel);
+ kgsl_regread(device, A3XX_VBIF_PERF_CNT_EN, &in);
+ kgsl_regread(device, A3XX_VBIF_PERF_CNT_SEL, &sel);
if (counter == 0) {
bit = VBIF_PERF_CNT_0;
@@ -2780,12 +2790,12 @@
out = in | bit;
- adreno_regwrite(device, A3XX_VBIF_PERF_CNT_SEL, sel);
+ kgsl_regwrite(device, A3XX_VBIF_PERF_CNT_SEL, sel);
- adreno_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, bit);
- adreno_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, 0);
+ kgsl_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, bit);
+ kgsl_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, 0);
- adreno_regwrite(device, A3XX_VBIF_PERF_CNT_EN, out);
+ kgsl_regwrite(device, A3XX_VBIF_PERF_CNT_EN, out);
}
static void a3xx_perfcounter_enable_vbif_pwr(struct kgsl_device *device,
@@ -2796,7 +2806,7 @@
if (countable > 2)
return;
- adreno_regread(device, A3XX_VBIF_PERF_CNT_EN, &in);
+ kgsl_regread(device, A3XX_VBIF_PERF_CNT_EN, &in);
if (countable == 0)
bit = VBIF_PERF_PWR_CNT_0;
else if (countable == 1)
@@ -2806,10 +2816,10 @@
out = in | bit;
- adreno_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, bit);
- adreno_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, 0);
+ kgsl_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, bit);
+ kgsl_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, 0);
- adreno_regwrite(device, A3XX_VBIF_PERF_CNT_EN, out);
+ kgsl_regwrite(device, A3XX_VBIF_PERF_CNT_EN, out);
}
/*
@@ -2846,14 +2856,14 @@
reg = &(a3xx_perfcounter_reglist[group].regs[counter]);
/* Select the desired perfcounter */
- adreno_regwrite(device, reg->select, countable);
+ kgsl_regwrite(device, reg->select, countable);
if (reg->load_bit < 32) {
val = 1 << reg->load_bit;
- adreno_regwrite(device, A3XX_RBBM_PERFCTR_LOAD_CMD0, val);
+ kgsl_regwrite(device, A3XX_RBBM_PERFCTR_LOAD_CMD0, val);
} else {
val = 1 << (reg->load_bit - 32);
- adreno_regwrite(device, A3XX_RBBM_PERFCTR_LOAD_CMD1, val);
+ kgsl_regwrite(device, A3XX_RBBM_PERFCTR_LOAD_CMD1, val);
}
}
@@ -2875,17 +2885,17 @@
reg = &(a3xx_perfcounter_reglist[group].regs[counter]);
/* Freeze the counter */
- adreno_regread(device, A3XX_RBBM_PERFCTR_CTL, &val);
+ kgsl_regread(device, A3XX_RBBM_PERFCTR_CTL, &val);
val &= ~reg->load_bit;
- adreno_regwrite(device, A3XX_RBBM_PERFCTR_CTL, val);
+ kgsl_regwrite(device, A3XX_RBBM_PERFCTR_CTL, val);
/* Read the values */
- adreno_regread(device, offset, &lo);
- adreno_regread(device, offset + 1, &hi);
+ kgsl_regread(device, offset, &lo);
+ kgsl_regread(device, offset + 1, &hi);
/* Re-Enable the counter */
val |= reg->load_bit;
- adreno_regwrite(device, A3XX_RBBM_PERFCTR_CTL, val);
+ kgsl_regwrite(device, A3XX_RBBM_PERFCTR_CTL, val);
return (((uint64_t) hi) << 32) | lo;
}
@@ -2945,7 +2955,7 @@
unsigned int status, tmp;
int i;
- adreno_regread(&adreno_dev->dev, A3XX_RBBM_INT_0_STATUS, &status);
+ kgsl_regread(&adreno_dev->dev, A3XX_RBBM_INT_0_STATUS, &status);
for (tmp = status, i = 0; tmp && i < ARRAY_SIZE(a3xx_irq_funcs); i++) {
if (tmp & 1) {
@@ -2964,7 +2974,7 @@
trace_kgsl_a3xx_irq_status(device, status);
if (status)
- adreno_regwrite(&adreno_dev->dev, A3XX_RBBM_INT_CLEAR_CMD,
+ kgsl_regwrite(&adreno_dev->dev, A3XX_RBBM_INT_CLEAR_CMD,
status);
return ret;
}
@@ -2974,16 +2984,16 @@
struct kgsl_device *device = &adreno_dev->dev;
if (state)
- adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK);
+ kgsl_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK);
else
- adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, 0);
+ kgsl_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);
+ kgsl_regread(&adreno_dev->dev, A3XX_RBBM_INT_0_STATUS, &status);
return (status & A3XX_INT_MASK) ? 1 : 0;
}
@@ -2995,7 +3005,7 @@
unsigned int ret = 0;
/* Read the value */
- adreno_regread(device, A3XX_RBBM_PERFCTR_PWR_1_LO, &val);
+ kgsl_regread(device, A3XX_RBBM_PERFCTR_PWR_1_LO, &val);
/* Return 0 for the first read */
if (adreno_dev->gpu_cycles != 0) {
@@ -3136,215 +3146,6 @@
{ adreno_is_a305b, a305b_vbif },
};
-static void a3xx_perfcounter_init(struct adreno_device *adreno_dev)
-{
- /*
- * Set SP to count SP_ALU_ACTIVE_CYCLES, it includes
- * all ALU instruction execution regardless precision or shader ID.
- * Set SP to count SP0_ICL1_MISSES, It counts
- * USP L1 instruction miss request.
- * Set SP to count SP_FS_FULL_ALU_INSTRUCTIONS, it
- * counts USP flow control instruction execution.
- * we will use this to augment our hang detection
- */
- if (adreno_dev->fast_hang_detect) {
- adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP,
- SP_ALU_ACTIVE_CYCLES, &ft_detect_regs[6],
- PERFCOUNTER_FLAG_KERNEL);
- ft_detect_regs[7] = ft_detect_regs[6] + 1;
- adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP,
- SP0_ICL1_MISSES, &ft_detect_regs[8],
- PERFCOUNTER_FLAG_KERNEL);
- ft_detect_regs[9] = ft_detect_regs[8] + 1;
- adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP,
- SP_FS_CFLOW_INSTRUCTIONS, &ft_detect_regs[10],
- PERFCOUNTER_FLAG_KERNEL);
- ft_detect_regs[11] = ft_detect_regs[10] + 1;
- }
-
- adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP,
- SP_FS_FULL_ALU_INSTRUCTIONS, NULL, PERFCOUNTER_FLAG_KERNEL);
-
- /* Reserve and start countable 1 in the PWR perfcounter group */
- adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_PWR, 1,
- NULL, PERFCOUNTER_FLAG_KERNEL);
-}
-
-static void a3xx_start(struct adreno_device *adreno_dev)
-{
- struct kgsl_device *device = &adreno_dev->dev;
- struct a3xx_vbif_data *vbif = NULL;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(a3xx_vbif_platforms); i++) {
- if (a3xx_vbif_platforms[i].devfunc(adreno_dev)) {
- vbif = a3xx_vbif_platforms[i].vbif;
- break;
- }
- }
-
- BUG_ON(vbif == NULL);
-
- while (vbif->reg != 0) {
- adreno_regwrite(device, vbif->reg, vbif->val);
- vbif++;
- }
-
- /* Make all blocks contribute to the GPU BUSY perf counter */
- adreno_regwrite(device, A3XX_RBBM_GPU_BUSY_MASKED, 0xFFFFFFFF);
-
- /* Tune the hystersis counters for SP and CP idle detection */
- adreno_regwrite(device, A3XX_RBBM_SP_HYST_CNT, 0x10);
- adreno_regwrite(device, A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10);
-
- /* Enable the RBBM error reporting bits. This lets us get
- useful information on failure */
-
- adreno_regwrite(device, A3XX_RBBM_AHB_CTL0, 0x00000001);
-
- /* Enable AHB error reporting */
- adreno_regwrite(device, A3XX_RBBM_AHB_CTL1, 0xA6FFFFFF);
-
- /* Turn on the power counters */
- adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, 0x00030000);
-
- /* Turn on hang detection - this spews a lot of useful information
- * into the RBBM registers on a hang */
-
- adreno_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
- (1 << 16) | 0xFFF);
-
- /* Enable 64-byte cacheline size. HW Default is 32-byte (0x000000E0). */
- adreno_regwrite(device, A3XX_UCHE_CACHE_MODE_CONTROL_REG, 0x00000001);
-
- /* Enable Clock gating */
- adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
- adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
-
- if (adreno_is_a330v2(adreno_dev))
- adreno_regwrite(device, A3XX_RBBM_GPR0_CTL,
- A330v2_RBBM_GPR0_CTL_DEFAULT);
- else if (adreno_is_a330(adreno_dev))
- adreno_regwrite(device, A3XX_RBBM_GPR0_CTL,
- A330_RBBM_GPR0_CTL_DEFAULT);
-
- /* Set the OCMEM base address for A330 */
- if (adreno_is_a330(adreno_dev) ||
- adreno_is_a305b(adreno_dev)) {
- adreno_regwrite(device, A3XX_RB_GMEM_BASE_ADDR,
- (unsigned int)(adreno_dev->ocmem_base >> 14));
- }
-
- /* Turn on performance counters */
- adreno_regwrite(device, A3XX_RBBM_PERFCTR_CTL, 0x01);
-
- /* Turn on the GPU busy counter and let it run free */
-
- adreno_dev->gpu_cycles = 0;
-}
-
-/**
- * a3xx_coresight_enable() - Enables debugging through coresight
- * debug bus for adreno a3xx devices.
- * @device: Pointer to GPU device structure
- */
-int a3xx_coresight_enable(struct kgsl_device *device)
-{
- mutex_lock(&device->mutex);
- if (!kgsl_active_count_get(device)) {
- adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, 0x0001093F);
- adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL0,
- 0x00000000);
- adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL1,
- 0xFFFFFFFE);
- adreno_regwrite(device, A3XX_RBBM_INT_TRACE_BUS_CTL,
- 0x00201111);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_BUS_CTL,
- 0x89100010);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_STOP_CNT,
- 0x00017fff);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_START_CNT,
- 0x0001000f);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT ,
- 0x0001ffff);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_CMD,
- 0x00000001);
- kgsl_active_count_put(device);
- }
- mutex_unlock(&device->mutex);
- return 0;
-}
-
-/**
- * a3xx_coresight_disable() - Disables debugging through coresight
- * debug bus for adreno a3xx devices.
- * @device: Pointer to GPU device structure
- */
-void a3xx_coresight_disable(struct kgsl_device *device)
-{
- mutex_lock(&device->mutex);
- if (!kgsl_active_count_get(device)) {
- adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, 0x0);
- adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL0, 0x0);
- adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL1, 0x0);
- adreno_regwrite(device, A3XX_RBBM_INT_TRACE_BUS_CTL, 0x0);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_BUS_CTL, 0x0);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_STOP_CNT, 0x0);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_START_CNT, 0x0);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT , 0x0);
- adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_CMD, 0x0);
- kgsl_active_count_put(device);
- }
- mutex_unlock(&device->mutex);
-}
-
-static void a3xx_coresight_write_reg(struct kgsl_device *device,
- unsigned int wordoffset, unsigned int val)
-{
- mutex_lock(&device->mutex);
- if (!kgsl_active_count_get(device)) {
- adreno_regwrite(device, wordoffset, val);
- kgsl_active_count_put(device);
- }
- mutex_unlock(&device->mutex);
-}
-
-void a3xx_coresight_config_debug_reg(struct kgsl_device *device,
- int debug_reg, unsigned int val)
-{
- switch (debug_reg) {
-
- case DEBUG_BUS_CTL:
- a3xx_coresight_write_reg(device, A3XX_RBBM_DEBUG_BUS_CTL, val);
- break;
-
- case TRACE_STOP_CNT:
- a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_STOP_CNT,
- val);
- break;
-
- case TRACE_START_CNT:
- a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_START_CNT,
- val);
- break;
-
- case TRACE_PERIOD_CNT:
- a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT,
- val);
- break;
-
- case TRACE_CMD:
- a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_CMD, val);
- break;
-
- case TRACE_BUS_CTL:
- a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_BUS_CTL,
- val);
- break;
- }
-
-}
-
/*
* Define the available perfcounter groups - these get used by
* adreno_perfcounter_get and adreno_perfcounter_put
@@ -3467,6 +3268,219 @@
ARRAY_SIZE(a3xx_perfcounter_groups),
};
+static void a3xx_perfcounter_init(struct adreno_device *adreno_dev)
+{
+ /* SP[3] counter is broken on a330 so disable it if a330 device */
+ if (adreno_is_a330(adreno_dev))
+ a3xx_perfcounters_sp[3].countable = KGSL_PERFCOUNTER_BROKEN;
+
+ /*
+ * Set SP to count SP_ALU_ACTIVE_CYCLES, it includes
+ * all ALU instruction execution regardless precision or shader ID.
+ * Set SP to count SP0_ICL1_MISSES, It counts
+ * USP L1 instruction miss request.
+ * Set SP to count SP_FS_FULL_ALU_INSTRUCTIONS, it
+ * counts USP flow control instruction execution.
+ * we will use this to augment our hang detection
+ */
+ if (adreno_dev->fast_hang_detect) {
+ adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP,
+ SP_ALU_ACTIVE_CYCLES, &ft_detect_regs[6],
+ PERFCOUNTER_FLAG_KERNEL);
+ ft_detect_regs[7] = ft_detect_regs[6] + 1;
+ adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP,
+ SP0_ICL1_MISSES, &ft_detect_regs[8],
+ PERFCOUNTER_FLAG_KERNEL);
+ ft_detect_regs[9] = ft_detect_regs[8] + 1;
+ adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP,
+ SP_FS_CFLOW_INSTRUCTIONS, &ft_detect_regs[10],
+ PERFCOUNTER_FLAG_KERNEL);
+ ft_detect_regs[11] = ft_detect_regs[10] + 1;
+ }
+
+ adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP,
+ SP_FS_FULL_ALU_INSTRUCTIONS, NULL, PERFCOUNTER_FLAG_KERNEL);
+
+ /* Reserve and start countable 1 in the PWR perfcounter group */
+ adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_PWR, 1,
+ NULL, PERFCOUNTER_FLAG_KERNEL);
+}
+
+static void a3xx_start(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ struct a3xx_vbif_data *vbif = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(a3xx_vbif_platforms); i++) {
+ if (a3xx_vbif_platforms[i].devfunc(adreno_dev)) {
+ vbif = a3xx_vbif_platforms[i].vbif;
+ break;
+ }
+ }
+
+ BUG_ON(vbif == NULL);
+
+ while (vbif->reg != 0) {
+ kgsl_regwrite(device, vbif->reg, vbif->val);
+ vbif++;
+ }
+
+ /* Make all blocks contribute to the GPU BUSY perf counter */
+ kgsl_regwrite(device, A3XX_RBBM_GPU_BUSY_MASKED, 0xFFFFFFFF);
+
+ /* Tune the hystersis counters for SP and CP idle detection */
+ kgsl_regwrite(device, A3XX_RBBM_SP_HYST_CNT, 0x10);
+ kgsl_regwrite(device, A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10);
+
+ /* Enable the RBBM error reporting bits. This lets us get
+ useful information on failure */
+
+ kgsl_regwrite(device, A3XX_RBBM_AHB_CTL0, 0x00000001);
+
+ /* Enable AHB error reporting */
+ kgsl_regwrite(device, A3XX_RBBM_AHB_CTL1, 0xA6FFFFFF);
+
+ /* Turn on the power counters */
+ kgsl_regwrite(device, A3XX_RBBM_RBBM_CTL, 0x00030000);
+
+ /* Turn on hang detection - this spews a lot of useful information
+ * into the RBBM registers on a hang */
+
+ kgsl_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
+ (1 << 16) | 0xFFF);
+
+ /* Enable 64-byte cacheline size. HW Default is 32-byte (0x000000E0). */
+ kgsl_regwrite(device, A3XX_UCHE_CACHE_MODE_CONTROL_REG, 0x00000001);
+
+ /* Enable Clock gating */
+ kgsl_regwrite(device, A3XX_RBBM_CLOCK_CTL,
+ adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
+
+ if (adreno_is_a330v2(adreno_dev))
+ kgsl_regwrite(device, A3XX_RBBM_GPR0_CTL,
+ A330v2_RBBM_GPR0_CTL_DEFAULT);
+ else if (adreno_is_a330(adreno_dev))
+ kgsl_regwrite(device, A3XX_RBBM_GPR0_CTL,
+ A330_RBBM_GPR0_CTL_DEFAULT);
+
+ /* Set the OCMEM base address for A330 */
+ if (adreno_is_a330(adreno_dev) ||
+ adreno_is_a305b(adreno_dev)) {
+ kgsl_regwrite(device, A3XX_RB_GMEM_BASE_ADDR,
+ (unsigned int)(adreno_dev->ocmem_base >> 14));
+ }
+
+ /* Turn on performance counters */
+ kgsl_regwrite(device, A3XX_RBBM_PERFCTR_CTL, 0x01);
+
+ /* Turn on the GPU busy counter and let it run free */
+
+ adreno_dev->gpu_cycles = 0;
+}
+
+/**
+ * a3xx_coresight_enable() - Enables debugging through coresight
+ * debug bus for adreno a3xx devices.
+ * @device: Pointer to GPU device structure
+ */
+int a3xx_coresight_enable(struct kgsl_device *device)
+{
+ mutex_lock(&device->mutex);
+ if (!kgsl_active_count_get(device)) {
+ kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, 0x0001093F);
+ kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL0,
+ 0x00000000);
+ kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL1,
+ 0xFFFFFFFE);
+ kgsl_regwrite(device, A3XX_RBBM_INT_TRACE_BUS_CTL,
+ 0x00201111);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_BUS_CTL,
+ 0x89100010);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_STOP_CNT,
+ 0x00017fff);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_START_CNT,
+ 0x0001000f);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT ,
+ 0x0001ffff);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_CMD,
+ 0x00000001);
+ kgsl_active_count_put(device);
+ }
+ mutex_unlock(&device->mutex);
+ return 0;
+}
+
+/**
+ * a3xx_coresight_disable() - Disables debugging through coresight
+ * debug bus for adreno a3xx devices.
+ * @device: Pointer to GPU device structure
+ */
+void a3xx_coresight_disable(struct kgsl_device *device)
+{
+ mutex_lock(&device->mutex);
+ if (!kgsl_active_count_get(device)) {
+ kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, 0x0);
+ kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL0, 0x0);
+ kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL1, 0x0);
+ kgsl_regwrite(device, A3XX_RBBM_INT_TRACE_BUS_CTL, 0x0);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_BUS_CTL, 0x0);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_STOP_CNT, 0x0);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_START_CNT, 0x0);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT , 0x0);
+ kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_CMD, 0x0);
+ kgsl_active_count_put(device);
+ }
+ mutex_unlock(&device->mutex);
+}
+
+static void a3xx_coresight_write_reg(struct kgsl_device *device,
+ unsigned int wordoffset, unsigned int val)
+{
+ mutex_lock(&device->mutex);
+ if (!kgsl_active_count_get(device)) {
+ kgsl_regwrite(device, wordoffset, val);
+ kgsl_active_count_put(device);
+ }
+ mutex_unlock(&device->mutex);
+}
+
+void a3xx_coresight_config_debug_reg(struct kgsl_device *device,
+ int debug_reg, unsigned int val)
+{
+ switch (debug_reg) {
+
+ case DEBUG_BUS_CTL:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_DEBUG_BUS_CTL, val);
+ break;
+
+ case TRACE_STOP_CNT:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_STOP_CNT,
+ val);
+ break;
+
+ case TRACE_START_CNT:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_START_CNT,
+ val);
+ break;
+
+ case TRACE_PERIOD_CNT:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT,
+ val);
+ break;
+
+ case TRACE_CMD:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_CMD, val);
+ break;
+
+ case TRACE_BUS_CTL:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_BUS_CTL,
+ val);
+ break;
+ }
+
+}
+
/*
* a3xx_soft_reset() - Soft reset GPU
* @adreno_dev: Pointer to adreno device
@@ -3482,23 +3496,246 @@
struct kgsl_device *device = &adreno_dev->dev;
unsigned int reg;
- adreno_regwrite(device, A3XX_RBBM_SW_RESET_CMD, 1);
+ kgsl_regwrite(device, A3XX_RBBM_SW_RESET_CMD, 1);
/*
* Do a dummy read to get a brief read cycle delay for the reset to take
* effect
*/
- adreno_regread(device, A3XX_RBBM_SW_RESET_CMD, ®);
- adreno_regwrite(device, A3XX_RBBM_SW_RESET_CMD, 0);
+ kgsl_regread(device, A3XX_RBBM_SW_RESET_CMD, ®);
+ kgsl_regwrite(device, A3XX_RBBM_SW_RESET_CMD, 0);
}
/* Defined in adreno_a3xx_snapshot.c */
void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
int *remain, int hang);
+static void a3xx_postmortem_dump(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ unsigned int r1, r2, r3, rbbm_status;
+ unsigned int cp_stat, rb_count;
+
+ kgsl_regread(device, REG_RBBM_STATUS, &rbbm_status);
+ KGSL_LOG_DUMP(device, "RBBM: STATUS = %08X\n", rbbm_status);
+
+ {
+ struct log_field lines[] = {
+ {rbbm_status & BIT(0), "HI busy "},
+ {rbbm_status & BIT(1), "CP ME busy "},
+ {rbbm_status & BIT(2), "CP PFP busy "},
+ {rbbm_status & BIT(14), "CP NRT busy "},
+ {rbbm_status & BIT(15), "VBIF busy "},
+ {rbbm_status & BIT(16), "TSE busy "},
+ {rbbm_status & BIT(17), "RAS busy "},
+ {rbbm_status & BIT(18), "RB busy "},
+ {rbbm_status & BIT(19), "PC DCALL bsy"},
+ {rbbm_status & BIT(20), "PC VSD busy "},
+ {rbbm_status & BIT(21), "VFD busy "},
+ {rbbm_status & BIT(22), "VPC busy "},
+ {rbbm_status & BIT(23), "UCHE busy "},
+ {rbbm_status & BIT(24), "SP busy "},
+ {rbbm_status & BIT(25), "TPL1 busy "},
+ {rbbm_status & BIT(26), "MARB busy "},
+ {rbbm_status & BIT(27), "VSC busy "},
+ {rbbm_status & BIT(28), "ARB busy "},
+ {rbbm_status & BIT(29), "HLSQ busy "},
+ {rbbm_status & BIT(30), "GPU bsy noHC"},
+ {rbbm_status & BIT(31), "GPU busy "},
+ };
+ adreno_dump_fields(device, " STATUS=", lines,
+ ARRAY_SIZE(lines));
+ }
+
+ kgsl_regread(device, REG_CP_RB_BASE, &r1);
+ kgsl_regread(device, REG_CP_RB_CNTL, &r2);
+ rb_count = 2 << (r2 & (BIT(6) - 1));
+ kgsl_regread(device, REG_CP_RB_RPTR_ADDR, &r3);
+ KGSL_LOG_DUMP(device,
+ "CP_RB: BASE = %08X | CNTL = %08X | RPTR_ADDR = %08X"
+ "| rb_count = %08X\n", r1, r2, r3, rb_count);
+
+ kgsl_regread(device, REG_CP_RB_RPTR, &r1);
+ kgsl_regread(device, REG_CP_RB_WPTR, &r2);
+ kgsl_regread(device, REG_CP_RB_RPTR_WR, &r3);
+ KGSL_LOG_DUMP(device,
+ "CP_RB: BASE = %08X | CNTL = %08X | RPTR_ADDR = %08X"
+ "| rb_count = %08X\n", r1, r2, r3, rb_count);
+
+ kgsl_regread(device, REG_CP_RB_RPTR, &r1);
+ kgsl_regread(device, REG_CP_RB_WPTR, &r2);
+ kgsl_regread(device, REG_CP_RB_RPTR_WR, &r3);
+ KGSL_LOG_DUMP(device,
+ " RPTR = %08X | WPTR = %08X | RPTR_WR = %08X"
+ "\n", r1, r2, r3);
+
+ kgsl_regread(device, REG_CP_IB1_BASE, &r1);
+ kgsl_regread(device, REG_CP_IB1_BUFSZ, &r2);
+ KGSL_LOG_DUMP(device, "CP_IB1: BASE = %08X | BUFSZ = %d\n", r1, r2);
+
+ kgsl_regread(device, REG_CP_ME_CNTL, &r1);
+ kgsl_regread(device, REG_CP_ME_STATUS, &r2);
+ KGSL_LOG_DUMP(device, "CP_ME: CNTL = %08X | STATUS = %08X\n", r1, r2);
+
+ kgsl_regread(device, REG_CP_STAT, &cp_stat);
+ KGSL_LOG_DUMP(device, "CP_STAT = %08X\n", cp_stat);
+#ifndef CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL
+ {
+ struct log_field lns[] = {
+ {cp_stat & BIT(0), "WR_BSY 0"},
+ {cp_stat & BIT(1), "RD_RQ_BSY 1"},
+ {cp_stat & BIT(2), "RD_RTN_BSY 2"},
+ };
+ adreno_dump_fields(device, " MIU=", lns, ARRAY_SIZE(lns));
+ }
+ {
+ struct log_field lns[] = {
+ {cp_stat & BIT(5), "RING_BUSY 5"},
+ {cp_stat & BIT(6), "NDRCTS_BSY 6"},
+ {cp_stat & BIT(7), "NDRCT2_BSY 7"},
+ {cp_stat & BIT(9), "ST_BUSY 9"},
+ {cp_stat & BIT(10), "BUSY 10"},
+ };
+ adreno_dump_fields(device, " CSF=", lns, ARRAY_SIZE(lns));
+ }
+ {
+ struct log_field lns[] = {
+ {cp_stat & BIT(11), "RNG_Q_BSY 11"},
+ {cp_stat & BIT(12), "NDRCTS_Q_B12"},
+ {cp_stat & BIT(13), "NDRCT2_Q_B13"},
+ {cp_stat & BIT(16), "ST_QUEUE_B16"},
+ {cp_stat & BIT(17), "PFP_BUSY 17"},
+ };
+ adreno_dump_fields(device, " RING=", lns, ARRAY_SIZE(lns));
+ }
+ {
+ struct log_field lns[] = {
+ {cp_stat & BIT(3), "RBIU_BUSY 3"},
+ {cp_stat & BIT(4), "RCIU_BUSY 4"},
+ {cp_stat & BIT(8), "EVENT_BUSY 8"},
+ {cp_stat & BIT(18), "MQ_RG_BSY 18"},
+ {cp_stat & BIT(19), "MQ_NDRS_BS19"},
+ {cp_stat & BIT(20), "MQ_NDR2_BS20"},
+ {cp_stat & BIT(21), "MIU_WC_STL21"},
+ {cp_stat & BIT(22), "CP_NRT_BSY22"},
+ {cp_stat & BIT(23), "3D_BUSY 23"},
+ {cp_stat & BIT(26), "ME_BUSY 26"},
+ {cp_stat & BIT(27), "RB_FFO_BSY27"},
+ {cp_stat & BIT(28), "CF_FFO_BSY28"},
+ {cp_stat & BIT(29), "PS_FFO_BSY29"},
+ {cp_stat & BIT(30), "VS_FFO_BSY30"},
+ {cp_stat & BIT(31), "CP_BUSY 31"},
+ };
+ adreno_dump_fields(device, " CP_STT=", lns, ARRAY_SIZE(lns));
+ }
+#endif
+
+ kgsl_regread(device, A3XX_RBBM_INT_0_STATUS, &r1);
+ KGSL_LOG_DUMP(device, "MSTR_INT_SGNL = %08X\n", r1);
+ {
+ struct log_field ints[] = {
+ {r1 & BIT(0), "RBBM_GPU_IDLE 0"},
+ {r1 & BIT(1), "RBBM_AHB_ERROR 1"},
+ {r1 & BIT(2), "RBBM_REG_TIMEOUT 2"},
+ {r1 & BIT(3), "RBBM_ME_MS_TIMEOUT 3"},
+ {r1 & BIT(4), "RBBM_PFP_MS_TIMEOUT 4"},
+ {r1 & BIT(5), "RBBM_ATB_BUS_OVERFLOW 5"},
+ {r1 & BIT(6), "VFD_ERROR 6"},
+ {r1 & BIT(7), "CP_SW_INT 7"},
+ {r1 & BIT(8), "CP_T0_PACKET_IN_IB 8"},
+ {r1 & BIT(9), "CP_OPCODE_ERROR 9"},
+ {r1 & BIT(10), "CP_RESERVED_BIT_ERROR 10"},
+ {r1 & BIT(11), "CP_HW_FAULT 11"},
+ {r1 & BIT(12), "CP_DMA 12"},
+ {r1 & BIT(13), "CP_IB2_INT 13"},
+ {r1 & BIT(14), "CP_IB1_INT 14"},
+ {r1 & BIT(15), "CP_RB_INT 15"},
+ {r1 & BIT(16), "CP_REG_PROTECT_FAULT 16"},
+ {r1 & BIT(17), "CP_RB_DONE_TS 17"},
+ {r1 & BIT(18), "CP_VS_DONE_TS 18"},
+ {r1 & BIT(19), "CP_PS_DONE_TS 19"},
+ {r1 & BIT(20), "CACHE_FLUSH_TS 20"},
+ {r1 & BIT(21), "CP_AHB_ERROR_HALT 21"},
+ {r1 & BIT(24), "MISC_HANG_DETECT 24"},
+ {r1 & BIT(25), "UCHE_OOB_ACCESS 25"},
+ };
+ adreno_dump_fields(device, "INT_SGNL=", ints, ARRAY_SIZE(ints));
+ }
+}
+
+/* Register offset defines for A3XX */
+static unsigned int a3xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_DEBUG, REG_CP_DEBUG),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_WADDR, REG_CP_ME_RAM_WADDR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_DATA, REG_CP_ME_RAM_DATA),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_PFP_UCODE_DATA, A3XX_CP_PFP_UCODE_DATA),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_PFP_UCODE_ADDR, A3XX_CP_PFP_UCODE_ADDR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_WFI_PEND_CTR, A3XX_CP_WFI_PEND_CTR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE, REG_CP_RB_BASE),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR, REG_CP_RB_RPTR_ADDR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR, REG_CP_RB_RPTR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_WPTR, REG_CP_RB_WPTR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_PROTECT_CTRL, A3XX_CP_PROTECT_CTRL),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_CNTL, REG_CP_ME_CNTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_CNTL, REG_CP_RB_CNTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_IB1_BASE, REG_CP_IB1_BASE),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_IB1_BUFSZ, REG_CP_IB1_BUFSZ),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_IB2_BASE, REG_CP_IB2_BASE),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_IB2_BUFSZ, REG_CP_IB2_BUFSZ),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_TIMESTAMP, REG_CP_TIMESTAMP),
+ ADRENO_REG_DEFINE(ADRENO_REG_SCRATCH_ADDR, REG_SCRATCH_ADDR),
+ ADRENO_REG_DEFINE(ADRENO_REG_SCRATCH_UMSK, REG_SCRATCH_UMSK),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_STATUS, A3XX_RBBM_STATUS),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_CTL, A3XX_RBBM_PERFCTR_CTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_CMD0,
+ A3XX_RBBM_PERFCTR_LOAD_CMD0),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_CMD1,
+ A3XX_RBBM_PERFCTR_LOAD_CMD1),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_PWR_1_LO,
+ A3XX_RBBM_PERFCTR_PWR_1_LO),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_INT_0_MASK, A3XX_RBBM_INT_0_MASK),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_INT_0_STATUS, A3XX_RBBM_INT_0_STATUS),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_AHB_ERROR_STATUS,
+ A3XX_RBBM_AHB_ERROR_STATUS),
+ ADRENO_REG_DEFINE(ADRENO_REG_VPC_VPC_DEBUG_RAM_SEL,
+ A3XX_VPC_VPC_DEBUG_RAM_SEL),
+ ADRENO_REG_DEFINE(ADRENO_REG_VPC_VPC_DEBUG_RAM_READ,
+ A3XX_VPC_VPC_DEBUG_RAM_READ),
+ ADRENO_REG_DEFINE(ADRENO_REG_VSC_PIPE_DATA_ADDRESS_0,
+ A3XX_VSC_PIPE_DATA_ADDRESS_0),
+ ADRENO_REG_DEFINE(ADRENO_REG_VSC_PIPE_DATA_LENGTH_7,
+ A3XX_VSC_PIPE_DATA_LENGTH_7),
+ ADRENO_REG_DEFINE(ADRENO_REG_VSC_SIZE_ADDRESS, A3XX_VSC_SIZE_ADDRESS),
+ ADRENO_REG_DEFINE(ADRENO_REG_VFD_CONTROL_0, A3XX_VFD_CONTROL_0),
+ ADRENO_REG_DEFINE(ADRENO_REG_VFD_FETCH_INSTR_0_0,
+ A3XX_VFD_FETCH_INSTR_0_0),
+ ADRENO_REG_DEFINE(ADRENO_REG_VFD_FETCH_INSTR_1_F,
+ A3XX_VFD_FETCH_INSTR_1_F),
+ ADRENO_REG_DEFINE(ADRENO_REG_VFD_INDEX_MAX, A3XX_VFD_INDEX_MAX),
+ ADRENO_REG_DEFINE(ADRENO_REG_SP_VS_PVT_MEM_ADDR_REG,
+ A3XX_SP_VS_PVT_MEM_ADDR_REG),
+ ADRENO_REG_DEFINE(ADRENO_REG_SP_FS_PVT_MEM_ADDR_REG,
+ A3XX_SP_FS_PVT_MEM_ADDR_REG),
+ ADRENO_REG_DEFINE(ADRENO_REG_SP_VS_OBJ_START_REG,
+ A3XX_SP_VS_OBJ_START_REG),
+ ADRENO_REG_DEFINE(ADRENO_REG_SP_FS_OBJ_START_REG,
+ A3XX_SP_FS_OBJ_START_REG),
+ ADRENO_REG_DEFINE(ADRENO_REG_PA_SC_AA_CONFIG, REG_PA_SC_AA_CONFIG),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PM_OVERRIDE2, REG_RBBM_PM_OVERRIDE2),
+ ADRENO_REG_DEFINE(ADRENO_REG_SCRATCH_REG2, REG_SCRATCH_REG2),
+ ADRENO_REG_DEFINE(ADRENO_REG_SQ_GPR_MANAGEMENT, REG_SQ_GPR_MANAGEMENT),
+ ADRENO_REG_DEFINE(ADRENO_REG_SQ_INST_STORE_MANAGMENT,
+ REG_SQ_INST_STORE_MANAGMENT),
+ ADRENO_REG_DEFINE(ADRENO_REG_TC_CNTL_STATUS, REG_TC_CNTL_STATUS),
+ ADRENO_REG_DEFINE(ADRENO_REG_TP0_CHICKEN, REG_TP0_CHICKEN),
+};
+
+struct adreno_reg_offsets a3xx_reg_offsets = {
+ .offsets = a3xx_register_offsets,
+ .offset_0 = ADRENO_REG_REGISTER_MAX,
+};
+
struct adreno_gpudev adreno_a3xx_gpudev = {
- .reg_rbbm_status = A3XX_RBBM_STATUS,
- .reg_cp_pfp_ucode_addr = A3XX_CP_PFP_UCODE_ADDR,
- .reg_cp_pfp_ucode_data = A3XX_CP_PFP_UCODE_DATA,
+ .reg_offsets = &a3xx_reg_offsets,
.perfcounters = &a3xx_perfcounters,
.ctxt_create = a3xx_drawctxt_create,
@@ -3519,4 +3756,5 @@
.coresight_disable = a3xx_coresight_disable,
.coresight_config_debug_reg = a3xx_coresight_config_debug_reg,
.soft_reset = a3xx_soft_reset,
+ .postmortem_dump = a3xx_postmortem_dump,
};
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index 58e3126..56c4305 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -35,8 +35,8 @@
unsigned int block_id, unsigned int index, unsigned int *val)
{
unsigned int block = (block_id << 8) | 1 << 16;
- adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, block | index);
- adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS, val);
+ kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, block | index);
+ kgsl_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS, val);
}
/**
@@ -108,9 +108,9 @@
for (bank = 0; bank < VPC_MEMORY_BANKS; bank++) {
for (addr = 0; addr < VPC_MEMORY_SIZE; addr++) {
unsigned int val = bank | (addr << 4);
- adreno_regwrite(device,
+ kgsl_regwrite(device,
A3XX_VPC_VPC_DEBUG_RAM_SEL, val);
- adreno_regread(device,
+ kgsl_regread(device,
A3XX_VPC_VPC_DEBUG_RAM_READ, &data[i++]);
}
}
@@ -134,9 +134,9 @@
header->type = SNAPSHOT_DEBUG_CP_MEQ;
header->size = CP_MEQ_SIZE;
- adreno_regwrite(device, A3XX_CP_MEQ_ADDR, 0x0);
+ kgsl_regwrite(device, A3XX_CP_MEQ_ADDR, 0x0);
for (i = 0; i < CP_MEQ_SIZE; i++)
- adreno_regread(device, A3XX_CP_MEQ_DATA, &data[i]);
+ kgsl_regread(device, A3XX_CP_MEQ_DATA, &data[i]);
return DEBUG_SECTION_SZ(CP_MEQ_SIZE);
}
@@ -164,9 +164,9 @@
* maintain always changing hardcoded constants
*/
- adreno_regwrite(device, REG_CP_ME_RAM_RADDR, 0x0);
+ kgsl_regwrite(device, REG_CP_ME_RAM_RADDR, 0x0);
for (i = 0; i < size; i++)
- adreno_regread(device, REG_CP_ME_RAM_DATA, &data[i]);
+ kgsl_regread(device, REG_CP_ME_RAM_DATA, &data[i]);
return DEBUG_SECTION_SZ(size);
}
@@ -195,7 +195,7 @@
*/
kgsl_regwrite(device, A3XX_CP_PFP_UCODE_ADDR, 0x0);
for (i = 0; i < size; i++)
- adreno_regread(device, A3XX_CP_PFP_UCODE_DATA, &data[i]);
+ kgsl_regread(device, A3XX_CP_PFP_UCODE_DATA, &data[i]);
return DEBUG_SECTION_SZ(size);
}
@@ -226,9 +226,9 @@
header->type = SNAPSHOT_DEBUG_CP_ROQ;
header->size = size;
- adreno_regwrite(device, A3XX_CP_ROQ_ADDR, 0x0);
+ kgsl_regwrite(device, A3XX_CP_ROQ_ADDR, 0x0);
for (i = 0; i < size; i++)
- adreno_regread(device, A3XX_CP_ROQ_DATA, &data[i]);
+ kgsl_regread(device, A3XX_CP_ROQ_DATA, &data[i]);
return DEBUG_SECTION_SZ(size);
}
@@ -253,12 +253,12 @@
header->type = SNAPSHOT_DEBUG_CP_MERCIU;
header->size = size;
- adreno_regwrite(device, A3XX_CP_MERCIU_ADDR, 0x0);
+ kgsl_regwrite(device, A3XX_CP_MERCIU_ADDR, 0x0);
for (i = 0; i < A330_CP_MERCIU_QUEUE_SIZE; i++) {
- adreno_regread(device, A3XX_CP_MERCIU_DATA,
+ kgsl_regread(device, A3XX_CP_MERCIU_DATA,
&data[(i * 2)]);
- adreno_regread(device, A3XX_CP_MERCIU_DATA2,
+ kgsl_regread(device, A3XX_CP_MERCIU_DATA2,
&data[(i * 2) + 1]);
}
@@ -444,7 +444,7 @@
list.count = 0;
/* Disable Clock gating temporarily for the debug bus to work */
- adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, 0x00);
+ kgsl_regwrite(device, A3XX_RBBM_CLOCK_CTL, 0x00);
/* Store relevant registers in list to snapshot */
_snapshot_a3xx_regs(regs, &list);
@@ -516,7 +516,7 @@
snapshot = a3xx_snapshot_debugbus(device, snapshot, remain);
/* Enable Clock gating */
- adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
+ kgsl_regwrite(device, A3XX_RBBM_CLOCK_CTL,
adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
return snapshot;
diff --git a/drivers/gpu/msm/adreno_coresight.c b/drivers/gpu/msm/adreno_coresight.c
index e18568d..1b827ff 100644
--- a/drivers/gpu/msm/adreno_coresight.c
+++ b/drivers/gpu/msm/adreno_coresight.c
@@ -109,7 +109,7 @@
mutex_lock(&device->mutex);
if (!kgsl_active_count_get(device)) {
- adreno_regread(device, offset, ®val);
+ kgsl_regread(device, offset, ®val);
kgsl_active_count_put(device);
}
mutex_unlock(&device->mutex);
diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c
index 90bd017..fc98d86 100644
--- a/drivers/gpu/msm/adreno_debugfs.c
+++ b/drivers/gpu/msm/adreno_debugfs.c
@@ -56,41 +56,6 @@
&adreno_dev->wait_timeout);
debugfs_create_u32("ib_check", 0644, device->d_debugfs,
&adreno_dev->ib_check_level);
- /* By Default enable fast hang detection */
- adreno_dev->fast_hang_detect = 1;
- debugfs_create_u32("fast_hang_detect", 0644, device->d_debugfs,
- &adreno_dev->fast_hang_detect);
- /*
- * FT policy can be set to any of the options below.
- * KGSL_FT_OFF -> BIT(0) Set to turn off FT
- * KGSL_FT_REPLAY -> BIT(1) Set to enable replay
- * KGSL_FT_SKIPIB -> BIT(2) Set to skip IB
- * KGSL_FT_SKIPFRAME -> BIT(3) Set to skip frame
- * KGSL_FT_DISABLE -> BIT(4) Set to disable FT for faulting context
- * by default set FT policy to KGSL_FT_DEFAULT_POLICY
- */
- adreno_dev->ft_policy = KGSL_FT_DEFAULT_POLICY;
- debugfs_create_u32("ft_policy", 0644, device->d_debugfs,
- &adreno_dev->ft_policy);
- /* By default enable long IB detection */
- adreno_dev->long_ib_detect = 1;
- debugfs_create_u32("long_ib_detect", 0644, device->d_debugfs,
- &adreno_dev->long_ib_detect);
-
- /*
- * FT pagefault policy can be set to any of the options below.
- * KGSL_FT_PAGEFAULT_INT_ENABLE -> BIT(0) set to enable pagefault INT
- * KGSL_FT_PAGEFAULT_GPUHALT_ENABLE -> BIT(1) Set to enable GPU HALT on
- * pagefaults. This stalls the GPU on a pagefault on IOMMU v1 HW.
- * KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE -> BIT(2) Set to log only one
- * pagefault per page.
- * KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT -> BIT(3) Set to log only one
- * pagefault per INT.
- */
- adreno_dev->ft_pf_policy = KGSL_FT_PAGEFAULT_DEFAULT_POLICY;
- debugfs_create_u32("ft_pagefault_policy", 0644, device->d_debugfs,
- &adreno_dev->ft_pf_policy);
-
debugfs_create_file("active_cnt", 0444, device->d_debugfs, device,
&_active_count_fops);
}
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index b32cdae..bf173a7 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -134,34 +134,32 @@
/**
* adreno_drawctxt_create - create a new adreno draw context
- * @device - KGSL device to create the context on
- * @pagetable - Pagetable for the context
- * @context- Generic KGSL context structure
- * @flags - flags for the context (passed from user space)
+ * @dev_priv: the owner of the context
+ * @flags: flags for the context (passed from user space)
*
- * Create a new draw context for the 3D core. Return 0 on success,
- * or error code on failure.
+ * Create and return a new draw context for the 3D core.
*/
-int adreno_drawctxt_create(struct kgsl_device *device,
- struct kgsl_pagetable *pagetable,
- struct kgsl_context *context, uint32_t *flags)
+struct kgsl_context *
+adreno_drawctxt_create(struct kgsl_device_private *dev_priv,
+ uint32_t *flags)
{
struct adreno_context *drawctxt;
+ struct kgsl_device *device = dev_priv->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
int ret;
drawctxt = kzalloc(sizeof(struct adreno_context), GFP_KERNEL);
-
if (drawctxt == NULL)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- drawctxt->pid = task_pid_nr(current);
- strlcpy(drawctxt->pid_name, current->comm, TASK_COMM_LEN);
- drawctxt->pagetable = pagetable;
+ ret = kgsl_context_init(dev_priv, &drawctxt->base);
+ if (ret != 0) {
+ kfree(drawctxt);
+ return ERR_PTR(ret);
+ }
+
drawctxt->bin_base_offset = 0;
- drawctxt->id = context->id;
- rb->timestamp[context->id] = 0;
+ drawctxt->timestamp = 0;
*flags &= (KGSL_CONTEXT_PREAMBLE |
KGSL_CONTEXT_NO_GMEM_ALLOC |
@@ -192,50 +190,47 @@
drawctxt->type =
(*flags & KGSL_CONTEXT_TYPE_MASK) >> KGSL_CONTEXT_TYPE_SHIFT;
- drawctxt->dev_priv = context->dev_priv;
ret = adreno_dev->gpudev->ctxt_create(adreno_dev, drawctxt);
if (ret)
goto err;
kgsl_sharedmem_writel(device, &device->memstore,
- KGSL_MEMSTORE_OFFSET(drawctxt->id, ref_wait_ts),
+ KGSL_MEMSTORE_OFFSET(drawctxt->base.id, ref_wait_ts),
KGSL_INIT_REFTIMESTAMP);
kgsl_sharedmem_writel(device, &device->memstore,
- KGSL_MEMSTORE_OFFSET(drawctxt->id, ts_cmp_enable), 0);
+ KGSL_MEMSTORE_OFFSET(drawctxt->base.id, ts_cmp_enable),
+ 0);
kgsl_sharedmem_writel(device, &device->memstore,
- KGSL_MEMSTORE_OFFSET(drawctxt->id, soptimestamp), 0);
+ KGSL_MEMSTORE_OFFSET(drawctxt->base.id, soptimestamp),
+ 0);
kgsl_sharedmem_writel(device, &device->memstore,
- KGSL_MEMSTORE_OFFSET(drawctxt->id, eoptimestamp), 0);
+ KGSL_MEMSTORE_OFFSET(drawctxt->base.id, eoptimestamp),
+ 0);
- context->devctxt = drawctxt;
- return 0;
+ return &drawctxt->base;
err:
- kfree(drawctxt);
- return ret;
+ kgsl_context_put(&drawctxt->base);
+ return ERR_PTR(ret);
}
/**
- * adreno_drawctxt_destroy - destroy a draw context
- * @device - KGSL device that owns the context
- * @context- Generic KGSL context container for the context
+ * adreno_drawctxt_detach(): detach a context from the GPU
+ * @context: Generic KGSL context container for the context
*
- * Destroy an existing context. Return 0 on success or error
- * code on failure.
*/
-
-/* destroy a drawing context */
-
-void adreno_drawctxt_destroy(struct kgsl_device *device,
- struct kgsl_context *context)
+void adreno_drawctxt_detach(struct kgsl_context *context)
{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct kgsl_device *device;
+ struct adreno_device *adreno_dev;
struct adreno_context *drawctxt;
- if (context == NULL || context->devctxt == NULL)
+ if (context == NULL)
return;
- drawctxt = context->devctxt;
+ device = context->device;
+ adreno_dev = ADRENO_DEVICE(device);
+ drawctxt = ADRENO_CONTEXT(context);
/* deactivate context */
if (adreno_dev->drawctxt_active == drawctxt) {
/* no need to save GMEM or shader, the context is
@@ -256,9 +251,17 @@
kgsl_sharedmem_free(&drawctxt->gpustate);
kgsl_sharedmem_free(&drawctxt->context_gmem_shadow.gmemshadow);
+}
+
+void adreno_drawctxt_destroy(struct kgsl_context *context)
+{
+ struct adreno_context *drawctxt;
+ if (context == NULL)
+ return;
+
+ drawctxt = ADRENO_CONTEXT(context);
kfree(drawctxt);
- context->devctxt = NULL;
}
/**
@@ -274,10 +277,12 @@
struct kgsl_context *context,
unsigned int offset)
{
- struct adreno_context *drawctxt = context->devctxt;
+ struct adreno_context *drawctxt;
- if (drawctxt)
- drawctxt->bin_base_offset = offset;
+ if (context == NULL)
+ return;
+ drawctxt = ADRENO_CONTEXT(context);
+ drawctxt->bin_base_offset = offset;
}
/**
@@ -316,12 +321,22 @@
KGSL_CTXT_INFO(device, "from %d to %d flags %d\n",
adreno_dev->drawctxt_active ?
- adreno_dev->drawctxt_active->id : 0,
- drawctxt ? drawctxt->id : 0, flags);
+ adreno_dev->drawctxt_active->base.id : 0,
+ drawctxt ? drawctxt->base.id : 0, flags);
/* Save the old context */
adreno_dev->gpudev->ctxt_save(adreno_dev, adreno_dev->drawctxt_active);
+ /* Put the old instance of the active drawctxt */
+ if (adreno_dev->drawctxt_active) {
+ kgsl_context_put(&adreno_dev->drawctxt_active->base);
+ adreno_dev->drawctxt_active = NULL;
+ }
+
+ /* Get a refcount to the new instance */
+ if (drawctxt)
+ _kgsl_context_get(&drawctxt->base);
+
/* Set the new context */
adreno_dev->gpudev->ctxt_restore(adreno_dev, drawctxt);
adreno_dev->drawctxt_active = drawctxt;
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
index 2b8e600..88d1b8c 100644
--- a/drivers/gpu/msm/adreno_drawctxt.h
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -13,8 +13,6 @@
#ifndef __ADRENO_DRAWCTXT_H
#define __ADRENO_DRAWCTXT_H
-#include <linux/sched.h>
-
#include "adreno_pm4types.h"
#include "a2xx_reg.h"
@@ -96,15 +94,11 @@
};
struct adreno_context {
- pid_t pid;
- char pid_name[TASK_COMM_LEN];
- unsigned int id;
+ struct kgsl_context base;
unsigned int ib_gpu_time_used;
+ unsigned int timestamp;
uint32_t flags;
- uint32_t pagefault;
- unsigned long pagefault_ts;
unsigned int type;
- struct kgsl_pagetable *pagetable;
struct kgsl_memdesc gpustate;
unsigned int reg_restore[3];
unsigned int shader_save[3];
@@ -131,16 +125,15 @@
struct kgsl_memdesc constant_load_commands[3];
struct kgsl_memdesc cond_execs[4];
struct kgsl_memdesc hlsqcontrol_restore_commands[1];
- struct kgsl_device_private *dev_priv;
};
-int adreno_drawctxt_create(struct kgsl_device *device,
- struct kgsl_pagetable *pagetable,
- struct kgsl_context *context,
+
+struct kgsl_context *adreno_drawctxt_create(struct kgsl_device_private *,
uint32_t *flags);
-void adreno_drawctxt_destroy(struct kgsl_device *device,
- struct kgsl_context *context);
+void adreno_drawctxt_detach(struct kgsl_context *context);
+
+void adreno_drawctxt_destroy(struct kgsl_context *context);
void adreno_drawctxt_switch(struct adreno_device *adreno_dev,
struct adreno_context *drawctxt,
diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c
index c7b6b5b..7a070a6 100644
--- a/drivers/gpu/msm/adreno_postmortem.c
+++ b/drivers/gpu/msm/adreno_postmortem.c
@@ -30,19 +30,19 @@
#define NUM_DWORDS_OF_RINGBUFFER_HISTORY 100
struct pm_id_name {
- uint32_t id;
+ enum adreno_regs id;
char name[9];
};
static const struct pm_id_name pm0_types[] = {
- {REG_PA_SC_AA_CONFIG, "RPASCAAC"},
- {REG_RBBM_PM_OVERRIDE2, "RRBBPMO2"},
- {REG_SCRATCH_REG2, "RSCRTRG2"},
- {REG_SQ_GPR_MANAGEMENT, "RSQGPRMN"},
- {REG_SQ_INST_STORE_MANAGMENT, "RSQINSTS"},
- {REG_TC_CNTL_STATUS, "RTCCNTLS"},
- {REG_TP0_CHICKEN, "RTP0CHCK"},
- {REG_CP_TIMESTAMP, "CP_TM_ST"},
+ {ADRENO_REG_PA_SC_AA_CONFIG, "RPASCAAC"},
+ {ADRENO_REG_RBBM_PM_OVERRIDE2, "RRBBPMO2"},
+ {ADRENO_REG_SCRATCH_REG2, "RSCRTRG2"},
+ {ADRENO_REG_SQ_GPR_MANAGEMENT, "RSQGPRMN"},
+ {ADRENO_REG_SQ_INST_STORE_MANAGMENT, "RSQINSTS"},
+ {ADRENO_REG_TC_CNTL_STATUS, "RTCCNTLS"},
+ {ADRENO_REG_TP0_CHICKEN, "RTP0CHCK"},
+ {ADRENO_REG_CP_TIMESTAMP, "CP_TM_ST"},
};
static const struct pm_id_name pm3_types[] = {
@@ -89,8 +89,9 @@
return (word >> 16) & 0x3FFF;
}
-static bool adreno_is_pm4_type(uint32_t word)
+static bool adreno_is_pm4_type(struct kgsl_device *device, uint32_t word)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int i;
if (word == INVALID_RB_CMD)
@@ -101,7 +102,8 @@
if ((word & (3<<30)) == CP_TYPE0_PKT) {
for (i = 0; i < ARRAY_SIZE(pm0_types); ++i) {
- if ((word & 0x7FFF) == pm0_types[i].id)
+ if ((word & 0x7FFF) == adreno_getreg(adreno_dev,
+ pm0_types[i].id))
return 1;
}
return 0;
@@ -258,8 +260,8 @@
}
}
-static void adreno_dump_rb_buffer(const void *buf, size_t len,
- char *linebuf, size_t linebuflen, int *argp)
+static void adreno_dump_rb_buffer(struct kgsl_device *device, const void *buf,
+ size_t len, char *linebuf, size_t linebuflen, int *argp)
{
const u32 *ptr4 = buf;
const int ngroups = len;
@@ -274,7 +276,7 @@
lx += scnprintf(linebuf + lx, linebuflen - lx, " ");
else
nxsp = 1;
- if (!*argp && adreno_is_pm4_type(ptr4[j])) {
+ if (!*argp && adreno_is_pm4_type(device, ptr4[j])) {
lx += scnprintf(linebuf + lx, linebuflen - lx,
"%s", adreno_pm4_name(ptr4[j]));
*argp = -(adreno_is_pm4_len(ptr4[j])+1);
@@ -326,18 +328,13 @@
hex_dump_to_buffer(ptr+i, linelen*4, rowsize*4, 4,
linebuf, sizeof(linebuf), 0);
else
- adreno_dump_rb_buffer(ptr+i, linelen, linebuf,
+ adreno_dump_rb_buffer(device, ptr+i, linelen, linebuf,
sizeof(linebuf), &args);
KGSL_LOG_DUMP(device,
"RB: %4.4X:%s\n", (start+i)%size, linebuf);
}
}
-struct log_field {
- bool show;
- const char *display;
-};
-
static int adreno_dump_fields_line(struct kgsl_device *device,
const char *start, char *str, int slen,
const struct log_field **lines,
@@ -372,7 +369,7 @@
return num;
}
-static void adreno_dump_fields(struct kgsl_device *device,
+void adreno_dump_fields(struct kgsl_device *device,
const char *start, const struct log_field *lines,
int num)
{
@@ -392,329 +389,7 @@
sstr = " ";
}
}
-
-static void adreno_dump_a3xx(struct kgsl_device *device)
-{
- unsigned int r1, r2, r3, rbbm_status;
- unsigned int cp_stat, rb_count;
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-
- kgsl_regread(device, adreno_dev->gpudev->reg_rbbm_status, &rbbm_status);
- KGSL_LOG_DUMP(device, "RBBM: STATUS = %08X\n", rbbm_status);
-
- {
- struct log_field lines[] = {
- {rbbm_status & BIT(0), "HI busy "},
- {rbbm_status & BIT(1), "CP ME busy "},
- {rbbm_status & BIT(2), "CP PFP busy "},
- {rbbm_status & BIT(14), "CP NRT busy "},
- {rbbm_status & BIT(15), "VBIF busy "},
- {rbbm_status & BIT(16), "TSE busy "},
- {rbbm_status & BIT(17), "RAS busy "},
- {rbbm_status & BIT(18), "RB busy "},
- {rbbm_status & BIT(19), "PC DCALL bsy"},
- {rbbm_status & BIT(20), "PC VSD busy "},
- {rbbm_status & BIT(21), "VFD busy "},
- {rbbm_status & BIT(22), "VPC busy "},
- {rbbm_status & BIT(23), "UCHE busy "},
- {rbbm_status & BIT(24), "SP busy "},
- {rbbm_status & BIT(25), "TPL1 busy "},
- {rbbm_status & BIT(26), "MARB busy "},
- {rbbm_status & BIT(27), "VSC busy "},
- {rbbm_status & BIT(28), "ARB busy "},
- {rbbm_status & BIT(29), "HLSQ busy "},
- {rbbm_status & BIT(30), "GPU bsy noHC"},
- {rbbm_status & BIT(31), "GPU busy "},
- };
- adreno_dump_fields(device, " STATUS=", lines,
- ARRAY_SIZE(lines));
- }
-
- kgsl_regread(device, REG_CP_RB_BASE, &r1);
- kgsl_regread(device, REG_CP_RB_CNTL, &r2);
- rb_count = 2 << (r2 & (BIT(6) - 1));
- kgsl_regread(device, REG_CP_RB_RPTR_ADDR, &r3);
- KGSL_LOG_DUMP(device,
- "CP_RB: BASE = %08X | CNTL = %08X | RPTR_ADDR = %08X"
- "| rb_count = %08X\n", r1, r2, r3, rb_count);
-
- kgsl_regread(device, REG_CP_RB_RPTR, &r1);
- kgsl_regread(device, REG_CP_RB_WPTR, &r2);
- kgsl_regread(device, REG_CP_RB_RPTR_WR, &r3);
- KGSL_LOG_DUMP(device,
- " RPTR = %08X | WPTR = %08X | RPTR_WR = %08X"
- "\n", r1, r2, r3);
-
- kgsl_regread(device, REG_CP_IB1_BASE, &r1);
- kgsl_regread(device, REG_CP_IB1_BUFSZ, &r2);
- KGSL_LOG_DUMP(device, "CP_IB1: BASE = %08X | BUFSZ = %d\n", r1, r2);
-
- kgsl_regread(device, REG_CP_ME_CNTL, &r1);
- kgsl_regread(device, REG_CP_ME_STATUS, &r2);
- KGSL_LOG_DUMP(device, "CP_ME: CNTL = %08X | STATUS = %08X\n", r1, r2);
-
- kgsl_regread(device, REG_CP_STAT, &cp_stat);
- KGSL_LOG_DUMP(device, "CP_STAT = %08X\n", cp_stat);
-#ifndef CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL
- {
- struct log_field lns[] = {
- {cp_stat & BIT(0), "WR_BSY 0"},
- {cp_stat & BIT(1), "RD_RQ_BSY 1"},
- {cp_stat & BIT(2), "RD_RTN_BSY 2"},
- };
- adreno_dump_fields(device, " MIU=", lns, ARRAY_SIZE(lns));
- }
- {
- struct log_field lns[] = {
- {cp_stat & BIT(5), "RING_BUSY 5"},
- {cp_stat & BIT(6), "NDRCTS_BSY 6"},
- {cp_stat & BIT(7), "NDRCT2_BSY 7"},
- {cp_stat & BIT(9), "ST_BUSY 9"},
- {cp_stat & BIT(10), "BUSY 10"},
- };
- adreno_dump_fields(device, " CSF=", lns, ARRAY_SIZE(lns));
- }
- {
- struct log_field lns[] = {
- {cp_stat & BIT(11), "RNG_Q_BSY 11"},
- {cp_stat & BIT(12), "NDRCTS_Q_B12"},
- {cp_stat & BIT(13), "NDRCT2_Q_B13"},
- {cp_stat & BIT(16), "ST_QUEUE_B16"},
- {cp_stat & BIT(17), "PFP_BUSY 17"},
- };
- adreno_dump_fields(device, " RING=", lns, ARRAY_SIZE(lns));
- }
- {
- struct log_field lns[] = {
- {cp_stat & BIT(3), "RBIU_BUSY 3"},
- {cp_stat & BIT(4), "RCIU_BUSY 4"},
- {cp_stat & BIT(8), "EVENT_BUSY 8"},
- {cp_stat & BIT(18), "MQ_RG_BSY 18"},
- {cp_stat & BIT(19), "MQ_NDRS_BS19"},
- {cp_stat & BIT(20), "MQ_NDR2_BS20"},
- {cp_stat & BIT(21), "MIU_WC_STL21"},
- {cp_stat & BIT(22), "CP_NRT_BSY22"},
- {cp_stat & BIT(23), "3D_BUSY 23"},
- {cp_stat & BIT(26), "ME_BUSY 26"},
- {cp_stat & BIT(27), "RB_FFO_BSY27"},
- {cp_stat & BIT(28), "CF_FFO_BSY28"},
- {cp_stat & BIT(29), "PS_FFO_BSY29"},
- {cp_stat & BIT(30), "VS_FFO_BSY30"},
- {cp_stat & BIT(31), "CP_BUSY 31"},
- };
- adreno_dump_fields(device, " CP_STT=", lns, ARRAY_SIZE(lns));
- }
-#endif
-
- kgsl_regread(device, A3XX_RBBM_INT_0_STATUS, &r1);
- KGSL_LOG_DUMP(device, "MSTR_INT_SGNL = %08X\n", r1);
- {
- struct log_field ints[] = {
- {r1 & BIT(0), "RBBM_GPU_IDLE 0"},
- {r1 & BIT(1), "RBBM_AHB_ERROR 1"},
- {r1 & BIT(2), "RBBM_REG_TIMEOUT 2"},
- {r1 & BIT(3), "RBBM_ME_MS_TIMEOUT 3"},
- {r1 & BIT(4), "RBBM_PFP_MS_TIMEOUT 4"},
- {r1 & BIT(5), "RBBM_ATB_BUS_OVERFLOW 5"},
- {r1 & BIT(6), "VFD_ERROR 6"},
- {r1 & BIT(7), "CP_SW_INT 7"},
- {r1 & BIT(8), "CP_T0_PACKET_IN_IB 8"},
- {r1 & BIT(9), "CP_OPCODE_ERROR 9"},
- {r1 & BIT(10), "CP_RESERVED_BIT_ERROR 10"},
- {r1 & BIT(11), "CP_HW_FAULT 11"},
- {r1 & BIT(12), "CP_DMA 12"},
- {r1 & BIT(13), "CP_IB2_INT 13"},
- {r1 & BIT(14), "CP_IB1_INT 14"},
- {r1 & BIT(15), "CP_RB_INT 15"},
- {r1 & BIT(16), "CP_REG_PROTECT_FAULT 16"},
- {r1 & BIT(17), "CP_RB_DONE_TS 17"},
- {r1 & BIT(18), "CP_VS_DONE_TS 18"},
- {r1 & BIT(19), "CP_PS_DONE_TS 19"},
- {r1 & BIT(20), "CACHE_FLUSH_TS 20"},
- {r1 & BIT(21), "CP_AHB_ERROR_HALT 21"},
- {r1 & BIT(24), "MISC_HANG_DETECT 24"},
- {r1 & BIT(25), "UCHE_OOB_ACCESS 25"},
- };
- adreno_dump_fields(device, "INT_SGNL=", ints, ARRAY_SIZE(ints));
- }
-}
-
-static void adreno_dump_a2xx(struct kgsl_device *device)
-{
- unsigned int r1, r2, r3, rbbm_status;
- unsigned int cp_stat, rb_count;
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-
- kgsl_regread(device, adreno_dev->gpudev->reg_rbbm_status, &rbbm_status);
-
- kgsl_regread(device, REG_RBBM_PM_OVERRIDE1, &r2);
- kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, &r3);
- KGSL_LOG_DUMP(device, "RBBM: STATUS = %08X | PM_OVERRIDE1 = %08X | "
- "PM_OVERRIDE2 = %08X\n", rbbm_status, r2, r3);
-
- kgsl_regread(device, REG_RBBM_INT_CNTL, &r1);
- kgsl_regread(device, REG_RBBM_INT_STATUS, &r2);
- kgsl_regread(device, REG_RBBM_READ_ERROR, &r3);
- KGSL_LOG_DUMP(device, " INT_CNTL = %08X | INT_STATUS = %08X | "
- "READ_ERROR = %08X\n", r1, r2, r3);
-
- {
- char cmdFifo[16];
- struct log_field lines[] = {
- {rbbm_status & 0x001F, cmdFifo},
- {rbbm_status & BIT(5), "TC busy "},
- {rbbm_status & BIT(8), "HIRQ pending"},
- {rbbm_status & BIT(9), "CPRQ pending"},
- {rbbm_status & BIT(10), "CFRQ pending"},
- {rbbm_status & BIT(11), "PFRQ pending"},
- {rbbm_status & BIT(12), "VGT 0DMA bsy"},
- {rbbm_status & BIT(14), "RBBM WU busy"},
- {rbbm_status & BIT(16), "CP NRT busy "},
- {rbbm_status & BIT(18), "MH busy "},
- {rbbm_status & BIT(19), "MH chncy bsy"},
- {rbbm_status & BIT(21), "SX busy "},
- {rbbm_status & BIT(22), "TPC busy "},
- {rbbm_status & BIT(24), "SC CNTX busy"},
- {rbbm_status & BIT(25), "PA busy "},
- {rbbm_status & BIT(26), "VGT busy "},
- {rbbm_status & BIT(27), "SQ cntx1 bsy"},
- {rbbm_status & BIT(28), "SQ cntx0 bsy"},
- {rbbm_status & BIT(30), "RB busy "},
- {rbbm_status & BIT(31), "Grphs pp bsy"},
- };
- snprintf(cmdFifo, sizeof(cmdFifo), "CMD FIFO=%01X ",
- rbbm_status & 0xf);
- adreno_dump_fields(device, " STATUS=", lines,
- ARRAY_SIZE(lines));
- }
-
- kgsl_regread(device, REG_CP_RB_BASE, &r1);
- kgsl_regread(device, REG_CP_RB_CNTL, &r2);
- rb_count = 2 << (r2 & (BIT(6)-1));
- kgsl_regread(device, REG_CP_RB_RPTR_ADDR, &r3);
- KGSL_LOG_DUMP(device,
- "CP_RB: BASE = %08X | CNTL = %08X | RPTR_ADDR = %08X"
- "| rb_count = %08X\n", r1, r2, r3, rb_count);
- {
- struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- if (rb->sizedwords != rb_count)
- rb_count = rb->sizedwords;
- }
-
- kgsl_regread(device, REG_CP_RB_RPTR, &r1);
- kgsl_regread(device, REG_CP_RB_WPTR, &r2);
- kgsl_regread(device, REG_CP_RB_RPTR_WR, &r3);
- KGSL_LOG_DUMP(device,
- " RPTR = %08X | WPTR = %08X | RPTR_WR = %08X"
- "\n", r1, r2, r3);
-
- kgsl_regread(device, REG_CP_IB1_BASE, &r1);
- kgsl_regread(device, REG_CP_IB1_BUFSZ, &r2);
- KGSL_LOG_DUMP(device, "CP_IB1: BASE = %08X | BUFSZ = %d\n", r1, r2);
-
- kgsl_regread(device, REG_CP_IB2_BASE, &r1);
- kgsl_regread(device, REG_CP_IB2_BUFSZ, &r2);
- KGSL_LOG_DUMP(device, "CP_IB2: BASE = %08X | BUFSZ = %d\n", r1, r2);
-
- kgsl_regread(device, REG_CP_INT_CNTL, &r1);
- kgsl_regread(device, REG_CP_INT_STATUS, &r2);
- KGSL_LOG_DUMP(device, "CP_INT: CNTL = %08X | STATUS = %08X\n", r1, r2);
-
- kgsl_regread(device, REG_CP_ME_CNTL, &r1);
- kgsl_regread(device, REG_CP_ME_STATUS, &r2);
- kgsl_regread(device, REG_MASTER_INT_SIGNAL, &r3);
- KGSL_LOG_DUMP(device,
- "CP_ME: CNTL = %08X | STATUS = %08X | MSTR_INT_SGNL = "
- "%08X\n", r1, r2, r3);
-
- kgsl_regread(device, REG_CP_STAT, &cp_stat);
- KGSL_LOG_DUMP(device, "CP_STAT = %08X\n", cp_stat);
-#ifndef CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL
- {
- struct log_field lns[] = {
- {cp_stat & BIT(0), "WR_BSY 0"},
- {cp_stat & BIT(1), "RD_RQ_BSY 1"},
- {cp_stat & BIT(2), "RD_RTN_BSY 2"},
- };
- adreno_dump_fields(device, " MIU=", lns, ARRAY_SIZE(lns));
- }
- {
- struct log_field lns[] = {
- {cp_stat & BIT(5), "RING_BUSY 5"},
- {cp_stat & BIT(6), "NDRCTS_BSY 6"},
- {cp_stat & BIT(7), "NDRCT2_BSY 7"},
- {cp_stat & BIT(9), "ST_BUSY 9"},
- {cp_stat & BIT(10), "BUSY 10"},
- };
- adreno_dump_fields(device, " CSF=", lns, ARRAY_SIZE(lns));
- }
- {
- struct log_field lns[] = {
- {cp_stat & BIT(11), "RNG_Q_BSY 11"},
- {cp_stat & BIT(12), "NDRCTS_Q_B12"},
- {cp_stat & BIT(13), "NDRCT2_Q_B13"},
- {cp_stat & BIT(16), "ST_QUEUE_B16"},
- {cp_stat & BIT(17), "PFP_BUSY 17"},
- };
- adreno_dump_fields(device, " RING=", lns, ARRAY_SIZE(lns));
- }
- {
- struct log_field lns[] = {
- {cp_stat & BIT(3), "RBIU_BUSY 3"},
- {cp_stat & BIT(4), "RCIU_BUSY 4"},
- {cp_stat & BIT(18), "MQ_RG_BSY 18"},
- {cp_stat & BIT(19), "MQ_NDRS_BS19"},
- {cp_stat & BIT(20), "MQ_NDR2_BS20"},
- {cp_stat & BIT(21), "MIU_WC_STL21"},
- {cp_stat & BIT(22), "CP_NRT_BSY22"},
- {cp_stat & BIT(23), "3D_BUSY 23"},
- {cp_stat & BIT(26), "ME_BUSY 26"},
- {cp_stat & BIT(29), "ME_WC_BSY 29"},
- {cp_stat & BIT(30), "MIU_FF EM 30"},
- {cp_stat & BIT(31), "CP_BUSY 31"},
- };
- adreno_dump_fields(device, " CP_STT=", lns, ARRAY_SIZE(lns));
- }
-#endif
-
- kgsl_regread(device, REG_SCRATCH_REG0, &r1);
- KGSL_LOG_DUMP(device, "SCRATCH_REG0 = %08X\n", r1);
-
- kgsl_regread(device, REG_COHER_SIZE_PM4, &r1);
- kgsl_regread(device, REG_COHER_BASE_PM4, &r2);
- kgsl_regread(device, REG_COHER_STATUS_PM4, &r3);
- KGSL_LOG_DUMP(device,
- "COHER: SIZE_PM4 = %08X | BASE_PM4 = %08X | STATUS_PM4"
- " = %08X\n", r1, r2, r3);
-
- kgsl_regread(device, MH_AXI_ERROR, &r1);
- KGSL_LOG_DUMP(device, "MH: AXI_ERROR = %08X\n", r1);
-
- kgsl_regread(device, MH_MMU_PAGE_FAULT, &r1);
- kgsl_regread(device, MH_MMU_CONFIG, &r2);
- kgsl_regread(device, MH_MMU_MPU_BASE, &r3);
- KGSL_LOG_DUMP(device,
- "MH_MMU: PAGE_FAULT = %08X | CONFIG = %08X | MPU_BASE ="
- " %08X\n", r1, r2, r3);
-
- kgsl_regread(device, MH_MMU_MPU_END, &r1);
- kgsl_regread(device, MH_MMU_VA_RANGE, &r2);
- r3 = kgsl_mmu_get_current_ptbase(&device->mmu);
- KGSL_LOG_DUMP(device,
- " MPU_END = %08X | VA_RANGE = %08X | PT_BASE ="
- " %08X\n", r1, r2, r3);
-
- KGSL_LOG_DUMP(device, "PAGETABLE SIZE: %08X ",
- kgsl_mmu_get_ptsize(&device->mmu));
-
- kgsl_regread(device, MH_MMU_TRAN_ERROR, &r1);
- KGSL_LOG_DUMP(device, " TRAN_ERROR = %08X\n", r1);
-
- kgsl_regread(device, MH_INTERRUPT_MASK, &r1);
- kgsl_regread(device, MH_INTERRUPT_STATUS, &r2);
- KGSL_LOG_DUMP(device,
- "MH_INTERRUPT: MASK = %08X | STATUS = %08X\n", r1, r2);
-}
+EXPORT_SYMBOL(adreno_dump_fields);
int adreno_dump(struct kgsl_device *device, int manual)
{
@@ -745,26 +420,42 @@
if (device->pm_dump_enable) {
msm_clk_dump_debug_info();
- if (adreno_is_a2xx(adreno_dev))
- adreno_dump_a2xx(device);
- else if (adreno_is_a3xx(adreno_dev))
- adreno_dump_a3xx(device);
+ if (adreno_dev->gpudev->postmortem_dump)
+ adreno_dev->gpudev->postmortem_dump(adreno_dev);
}
- kgsl_regread(device, adreno_dev->gpudev->reg_rbbm_status, &rbbm_status);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_RBBM_STATUS),
+ &rbbm_status);
pt_base = kgsl_mmu_get_current_ptbase(&device->mmu);
cur_pt_base = pt_base;
- kgsl_regread(device, REG_CP_RB_BASE, &cp_rb_base);
- kgsl_regread(device, REG_CP_RB_CNTL, &cp_rb_ctrl);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_CP_RB_BASE),
+ &cp_rb_base);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_CP_RB_CNTL),
+ &cp_rb_ctrl);
rb_count = 2 << (cp_rb_ctrl & (BIT(6) - 1));
- kgsl_regread(device, REG_CP_RB_RPTR, &cp_rb_rptr);
- kgsl_regread(device, REG_CP_RB_WPTR, &cp_rb_wptr);
- kgsl_regread(device, REG_CP_IB1_BASE, &cp_ib1_base);
- kgsl_regread(device, REG_CP_IB1_BUFSZ, &cp_ib1_bufsz);
- kgsl_regread(device, REG_CP_IB2_BASE, &cp_ib2_base);
- kgsl_regread(device, REG_CP_IB2_BUFSZ, &cp_ib2_bufsz);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_CP_RB_RPTR),
+ &cp_rb_rptr);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_CP_RB_WPTR),
+ &cp_rb_wptr);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_CP_IB1_BASE),
+ &cp_ib1_base);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_CP_IB1_BUFSZ),
+ &cp_ib1_bufsz);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_CP_IB2_BASE),
+ &cp_ib2_base);
+ kgsl_regread(device,
+ adreno_getreg(adreno_dev, ADRENO_REG_CP_IB2_BUFSZ),
+ &cp_ib2_bufsz);
/* If postmortem dump is not enabled, dump minimal set and return */
if (!device->pm_dump_enable) {
@@ -782,7 +473,9 @@
(unsigned int *) &context_id,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
current_context));
- context = idr_find(&device->context_idr, context_id);
+
+ context = kgsl_context_get(device, context_id);
+
if (context) {
ts_processed = kgsl_readtimestamp(device, context,
KGSL_TIMESTAMP_RETIRED);
@@ -791,6 +484,8 @@
} else
KGSL_LOG_DUMP(device, "BAD CTXT: %d\n", context_id);
+ kgsl_context_put(context);
+
num_item = adreno_ringbuffer_count(&adreno_dev->ringbuffer,
cp_rb_rptr);
if (num_item <= 0)
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 2d87263..999b782 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -40,6 +40,7 @@
void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device);
BUG_ON(rb->wptr == 0);
/* Let the pwrscale policy know that new commands have
@@ -51,7 +52,7 @@
*/
mb();
- adreno_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, rb->wptr);
}
static int
@@ -137,7 +138,7 @@
if (context && context->flags & CTXT_FLAGS_GPU_HANG) {
KGSL_CTXT_WARN(rb->device,
"Context %p caused a gpu hang. Will not accept commands for context %d\n",
- context, context->id);
+ context, context->base.id);
return -EDEADLK;
}
wait_time = jiffies + wait_timeout;
@@ -269,11 +270,11 @@
KGSL_DRV_INFO(device, "loading pm4 ucode version: %d\n",
adreno_dev->pm4_fw_version);
- adreno_regwrite(device, REG_CP_DEBUG, CP_DEBUG_DEFAULT);
- adreno_regwrite(device, REG_CP_ME_RAM_WADDR, addr);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_DEBUG, CP_DEBUG_DEFAULT);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_ME_RAM_WADDR, addr);
for (i = start; i < adreno_dev->pm4_fw_size; i++)
- adreno_regwrite(device, REG_CP_ME_RAM_DATA,
- adreno_dev->pm4_fw[i]);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_ME_RAM_DATA,
+ adreno_dev->pm4_fw[i]);
return 0;
}
@@ -332,12 +333,11 @@
KGSL_DRV_INFO(device, "loading pfp ucode version: %d\n",
adreno_dev->pfp_fw_version);
- adreno_regwrite(device, adreno_dev->gpudev->reg_cp_pfp_ucode_addr,
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_PFP_UCODE_ADDR,
addr);
for (i = start; i < adreno_dev->pfp_fw_size; i++)
- adreno_regwrite(device,
- adreno_dev->gpudev->reg_cp_pfp_ucode_data,
- adreno_dev->pfp_fw[i]);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_PFP_UCODE_DATA,
+ adreno_dev->pfp_fw[i]);
return 0;
}
@@ -367,17 +367,17 @@
(rb->sizedwords << 2));
if (adreno_is_a2xx(adreno_dev)) {
- adreno_regwrite(device, REG_CP_RB_WPTR_BASE,
+ kgsl_regwrite(device, REG_CP_RB_WPTR_BASE,
(rb->memptrs_desc.gpuaddr
+ GSL_RB_MEMPTRS_WPTRPOLL_OFFSET));
/* setup WPTR delay */
- adreno_regwrite(device, REG_CP_RB_WPTR_DELAY,
+ kgsl_regwrite(device, REG_CP_RB_WPTR_DELAY,
0 /*0x70000010 */);
}
/*setup REG_CP_RB_CNTL */
- adreno_regread(device, REG_CP_RB_CNTL, &rb_cntl);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_CNTL, &rb_cntl);
cp_rb_cntl.val = rb_cntl;
/*
@@ -401,64 +401,66 @@
/* mem RPTR writebacks */
cp_rb_cntl.f.rb_no_update = GSL_RB_CNTL_NO_UPDATE;
- adreno_regwrite(device, REG_CP_RB_CNTL, cp_rb_cntl.val);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_CNTL, cp_rb_cntl.val);
- adreno_regwrite(device, REG_CP_RB_BASE, rb->buffer_desc.gpuaddr);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_BASE,
+ rb->buffer_desc.gpuaddr);
- adreno_regwrite(device, REG_CP_RB_RPTR_ADDR,
- rb->memptrs_desc.gpuaddr +
- GSL_RB_MEMPTRS_RPTR_OFFSET);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_RPTR_ADDR,
+ rb->memptrs_desc.gpuaddr +
+ GSL_RB_MEMPTRS_RPTR_OFFSET);
if (adreno_is_a3xx(adreno_dev)) {
/* enable access protection to privileged registers */
- adreno_regwrite(device, A3XX_CP_PROTECT_CTRL, 0x00000007);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_CTRL, 0x00000007);
/* RBBM registers */
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_0, 0x63000040);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_1, 0x62000080);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_2, 0x600000CC);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_3, 0x60000108);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_4, 0x64000140);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_5, 0x66000400);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_0, 0x63000040);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_1, 0x62000080);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_2, 0x600000CC);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_3, 0x60000108);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_4, 0x64000140);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_5, 0x66000400);
/* CP registers */
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_6, 0x65000700);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_7, 0x610007D8);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_8, 0x620007E0);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_9, 0x61001178);
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_A, 0x64001180);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_6, 0x65000700);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_7, 0x610007D8);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_8, 0x620007E0);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_9, 0x61001178);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_A, 0x64001180);
/* RB registers */
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_B, 0x60003300);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_B, 0x60003300);
/* VBIF registers */
- adreno_regwrite(device, A3XX_CP_PROTECT_REG_C, 0x6B00C000);
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_C, 0x6B00C000);
}
if (adreno_is_a2xx(adreno_dev)) {
/* explicitly clear all cp interrupts */
- adreno_regwrite(device, REG_CP_INT_ACK, 0xFFFFFFFF);
+ kgsl_regwrite(device, REG_CP_INT_ACK, 0xFFFFFFFF);
}
/* setup scratch/timestamp */
- adreno_regwrite(device, REG_SCRATCH_ADDR, device->memstore.gpuaddr +
- KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
- soptimestamp));
+ adreno_writereg(adreno_dev, ADRENO_REG_SCRATCH_ADDR,
+ device->memstore.gpuaddr +
+ KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+ soptimestamp));
- adreno_regwrite(device, REG_SCRATCH_UMSK,
+ adreno_writereg(adreno_dev, ADRENO_REG_SCRATCH_UMSK,
GSL_RB_MEMPTRS_SCRATCH_MASK);
/* CP ROQ queue sizes (bytes) - RB:16, ST:16, IB1:32, IB2:64 */
if (adreno_is_a305(adreno_dev) || adreno_is_a305c(adreno_dev) ||
adreno_is_a320(adreno_dev))
- adreno_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000E0602);
+ kgsl_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000E0602);
else if (adreno_is_a330(adreno_dev) || adreno_is_a305b(adreno_dev))
- adreno_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x003E2008);
+ kgsl_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x003E2008);
rb->wptr = 0;
/* clear ME_HALT to start micro engine */
- adreno_regwrite(device, REG_CP_ME_CNTL, 0);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_ME_CNTL, 0);
/* ME init is GPU specific, so jump into the sub-function */
status = adreno_dev->gpudev->rb_init(adreno_dev, rb);
@@ -529,7 +531,7 @@
if (rb->flags & KGSL_FLAGS_STARTED) {
if (adreno_is_a200(adreno_dev))
- adreno_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000);
+ kgsl_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000);
rb->flags &= ~KGSL_FLAGS_STARTED;
}
@@ -573,6 +575,8 @@
/* overlay structure on memptrs memory */
rb->memptrs = (struct kgsl_rbmemptrs *) rb->memptrs_desc.hostptr;
+ rb->global_ts = 0;
+
return 0;
}
@@ -592,11 +596,11 @@
memset(rb, 0, sizeof(struct adreno_ringbuffer));
}
-static uint32_t
+static int
adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb,
struct adreno_context *context,
unsigned int flags, unsigned int *cmds,
- int sizedwords, uint32_t timestamp)
+ int sizedwords)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device);
unsigned int *ringcmds;
@@ -605,6 +609,7 @@
unsigned int rcmd_gpu;
unsigned int context_id = KGSL_MEMSTORE_GLOBAL;
unsigned int gpuaddr = rb->device->memstore.gpuaddr;
+ unsigned int timestamp;
/*
* if the context was not created with per context timestamp
@@ -614,20 +619,7 @@
*/
if ((context && (context->flags & CTXT_FLAGS_PER_CONTEXT_TS)) &&
!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE))
- context_id = context->id;
-
- if ((context && (context->flags & CTXT_FLAGS_USER_GENERATED_TS)) &&
- !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
- if (timestamp_cmp(rb->timestamp[context_id],
- timestamp) >= 0) {
- KGSL_DRV_ERR(rb->device,
- "Invalid user generated ts <%d:0x%x>, "
- "less than last issued ts <%d:0x%x>\n",
- context_id, timestamp, context_id,
- rb->timestamp[context_id]);
- return -ERANGE;
- }
- }
+ context_id = context->base.id;
/* reserve space to temporarily turn off protected mode
* error checking if needed
@@ -667,13 +659,8 @@
total_sizedwords += 2;
ringcmds = adreno_ringbuffer_allocspace(rb, context, total_sizedwords);
- if (!ringcmds) {
- /*
- * We could not allocate space in ringbuffer, just return the
- * last timestamp
- */
- return rb->timestamp[context_id];
- }
+ if (!ringcmds)
+ return -ENOSPC;
rcmd_gpu = rb->buffer_desc.gpuaddr
+ sizeof(uint)*(rb->wptr-total_sizedwords);
@@ -688,25 +675,19 @@
}
/* always increment the global timestamp. once. */
- rb->timestamp[KGSL_MEMSTORE_GLOBAL]++;
+ rb->global_ts++;
- /*
- * If global timestamp then we are not using per context ts for
- * this submission
- */
- if (context_id != KGSL_MEMSTORE_GLOBAL) {
- if (context->flags & CTXT_FLAGS_USER_GENERATED_TS)
- rb->timestamp[context_id] = timestamp;
- else
- rb->timestamp[context_id]++;
- }
- timestamp = rb->timestamp[context_id];
+ if (KGSL_MEMSTORE_GLOBAL != context_id)
+ timestamp = context->timestamp;
+ else
+ timestamp = rb->global_ts;
/* scratchpad ts for fault tolerance */
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
- cp_type0_packet(REG_CP_TIMESTAMP, 1));
+ cp_type0_packet(adreno_getreg(adreno_dev,
+ ADRENO_REG_CP_TIMESTAMP), 1));
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
- rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
+ rb->global_ts);
/* start-of-pipeline timestamp */
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
@@ -778,7 +759,7 @@
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
eoptimestamp)));
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
- rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
+ rb->global_ts);
}
if (adreno_is_a20x(adreno_dev)) {
@@ -854,7 +835,7 @@
adreno_ringbuffer_submit(rb);
- return timestamp;
+ return 0;
}
unsigned int
@@ -874,7 +855,7 @@
flags |= KGSL_CMD_FLAGS_INTERNAL_ISSUE;
return adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds,
- sizedwords, 0);
+ sizedwords);
}
static bool _parse_ibs(struct kgsl_device_private *dev_priv, uint gpuaddr,
@@ -1094,12 +1075,9 @@
ret = -EINVAL;
goto done;
}
- drawctxt = context->devctxt;
+ drawctxt = ADRENO_CONTEXT(context);
if (drawctxt->flags & CTXT_FLAGS_GPU_HANG) {
- KGSL_CTXT_ERR(device, "proc %s failed fault tolerance"
- " will not accept commands for context %d\n",
- drawctxt->pid_name, drawctxt->id);
ret = -EDEADLK;
goto done;
}
@@ -1113,10 +1091,6 @@
start_index = 1;
if (drawctxt->flags & CTXT_FLAGS_SKIP_EOF) {
- KGSL_CTXT_ERR(device,
- "proc %s triggered fault tolerance"
- " skipping commands for context till EOF %d\n",
- drawctxt->pid_name, drawctxt->id);
if (flags & KGSL_CMD_FLAGS_EOF)
drawctxt->flags &= ~CTXT_FLAGS_SKIP_EOF;
if (start_index)
@@ -1169,10 +1143,30 @@
adreno_drawctxt_switch(adreno_dev, drawctxt, flags);
- *timestamp = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
+ if (drawctxt->flags & CTXT_FLAGS_USER_GENERATED_TS) {
+ if (timestamp_cmp(drawctxt->timestamp, *timestamp) >= 0) {
+ KGSL_DRV_ERR(device,
+ "Invalid user generated ts <%d:0x%x>, "
+ "less than last issued ts <%d:0x%x>\n",
+ context->id, *timestamp, context->id,
+ drawctxt->timestamp);
+ return -ERANGE;
+ }
+ drawctxt->timestamp = *timestamp;
+ } else
+ drawctxt->timestamp++;
+
+ ret = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
drawctxt,
(flags & KGSL_CMD_FLAGS_EOF),
- &link[0], (cmds - link), *timestamp);
+ &link[0], (cmds - link));
+ if (ret)
+ goto done;
+
+ if (drawctxt->flags & CTXT_FLAGS_PER_CONTEXT_TS)
+ *timestamp = drawctxt->timestamp;
+ else
+ *timestamp = adreno_dev->ringbuffer.global_ts;
#ifdef CONFIG_MSM_KGSL_CFF_DUMP
/*
@@ -1276,11 +1270,13 @@
if (0xFFFFFFFF == ft_data->start_of_replay_cmds)
return;
- k_ctxt = idr_find(&device->context_idr, ft_data->context_id);
+ k_ctxt = kgsl_context_get(device, ft_data->context_id);
+
if (k_ctxt) {
- a_ctxt = k_ctxt->devctxt;
+ a_ctxt = ADRENO_CONTEXT(k_ctxt);
if (a_ctxt->flags & CTXT_FLAGS_PREAMBLE)
_turn_preamble_on_for_ib_seq(rb, rb_rptr);
+ kgsl_context_put(k_ctxt);
}
k_ctxt = NULL;
@@ -1311,9 +1307,10 @@
/* if context switches to a context that did not cause
* hang then start saving the rb contents as those
* commands can be executed */
- k_ctxt = idr_find(&rb->device->context_idr, val2);
+ k_ctxt = kgsl_context_get(rb->device, val2);
+
if (k_ctxt) {
- a_ctxt = k_ctxt->devctxt;
+ a_ctxt = ADRENO_CONTEXT(k_ctxt);
/* If we are changing to a good context and were not
* copying commands then copy over commands to the good
@@ -1349,6 +1346,7 @@
copy_rb_contents = 0;
}
}
+ kgsl_context_put(k_ctxt);
}
if (copy_rb_contents)
@@ -1373,12 +1371,13 @@
int i;
unsigned int *ringcmds;
unsigned int rcmd_gpu;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device);
if (!num_rb_contents)
return;
if (num_rb_contents > (rb->buffer_desc.size - rb->wptr)) {
- adreno_regwrite(rb->device, REG_CP_RB_RPTR, 0);
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_RPTR, 0);
BUG_ON(num_rb_contents > rb->buffer_desc.size);
}
ringcmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr;
diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h
index e9fb050..9634e32 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.h
+++ b/drivers/gpu/msm/adreno_ringbuffer.h
@@ -55,7 +55,7 @@
unsigned int wptr; /* write pointer offset in dwords from baseaddr */
- unsigned int timestamp[KGSL_MEMSTORE_MAX];
+ unsigned int global_ts;
};
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index d6ce298..b7c2237 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -467,6 +467,7 @@
static void ib_parse_type0(struct kgsl_device *device, unsigned int *ptr,
phys_addr_t ptbase)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int size = type0_pkt_size(*ptr);
int offset = type0_pkt_offset(*ptr);
int i;
@@ -475,9 +476,12 @@
/* Visiblity stream buffer */
- if (offset >= A3XX_VSC_PIPE_DATA_ADDRESS_0 &&
- offset <= A3XX_VSC_PIPE_DATA_LENGTH_7) {
- int index = offset - A3XX_VSC_PIPE_DATA_ADDRESS_0;
+ if (offset >= adreno_getreg(adreno_dev,
+ ADRENO_REG_VSC_PIPE_DATA_ADDRESS_0) &&
+ offset <= adreno_getreg(adreno_dev,
+ ADRENO_REG_VSC_PIPE_DATA_LENGTH_7)) {
+ int index = offset - adreno_getreg(adreno_dev,
+ ADRENO_REG_VSC_PIPE_DATA_ADDRESS_0);
/* Each bank of address and length registers are
* interleaved with an empty register:
@@ -495,9 +499,13 @@
vsc_pipe[index / 3].base = ptr[i + 1];
else if ((index % 3) == 1)
vsc_pipe[index / 3].size = ptr[i + 1];
- } else if ((offset >= A3XX_VFD_FETCH_INSTR_0_0) &&
- (offset <= A3XX_VFD_FETCH_INSTR_1_F)) {
- int index = offset - A3XX_VFD_FETCH_INSTR_0_0;
+ } else if ((offset >= adreno_getreg(adreno_dev,
+ ADRENO_REG_VFD_FETCH_INSTR_0_0)) &&
+ (offset <= adreno_getreg(adreno_dev,
+ ADRENO_REG_VFD_FETCH_INSTR_1_F))) {
+ int index = offset -
+ adreno_getreg(adreno_dev,
+ ADRENO_REG_VFD_FETCH_INSTR_0_0);
/*
* FETCH_INSTR_0_X and FETCH_INSTR_1_X banks are
@@ -516,29 +524,30 @@
* buffer sizes
*/
- switch (offset) {
- case A3XX_VFD_CONTROL_0:
+ if (offset ==
+ adreno_getreg(adreno_dev,
+ ADRENO_REG_VFD_CONTROL_0))
vfd_control_0 = ptr[i + 1];
- break;
- case A3XX_VFD_INDEX_MAX:
+ else if (offset ==
+ adreno_getreg(adreno_dev,
+ ADRENO_REG_VFD_INDEX_MAX))
vfd_index_max = ptr[i + 1];
- break;
- case A3XX_VSC_SIZE_ADDRESS:
+ else if (offset ==
+ adreno_getreg(adreno_dev,
+ ADRENO_REG_VSC_SIZE_ADDRESS))
vsc_size_address = ptr[i + 1];
- break;
- case A3XX_SP_VS_PVT_MEM_ADDR_REG:
+ else if (offset == adreno_getreg(adreno_dev,
+ ADRENO_REG_SP_VS_PVT_MEM_ADDR_REG))
sp_vs_pvt_mem_addr = ptr[i + 1];
- break;
- case A3XX_SP_FS_PVT_MEM_ADDR_REG:
+ else if (offset == adreno_getreg(adreno_dev,
+ ADRENO_REG_SP_FS_PVT_MEM_ADDR_REG))
sp_fs_pvt_mem_addr = ptr[i + 1];
- break;
- case A3XX_SP_VS_OBJ_START_REG:
+ else if (offset == adreno_getreg(adreno_dev,
+ ADRENO_REG_SP_VS_OBJ_START_REG))
sp_vs_obj_start_reg = ptr[i + 1];
- break;
- case A3XX_SP_FS_OBJ_START_REG:
+ else if (offset == adreno_getreg(adreno_dev,
+ ADRENO_REG_SP_FS_OBJ_START_REG))
sp_fs_obj_start_reg = ptr[i + 1];
- break;
- }
}
}
}
@@ -629,6 +638,7 @@
static inline int parse_ib(struct kgsl_device *device, phys_addr_t ptbase,
unsigned int gpuaddr, unsigned int dwords)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
unsigned int ib1base, ib2base;
int ret = 0;
@@ -638,8 +648,8 @@
* it in the dynamic list
*/
- kgsl_regread(device, REG_CP_IB1_BASE, &ib1base);
- kgsl_regread(device, REG_CP_IB2_BASE, &ib2base);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB1_BASE, &ib1base);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB2_BASE, &ib2base);
if (gpuaddr == ib1base || gpuaddr == ib2base)
push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
@@ -667,10 +677,10 @@
ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
/* Get the current read pointers for the RB */
- kgsl_regread(device, REG_CP_RB_RPTR, &rptr);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, &rptr);
/* Address of the last processed IB */
- kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB1_BASE, &ibbase);
/*
* Figure out the window of ringbuffer data to dump. First we need to
@@ -998,8 +1008,8 @@
* want to be double plus sure.
*/
- kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
- kgsl_regread(device, REG_CP_IB1_BUFSZ, &ibsize);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB1_BASE, &ibbase);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB1_BUFSZ, &ibsize);
/*
* The problem is that IB size from the register is the unprocessed size
@@ -1016,8 +1026,8 @@
"Dumping %x dwords of the buffer.\n", ibsize);
}
- kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
- kgsl_regread(device, REG_CP_IB2_BUFSZ, &ibsize);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB2_BASE, &ibbase);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB2_BUFSZ, &ibsize);
/*
* Add the last parsed IB2 to the list. The IB2 should be found as we
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index e62dac9..966e035 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -448,60 +448,62 @@
entry->priv = NULL;
}
-/* Allocate a new context id */
-
-static struct kgsl_context *
-kgsl_create_context(struct kgsl_device_private *dev_priv)
+/**
+ * kgsl_context_init() - helper to initialize kgsl_context members
+ * @dev_priv: the owner of the context
+ * @context: the newly created context struct, should be allocated by
+ * the device specific drawctxt_create function.
+ *
+ * This is a helper function for the device specific drawctxt_create
+ * function to initialize the common members of its context struct.
+ * If this function succeeds, reference counting is active in the context
+ * struct and the caller should kgsl_context_put() it on error.
+ * If it fails, the caller should just free the context structure
+ * it passed in.
+ */
+int kgsl_context_init(struct kgsl_device_private *dev_priv,
+ struct kgsl_context *context)
{
- struct kgsl_context *context;
- int ret, id;
-
- context = kzalloc(sizeof(*context), GFP_KERNEL);
-
- if (context == NULL) {
- KGSL_DRV_INFO(dev_priv->device, "kzalloc(%d) failed\n",
- sizeof(*context));
- return ERR_PTR(-ENOMEM);
- }
+ int ret = 0, id;
+ struct kgsl_device *device = dev_priv->device;
while (1) {
- if (idr_pre_get(&dev_priv->device->context_idr,
- GFP_KERNEL) == 0) {
- KGSL_DRV_INFO(dev_priv->device,
- "idr_pre_get: ENOMEM\n");
+ if (idr_pre_get(&device->context_idr, GFP_KERNEL) == 0) {
+ KGSL_DRV_INFO(device, "idr_pre_get: ENOMEM\n");
ret = -ENOMEM;
- goto func_end;
+ break;
}
- ret = idr_get_new_above(&dev_priv->device->context_idr,
- context, 1, &id);
+ write_lock(&device->context_lock);
+ ret = idr_get_new_above(&device->context_idr, context, 1, &id);
+ context->id = id;
+ write_unlock(&device->context_lock);
if (ret != -EAGAIN)
break;
}
if (ret)
- goto func_end;
+ goto fail;
/* MAX - 1, there is one memdesc in memstore for device info */
if (id >= KGSL_MEMSTORE_MAX) {
- KGSL_DRV_INFO(dev_priv->device, "cannot have more than %d "
+ KGSL_DRV_INFO(device, "cannot have more than %d "
"ctxts due to memstore limitation\n",
KGSL_MEMSTORE_MAX);
- idr_remove(&dev_priv->device->context_idr, id);
ret = -ENOSPC;
- goto func_end;
+ goto fail_free_id;
}
kref_init(&context->refcount);
- context->id = id;
- context->dev_priv = dev_priv;
+ context->device = dev_priv->device;
+ context->pagetable = dev_priv->process_priv->pagetable;
+
+ context->pid = dev_priv->process_priv->pid;
ret = kgsl_sync_timeline_create(context);
- if (ret) {
- idr_remove(&dev_priv->device->context_idr, id);
- goto func_end;
- }
+ if (ret)
+ goto fail_free_id;
/* Initialize the pending event list */
INIT_LIST_HEAD(&context->events);
@@ -516,15 +518,15 @@
*/
INIT_LIST_HEAD(&context->events_list);
-
-func_end:
- if (ret) {
- kfree(context);
- return ERR_PTR(ret);
- }
-
- return context;
+ return 0;
+fail_free_id:
+ write_lock(&device->context_lock);
+ idr_remove(&dev_priv->device->context_idr, id);
+ write_unlock(&device->context_lock);
+fail:
+ return ret;
}
+EXPORT_SYMBOL(kgsl_context_init);
/**
* kgsl_context_detach - Release the "master" context reference
@@ -534,31 +536,36 @@
* has requested for it to be destroyed. The context itself may
* exist a bit longer until its reference count goes to zero.
* Other code referencing the context can detect that it has been
- * detached because the context id will be set to KGSL_CONTEXT_INVALID.
+ * detached by checking the KGSL_CONTEXT_DETACHED bit in
+ * context->priv.
*/
void
kgsl_context_detach(struct kgsl_context *context)
{
- int id;
struct kgsl_device *device;
if (context == NULL)
return;
- device = context->dev_priv->device;
- trace_kgsl_context_detach(device, context);
- id = context->id;
- if (device->ftbl->drawctxt_destroy)
- device->ftbl->drawctxt_destroy(device, context);
- /*device specific drawctxt_destroy MUST clean up devctxt */
- BUG_ON(context->devctxt);
+ device = context->device;
+
+ /*
+ * Mark the context as detached to keep others from using
+ * the context before it gets fully removed, and to make sure
+ * we don't try to detach twice.
+ */
+ if (test_and_set_bit(KGSL_CONTEXT_DETACHED, &context->priv))
+ return;
+
+ trace_kgsl_context_detach(device, context);
+
+ device->ftbl->drawctxt_detach(context);
/*
* Cancel events after the device-specific context is
- * destroyed, to avoid possibly freeing memory while
+ * detached, to avoid possibly freeing memory while
* it is still in use by the GPU.
*/
kgsl_cancel_events_ctxt(device, context);
- idr_remove(&device->context_idr, id);
- context->id = KGSL_CONTEXT_INVALID;
+
kgsl_context_put(context);
}
@@ -567,8 +574,19 @@
{
struct kgsl_context *context = container_of(kref, struct kgsl_context,
refcount);
+ struct kgsl_device *device = context->device;
+
+ trace_kgsl_context_destroy(device, context);
+
+ write_lock(&device->context_lock);
+ if (context->id != KGSL_CONTEXT_INVALID) {
+ idr_remove(&device->context_idr, context->id);
+ context->id = KGSL_CONTEXT_INVALID;
+ }
+ write_unlock(&device->context_lock);
kgsl_sync_timeline_destroy(context);
- kfree(context);
+
+ device->ftbl->drawctxt_destroy(context);
}
struct kgsl_device *kgsl_get_device(int dev_idx)
@@ -960,14 +978,15 @@
kgsl_active_count_get(device);
while (1) {
+ read_lock(&device->context_lock);
context = idr_get_next(&device->context_idr, &next);
+ read_unlock(&device->context_lock);
+
if (context == NULL)
break;
- if (context->dev_priv == dev_priv) {
+ if (context->pid == private->pid)
kgsl_context_detach(context);
- context->dev_priv = NULL;
- }
next = next + 1;
}
@@ -1309,7 +1328,7 @@
result = -EFAULT;
break;
}
- context = kgsl_find_context(dev_priv, id);
+ context = kgsl_context_get_owner(dev_priv, id);
if (!context) {
result = -EINVAL;
break;
@@ -1319,12 +1338,14 @@
* the out parameter
*/
if (copy_to_user(param->value, &(context->reset_status),
- sizeof(unsigned int))) {
+ sizeof(unsigned int)))
result = -EFAULT;
- break;
+ else {
+ /* Clear reset status once its been queried */
+ context->reset_status = KGSL_CTX_STAT_NO_ERROR;
}
- /* Clear reset status once its been queried */
- context->reset_status = KGSL_CTX_STAT_NO_ERROR;
+
+ kgsl_context_put(context);
break;
}
default:
@@ -1393,19 +1414,14 @@
{
struct kgsl_device_waittimestamp_ctxtid *param = data;
struct kgsl_context *context;
- int result;
+ long result = -EINVAL;
- context = kgsl_find_context(dev_priv, param->context_id);
- if (context == NULL)
- return -EINVAL;
- /*
- * A reference count is needed here, because waittimestamp may
- * block with the device mutex unlocked and userspace could
- * request for the context to be destroyed during that time.
- */
- kgsl_context_get(context);
- result = _device_waittimestamp(dev_priv, context,
+ context = kgsl_context_get_owner(dev_priv, param->context_id);
+
+ if (context)
+ result = _device_waittimestamp(dev_priv, context,
param->timestamp, param->timeout);
+
kgsl_context_put(context);
return result;
}
@@ -1419,7 +1435,7 @@
struct kgsl_ibdesc *ibdesc;
struct kgsl_context *context;
- context = kgsl_find_context(dev_priv, param->drawctxt_id);
+ context = kgsl_context_get_owner(dev_priv, param->drawctxt_id);
if (context == NULL) {
result = -EINVAL;
goto done;
@@ -1498,7 +1514,7 @@
free_ibdesc:
kfree(ibdesc);
done:
-
+ kgsl_context_put(context);
return result;
}
@@ -1531,13 +1547,16 @@
{
struct kgsl_cmdstream_readtimestamp_ctxtid *param = data;
struct kgsl_context *context;
+ long result = -EINVAL;
- context = kgsl_find_context(dev_priv, param->context_id);
- if (context == NULL)
- return -EINVAL;
+ context = kgsl_context_get_owner(dev_priv, param->context_id);
- return _cmdstream_readtimestamp(dev_priv, context,
+ if (context)
+ result = _cmdstream_readtimestamp(dev_priv, context,
param->type, ¶m->timestamp);
+
+ kgsl_context_put(context);
+ return result;
}
static void kgsl_freemem_event_cb(struct kgsl_device *device,
@@ -1598,13 +1617,14 @@
{
struct kgsl_cmdstream_freememontimestamp_ctxtid *param = data;
struct kgsl_context *context;
+ long result = -EINVAL;
- context = kgsl_find_context(dev_priv, param->context_id);
- if (context == NULL)
- return -EINVAL;
-
- return _cmdstream_freememontimestamp(dev_priv, param->gpuaddr,
+ context = kgsl_context_get_owner(dev_priv, param->context_id);
+ if (context)
+ result = _cmdstream_freememontimestamp(dev_priv, param->gpuaddr,
context, param->timestamp, param->type);
+ kgsl_context_put(context);
+ return result;
}
static long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv,
@@ -1613,46 +1633,34 @@
int result = 0;
struct kgsl_drawctxt_create *param = data;
struct kgsl_context *context = NULL;
+ struct kgsl_device *device = dev_priv->device;
- context = kgsl_create_context(dev_priv);
-
+ context = device->ftbl->drawctxt_create(dev_priv, ¶m->flags);
if (IS_ERR(context)) {
result = PTR_ERR(context);
goto done;
}
-
- if (dev_priv->device->ftbl->drawctxt_create) {
- result = dev_priv->device->ftbl->drawctxt_create(
- dev_priv->device, dev_priv->process_priv->pagetable,
- context, ¶m->flags);
- if (result)
- goto done;
- }
trace_kgsl_context_create(dev_priv->device, context, param->flags);
param->drawctxt_id = context->id;
done:
- if (result && !IS_ERR(context))
- kgsl_context_detach(context);
-
return result;
}
static long kgsl_ioctl_drawctxt_destroy(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
- int result = 0;
struct kgsl_drawctxt_destroy *param = data;
struct kgsl_context *context;
+ long result = -EINVAL;
- context = kgsl_find_context(dev_priv, param->drawctxt_id);
+ context = kgsl_context_get_owner(dev_priv, param->drawctxt_id);
- if (context == NULL) {
- result = -EINVAL;
- goto done;
+ if (context) {
+ kgsl_context_detach(context);
+ result = 0;
}
- kgsl_context_detach(context);
-done:
+ kgsl_context_put(context);
return result;
}
@@ -2370,7 +2378,8 @@
entries[actual_count++] = entry;
/* If we exceed the breakeven point, flush the entire cache */
- if (op_size >= kgsl_driver.full_cache_threshold &&
+ if (kgsl_driver.full_cache_threshold != 0 &&
+ op_size >= kgsl_driver.full_cache_threshold &&
param->op == KGSL_GPUMEM_CACHE_FLUSH) {
full_flush = true;
break;
@@ -2429,6 +2438,7 @@
int result;
struct kgsl_process_private *private = dev_priv->process_priv;
struct kgsl_mem_entry *entry;
+ int align;
/*
* Mask off unknown flags from userspace. This way the caller can
@@ -2440,6 +2450,16 @@
| KGSL_MEMALIGN_MASK
| KGSL_MEMFLAGS_USE_CPU_MAP;
+ /* Cap the alignment bits to the highest number we can handle */
+
+ align = (flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+ if (align >= 32) {
+ KGSL_CORE_ERR("Alignment too big, restricting to 2^31\n");
+
+ flags &= ~KGSL_MEMALIGN_MASK;
+ flags |= (31 << KGSL_MEMALIGN_SHIFT) & KGSL_MEMALIGN_MASK;
+ }
+
entry = kgsl_mem_entry_create();
if (entry == NULL)
return -ENOMEM;
@@ -2579,7 +2599,7 @@
if (!entry)
return -EINVAL;
- kgsl_cffdump_syncmem(dev_priv, &entry->memdesc, param->gpuaddr,
+ kgsl_cffdump_syncmem(dev_priv->device, &entry->memdesc, param->gpuaddr,
param->len, true);
kgsl_mem_entry_put(entry);
@@ -3418,6 +3438,8 @@
device->id, device->reg_phys, device->reg_len,
device->reg_virt);
+ rwlock_init(&device->context_lock);
+
result = kgsl_drm_init(pdev);
if (result)
goto error_pwrctrl_close;
diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c
index b07a1cad..43bcc30 100644
--- a/drivers/gpu/msm/kgsl_cffdump.c
+++ b/drivers/gpu/msm/kgsl_cffdump.c
@@ -410,29 +410,19 @@
cffdump_printline(-1, cff_opcode, op1, op2, op3, op4, op5);
}
-void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv,
- struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes,
- bool clean_cache)
+void kgsl_cffdump_syncmem(struct kgsl_device *device,
+ struct kgsl_memdesc *memdesc, uint gpuaddr,
+ uint sizebytes, bool clean_cache)
{
- struct kgsl_device *device = dev_priv->device;
const void *src;
if (!device->cff_dump_enable)
return;
+ BUG_ON(memdesc == NULL);
+
total_syncmem += sizebytes;
- if (memdesc == NULL) {
- struct kgsl_mem_entry *entry;
- entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
- gpuaddr, sizebytes);
- if (entry == NULL) {
- KGSL_CORE_ERR("did not find mapping "
- "for gpuaddr: 0x%08x\n", gpuaddr);
- return;
- }
- memdesc = &entry->memdesc;
- }
src = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr);
if (memdesc->hostptr == NULL) {
KGSL_CORE_ERR(
diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h
index 641348e..2852e0f 100644
--- a/drivers/gpu/msm/kgsl_cffdump.h
+++ b/drivers/gpu/msm/kgsl_cffdump.h
@@ -14,19 +14,21 @@
#ifndef __KGSL_CFFDUMP_H
#define __KGSL_CFFDUMP_H
-extern unsigned int kgsl_cff_dump_enable;
-
-#ifdef CONFIG_MSM_KGSL_CFF_DUMP
-
#include <linux/types.h>
-#include "kgsl_device.h"
+extern unsigned int kgsl_cff_dump_enable;
+
+static inline bool kgsl_cffdump_flags_no_memzero(void) { return true; }
+
+struct kgsl_device_private;
+
+#ifdef CONFIG_MSM_KGSL_CFF_DUMP
void kgsl_cffdump_init(void);
void kgsl_cffdump_destroy(void);
void kgsl_cffdump_open(struct kgsl_device *device);
void kgsl_cffdump_close(struct kgsl_device *device);
-void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv,
+void kgsl_cffdump_syncmem(struct kgsl_device *,
struct kgsl_memdesc *memdesc, uint physaddr, uint sizebytes,
bool clean_cache);
void kgsl_cffdump_setmem(struct kgsl_device *device, uint addr,
@@ -42,7 +44,6 @@
unsigned int cff_opcode, unsigned int op1,
unsigned int op2, unsigned int op3,
unsigned int op4, unsigned int op5);
-static inline bool kgsl_cffdump_flags_no_memzero(void) { return true; }
void kgsl_cffdump_memory_base(struct kgsl_device *device, unsigned int base,
unsigned int range, unsigned int gmemsize);
@@ -53,20 +54,69 @@
#else
-#define kgsl_cffdump_init() (void)0
-#define kgsl_cffdump_destroy() (void)0
-#define kgsl_cffdump_open(device) (void)0
-#define kgsl_cffdump_close(device) (void)0
-#define kgsl_cffdump_syncmem(dev_priv, memdesc, addr, sizebytes, clean_cache) \
- (void) 0
-#define kgsl_cffdump_setmem(device, addr, value, sizebytes) (void)0
-#define kgsl_cffdump_regwrite(device, addr, value) (void)0
-#define kgsl_cffdump_regpoll(device, addr, value, mask) (void)0
-#define kgsl_cffdump_parse_ibs(dev_priv, memdesc, gpuaddr, \
- sizedwords, check_only) true
-#define kgsl_cffdump_flags_no_memzero() true
-#define kgsl_cffdump_memory_base(davice, base, range, gmemsize) (void)0
-#define kgsl_cffdump_hang(device) (void)0
+static inline void kgsl_cffdump_init(void)
+{
+ return;
+}
+
+static inline void kgsl_cffdump_destroy(void)
+{
+ return;
+}
+
+static inline void kgsl_cffdump_open(struct kgsl_device *device)
+{
+ return;
+}
+
+static inline void kgsl_cffdump_close(struct kgsl_device *device)
+{
+ return;
+}
+
+static inline void kgsl_cffdump_syncmem(struct kgsl_device *device,
+ struct kgsl_memdesc *memdesc, uint physaddr, uint sizebytes,
+ bool clean_cache)
+{
+ return;
+}
+
+static inline void kgsl_cffdump_setmem(struct kgsl_device *device, uint addr,
+ uint value, uint sizebytes)
+{
+ return;
+}
+
+static inline void kgsl_cffdump_regwrite(struct kgsl_device *device, uint addr,
+ uint value)
+{
+ return;
+}
+
+static inline void kgsl_cffdump_regpoll(struct kgsl_device *device, uint addr,
+ uint value, uint mask)
+{
+ return;
+}
+
+static inline bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv,
+ const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords,
+ bool check_only)
+{
+ return false;
+}
+
+static inline void kgsl_cffdump_memory_base(struct kgsl_device *device,
+ unsigned int base, unsigned int range, unsigned int gmemsize)
+{
+ return;
+}
+
+static inline void kgsl_cffdump_hang(struct kgsl_device *device)
+{
+ return;
+}
+
static inline void kgsl_cffdump_user_event(struct kgsl_device *device,
unsigned int cff_opcode, unsigned int op1,
unsigned int op2, unsigned int op3,
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 2af5ccd..1f8bcbe 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -15,6 +15,7 @@
#include <linux/idr.h>
#include <linux/pm_qos.h>
+#include <linux/sched.h>
#include "kgsl.h"
#include "kgsl_mmu.h"
@@ -103,11 +104,10 @@
calling the hook */
void (*setstate) (struct kgsl_device *device, unsigned int context_id,
uint32_t flags);
- int (*drawctxt_create) (struct kgsl_device *device,
- struct kgsl_pagetable *pagetable, struct kgsl_context *context,
- uint32_t *flags);
- void (*drawctxt_destroy) (struct kgsl_device *device,
- struct kgsl_context *context);
+ struct kgsl_context *(*drawctxt_create) (struct kgsl_device_private *,
+ uint32_t *flags);
+ void (*drawctxt_detach) (struct kgsl_context *context);
+ void (*drawctxt_destroy) (struct kgsl_context *context);
long (*ioctl) (struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data);
int (*setproperty) (struct kgsl_device *device,
@@ -191,6 +191,7 @@
struct completion ft_gate;
struct dentry *d_debugfs;
struct idr context_idr;
+ rwlock_t context_lock;
void *snapshot; /* Pointer to the snapshot memory region */
int snapshot_maxsize; /* Max size of the snapshot region */
@@ -253,31 +254,44 @@
.ver_minor = DRIVER_VERSION_MINOR
+/* bits for struct kgsl_context.priv */
+/* the context has been destroyed by userspace and is no longer using the gpu */
+#define KGSL_CONTEXT_DETACHED 0
+/* the context has caused a pagefault */
+#define KGSL_CONTEXT_PAGEFAULT 1
+
/**
* struct kgsl_context - Master structure for a KGSL context object
- * @refcount - kref object for reference counting the context
- * @id - integer identifier for the context
- * @dev_priv - pointer to the owning device instance
- * @devctxt - pointer to the device specific context information
- * @reset_status - status indication whether a gpu reset occured and whether
+ * @refcount: kref object for reference counting the context
+ * @id: integer identifier for the context
+ * @priv: in-kernel context flags, use KGSL_CONTEXT_* values
+ * @dev_priv: pointer to the owning device instance
+ * @reset_status: status indication whether a gpu reset occured and whether
* this context was responsible for causing it
- * @wait_on_invalid_ts - flag indicating if this context has tried to wait on a
+ * @wait_on_invalid_ts: flag indicating if this context has tried to wait on a
* bad timestamp
- * @timeline - sync timeline used to create fences that can be signaled when a
+ * @timeline: sync timeline used to create fences that can be signaled when a
* sync_pt timestamp expires
- * @events - list head of pending events for this context
- * @events_list - list node for the list of all contexts that have pending events
+ * @events: list head of pending events for this context
+ * @events_list: list node for the list of all contexts that have pending events
+ * @pid: process that owns this context.
+ * @pagefault: flag set if this context caused a pagefault.
+ * @pagefault_ts: global timestamp of the pagefault, if KGSL_CONTEXT_PAGEFAULT
+ * is set.
*/
struct kgsl_context {
struct kref refcount;
uint32_t id;
- struct kgsl_device_private *dev_priv;
- void *devctxt;
+ pid_t pid;
+ unsigned long priv;
+ struct kgsl_device *device;
+ struct kgsl_pagetable *pagetable;
unsigned int reset_status;
bool wait_on_invalid_ts;
struct sync_timeline *timeline;
struct list_head events;
struct list_head events_list;
+ unsigned int pagefault_ts;
};
struct kgsl_process_private {
@@ -402,17 +416,7 @@
return 0;
}
-static inline struct kgsl_context *
-kgsl_find_context(struct kgsl_device_private *dev_priv, uint32_t id)
-{
- struct kgsl_context *ctxt =
- idr_find(&dev_priv->device->context_idr, id);
- /* Make sure that the context belongs to the current instance so
- that other processes can't guess context IDs and mess things up */
-
- return (ctxt && ctxt->dev_priv == dev_priv) ? ctxt : NULL;
-}
int kgsl_check_timestamp(struct kgsl_device *device,
struct kgsl_context *context, unsigned int timestamp);
@@ -436,32 +440,111 @@
return pdev->dev.platform_data;
}
-/**
- * kgsl_context_get - Get context reference count
- * @context
- *
- * Asynchronous code that holds a pointer to a context
- * must hold a reference count on it. The kgsl device
- * mutex must be held while the context reference count
- * is changed.
- */
-static inline void
-kgsl_context_get(struct kgsl_context *context)
-{
- kref_get(&context->refcount);
-}
-
void kgsl_context_destroy(struct kref *kref);
+int kgsl_context_init(struct kgsl_device_private *, struct kgsl_context
+ *context);
+
/**
- * kgsl_context_put - Release context reference count
- * @context
+ * kgsl_context_put() - Release context reference count
+ * @context: Pointer to the KGSL context to be released
*
+ * Reduce the reference count on a KGSL context and destroy it if it is no
+ * longer needed
*/
static inline void
kgsl_context_put(struct kgsl_context *context)
{
- kref_put(&context->refcount, kgsl_context_destroy);
+ if (context)
+ kref_put(&context->refcount, kgsl_context_destroy);
+}
+
+/**
+ * kgsl_context_detached() - check if a context is detached
+ * @context: the context
+ *
+ * Check if a context has been destroyed by userspace and is only waiting
+ * for reference counts to go away. This check is used to weed out
+ * contexts that shouldn't use the gpu so NULL is considered detached.
+ */
+static inline bool kgsl_context_detached(struct kgsl_context *context)
+{
+ return (context == NULL || test_bit(KGSL_CONTEXT_DETACHED,
+ &context->priv));
+}
+
+
+/**
+ * kgsl_context_get() - get a pointer to a KGSL context
+ * @device: Pointer to the KGSL device that owns the context
+ * @id: Context ID
+ *
+ * Find the context associated with the given ID number, increase the reference
+ * count on it and return it. The caller must make sure that this call is
+ * paired with a kgsl_context_put. This function is for internal use because it
+ * doesn't validate the ownership of the context with the calling process - use
+ * kgsl_context_get_owner for that
+ */
+static inline struct kgsl_context *kgsl_context_get(struct kgsl_device *device,
+ uint32_t id)
+{
+ struct kgsl_context *context = NULL;
+
+ read_lock(&device->context_lock);
+
+ context = idr_find(&device->context_idr, id);
+
+ /* Don't return a context that has been detached */
+ if (kgsl_context_detached(context))
+ context = NULL;
+ else
+ kref_get(&context->refcount);
+
+ read_unlock(&device->context_lock);
+
+ return context;
+}
+
+/**
+* _kgsl_context_get() - lightweight function to just increment the ref count
+* @context: Pointer to the KGSL context
+*
+* Get a reference to the specified KGSL context structure. This is a
+* lightweight way to just increase the refcount on a known context rather than
+* walking through kgsl_context_get and searching the iterator
+*/
+static inline void _kgsl_context_get(struct kgsl_context *context)
+{
+ if (context)
+ kref_get(&context->refcount);
+}
+
+/**
+ * kgsl_context_get_owner() - get a pointer to a KGSL context in a specific
+ * process
+ * @dev_priv: Pointer to the process struct
+ * @id: Context ID to return
+ *
+ * Find the context associated with the given ID number, increase the reference
+ * count on it and return it. The caller must make sure that this call is
+ * paired with a kgsl_context_put. This function validates that the context id
+ * given is owned by the dev_priv instancet that is passed in. See
+ * kgsl_context_get for the internal version that doesn't do the check
+ */
+static inline struct kgsl_context *kgsl_context_get_owner(
+ struct kgsl_device_private *dev_priv, uint32_t id)
+{
+ struct kgsl_context *context;
+
+ context = kgsl_context_get(dev_priv->device, id);
+
+ /* Verify that the context belongs to current calling process. */
+ if (context != NULL && context->pid != dev_priv->process_priv->pid) {
+ kgsl_context_put(context);
+ return NULL;
+ }
+
+ return context;
}
#endif /* __KGSL_DEVICE_H */
diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c
index daeefd0..1fc7467 100644
--- a/drivers/gpu/msm/kgsl_drm.c
+++ b/drivers/gpu/msm/kgsl_drm.c
@@ -722,7 +722,7 @@
ret = -EINVAL;
else if (TYPE_IS_PMEM(priv->type) || TYPE_IS_MEM(priv->type)) {
if (priv->ion_handle) {
- args->ion_fd = ion_share_dma_buf(
+ args->ion_fd = ion_share_dma_buf_fd(
kgsl_drm_ion_client, priv->ion_handle);
if (args->ion_fd < 0) {
DRM_ERROR(
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index a1fc5a2..40974b0 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -60,7 +60,7 @@
return -EINVAL;
if (id != KGSL_MEMSTORE_GLOBAL) {
- context = idr_find(&device->context_idr, id);
+ context = kgsl_context_get(device, id);
if (context == NULL)
return -EINVAL;
}
@@ -76,12 +76,15 @@
if (timestamp_cmp(cur_ts, ts) >= 0) {
trace_kgsl_fire_event(id, ts, 0);
cb(device, priv, id, ts);
+ kgsl_context_put(context);
return 0;
}
event = kzalloc(sizeof(*event), GFP_KERNEL);
- if (event == NULL)
+ if (event == NULL) {
+ kgsl_context_put(context);
return -ENOMEM;
+ }
/*
* Increase the active count on the device to avoid going into power
@@ -89,6 +92,7 @@
*/
ret = kgsl_active_count_get_light(device);
if (ret < 0) {
+ kgsl_context_put(context);
kfree(event);
return ret;
}
@@ -102,10 +106,6 @@
trace_kgsl_register_event(id, ts);
- /* inc refcount to avoid race conditions in cleanup */
- if (context)
- kgsl_context_get(context);
-
/* Add the event to either the owning context or the global list */
if (context) {
@@ -147,7 +147,7 @@
* Increment the refcount to avoid freeing the context while
* cancelling its events
*/
- kgsl_context_get(context);
+ _kgsl_context_get(context);
/* Remove ourselves from the master pending list */
list_del_init(&context->events_list);
@@ -324,7 +324,7 @@
* Increment the refcount to make sure that the list_del_init
* is called with a valid context's list
*/
- kgsl_context_get(context);
+ _kgsl_context_get(context);
/*
* If kgsl_timestamp_expired_context returns 0 then it no longer
* has any pending events and can be removed from the list
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 0bacc5e..14aae9f 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -38,6 +38,7 @@
static struct kgsl_iommu_register_list kgsl_iommuv0_reg[KGSL_IOMMU_REG_MAX] = {
{ 0, 0 }, /* GLOBAL_BASE */
+ { 0x0, 1 }, /* SCTLR */
{ 0x10, 1 }, /* TTBR0 */
{ 0x14, 1 }, /* TTBR1 */
{ 0x20, 1 }, /* FSR */
@@ -54,6 +55,7 @@
static struct kgsl_iommu_register_list kgsl_iommuv1_reg[KGSL_IOMMU_REG_MAX] = {
{ 0, 0 }, /* GLOBAL_BASE */
+ { 0x0, 1 }, /* SCTLR */
{ 0x20, 1 }, /* TTBR0 */
{ 0x28, 1 }, /* TTBR1 */
{ 0x58, 1 }, /* FSR */
@@ -287,7 +289,7 @@
addr < (p->gpuaddr + p->size)) {
kgsl_get_memory_usage(name, sizeof(name) - 1,
- p->flags),
+ p->flags);
KGSL_LOG_DUMP(iommu_dev->kgsldev,
"---- premature free ----\n");
KGSL_LOG_DUMP(iommu_dev->kgsldev,
@@ -325,8 +327,7 @@
unsigned int no_page_fault_log = 0;
unsigned int curr_context_id = 0;
unsigned int curr_global_ts = 0;
- static struct adreno_context *curr_context;
- static struct kgsl_context *context;
+ struct kgsl_context *context;
ret = get_iommu_unit(dev, &mmu, &iommu_unit);
if (ret)
@@ -396,20 +397,20 @@
kgsl_sharedmem_readl(&device->memstore, &curr_context_id,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context));
- context = idr_find(&device->context_idr, curr_context_id);
- if (context != NULL) {
- curr_context = context->devctxt;
+ context = kgsl_context_get(device, curr_context_id);
+
+ if (context != NULL) {
kgsl_sharedmem_readl(&device->memstore, &curr_global_ts,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
eoptimestamp));
- /*
- * Store pagefault's timestamp in adreno context,
- * this information will be used in GFT
- */
- curr_context->pagefault = 1;
- curr_context->pagefault_ts = curr_global_ts;
+ /* save pagefault timestamp for GFT */
+ set_bit(KGSL_CONTEXT_PAGEFAULT, &context->priv);
+ context->pagefault_ts = curr_global_ts;
+
+ kgsl_context_put(context);
+ context = NULL;
}
trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
@@ -1534,6 +1535,7 @@
KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit,
iommu_unit->dev[j].ctx_id,
V2PUR, v2pxx);
+ mb();
vaddr += PAGE_SIZE;
for (l = 0; l < iommu_unit->dev_count; l++) {
tlblkcr = KGSL_IOMMU_GET_CTX_REG(iommu,
@@ -1574,6 +1576,8 @@
int status;
struct kgsl_iommu *iommu = mmu->priv;
int i, j;
+ int sctlr_val = 0;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(mmu->device);
if (mmu->flags & KGSL_FLAGS_STARTED)
return 0;
@@ -1625,6 +1629,25 @@
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
for (j = 0; j < iommu_unit->dev_count; j++) {
+
+ /*
+ * For IOMMU V1 do not halt IOMMU on pagefault if
+ * FT pagefault policy is set accordingly
+ */
+ if ((!msm_soc_version_supports_iommu_v0()) &&
+ (!(adreno_dev->ft_pf_policy &
+ KGSL_FT_PAGEFAULT_GPUHALT_ENABLE))) {
+ sctlr_val = KGSL_IOMMU_GET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ SCTLR);
+ sctlr_val |= (0x1 <<
+ KGSL_IOMMU_SCTLR_HUPCF_SHIFT);
+ KGSL_IOMMU_SET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ SCTLR, sctlr_val);
+ }
if (sizeof(phys_addr_t) > sizeof(unsigned long)) {
iommu_unit->dev[j].default_ttbr0 =
KGSL_IOMMU_GET_CTX_REG_LL(iommu,
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index 5c4b17e..7dca40e 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -61,8 +61,12 @@
#define KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_HALT BIT(2)
#define KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_IDLE BIT(3)
+/* SCTLR fields */
+#define KGSL_IOMMU_SCTLR_HUPCF_SHIFT 8
+
enum kgsl_iommu_reg_map {
KGSL_IOMMU_GLOBAL_BASE = 0,
+ KGSL_IOMMU_CTX_SCTLR,
KGSL_IOMMU_CTX_TTBR0,
KGSL_IOMMU_CTX_TTBR1,
KGSL_IOMMU_CTX_FSR,
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 91d462b..20391be 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -1214,6 +1214,11 @@
}
}
+bool kgsl_pwrctrl_isenabled(struct kgsl_device *device)
+{
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ return (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags) != 0);
+}
/**
* kgsl_pre_hwaccess - Enforce preconditions for touching registers
@@ -1230,7 +1235,7 @@
/* In order to touch a register you must hold the device mutex...*/
BUG_ON(!mutex_is_locked(&device->mutex));
/* and have the clock on! */
- BUG_ON(!test_bit(KGSL_PWRFLAGS_CLK_ON, &device->pwrctrl.power_flags));
+ BUG_ON(!kgsl_pwrctrl_isenabled(device));
}
EXPORT_SYMBOL(kgsl_pre_hwaccess);
@@ -1389,13 +1394,14 @@
(unsigned int *) &context_id,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
current_context));
- context = idr_find(&device->context_idr, context_id);
+ context = kgsl_context_get(device, context_id);
if (context)
ts_processed = kgsl_readtimestamp(device, context,
KGSL_TIMESTAMP_RETIRED);
KGSL_PWR_INFO(device, "Wake from %s state. CTXT: %d RTRD TS: %08X\n",
kgsl_pwrstate_to_str(state),
context ? context->id : -1, ts_processed);
+ kgsl_context_put(context);
/* fall through */
case KGSL_STATE_NAP:
/* Turn on the core clocks */
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 3bf65ee..b7d9226 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -110,6 +110,8 @@
void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device);
void kgsl_pwrctrl_enable(struct kgsl_device *device);
void kgsl_pwrctrl_disable(struct kgsl_device *device);
+bool kgsl_pwrctrl_isenabled(struct kgsl_device *device);
+
static inline unsigned long kgsl_get_clkrate(struct clk *clk)
{
return (clk != NULL) ? clk_get_rate(clk) : 0;
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index d3edbba..6094e04 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -109,7 +109,7 @@
struct kgsl_device *device;
if (context)
- device = context->dev_priv->device;
+ device = context->device;
else
device = (struct kgsl_device *)data;
@@ -145,7 +145,9 @@
/* Figure out how many active contexts there are - these will
* be appended on the end of the structure */
+ read_lock(&device->context_lock);
idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount);
+ read_unlock(&device->context_lock);
/* Increment ctxcount for the global memstore */
ctxtcount++;
@@ -202,7 +204,10 @@
/* append information for the global context */
snapshot_context_info(KGSL_MEMSTORE_GLOBAL, NULL, device);
/* append information for each context */
+
+ read_lock(&device->context_lock);
idr_for_each(&device->context_idr, snapshot_context_info, NULL);
+ read_unlock(&device->context_lock);
/* Return the size of the data segment */
return size;
diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index 2f67405..62ecdeb 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -121,16 +121,17 @@
if (len != sizeof(priv))
return -EINVAL;
- context = kgsl_find_context(owner, context_id);
- if (context == NULL)
- return -EINVAL;
-
event = kzalloc(sizeof(*event), GFP_KERNEL);
if (event == NULL)
return -ENOMEM;
+
+ context = kgsl_context_get_owner(owner, context_id);
+
+ if (context == NULL)
+ goto fail_pt;
+
event->context = context;
event->timestamp = timestamp;
- kgsl_context_get(context);
pt = kgsl_sync_pt_create(context->timeline, timestamp);
if (pt == NULL) {
@@ -161,6 +162,10 @@
goto fail_copy_fd;
}
+ /*
+ * Hold the context ref-count for the event - it will get released in
+ * the callback
+ */
ret = kgsl_add_event(device, context_id, timestamp,
kgsl_fence_event_cb, event, owner);
if (ret)
@@ -185,12 +190,16 @@
static unsigned int kgsl_sync_get_timestamp(
struct kgsl_sync_timeline *ktimeline, enum kgsl_timestamp_type type)
{
- struct kgsl_context *context = idr_find(&ktimeline->device->context_idr,
- ktimeline->context_id);
- if (context == NULL)
- return 0;
+ unsigned int ret = 0;
- return kgsl_readtimestamp(ktimeline->device, context, type);
+ struct kgsl_context *context = kgsl_context_get(ktimeline->device,
+ ktimeline->context_id);
+
+ if (context)
+ ret = kgsl_readtimestamp(ktimeline->device, context, type);
+
+ kgsl_context_put(context);
+ return ret;
}
static void kgsl_sync_timeline_value_str(struct sync_timeline *sync_timeline,
@@ -230,7 +239,7 @@
char ktimeline_name[sizeof(context->timeline->name)] = {};
snprintf(ktimeline_name, sizeof(ktimeline_name),
"%s_%.15s(%d)-%.15s(%d)-%d",
- context->dev_priv->device->name,
+ context->device->name,
current->group_leader->comm, current->group_leader->pid,
current->comm, current->pid, context->id);
@@ -241,7 +250,7 @@
ktimeline = (struct kgsl_sync_timeline *) context->timeline;
ktimeline->last_timestamp = 0;
- ktimeline->device = context->dev_priv->device;
+ ktimeline->device = context->device;
ktimeline->context_id = context->id;
return 0;
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index 48c7301..5981163 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -70,7 +70,7 @@
),
TP_printk(
- "d_name=%s ctx=%u ib=0x%u numibs=%u timestamp=0x%x "
+ "d_name=%s ctx=%u ib=0x%u numibs=%u ts=%u "
"flags=0x%x(%s) result=%d type=%s",
__get_str(device_name),
__entry->drawctxt_id,
@@ -116,7 +116,7 @@
),
TP_printk(
- "d_name=%s context_id=%u type=%u timestamp=0x%x",
+ "d_name=%s context_id=%u type=%u ts=%u",
__get_str(device_name),
__entry->context_id,
__entry->type,
@@ -154,7 +154,7 @@
),
TP_printk(
- "d_name=%s context_id=%u curr_ts=0x%x timestamp=0x%x timeout=%u",
+ "d_name=%s ctx=%u curr_ts=%u ts=%u timeout=%u",
__get_str(device_name),
__entry->context_id,
__entry->curr_ts,
@@ -186,7 +186,7 @@
),
TP_printk(
- "d_name=%s curr_ts=0x%x result=%d",
+ "d_name=%s curr_ts=%u result=%d",
__get_str(device_name),
__entry->curr_ts,
__entry->result
@@ -209,7 +209,7 @@
),
TP_printk(
- "d_name=%s %s",
+ "d_name=%s flag=%s",
__get_str(device_name),
__entry->on ? "on" : "off"
)
@@ -304,7 +304,7 @@
),
TP_printk(
- "d_name=%s busy=%d elapsed=%d",
+ "d_name=%s busy=%u elapsed=%d",
__get_str(device_name),
__entry->busy,
__entry->elapsed
@@ -327,7 +327,7 @@
),
TP_printk(
- "d_name=%s %s",
+ "d_name=%s state=%s",
__get_str(device_name),
kgsl_pwrstate_to_str(__entry->state)
)
@@ -369,7 +369,7 @@
),
TP_printk(
- "gpuaddr=0x%08x size=%d tgid=%d usage=%s id=%d flags=0x%08x",
+ "gpuaddr=0x%08x size=%u tgid=%u usage=%s id=%u flags=0x%08x",
__entry->gpuaddr, __entry->size, __entry->tgid,
__entry->usage, __entry->id, __entry->flags
)
@@ -401,7 +401,7 @@
),
TP_printk(
- "useraddr=%lx gpuaddr=0x%08x size=%d usage=%s id=%d"
+ "useraddr=0x%lx gpuaddr=0x%08x size=%u usage=%s id=%u"
" flags=0x%08x",
__entry->useraddr, __entry->gpuaddr, __entry->size,
__entry->usage, __entry->id, __entry->flags
@@ -432,7 +432,7 @@
),
TP_printk(
- "id=%d hint=0x%lx len=%ld addr=0x%lx",
+ "id=%u hint=0x%lx len=%lu addr=0x%lx",
__entry->id, __entry->hint, __entry->len, __entry->addr
)
);
@@ -465,7 +465,7 @@
),
TP_printk(
- "gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d usage=%s id=%d",
+ "gpuaddr=0x%08x size=%u type=%d fd=%d tgid=%u usage=%s id=%u",
__entry->gpuaddr, __entry->size,
__entry->type, __entry->fd, __entry->tgid,
__entry->usage, __entry->id
@@ -499,7 +499,7 @@
),
TP_printk(
- "gpuaddr=0x%08x size=%d type=%d tgid=%d usage=%s id=%d",
+ "gpuaddr=0x%08x size=%u type=%d tgid=%u usage=%s id=%u",
__entry->gpuaddr, __entry->size, __entry->type,
__entry->tgid, __entry->usage, __entry->id
)
@@ -531,7 +531,7 @@
),
TP_printk(
- "gpuaddr=0x%08x size=%d tgid=%d usage=%s id=%d op=%c%c",
+ "gpuaddr=0x%08x size=%u tgid=%u usage=%s id=%u op=%c%c",
__entry->gpuaddr, __entry->size, __entry->tgid, __entry->usage,
__entry->id,
(__entry->op & KGSL_GPUMEM_CACHE_CLEAN) ? 'c' : '.',
@@ -599,8 +599,8 @@
),
TP_printk(
- "d_name=%s gpuaddr=0x%08x size=%d type=%d usage=%s id=%d ctx=%u"
- " curr_ts=0x%x free_ts=0x%x",
+ "d_name=%s gpuaddr=0x%08x size=%u type=%d usage=%s id=%u ctx=%u"
+ " curr_ts=%u free_ts=%u",
__get_str(device_name),
__entry->gpuaddr,
__entry->size,
@@ -678,6 +678,28 @@
)
);
+TRACE_EVENT(kgsl_context_destroy,
+
+ TP_PROTO(struct kgsl_device *device, struct kgsl_context *context),
+
+ TP_ARGS(device, context),
+
+ TP_STRUCT__entry(
+ __string(device_name, device->name)
+ __field(unsigned int, id)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device_name, device->name);
+ __entry->id = context->id;
+ ),
+
+ TP_printk(
+ "d_name=%s ctx=%u",
+ __get_str(device_name), __entry->id
+ )
+);
+
TRACE_EVENT(kgsl_mmu_pagefault,
TP_PROTO(struct kgsl_device *device, unsigned int page,
@@ -700,7 +722,7 @@
),
TP_printk(
- "d_name=%s page=0x%08x pt=%d op=%s",
+ "d_name=%s page=0x%08x pt=%u op=%s",
__get_str(device_name), __entry->page, __entry->pt,
__get_str(op)
)
@@ -726,7 +748,7 @@
),
TP_printk(
- "d_name=%s reg=%x value=%x",
+ "d_name=%s reg=0x%x value=0x%x",
__get_str(device_name), __entry->offset, __entry->value
)
);
@@ -743,7 +765,7 @@
__entry->timestamp = timestamp;
),
TP_printk(
- "ctx=%d ts=%d",
+ "ctx=%u ts=%u",
__entry->id, __entry->timestamp)
);
@@ -762,7 +784,7 @@
__entry->age = age;
),
TP_printk(
- "ctx=%d ts=%d age=%u",
+ "ctx=%u ts=%u age=%u",
__entry->id, __entry->ts, __entry->age)
);
@@ -785,7 +807,7 @@
),
TP_printk(
- "d_name=%s active_cnt=%x func=%pf",
+ "d_name=%s active_cnt=%u func=%pf",
__get_str(device_name), __entry->count, (void *) __entry->ip
)
);
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index cc1819d..883417f 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -859,11 +859,30 @@
return status;
}
-static void
-z180_drawctxt_destroy(struct kgsl_device *device,
- struct kgsl_context *context)
+struct kgsl_context *
+z180_drawctxt_create(struct kgsl_device_private *dev_priv,
+ uint32_t *flags)
{
- struct z180_device *z180_dev = Z180_DEVICE(device);
+ int ret;
+ struct kgsl_context *context = kzalloc(sizeof(*context), GFP_KERNEL);
+ if (context == NULL)
+ return ERR_PTR(-ENOMEM);
+ ret = kgsl_context_init(dev_priv, context);
+ if (ret != 0) {
+ kfree(context);
+ return ERR_PTR(ret);
+ }
+ return context;
+}
+
+static void
+z180_drawctxt_detach(struct kgsl_context *context)
+{
+ struct kgsl_device *device;
+ struct z180_device *z180_dev;
+
+ device = context->device;
+ z180_dev = Z180_DEVICE(device);
z180_idle(device);
@@ -875,6 +894,12 @@
}
}
+static void
+z180_drawctxt_destroy(struct kgsl_context *context)
+{
+ kfree(context);
+}
+
static void z180_power_stats(struct kgsl_device *device,
struct kgsl_power_stats *stats)
{
@@ -941,7 +966,8 @@
.gpuid = z180_gpuid,
.irq_handler = z180_irq_handler,
/* Optional functions */
- .drawctxt_create = NULL,
+ .drawctxt_create = z180_drawctxt_create,
+ .drawctxt_detach = z180_drawctxt_detach,
.drawctxt_destroy = z180_drawctxt_destroy,
.ioctl = NULL,
.postmortem_dump = z180_dump,
diff --git a/drivers/hwmon/epm_adc.c b/drivers/hwmon/epm_adc.c
index a587ed2..a309ceb 100644
--- a/drivers/hwmon/epm_adc.c
+++ b/drivers/hwmon/epm_adc.c
@@ -65,6 +65,8 @@
#define EPM_ADC_MILLI_VOLTS_SOURCE 4750
#define EPM_ADC_SCALE_FACTOR 64
#define GPIO_EPM_GLOBAL_ENABLE 86
+#define GPIO_EPM_MARKER1 85
+#define GPIO_EPM_MARKER2 96
#define EPM_ADC_CONVERSION_TIME_MIN 50000
#define EPM_ADC_CONVERSION_TIME_MAX 51000
/* PSoc Commands */
@@ -715,6 +717,56 @@
return 0;
}
+static int epm_set_marker1(struct epm_marker_level *marker_init)
+{
+ int rc = 0;
+
+ rc = gpio_request(GPIO_EPM_MARKER1, "EPM_MARKER1");
+ if (!rc) {
+ gpio_direction_output(GPIO_EPM_MARKER1, 1);
+ } else {
+ pr_err("%s: Configure MARKER1 GPIO Failed\n",
+ __func__);
+ return rc;
+ }
+
+ gpio_set_value(GPIO_EPM_MARKER1, marker_init->level);
+
+ return 0;
+}
+
+static int epm_set_marker2(struct epm_marker_level *marker_init)
+{
+ int rc = 0;
+
+ rc = gpio_request(GPIO_EPM_MARKER2, "EPM_MARKER2");
+ if (!rc) {
+ gpio_direction_output(GPIO_EPM_MARKER2, 1);
+ } else {
+ pr_err("%s: Configure MARKER2 GPIO Failed\n",
+ __func__);
+ return rc;
+ }
+
+ gpio_set_value(GPIO_EPM_MARKER2, marker_init->level);
+
+ return 0;
+}
+
+static int epm_marker1_release(void)
+{
+ gpio_free(GPIO_EPM_MARKER1);
+
+ return 0;
+}
+
+static int epm_marker2_release(void)
+{
+ gpio_free(GPIO_EPM_MARKER2);
+
+ return 0;
+}
+
static int epm_psoc_init(struct epm_adc_drv *epm_adc,
struct epm_psoc_init_resp *init_resp)
{
@@ -1296,6 +1348,58 @@
return -EFAULT;
break;
}
+ case EPM_MARKER1_REQUEST:
+ {
+ struct epm_marker_level marker_init;
+ uint32_t result;
+
+ if (copy_from_user(&marker_init, (void __user *)arg,
+ sizeof(struct epm_marker_level)))
+ return -EFAULT;
+
+ result = epm_set_marker1(&marker_init);
+
+ if (copy_to_user((void __user *)arg, &result,
+ sizeof(uint32_t)))
+ return -EFAULT;
+ break;
+ }
+ case EPM_MARKER2_REQUEST:
+ {
+ struct epm_marker_level marker_init;
+ uint32_t result;
+
+ if (copy_from_user(&marker_init, (void __user *)arg,
+ sizeof(struct epm_marker_level)))
+ return -EFAULT;
+
+ result = epm_set_marker2(&marker_init);
+
+ if (copy_to_user((void __user *)arg, &result,
+ sizeof(uint32_t)))
+ return -EFAULT;
+ break;
+ }
+ case EPM_MARKER1_RELEASE:
+ {
+ uint32_t result;
+ result = epm_marker1_release();
+
+ if (copy_to_user((void __user *)arg, &result,
+ sizeof(uint32_t)))
+ return -EFAULT;
+ break;
+ }
+ case EPM_MARKER2_RELEASE:
+ {
+ uint32_t result;
+ result = epm_marker2_release();
+
+ if (copy_to_user((void __user *)arg, &result,
+ sizeof(uint32_t)))
+ return -EFAULT;
+ break;
+ }
case EPM_PSOC_ADC_INIT:
{
struct epm_psoc_init_resp psoc_init;
@@ -1312,11 +1416,29 @@
return -EINVAL;
}
+ if (!rc) {
+ rc = epm_adc_psoc_gpio_init(true);
+ if (rc) {
+ pr_err("GPIO init failed\n");
+ return -EINVAL;
+ }
+ }
+
if (copy_to_user((void __user *)arg, &psoc_init,
sizeof(struct epm_psoc_init_resp)))
return -EFAULT;
break;
}
+ case EPM_PSOC_ADC_DEINIT:
+ {
+ uint32_t result;
+ result = epm_adc_psoc_gpio_init(false);
+
+ if (copy_to_user((void __user *)arg, &result,
+ sizeof(uint32_t)))
+ return -EFAULT;
+ break;
+ }
case EPM_PSOC_ADC_CHANNEL_ENABLE:
case EPM_PSOC_ADC_CHANNEL_DISABLE:
{
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 8e350f0..4e77ca2 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -923,8 +923,12 @@
pr_err("Invalid channel fast average setup\n");
return -EINVAL;
}
- calibration_param = of_get_property(child,
- "qcom,calibration-type", NULL);
+ rc = of_property_read_string(child, "qcom,calibration-type",
+ &calibration_param);
+ if (rc) {
+ pr_err("Invalid calibration type\n");
+ return -EINVAL;
+ }
if (!strncmp(calibration_param, "absolute", 8))
calib_type = CALIB_ABSOLUTE;
else if (!strncmp(calibration_param, "ratiometric", 11))
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index de7b0e9..27818b4 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -389,8 +389,13 @@
rc = qpnp_iadc_read_reg(QPNP_IADC_ATE_GAIN_CALIB_OFFSET,
&iadc->iadc_comp.sys_gain);
- if (rc < 0)
+ if (rc < 0) {
pr_err("full scale read failed with %d\n", rc);
+ return rc;
+ }
+
+ if (iadc->external_rsense)
+ iadc->iadc_comp.ext_rsense = true;
pr_debug("fab id = %u, revision = %u, sys gain = %u, external_rsense = %d\n",
iadc->iadc_comp.id,
@@ -525,6 +530,9 @@
uint16_t raw_data;
uint32_t mode_sel = 0;
+ if (!iadc || !iadc->iadc_initialized)
+ return -EPROBE_DEFER;
+
mutex_lock(&iadc->adc->adc_lock);
rc = qpnp_iadc_configure(GAIN_CALIBRATION_17P857MV,
@@ -536,11 +544,22 @@
iadc->adc->calib.gain_raw = raw_data;
- rc = qpnp_iadc_configure(OFFSET_CALIBRATION_CSP2_CSN2,
+ if (iadc->external_rsense) {
+ /* external offset calculation */
+ rc = qpnp_iadc_configure(OFFSET_CALIBRATION_CSP_CSN,
&raw_data, mode_sel);
- if (rc < 0) {
- pr_err("qpnp adc result read failed with %d\n", rc);
- goto fail;
+ if (rc < 0) {
+ pr_err("qpnp adc result read failed with %d\n", rc);
+ goto fail;
+ }
+ } else {
+ /* internal offset calculation */
+ rc = qpnp_iadc_configure(OFFSET_CALIBRATION_CSP2_CSN2,
+ &raw_data, mode_sel);
+ if (rc < 0) {
+ pr_err("qpnp adc result read failed with %d\n", rc);
+ goto fail;
+ }
}
iadc->adc->calib.offset_raw = raw_data;
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 4306b1d..53e43d1 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -866,6 +866,11 @@
amux_prescaling =
vadc->adc->adc_channels[dt_index].chan_path_prescaling;
+ if (amux_prescaling >= PATH_SCALING_NONE) {
+ rc = -EINVAL;
+ goto fail_unlock;
+ }
+
vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
@@ -1016,6 +1021,11 @@
amux_prescaling =
vadc->adc->adc_channels[dt_index].chan_path_prescaling;
+ if (amux_prescaling >= PATH_SCALING_NONE) {
+ rc = -EINVAL;
+ goto fail;
+ }
+
vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
@@ -1164,14 +1174,14 @@
rc = qpnp_vadc_read_reg(QPNP_INT_TEST_VAL, &fab_id);
if (rc < 0) {
pr_err("qpnp adc comp id failed with %d\n", rc);
- return rc;
+ goto fail;
}
vadc->id = fab_id;
rc = qpnp_vadc_warm_rst_configure();
if (rc < 0) {
pr_err("Setting perp reset on warm reset failed %d\n", rc);
- return rc;
+ goto fail;
}
vadc->vadc_initialized = true;
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index b96349e..74a252f 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -1385,7 +1385,8 @@
qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"qup_phys_addr");
if (!qup_mem) {
- dev_err(&pdev->dev, "no qup mem resource?\n");
+ dev_err(&pdev->dev,
+ "platform_get_resource_byname(qup_phys_addr) failed\n");
ret = -ENODEV;
goto get_res_failed;
}
@@ -1655,11 +1656,22 @@
return ret;
}
+static void qup_i2c_mem_release(struct platform_device *pdev, const char *name)
+{
+ struct resource *res =
+ platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ else
+ dev_dbg(&pdev->dev,
+ "platform_get_resource_byname(%s) failed\n", name);
+}
+
static int __devexit
qup_i2c_remove(struct platform_device *pdev)
{
- struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
- struct resource *qup_mem, *gsbi_mem;
+ struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
/* Grab mutex to ensure ongoing transaction is over */
mutex_lock(&dev->mlock);
@@ -1693,14 +1705,11 @@
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
- if (!(dev->pdata->use_gsbi_shared_mode)) {
- gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "gsbi_qup_i2c_addr");
- release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
- }
- qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "qup_phys_addr");
- release_mem_region(qup_mem->start, resource_size(qup_mem));
+ if (!(dev->pdata->use_gsbi_shared_mode))
+ qup_i2c_mem_release(pdev, "gsbi_qup_i2c_addr");
+
+ qup_i2c_mem_release(pdev, "qup_phys_addr");
+
if (dev->dev->of_node)
kfree(dev->pdata);
kfree(dev);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 1464dab..a052bd3 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -654,4 +654,42 @@
If you say yes here you get support for STMicroelectronics's
acceleration sensors LIS3DH.
+config BMP18X
+ tristate "BMP18X digital pressure sensor"
+ depends on (I2C ) && SYSFS
+ help
+ If you say yes here you get support for Bosch Sensortec
+ digital pressure sensors BMP085, BMP180.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bmp18x-core.
+
+config BMP18X_I2C
+ tristate "support I2C bus connection"
+ depends on BMP18X && I2C
+ help
+ Say Y here if you want to support Bosch Sensortec digital pressure
+ sensor hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bmp18x-i2c.
+
+config SENSORS_MMA8X5X
+ tristate "MMA8451/MMA8452/MMA8453/MMA8652/MMA8653 device driver"
+ depends on I2C && SYSFS
+ select INPUT_POLLDEV
+ default n
+ help
+ If you say yes here you get support for the Freescale MMA8451/
+ MMA8452/MMA8453/MMA8652/MMA8653 sensors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mma8x5x.
+
+config SENSORS_MMA_POSITION
+ int "MMA8x5x Accelerate Sensor Position Setting"
+ depends on SENSORS_MMA8X5X
+ default "0"
+ help
+ this provide the sensor position setting , value is between 0~7
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 96c9288..4f29e05 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -61,3 +61,6 @@
obj-$(CONFIG_INPUT_PMIC8058_VIBRA_MEMLESS) += pmic8058-vib-memless.o
obj-$(CONFIG_BOSCH_BMA150) += bma150.o
obj-$(CONFIG_STM_LIS3DH) += lis3dh_acc.o
+obj-$(CONFIG_BMP18X) += bmp18x-core.o
+obj-$(CONFIG_BMP18X_I2C) += bmp18x-i2c.o
+obj-$(CONFIG_SENSORS_MMA8X5X) += mma8x5x.o
diff --git a/drivers/input/misc/bmp18x-core.c b/drivers/input/misc/bmp18x-core.c
new file mode 100644
index 0000000..4b5b710
--- /dev/null
+++ b/drivers/input/misc/bmp18x-core.c
@@ -0,0 +1,705 @@
+/* Copyright (c) 2011 Bosch Sensortec GmbH
+ Copyright (c) 2011 Unixphere
+
+ Based on:
+ BMP085 driver, bmp085.c
+ Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
+
+ This driver supports the bmp18x digital barometric pressure
+ and temperature sensors from Bosch Sensortec.
+
+ A pressure measurement is issued by reading from pressure0_input.
+ The return value ranges from 30000 to 110000 pascal with a resulution
+ of 1 pascal (0.01 millibar) which enables measurements from 9000m above
+ to 500m below sea level.
+
+ The temperature can be read from temp0_input. Values range from
+ -400 to 850 representing the ambient temperature in degree celsius
+ multiplied by 10.The resolution is 0.1 celsius.
+
+ Because ambient pressure is temperature dependent, a temperature
+ measurement will be executed automatically even if the user is reading
+ from pressure0_input. This happens if the last temperature measurement
+ has been executed more then one second ago.
+
+ To decrease RMS noise from pressure measurements, the bmp18x can
+ autonomously calculate the average of up to eight samples. This is
+ set up by writing to the oversampling sysfs file. Accepted values
+ are 0, 1, 2 and 3. 2^x when x is the value written to this file
+ specifies the number of samples used to calculate the ambient pressure.
+ RMS noise is specified with six pascal (without averaging) and decreases
+ down to 3 pascal when using an oversampling setting of 3.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include "bmp18x.h"
+
+#define BMP18X_CHIP_ID 0x55
+
+#define BMP18X_CALIBRATION_DATA_START 0xAA
+#define BMP18X_CALIBRATION_DATA_LENGTH 11 /* 16 bit values */
+#define BMP18X_CHIP_ID_REG 0xD0
+#define BMP18X_CTRL_REG 0xF4
+#define BMP18X_TEMP_MEASUREMENT 0x2E
+#define BMP18X_PRESSURE_MEASUREMENT 0x34
+#define BMP18X_CONVERSION_REGISTER_MSB 0xF6
+#define BMP18X_CONVERSION_REGISTER_LSB 0xF7
+#define BMP18X_CONVERSION_REGISTER_XLSB 0xF8
+#define BMP18X_TEMP_CONVERSION_TIME 5
+
+#define ABS_MIN_PRESSURE 30000
+#define ABS_MAX_PRESSURE 120000
+#define BMP_DELAY_DEFAULT 200
+
+struct bmp18x_calibration_data {
+ s16 AC1, AC2, AC3;
+ u16 AC4, AC5, AC6;
+ s16 B1, B2;
+ s16 MB, MC, MD;
+};
+
+/* Each client has this additional data */
+struct bmp18x_data {
+ struct bmp18x_data_bus data_bus;
+ struct device *dev;
+ struct mutex lock;
+ struct bmp18x_calibration_data calibration;
+ u8 oversampling_setting;
+ u8 sw_oversampling_setting;
+ u32 raw_temperature;
+ u32 raw_pressure;
+ u32 temp_measurement_period;
+ u32 last_temp_measurement;
+ s32 b6; /* calculated temperature correction coefficient */
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ struct input_dev *input;
+ struct delayed_work work;
+ u32 delay;
+ u32 enable;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bmp18x_early_suspend(struct early_suspend *h);
+static void bmp18x_late_resume(struct early_suspend *h);
+#endif
+
+static s32 bmp18x_read_calibration_data(struct bmp18x_data *data)
+{
+ u16 tmp[BMP18X_CALIBRATION_DATA_LENGTH];
+ struct bmp18x_calibration_data *cali = &(data->calibration);
+ s32 status = data->data_bus.bops->read_block(data->data_bus.client,
+ BMP18X_CALIBRATION_DATA_START,
+ BMP18X_CALIBRATION_DATA_LENGTH*sizeof(u16),
+ (u8 *)tmp);
+ if (status < 0)
+ return status;
+
+ if (status != BMP18X_CALIBRATION_DATA_LENGTH*sizeof(u16))
+ return -EIO;
+
+ cali->AC1 = be16_to_cpu(tmp[0]);
+ cali->AC2 = be16_to_cpu(tmp[1]);
+ cali->AC3 = be16_to_cpu(tmp[2]);
+ cali->AC4 = be16_to_cpu(tmp[3]);
+ cali->AC5 = be16_to_cpu(tmp[4]);
+ cali->AC6 = be16_to_cpu(tmp[5]);
+ cali->B1 = be16_to_cpu(tmp[6]);
+ cali->B2 = be16_to_cpu(tmp[7]);
+ cali->MB = be16_to_cpu(tmp[8]);
+ cali->MC = be16_to_cpu(tmp[9]);
+ cali->MD = be16_to_cpu(tmp[10]);
+ return 0;
+}
+
+
+static s32 bmp18x_update_raw_temperature(struct bmp18x_data *data)
+{
+ u16 tmp;
+ s32 status;
+
+ mutex_lock(&data->lock);
+ status = data->data_bus.bops->write_byte(data->data_bus.client,
+ BMP18X_CTRL_REG, BMP18X_TEMP_MEASUREMENT);
+ if (status != 0) {
+ dev_err(data->dev,
+ "Error while requesting temperature measurement.\n");
+ goto exit;
+ }
+ msleep(BMP18X_TEMP_CONVERSION_TIME);
+
+ status = data->data_bus.bops->read_block(data->data_bus.client,
+ BMP18X_CONVERSION_REGISTER_MSB, sizeof(tmp), (u8 *)&tmp);
+ if (status < 0)
+ goto exit;
+ if (status != sizeof(tmp)) {
+ dev_err(data->dev,
+ "Error while reading temperature measurement result\n");
+ status = -EIO;
+ goto exit;
+ }
+ data->raw_temperature = be16_to_cpu(tmp);
+ data->last_temp_measurement = jiffies;
+ status = 0; /* everything ok, return 0 */
+
+exit:
+ mutex_unlock(&data->lock);
+ return status;
+}
+
+static s32 bmp18x_update_raw_pressure(struct bmp18x_data *data)
+{
+ u32 tmp = 0;
+ s32 status;
+
+ mutex_lock(&data->lock);
+ status = data->data_bus.bops->write_byte(data->data_bus.client,
+ BMP18X_CTRL_REG, BMP18X_PRESSURE_MEASUREMENT +
+ (data->oversampling_setting<<6));
+ if (status != 0) {
+ dev_err(data->dev,
+ "Error while requesting pressure measurement.\n");
+ goto exit;
+ }
+
+ /* wait for the end of conversion */
+ msleep(2+(3 << data->oversampling_setting));
+
+ /* copy data into a u32 (4 bytes), but skip the first byte. */
+ status = data->data_bus.bops->read_block(data->data_bus.client,
+ BMP18X_CONVERSION_REGISTER_MSB, 3, ((u8 *)&tmp)+1);
+ if (status < 0)
+ goto exit;
+ if (status != 3) {
+ dev_err(data->dev,
+ "Error while reading pressure measurement results\n");
+ status = -EIO;
+ goto exit;
+ }
+ data->raw_pressure = be32_to_cpu((tmp));
+ data->raw_pressure >>= (8-data->oversampling_setting);
+ status = 0; /* everything ok, return 0 */
+
+exit:
+ mutex_unlock(&data->lock);
+ return status;
+}
+
+
+/*
+ * This function starts the temperature measurement and returns the value
+ * in tenth of a degree celsius.
+ */
+static s32 bmp18x_get_temperature(struct bmp18x_data *data, int *temperature)
+{
+ struct bmp18x_calibration_data *cali = &data->calibration;
+ long x1, x2;
+ int status;
+
+ status = bmp18x_update_raw_temperature(data);
+ if (status != 0)
+ goto exit;
+
+ x1 = ((data->raw_temperature - cali->AC6) * cali->AC5) >> 15;
+ x2 = (cali->MC << 11) / (x1 + cali->MD);
+ data->b6 = x1 + x2 - 4000;
+ /* if NULL just update b6. Used for pressure only measurements */
+ if (temperature != NULL)
+ *temperature = (x1+x2+8) >> 4;
+
+exit:
+ return status;
+}
+
+/*
+ * This function starts the pressure measurement and returns the value
+ * in millibar. Since the pressure depends on the ambient temperature,
+ * a temperature measurement is executed according to the given temperature
+ * measurememt period (default is 1 sec boundary). This period could vary
+ * and needs to be adjusted accoring to the sensor environment, i.e. if big
+ * temperature variations then the temperature needs to be read out often.
+ */
+static s32 bmp18x_get_pressure(struct bmp18x_data *data, int *pressure)
+{
+ struct bmp18x_calibration_data *cali = &data->calibration;
+ s32 x1, x2, x3, b3;
+ u32 b4, b7;
+ s32 p;
+ int status;
+ int i_loop, i;
+ u32 p_tmp;
+
+ /* update the ambient temperature according to the given meas. period */
+ if (data->last_temp_measurement +
+ data->temp_measurement_period < jiffies) {
+ status = bmp18x_get_temperature(data, NULL);
+ if (status != 0)
+ goto exit;
+ }
+
+ if ((data->oversampling_setting == 3)
+ && (data->sw_oversampling_setting == 1)) {
+ i_loop = 3;
+ } else {
+ i_loop = 1;
+ }
+
+ p_tmp = 0;
+ for (i = 0; i < i_loop; i++) {
+ status = bmp18x_update_raw_pressure(data);
+ if (status != 0)
+ goto exit;
+ p_tmp += data->raw_pressure;
+ }
+
+ data->raw_pressure = (p_tmp + (i_loop >> 1)) / i_loop;
+
+ x1 = (data->b6 * data->b6) >> 12;
+ x1 *= cali->B2;
+ x1 >>= 11;
+
+ x2 = cali->AC2 * data->b6;
+ x2 >>= 11;
+
+ x3 = x1 + x2;
+
+ b3 = (((((s32)cali->AC1) * 4 + x3) << data->oversampling_setting) + 2);
+ b3 >>= 2;
+
+ x1 = (cali->AC3 * data->b6) >> 13;
+ x2 = (cali->B1 * ((data->b6 * data->b6) >> 12)) >> 16;
+ x3 = (x1 + x2 + 2) >> 2;
+ b4 = (cali->AC4 * (u32)(x3 + 32768)) >> 15;
+
+ b7 = ((u32)data->raw_pressure - b3) *
+ (50000 >> data->oversampling_setting);
+ p = ((b7 < 0x80000000) ? ((b7 << 1) / b4) : ((b7 / b4) * 2));
+
+ x1 = p >> 8;
+ x1 *= x1;
+ x1 = (x1 * 3038) >> 16;
+ x2 = (-7357 * p) >> 16;
+ p += (x1 + x2 + 3791) >> 4;
+
+ *pressure = p;
+
+exit:
+ return status;
+}
+
+/*
+ * This function sets the chip-internal oversampling. Valid values are 0..3.
+ * The chip will use 2^oversampling samples for internal averaging.
+ * This influences the measurement time and the accuracy; larger values
+ * increase both. The datasheet gives on overview on how measurement time,
+ * accuracy and noise correlate.
+ */
+static void bmp18x_set_oversampling(struct bmp18x_data *data,
+ unsigned char oversampling)
+{
+ if (oversampling > 3)
+ oversampling = 3;
+ data->oversampling_setting = oversampling;
+}
+
+/*
+ * Returns the currently selected oversampling. Range: 0..3
+ */
+static unsigned char bmp18x_get_oversampling(struct bmp18x_data *data)
+{
+ return data->oversampling_setting;
+}
+
+/* sysfs callbacks */
+static ssize_t set_oversampling(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ unsigned long oversampling;
+ int success = kstrtoul(buf, 10, &oversampling);
+ if (success == 0) {
+ mutex_lock(&data->lock);
+ bmp18x_set_oversampling(data, oversampling);
+ if (oversampling != 3)
+ data->sw_oversampling_setting = 0;
+ mutex_unlock(&data->lock);
+ return count;
+ }
+ return success;
+}
+
+static ssize_t show_oversampling(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE,
+ "%u\n", bmp18x_get_oversampling(data));
+}
+static DEVICE_ATTR(oversampling, S_IWUSR | S_IRUGO,
+ show_oversampling, set_oversampling);
+
+static ssize_t set_sw_oversampling(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ unsigned long sw_oversampling;
+ int success = kstrtoul(buf, 10, &sw_oversampling);
+ if (success == 0) {
+ mutex_lock(&data->lock);
+ data->sw_oversampling_setting = sw_oversampling ? 1 : 0;
+ mutex_unlock(&data->lock);
+ }
+ return success;
+}
+
+static ssize_t show_sw_oversampling(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE,
+ "%u\n", data->sw_oversampling_setting);
+}
+static DEVICE_ATTR(sw_oversampling, S_IWUSR | S_IRUGO,
+ show_sw_oversampling, set_sw_oversampling);
+
+static ssize_t show_delay(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->delay);
+}
+
+static ssize_t set_delay(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ unsigned long delay;
+ int success = kstrtoul(buf, 10, &delay);
+ if (success == 0) {
+ mutex_lock(&data->lock);
+ data->delay = delay;
+ mutex_unlock(&data->lock);
+ }
+ return success;
+}
+static DEVICE_ATTR(delay, S_IWUSR | S_IRUGO,
+ show_delay, set_delay);
+
+static ssize_t show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->enable);
+}
+
+static ssize_t set_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ unsigned long enable;
+ int success = kstrtoul(buf, 10, &enable);
+ if (success == 0) {
+ mutex_lock(&data->lock);
+ data->enable = enable ? 1 : 0;
+
+ if (data->enable) {
+ bmp18x_enable(dev);
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(data->delay));
+ } else {
+ cancel_delayed_work_sync(&data->work);
+ bmp18x_disable(dev);
+ }
+ mutex_unlock(&data->lock);
+
+ }
+ return count;
+}
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+ show_enable, set_enable);
+
+static ssize_t show_temperature(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int temperature;
+ int status;
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+
+ status = bmp18x_get_temperature(data, &temperature);
+ if (status != 0)
+ return status;
+ else
+ return snprintf(buf, PAGE_SIZE,
+ "%d\n", temperature);
+}
+static DEVICE_ATTR(temp0_input, S_IRUGO, show_temperature, NULL);
+
+
+static ssize_t show_pressure(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int pressure;
+ int status;
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+
+ status = bmp18x_get_pressure(data, &pressure);
+ if (status != 0)
+ return status;
+ else
+ return snprintf(buf, PAGE_SIZE, "%d\n", pressure);
+}
+static DEVICE_ATTR(pressure0_input, S_IRUGO, show_pressure, NULL);
+
+
+static struct attribute *bmp18x_attributes[] = {
+ &dev_attr_temp0_input.attr,
+ &dev_attr_pressure0_input.attr,
+ &dev_attr_oversampling.attr,
+ &dev_attr_sw_oversampling.attr,
+ &dev_attr_delay.attr,
+ &dev_attr_enable.attr,
+ NULL
+};
+
+static const struct attribute_group bmp18x_attr_group = {
+ .attrs = bmp18x_attributes,
+};
+
+static void bmp18x_work_func(struct work_struct *work)
+{
+ struct bmp18x_data *client_data =
+ container_of((struct delayed_work *)work,
+ struct bmp18x_data, work);
+ unsigned long delay = msecs_to_jiffies(client_data->delay);
+ unsigned long j1 = jiffies;
+ int pressure;
+ int status;
+
+ status = bmp18x_get_pressure(client_data, &pressure);
+
+ if (status == 0) {
+ input_report_abs(client_data->input, ABS_PRESSURE, pressure);
+ input_sync(client_data->input);
+ }
+
+ schedule_delayed_work(&client_data->work, delay-(jiffies-j1));
+}
+
+static int bmp18x_input_init(struct bmp18x_data *data)
+{
+ struct input_dev *dev;
+ int err;
+
+ dev = input_allocate_device();
+ if (!dev)
+ return -ENOMEM;
+ dev->name = BMP18X_NAME;
+ dev->id.bustype = BUS_I2C;
+
+ input_set_capability(dev, EV_ABS, ABS_MISC);
+ input_set_abs_params(dev, ABS_PRESSURE,
+ ABS_MIN_PRESSURE, ABS_MAX_PRESSURE, 0, 0);
+ input_set_drvdata(dev, data);
+
+ err = input_register_device(dev);
+ if (err < 0) {
+ input_free_device(dev);
+ return err;
+ }
+ data->input = dev;
+
+ return 0;
+}
+
+static void bmp18x_input_delete(struct bmp18x_data *data)
+{
+ struct input_dev *dev = data->input;
+
+ input_unregister_device(dev);
+ input_free_device(dev);
+}
+
+static int bmp18x_init_client(struct bmp18x_data *data,
+ struct bmp18x_platform_data *pdata)
+{
+ int status = bmp18x_read_calibration_data(data);
+ if (status != 0)
+ goto exit;
+ data->last_temp_measurement = 0;
+ data->temp_measurement_period =
+ pdata ? (pdata->temp_measurement_period/1000)*HZ : 1*HZ;
+ data->oversampling_setting = pdata ? pdata->default_oversampling : 3;
+ if (data->oversampling_setting == 3)
+ data->sw_oversampling_setting
+ = pdata ? pdata->default_sw_oversampling : 0;
+ mutex_init(&data->lock);
+exit:
+ return status;
+}
+
+__devinit int bmp18x_probe(struct device *dev, struct bmp18x_data_bus *data_bus)
+{
+ struct bmp18x_data *data;
+ struct bmp18x_platform_data *pdata = dev->platform_data;
+ u8 chip_id = pdata && pdata->chip_id ? pdata->chip_id : BMP18X_CHIP_ID;
+ int err = 0;
+
+ if (pdata && pdata->init_hw) {
+ err = pdata->init_hw(data_bus);
+ if (err) {
+ printk(KERN_ERR "%s: init_hw failed!\n",
+ BMP18X_NAME);
+ goto exit;
+ }
+ }
+
+ if (data_bus->bops->read_byte(data_bus->client,
+ BMP18X_CHIP_ID_REG) != chip_id) {
+ printk(KERN_ERR "%s: chip_id failed!\n", BMP18X_NAME);
+ err = -ENODEV;
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(struct bmp18x_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ dev_set_drvdata(dev, data);
+ data->data_bus = *data_bus;
+ data->dev = dev;
+
+ /* Initialize the BMP18X chip */
+ err = bmp18x_init_client(data, pdata);
+ if (err != 0)
+ goto exit_free;
+
+ /* Initialize the BMP18X input device */
+ err = bmp18x_input_init(data);
+ if (err != 0)
+ goto exit_free;
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&data->input->dev.kobj, &bmp18x_attr_group);
+ if (err)
+ goto error_sysfs;
+ /* workqueue init */
+ INIT_DELAYED_WORK(&data->work, bmp18x_work_func);
+ data->delay = BMP_DELAY_DEFAULT;
+ data->enable = 0;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ data->early_suspend.suspend = bmp18x_early_suspend;
+ data->early_suspend.resume = bmp18x_late_resume;
+ register_early_suspend(&data->early_suspend);
+#endif
+
+ dev_info(dev, "Succesfully initialized bmp18x!\n");
+ return 0;
+
+error_sysfs:
+ bmp18x_input_delete(data);
+exit_free:
+ kfree(data);
+exit:
+ if (pdata && pdata->deinit_hw)
+ pdata->deinit_hw(data_bus);
+ return err;
+}
+EXPORT_SYMBOL(bmp18x_probe);
+
+int bmp18x_remove(struct device *dev)
+{
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&data->early_suspend);
+#endif
+ sysfs_remove_group(&data->input->dev.kobj, &bmp18x_attr_group);
+ kfree(data);
+
+ return 0;
+}
+EXPORT_SYMBOL(bmp18x_remove);
+
+#ifdef CONFIG_PM
+int bmp18x_disable(struct device *dev)
+{
+ struct bmp18x_platform_data *pdata = dev->platform_data;
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ if (pdata && pdata->deinit_hw)
+ pdata->deinit_hw(&data->data_bus);
+
+ return 0;
+}
+EXPORT_SYMBOL(bmp18x_disable);
+
+int bmp18x_enable(struct device *dev)
+{
+ struct bmp18x_platform_data *pdata = dev->platform_data;
+ struct bmp18x_data *data = dev_get_drvdata(dev);
+ if (pdata && pdata->init_hw)
+ return pdata->init_hw(&data->data_bus);
+
+ return 0;
+}
+EXPORT_SYMBOL(bmp18x_enable);
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bmp18x_early_suspend(struct early_suspend *h)
+{
+ struct bmp18x_data *data =
+ container_of(h, struct bmp18x_data, early_suspend);
+ if (data->enable) {
+ cancel_delayed_work_sync(&data->work);
+ (void) bmp18x_disable(data->dev);
+ }
+}
+
+static void bmp18x_late_resume(struct early_suspend *h)
+{
+ struct bmp18x_data *data =
+ container_of(h, struct bmp18x_data, early_suspend);
+
+ if (data->enable) {
+ (void) bmp18x_enable(data->dev);
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(data->delay));
+ }
+
+}
+#endif
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("BMP18X driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/bmp18x-i2c.c b/drivers/input/misc/bmp18x-i2c.c
new file mode 100644
index 0000000..abbe6e5
--- /dev/null
+++ b/drivers/input/misc/bmp18x-i2c.c
@@ -0,0 +1,275 @@
+/* Copyright (c) 2011 Bosch Sensortec GmbH
+ Copyright (c) 2011 Unixphere
+
+ Based on:
+ BMP085 driver, bmp085.c
+ Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include "bmp18x.h"
+
+struct sensor_regulator {
+ struct regulator *vreg;
+ const char *name;
+ u32 min_uV;
+ u32 max_uV;
+};
+
+struct sensor_regulator bmp_vreg[] = {
+ {NULL, "vdd", 2850000, 2850000},
+ {NULL, "vddio", 1800000, 1800000},
+};
+
+
+static int bmp18x_config_regulator(struct i2c_client *client, bool on)
+{
+ int rc = 0, i;
+ int num_vreg = ARRAY_SIZE(bmp_vreg);
+
+ if (on) {
+ for (i = 0; i < num_vreg; i++) {
+ bmp_vreg[i].vreg = regulator_get(&client->dev,
+ bmp_vreg[i].name);
+ if (IS_ERR(bmp_vreg[i].vreg)) {
+ rc = PTR_ERR(bmp_vreg[i].vreg);
+ dev_err(&client->dev, "%s:regulator get failed rc=%d\n",
+ __func__, rc);
+ bmp_vreg[i].vreg = NULL;
+ goto error_vdd;
+ }
+ if (regulator_count_voltages(bmp_vreg[i].vreg) > 0) {
+ rc = regulator_set_voltage(bmp_vreg[i].vreg,
+ bmp_vreg[i].min_uV, bmp_vreg[i].max_uV);
+ if (rc) {
+ dev_err(&client->dev, "%s:set_voltage failed rc=%d\n",
+ __func__, rc);
+ regulator_put(bmp_vreg[i].vreg);
+ bmp_vreg[i].vreg = NULL;
+ goto error_vdd;
+ }
+ }
+ rc = regulator_enable(bmp_vreg[i].vreg);
+ if (rc) {
+ dev_err(&client->dev, "%s: regulator_enable failed rc =%d\n",
+ __func__, rc);
+ if (regulator_count_voltages(bmp_vreg[i].vreg)
+ > 0) {
+ regulator_set_voltage(bmp_vreg[i].vreg,
+ 0, bmp_vreg[i].max_uV);
+ }
+ regulator_put(bmp_vreg[i].vreg);
+ bmp_vreg[i].vreg = NULL;
+ goto error_vdd;
+ }
+ }
+ return rc;
+ } else {
+ i = num_vreg;
+ }
+error_vdd:
+ while (--i >= 0) {
+ if (!IS_ERR_OR_NULL(bmp_vreg[i].vreg)) {
+ if (regulator_count_voltages(
+ bmp_vreg[i].vreg) > 0) {
+ regulator_set_voltage(bmp_vreg[i].vreg, 0,
+ bmp_vreg[i].max_uV);
+ }
+ regulator_disable(bmp_vreg[i].vreg);
+ regulator_put(bmp_vreg[i].vreg);
+ bmp_vreg[i].vreg = NULL;
+ }
+ }
+ return rc;
+}
+
+static int bmp18x_init_hw(struct bmp18x_data_bus *data_bus)
+{
+ if (data_bus->client)
+ return bmp18x_config_regulator(data_bus->client, 1);
+ return 0;
+}
+
+static void bmp18x_deinit_hw(struct bmp18x_data_bus *data_bus)
+{
+ if (data_bus->client)
+ bmp18x_config_regulator(data_bus->client, 0);
+}
+
+#ifdef CONFIG_OF
+static int bmp18x_parse_dt(struct device *dev,
+ struct bmp18x_platform_data *pdata)
+{
+ int ret = 0;
+ u32 val;
+
+ ret = of_property_read_u32(dev->of_node, "bosch,chip-id", &val);
+ if (ret) {
+ dev_err(dev, "no chip_id from dt\n");
+ return ret;
+ }
+ pdata->chip_id = (u8)val;
+
+ ret = of_property_read_u32(dev->of_node, "bosch,oversample", &val);
+ if (ret) {
+ dev_err(dev, "no default_oversampling from dt\n");
+ return ret;
+ }
+ pdata->default_oversampling = (u8)val;
+
+ ret = of_property_read_u32(dev->of_node, "bosch,period",
+ &pdata->temp_measurement_period);
+ if (ret) {
+ dev_err(dev, "no temp_measurement_period from dt\n");
+ return ret;
+ }
+
+ pdata->default_sw_oversampling = of_property_read_bool(dev->of_node,
+ "bosch,sw-oversample");
+ return 0;
+}
+#else
+static int bmp18x_parse_dt(struct device *dev,
+ struct bmp18x_platform_data *pdata)
+{
+ return -EINVAL;
+}
+#endif
+
+static int bmp18x_i2c_read_block(void *client, u8 reg, int len, char *buf)
+{
+ return i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+}
+
+static int bmp18x_i2c_read_byte(void *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int bmp18x_i2c_write_byte(void *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static const struct bmp18x_bus_ops bmp18x_i2c_bus_ops = {
+ .read_block = bmp18x_i2c_read_block,
+ .read_byte = bmp18x_i2c_read_byte,
+ .write_byte = bmp18x_i2c_write_byte
+};
+
+static int __devinit bmp18x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bmp18x_data_bus data_bus = {
+ .bops = &bmp18x_i2c_bus_ops,
+ .client = client
+ };
+ struct bmp18x_platform_data *pdata;
+ int ret;
+
+ if (client->dev.of_node) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct bmp18x_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ ret = bmp18x_parse_dt(&client->dev, pdata);
+ if (ret) {
+ dev_err(&client->dev, "Failed to parse device tree\n");
+ return ret;
+ }
+ pdata->init_hw = bmp18x_init_hw;
+ pdata->deinit_hw = bmp18x_deinit_hw;
+ client->dev.platform_data = pdata;
+ }
+ return bmp18x_probe(&client->dev, &data_bus);
+}
+
+static void bmp18x_i2c_shutdown(struct i2c_client *client)
+{
+ bmp18x_disable(&client->dev);
+}
+
+static int bmp18x_i2c_remove(struct i2c_client *client)
+{
+ return bmp18x_remove(&client->dev);
+}
+
+#ifdef CONFIG_PM
+static int bmp18x_i2c_suspend(struct device *dev)
+{
+ return bmp18x_disable(dev);
+}
+
+static int bmp18x_i2c_resume(struct device *dev)
+{
+ return bmp18x_enable(dev);
+}
+
+static const struct dev_pm_ops bmp18x_i2c_pm_ops = {
+ .suspend = bmp18x_i2c_suspend,
+ .resume = bmp18x_i2c_resume
+};
+#endif
+
+static const struct i2c_device_id bmp18x_id[] = {
+ { BMP18X_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bmp18x_id);
+
+static const struct of_device_id bmp18x_of_match[] = {
+ { .compatible = "bosch,bmp180", },
+ { },
+};
+
+static struct i2c_driver bmp18x_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = BMP18X_NAME,
+#ifdef CONFIG_PM
+ .pm = &bmp18x_i2c_pm_ops,
+#endif
+ .of_match_table = bmp18x_of_match,
+ },
+ .id_table = bmp18x_id,
+ .probe = bmp18x_i2c_probe,
+ .shutdown = bmp18x_i2c_shutdown,
+ .remove = __devexit_p(bmp18x_i2c_remove)
+};
+
+static int __init bmp18x_i2c_init(void)
+{
+ return i2c_add_driver(&bmp18x_i2c_driver);
+}
+
+static void __exit bmp18x_i2c_exit(void)
+{
+ i2c_del_driver(&bmp18x_i2c_driver);
+}
+
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("BMP18X I2C bus driver");
+MODULE_LICENSE("GPL");
+
+module_init(bmp18x_i2c_init);
+module_exit(bmp18x_i2c_exit);
diff --git a/drivers/input/misc/bmp18x.h b/drivers/input/misc/bmp18x.h
new file mode 100644
index 0000000..d1b1ee7
--- /dev/null
+++ b/drivers/input/misc/bmp18x.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
+ Copyright (c) 2011 Bosch Sensortec GmbH
+ Copyright (c) 2011 Unixphere AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef _BMP18X_H
+#define _BMP18X_H
+
+#define BMP18X_NAME "bmp18x"
+
+/**
+ * struct bmp18x_platform_data - represents platform data for the bmp18x driver
+ * @chip_id: Configurable chip id for non-default chip revisions
+ * @default_oversampling: Default oversampling value to be used at startup,
+ * value range is 0-3 with rising sensitivity.
+ * @default_sw_oversampling: Default software oversampling value to be used
+ * at startup,value range is 0(Disabled) or 1(Enabled). Only take effect
+ * when default_oversampling is 3.
+ * @temp_measurement_period: Temperature measurement period (milliseconds), set
+ * to zero if unsure.
+ * @init_hw: Callback for hw specific startup
+ * @deinit_hw: Callback for hw specific shutdown
+ */
+
+struct bmp18x_bus_ops {
+ int (*read_block)(void *client, u8 reg, int len, char *buf);
+ int (*read_byte)(void *client, u8 reg);
+ int (*write_byte)(void *client, u8 reg, u8 value);
+};
+
+struct bmp18x_data_bus {
+ const struct bmp18x_bus_ops *bops;
+ void *client;
+};
+
+struct bmp18x_platform_data {
+ u8 chip_id;
+ u8 default_oversampling;
+ u8 default_sw_oversampling;
+ u32 temp_measurement_period;
+ int (*init_hw)(struct bmp18x_data_bus *);
+ void (*deinit_hw)(struct bmp18x_data_bus *);
+};
+
+int bmp18x_probe(struct device *dev, struct bmp18x_data_bus *data_bus);
+int bmp18x_remove(struct device *dev);
+#ifdef CONFIG_PM
+int bmp18x_enable(struct device *dev);
+int bmp18x_disable(struct device *dev);
+#endif
+
+#endif
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index f54707c..f879d78 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -31,7 +31,9 @@
#include <linux/of_gpio.h>
#endif /* CONFIG_OF */
-#define NAME "kxtj9"
+#define ACCEL_INPUT_DEV_NAME "accelerometer"
+#define DEVICE_NAME "kxtj9"
+
#define G_MAX 8000
/* OUTPUT REGISTERS */
#define XOUT_L 0x06
@@ -142,6 +144,13 @@
y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]);
z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]);
+ /* 8 bits output mode support */
+ if (!(tj9->ctrl_reg1 & RES_12BIT)) {
+ x <<= 4;
+ y <<= 4;
+ z <<= 4;
+ }
+
x >>= tj9->shift;
y >>= tj9->shift;
z >>= tj9->shift;
@@ -421,19 +430,6 @@
tj9->enable = false;
}
-static int kxtj9_input_open(struct input_dev *input)
-{
- struct kxtj9_data *tj9 = input_get_drvdata(input);
-
- return kxtj9_enable(tj9);
-}
-
-static void kxtj9_input_close(struct input_dev *dev)
-{
- struct kxtj9_data *tj9 = input_get_drvdata(dev);
-
- kxtj9_disable(tj9);
-}
static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
struct input_dev *input_dev)
@@ -443,7 +439,7 @@
input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
- input_dev->name = "kxtj9_accel";
+ input_dev->name = ACCEL_INPUT_DEV_NAME;
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &tj9->client->dev;
}
@@ -461,8 +457,6 @@
tj9->input_dev = input_dev;
- input_dev->open = kxtj9_input_open;
- input_dev->close = kxtj9_input_close;
input_set_drvdata(input_dev, tj9);
kxtj9_init_input_device(tj9, input_dev);
@@ -534,7 +528,7 @@
*/
/* Returns currently selected poll interval (in ms) */
-static ssize_t kxtj9_get_poll(struct device *dev,
+static ssize_t kxtj9_get_poll_delay(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -544,8 +538,9 @@
}
/* Allow users to select a new poll interval (in ms) */
-static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t kxtj9_set_poll_delay(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
@@ -576,11 +571,12 @@
return count;
}
-static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+static DEVICE_ATTR(poll_delay, S_IRUGO|S_IWUSR|S_IWGRP,
+ kxtj9_get_poll_delay, kxtj9_set_poll_delay);
static struct attribute *kxtj9_attributes[] = {
&dev_attr_enable.attr,
- &dev_attr_poll.attr,
+ &dev_attr_poll_delay.attr,
NULL
};
@@ -679,7 +675,8 @@
goto out;
}
- retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
+ retval = (retval != 0x05 && retval != 0x07 && retval != 0x08)
+ ? -EIO : 0;
out:
return retval;
@@ -759,7 +756,10 @@
kxtj9_pdata->negate_z = of_property_read_bool(np, "kionix,negate-z");
- kxtj9_pdata->res_12bit = of_property_read_bool(np, "kionix,res-12bit");
+ if (of_property_read_bool(np, "kionix,res-12bit"))
+ kxtj9_pdata->res_ctl = RES_12BIT;
+ else
+ kxtj9_pdata->res_ctl = RES_8BIT;
return 0;
}
@@ -837,7 +837,7 @@
i2c_set_clientdata(client, tj9);
- tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
+ tj9->ctrl_reg1 = tj9->pdata.res_ctl | tj9->pdata.g_range;
tj9->last_poll_interval = tj9->pdata.init_interval;
if (client->irq) {
@@ -950,7 +950,7 @@
static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
static const struct i2c_device_id kxtj9_id[] = {
- { NAME, 0 },
+ { DEVICE_NAME, 0 },
{ },
};
@@ -964,7 +964,7 @@
static struct i2c_driver kxtj9_driver = {
.driver = {
- .name = NAME,
+ .name = DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = kxtj9_match_table,
.pm = &kxtj9_pm_ops,
diff --git a/drivers/input/misc/lis3dh_acc.c b/drivers/input/misc/lis3dh_acc.c
index ea1b079..03fabd0 100644
--- a/drivers/input/misc/lis3dh_acc.c
+++ b/drivers/input/misc/lis3dh_acc.c
@@ -1093,7 +1093,7 @@
static struct device_attribute attributes[] = {
- __ATTR(pollrate_ms, 0664, attr_get_polling_rate,
+ __ATTR(poll_delay, 0664, attr_get_polling_rate,
attr_set_polling_rate),
__ATTR(range, 0664, attr_get_range, attr_set_range),
__ATTR(enable, 0664, attr_get_enable, attr_set_enable),
@@ -1226,7 +1226,7 @@
acc->input_dev->open = lis3dh_acc_input_open;
acc->input_dev->close = lis3dh_acc_input_close;
- acc->input_dev->name = LIS3DH_ACC_DEV_NAME;
+ acc->input_dev->name = ACCEL_INPUT_DEV_NAME;
acc->input_dev->id.bustype = BUS_I2C;
acc->input_dev->dev.parent = &acc->client->dev;
diff --git a/drivers/input/misc/mma8x5x.c b/drivers/input/misc/mma8x5x.c
new file mode 100644
index 0000000..f1ea705
--- /dev/null
+++ b/drivers/input/misc/mma8x5x.c
@@ -0,0 +1,684 @@
+/*
+ * mma8x5x.c - Linux kernel modules for 3-Axis Orientation/Motion
+ * Detection Sensor MMA8451/MMA8452/MMA8453
+ *
+ * Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
+ * Linux Foundation chooses to take subject only to the GPLv2 license
+ * terms, and distributes only under these terms.
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input-polldev.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
+
+#define ACCEL_INPUT_DEV_NAME "accelerometer"
+#define MMA8451_ID 0x1A
+#define MMA8452_ID 0x2A
+#define MMA8453_ID 0x3A
+#define MMA8652_ID 0x4A
+#define MMA8653_ID 0x5A
+
+
+#define POLL_INTERVAL_MIN 1
+#define POLL_INTERVAL_MAX 500
+#define POLL_INTERVAL 100 /* msecs */
+
+/* if sensor is standby ,set POLL_STOP_TIME to slow down the poll */
+#define POLL_STOP_TIME 200
+#define INPUT_FUZZ 32
+#define INPUT_FLAT 32
+#define MODE_CHANGE_DELAY_MS 100
+
+#define MMA8X5X_STATUS_ZYXDR 0x08
+#define MMA8X5X_BUF_SIZE 7
+
+struct sensor_regulator {
+ struct regulator *vreg;
+ const char *name;
+ u32 min_uV;
+ u32 max_uV;
+};
+
+static struct sensor_regulator mma_vreg[] = {
+ {NULL, "vdd", 2850000, 2850000},
+ {NULL, "vio", 1800000, 1800000},
+};
+
+/* register enum for mma8x5x registers */
+enum {
+ MMA8X5X_STATUS = 0x00,
+ MMA8X5X_OUT_X_MSB,
+ MMA8X5X_OUT_X_LSB,
+ MMA8X5X_OUT_Y_MSB,
+ MMA8X5X_OUT_Y_LSB,
+ MMA8X5X_OUT_Z_MSB,
+ MMA8X5X_OUT_Z_LSB,
+
+ MMA8X5X_F_SETUP = 0x09,
+ MMA8X5X_TRIG_CFG,
+ MMA8X5X_SYSMOD,
+ MMA8X5X_INT_SOURCE,
+ MMA8X5X_WHO_AM_I,
+ MMA8X5X_XYZ_DATA_CFG,
+ MMA8X5X_HP_FILTER_CUTOFF,
+
+ MMA8X5X_PL_STATUS,
+ MMA8X5X_PL_CFG,
+ MMA8X5X_PL_COUNT,
+ MMA8X5X_PL_BF_ZCOMP,
+ MMA8X5X_P_L_THS_REG,
+
+ MMA8X5X_FF_MT_CFG,
+ MMA8X5X_FF_MT_SRC,
+ MMA8X5X_FF_MT_THS,
+ MMA8X5X_FF_MT_COUNT,
+
+ MMA8X5X_TRANSIENT_CFG = 0x1D,
+ MMA8X5X_TRANSIENT_SRC,
+ MMA8X5X_TRANSIENT_THS,
+ MMA8X5X_TRANSIENT_COUNT,
+
+ MMA8X5X_PULSE_CFG,
+ MMA8X5X_PULSE_SRC,
+ MMA8X5X_PULSE_THSX,
+ MMA8X5X_PULSE_THSY,
+ MMA8X5X_PULSE_THSZ,
+ MMA8X5X_PULSE_TMLT,
+ MMA8X5X_PULSE_LTCY,
+ MMA8X5X_PULSE_WIND,
+
+ MMA8X5X_ASLP_COUNT,
+ MMA8X5X_CTRL_REG1,
+ MMA8X5X_CTRL_REG2,
+ MMA8X5X_CTRL_REG3,
+ MMA8X5X_CTRL_REG4,
+ MMA8X5X_CTRL_REG5,
+
+ MMA8X5X_OFF_X,
+ MMA8X5X_OFF_Y,
+ MMA8X5X_OFF_Z,
+
+ MMA8X5X_REG_END,
+};
+
+/* The sensitivity is represented in counts/g. In 2g mode the
+sensitivity is 1024 counts/g. In 4g mode the sensitivity is 512
+counts/g and in 8g mode the sensitivity is 256 counts/g.
+ */
+enum {
+ MODE_2G = 0,
+ MODE_4G,
+ MODE_8G,
+};
+
+enum {
+ MMA_STANDBY = 0,
+ MMA_ACTIVED,
+};
+struct mma8x5x_data_axis {
+ short x;
+ short y;
+ short z;
+};
+struct mma8x5x_data {
+ struct i2c_client *client;
+ struct input_polled_dev *poll_dev;
+ struct mutex data_lock;
+ int active;
+ int position;
+ u8 chip_id;
+ int mode;
+ int int_pin;
+ u32 int_flags;
+};
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = {0x1c, 0x1d, I2C_CLIENT_END};
+
+static int mma8x5x_chip_id[] = {
+ MMA8451_ID,
+ MMA8452_ID,
+ MMA8453_ID,
+ MMA8652_ID,
+ MMA8653_ID,
+};
+static char *mma8x5x_names[] = {
+ "mma8451",
+ "mma8452",
+ "mma8453",
+ "mma8652",
+ "mma8653",
+};
+static int mma8x5x_position_setting[8][3][3] = {
+ {{ 0, -1, 0}, { 1, 0, 0}, {0, 0, 1} },
+ {{-1, 0, 0}, { 0, -1, 0}, {0, 0, 1} },
+ {{ 0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
+ {{ 1, 0, 0}, { 0, 1, 0}, {0, 0, 1} },
+ {{ 0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
+ {{-1, 0, 0}, { 0, 1, 0}, {0, 0, -1} },
+ {{ 0, 1, 0}, { 1, 0, 0}, {0, 0, -1} },
+ {{ 1, 0, 0}, { 0, -1, 0}, {0, 0, -1} },
+};
+
+static int mma8x5x_config_regulator(struct i2c_client *client, bool on)
+{
+ int rc = 0, i;
+ int num_vreg = sizeof(mma_vreg)/sizeof(struct sensor_regulator);
+
+ if (on) {
+ for (i = 0; i < num_vreg; i++) {
+ mma_vreg[i].vreg = regulator_get(&client->dev,
+ mma_vreg[i].name);
+ if (IS_ERR(mma_vreg[i].vreg)) {
+ rc = PTR_ERR(mma_vreg[i].vreg);
+ dev_err(&client->dev, "%s:regulator get failed rc=%d\n",
+ __func__, rc);
+ mma_vreg[i].vreg = NULL;
+ goto error_vdd;
+ }
+ if (regulator_count_voltages(mma_vreg[i].vreg) > 0) {
+ rc = regulator_set_voltage(mma_vreg[i].vreg,
+ mma_vreg[i].min_uV, mma_vreg[i].max_uV);
+ if (rc) {
+ dev_err(&client->dev, "%s:set_voltage failed rc=%d\n",
+ __func__, rc);
+ regulator_put(mma_vreg[i].vreg);
+ mma_vreg[i].vreg = NULL;
+ goto error_vdd;
+ }
+ }
+ rc = regulator_enable(mma_vreg[i].vreg);
+ if (rc) {
+ dev_err(&client->dev, "%s: regulator_enable failed rc =%d\n",
+ __func__, rc);
+ if (regulator_count_voltages(mma_vreg[i].vreg)
+ > 0) {
+ regulator_set_voltage(mma_vreg[i].vreg,
+ 0, mma_vreg[i].max_uV);
+ }
+ regulator_put(mma_vreg[i].vreg);
+ mma_vreg[i].vreg = NULL;
+ goto error_vdd;
+ }
+ }
+ return rc;
+ } else {
+ i = num_vreg;
+ }
+error_vdd:
+ while (--i >= 0) {
+ if (!IS_ERR_OR_NULL(mma_vreg[i].vreg)) {
+ if (regulator_count_voltages(
+ mma_vreg[i].vreg) > 0) {
+ regulator_set_voltage(mma_vreg[i].vreg, 0,
+ mma_vreg[i].max_uV);
+ }
+ regulator_disable(mma_vreg[i].vreg);
+ regulator_put(mma_vreg[i].vreg);
+ mma_vreg[i].vreg = NULL;
+ }
+ }
+ return rc;
+}
+
+static int mma8x5x_data_convert(struct mma8x5x_data *pdata,
+ struct mma8x5x_data_axis *axis_data)
+{
+ short rawdata[3], data[3];
+ int i, j;
+ int position = pdata->position ;
+ if (position < 0 || position > 7)
+ position = 0;
+ rawdata[0] = axis_data->x;
+ rawdata[1] = axis_data->y;
+ rawdata[2] = axis_data->z;
+ for (i = 0; i < 3 ; i++) {
+ data[i] = 0;
+ for (j = 0; j < 3; j++)
+ data[i] += rawdata[j] *
+ mma8x5x_position_setting[position][i][j];
+ }
+ axis_data->x = data[0];
+ axis_data->y = data[1];
+ axis_data->z = data[2];
+ return 0;
+}
+static int mma8x5x_check_id(int id)
+{
+ int i = 0;
+ for (i = 0; i < sizeof(mma8x5x_chip_id)/sizeof(mma8x5x_chip_id[0]);
+ i++)
+ if (id == mma8x5x_chip_id[i])
+ return 1;
+ return 0;
+}
+static char *mma8x5x_id2name(u8 id)
+{
+ return mma8x5x_names[(id >> 4)-1];
+}
+static int mma8x5x_device_init(struct i2c_client *client)
+{
+ int result;
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
+ result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, 0);
+ if (result < 0)
+ goto out;
+
+ result = i2c_smbus_write_byte_data(client, MMA8X5X_XYZ_DATA_CFG,
+ pdata->mode);
+ if (result < 0)
+ goto out;
+ pdata->active = MMA_STANDBY;
+ msleep(MODE_CHANGE_DELAY_MS);
+ return 0;
+out:
+ dev_err(&client->dev, "error when init mma8x5x:(%d)", result);
+ return result;
+}
+static int mma8x5x_device_stop(struct i2c_client *client)
+{
+ u8 val;
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & 0xfe);
+ return 0;
+}
+
+static int mma8x5x_read_data(struct i2c_client *client,
+ struct mma8x5x_data_axis *data)
+{
+ u8 tmp_data[MMA8X5X_BUF_SIZE];
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client,
+ MMA8X5X_OUT_X_MSB, 7, tmp_data);
+ if (ret < MMA8X5X_BUF_SIZE) {
+ dev_err(&client->dev, "i2c block read failed\n");
+ return -EIO;
+ }
+ data->x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
+ data->y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
+ data->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
+ return 0;
+}
+
+static void mma8x5x_report_data(struct mma8x5x_data *pdata)
+{
+ struct input_polled_dev *poll_dev = pdata->poll_dev;
+ struct mma8x5x_data_axis data;
+ mutex_lock(&pdata->data_lock);
+ if (pdata->active == MMA_STANDBY) {
+ poll_dev->poll_interval = POLL_STOP_TIME;
+ /* if standby ,set as 10s to slow the poll. */
+ goto out;
+ } else {
+ if (poll_dev->poll_interval == POLL_STOP_TIME)
+ poll_dev->poll_interval = POLL_INTERVAL;
+ }
+ if (mma8x5x_read_data(pdata->client, &data) != 0)
+ goto out;
+ mma8x5x_data_convert(pdata, &data);
+ input_report_abs(poll_dev->input, ABS_X, data.x);
+ input_report_abs(poll_dev->input, ABS_Y, data.y);
+ input_report_abs(poll_dev->input, ABS_Z, data.z);
+ input_sync(poll_dev->input);
+out:
+ mutex_unlock(&pdata->data_lock);
+}
+
+static void mma8x5x_dev_poll(struct input_polled_dev *dev)
+{
+ struct mma8x5x_data *pdata = (struct mma8x5x_data *)dev->private;
+ mma8x5x_report_data(pdata);
+}
+
+static ssize_t mma8x5x_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
+ struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
+ struct i2c_client *client = pdata->client;
+ u8 val;
+ int enable;
+
+ mutex_lock(&pdata->data_lock);
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ if ((val & 0x01) && pdata->active == MMA_ACTIVED)
+ enable = 1;
+ else
+ enable = 0;
+ mutex_unlock(&pdata->data_lock);
+ return snprintf(buf, PAGE_SIZE, "%d\n", enable);
+}
+
+static ssize_t mma8x5x_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
+ struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
+ struct i2c_client *client = pdata->client;
+ int ret;
+ unsigned long enable;
+ u8 val = 0;
+ ret = kstrtoul(buf, 10, &enable);
+ if (ret)
+ return ret;
+ mutex_lock(&pdata->data_lock);
+ enable = (enable > 0) ? 1 : 0;
+ if (enable && pdata->active == MMA_STANDBY) {
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ ret = i2c_smbus_write_byte_data(client,
+ MMA8X5X_CTRL_REG1, val|0x01);
+ if (!ret) {
+ pdata->active = MMA_ACTIVED;
+ dev_dbg(dev,
+ "%s:mma enable setting active.\n", __func__);
+ }
+ } else if (enable == 0 && pdata->active == MMA_ACTIVED) {
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ ret = i2c_smbus_write_byte_data(client,
+ MMA8X5X_CTRL_REG1, val & 0xFE);
+ if (!ret) {
+ pdata->active = MMA_STANDBY;
+ dev_dbg(dev,
+ "%s:mma enable setting inactive.\n", __func__);
+ }
+ }
+ mutex_unlock(&pdata->data_lock);
+ return count;
+}
+static ssize_t mma8x5x_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
+ struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
+ int position = 0;
+ mutex_lock(&pdata->data_lock);
+ position = pdata->position ;
+ mutex_unlock(&pdata->data_lock);
+ return snprintf(buf, PAGE_SIZE, "%d\n", position);
+}
+
+static ssize_t mma8x5x_position_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
+ struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
+ int position;
+ int ret;
+ ret = kstrtoint(buf, 10, &position);
+ if (ret)
+ return ret;
+ mutex_lock(&pdata->data_lock);
+ pdata->position = position;
+ mutex_unlock(&pdata->data_lock);
+ return count;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+ mma8x5x_enable_show, mma8x5x_enable_store);
+static DEVICE_ATTR(position, S_IWUSR | S_IRUGO,
+ mma8x5x_position_show, mma8x5x_position_store);
+
+static struct attribute *mma8x5x_attributes[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_position.attr,
+ NULL
+};
+
+static const struct attribute_group mma8x5x_attr_group = {
+ .attrs = mma8x5x_attributes,
+};
+static int mma8x5x_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int chip_id;
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+ chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I);
+ if (!mma8x5x_check_id(chip_id))
+ return -ENODEV;
+ dev_dbg(&client->dev, "%s,check %s i2c address 0x%x.\n",
+ __func__, mma8x5x_id2name(chip_id), client->addr);
+ strlcpy(info->type, "mma8x5x", I2C_NAME_SIZE);
+ return 0;
+}
+
+static int mma8x5x_parse_dt(struct device *dev, struct mma8x5x_data *data)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+
+ data->int_pin = of_get_named_gpio_flags(np, "fsl,irq-gpio",
+ 0, &data->int_flags);
+ if (data->int_pin < 0) {
+ dev_err(dev, "Unable to read irq-gpio\n");
+ return data->int_pin;
+ }
+
+ rc = of_property_read_u32(np, "fsl,sensors-position", &temp_val);
+ if (!rc)
+ data->position = temp_val;
+ else {
+ dev_err(dev, "Unable to read sensors-position\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int __devinit mma8x5x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int result, chip_id;
+ struct input_dev *idev;
+ struct mma8x5x_data *pdata;
+ struct i2c_adapter *adapter;
+ struct input_polled_dev *poll_dev;
+ adapter = to_i2c_adapter(client->dev.parent);
+ /* power on the device */
+ result = mma8x5x_config_regulator(client, 1);
+ if (result)
+ goto err_power_on;
+
+ result = i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!result)
+ goto err_out;
+
+ chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I);
+
+ if (!mma8x5x_check_id(chip_id)) {
+ dev_err(&client->dev,
+ "read chip ID 0x%x is not equal to 0x%x,0x%x,0x%x,0x%x,0x%x!\n",
+ chip_id, MMA8451_ID, MMA8452_ID, MMA8453_ID,
+ MMA8652_ID, MMA8653_ID);
+ result = -EINVAL;
+ goto err_out;
+ }
+ /* set the private data */
+ pdata = kzalloc(sizeof(struct mma8x5x_data), GFP_KERNEL);
+ if (!pdata) {
+ result = -ENOMEM;
+ dev_err(&client->dev, "alloc data memory error!\n");
+ goto err_out;
+ }
+
+ if (client->dev.of_node) {
+ result = mma8x5x_parse_dt(&client->dev, pdata);
+ if (result)
+ return result;
+ } else {
+ pdata->position = CONFIG_SENSORS_MMA_POSITION;
+ pdata->int_pin = -1;
+ pdata->int_flags = 0;
+ }
+
+ /* Initialize the MMA8X5X chip */
+ pdata->client = client;
+ pdata->chip_id = chip_id;
+ pdata->mode = MODE_2G;
+
+ mutex_init(&pdata->data_lock);
+ i2c_set_clientdata(client, pdata);
+ /* Initialize the MMA8X5X chip */
+ mma8x5x_device_init(client);
+ /* create the input poll device */
+ poll_dev = input_allocate_polled_device();
+ if (!poll_dev) {
+ result = -ENOMEM;
+ dev_err(&client->dev, "alloc poll device failed!\n");
+ goto err_alloc_poll_device;
+ }
+ poll_dev->poll = mma8x5x_dev_poll;
+ poll_dev->poll_interval = POLL_STOP_TIME;
+ poll_dev->poll_interval_min = POLL_INTERVAL_MIN;
+ poll_dev->poll_interval_max = POLL_INTERVAL_MAX;
+ poll_dev->private = pdata;
+ idev = poll_dev->input;
+ idev->name = ACCEL_INPUT_DEV_NAME;
+ idev->uniq = mma8x5x_id2name(pdata->chip_id);
+ idev->id.bustype = BUS_I2C;
+ idev->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(idev, ABS_X, -0x7fff, 0x7fff, 0, 0);
+ input_set_abs_params(idev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
+ input_set_abs_params(idev, ABS_Z, -0x7fff, 0x7fff, 0, 0);
+ pdata->poll_dev = poll_dev;
+ result = input_register_polled_device(pdata->poll_dev);
+ if (result) {
+ dev_err(&client->dev, "register poll device failed!\n");
+ goto err_register_polled_device;
+ }
+ result = sysfs_create_group(&idev->dev.kobj, &mma8x5x_attr_group);
+ if (result) {
+ dev_err(&client->dev, "create device file failed!\n");
+ result = -EINVAL;
+ goto err_create_sysfs;
+ }
+ dev_info(&client->dev,
+ "%s:mma8x5x device driver probe successfully, position =%d\n",
+ __func__, pdata->position);
+
+ return 0;
+err_create_sysfs:
+ input_unregister_polled_device(pdata->poll_dev);
+err_register_polled_device:
+ input_free_polled_device(poll_dev);
+err_alloc_poll_device:
+ kfree(pdata);
+err_out:
+ mma8x5x_config_regulator(client, 0);
+err_power_on:
+ return result;
+}
+static int __devexit mma8x5x_remove(struct i2c_client *client)
+{
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
+ struct input_polled_dev *poll_dev;
+ mma8x5x_device_stop(client);
+ if (pdata) {
+ poll_dev = pdata->poll_dev;
+ input_unregister_polled_device(poll_dev);
+ input_free_polled_device(poll_dev);
+ kfree(pdata);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mma8x5x_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
+ if (pdata->active == MMA_ACTIVED)
+ mma8x5x_device_stop(client);
+ return 0;
+}
+
+static int mma8x5x_resume(struct device *dev)
+{
+ int val = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
+ if (pdata->active == MMA_ACTIVED) {
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val|0x01);
+ }
+ return 0;
+
+}
+#endif
+
+static const struct i2c_device_id mma8x5x_id[] = {
+ {"mma8x5x", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mma8x5x_id);
+
+static const struct of_device_id mma8x5x_of_match[] = {
+ { .compatible = "fsl,mma8x5x", },
+ { },
+};
+
+static SIMPLE_DEV_PM_OPS(mma8x5x_pm_ops, mma8x5x_suspend, mma8x5x_resume);
+static struct i2c_driver mma8x5x_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "mma8x5x",
+ .owner = THIS_MODULE,
+ .pm = &mma8x5x_pm_ops,
+ .of_match_table = mma8x5x_of_match,
+ },
+ .probe = mma8x5x_probe,
+ .remove = __devexit_p(mma8x5x_remove),
+ .id_table = mma8x5x_id,
+ .detect = mma8x5x_detect,
+ .address_list = normal_i2c,
+};
+
+static int __init mma8x5x_init(void)
+{
+ /* register driver */
+ int res;
+
+ res = i2c_add_driver(&mma8x5x_driver);
+ if (res < 0) {
+ pr_info("%s:add mma8x5x i2c driver failed\n", __func__);
+ return -ENODEV;
+ }
+ return res;
+}
+
+static void __exit mma8x5x_exit(void)
+{
+ i2c_del_driver(&mma8x5x_driver);
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MMA8X5X 3-Axis Orientation/Motion Detection Sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(mma8x5x_init);
+module_exit(mma8x5x_exit);
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 6c64a57..519b7e4 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -43,8 +43,8 @@
#include <linux/gpio.h>
#include <linux/input/mpu3050.h>
#include <linux/regulator/consumer.h>
-
-#define MPU3050_CHIP_ID 0x69
+#include <linux/of_gpio.h>
+#include <mach/gpiomux.h>
#define MPU3050_AUTO_DELAY 1000
@@ -124,6 +124,8 @@
u32 use_poll;
u32 poll_interval;
u32 dlpf_index;
+ u32 enable_gpio;
+ u32 enable;
};
struct sensor_regulator {
@@ -138,6 +140,11 @@
{NULL, "vlogic", 1800000, 1800000},
};
+static const int mpu3050_chip_ids[] = {
+ 0x68,
+ 0x69,
+};
+
struct dlpf_cfg_tb {
u8 cfg; /* cfg index */
u32 lpf_bw; /* low pass filter bandwidth in Hz */
@@ -293,11 +300,42 @@
return size;
}
-static struct device_attribute attributes[] = {
+/**
+ * Set/get enable function is just needed by sensor HAL.
+ * Normally, the open function does all the initialization
+ * and power work. And close undo that open does.
+ * Just keeping the function simple.
+ */
+static ssize_t mpu3050_attr_set_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mpu3050_sensor *sensor = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+ sensor->enable = (u32)val;
+
+ return count;
+}
+
+static ssize_t mpu3050_attr_get_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mpu3050_sensor *sensor = dev_get_drvdata(dev);
+
+ return snprintf(buf, 4, "%d\n", sensor->enable);
+}
+
+static struct device_attribute attributes[] = {
__ATTR(pollrate_ms, 0664,
mpu3050_attr_get_polling_rate,
mpu3050_attr_set_polling_rate),
+ __ATTR(enable, 0644,
+ mpu3050_attr_get_enable,
+ mpu3050_attr_set_enable),
};
static int create_sysfs_interfaces(struct device *dev)
@@ -390,10 +428,12 @@
static void mpu3050_set_power_mode(struct i2c_client *client, u8 val)
{
u8 value;
+ struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
if (val) {
mpu3050_config_regulator(client, 1);
udelay(10);
+ gpio_set_value(sensor->enable_gpio, 1);
}
value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
@@ -404,6 +444,8 @@
if (!val) {
udelay(10);
+ gpio_set_value(sensor->enable_gpio, 0);
+ udelay(10);
mpu3050_config_regulator(client, 0);
}
}
@@ -550,6 +592,37 @@
return 0;
}
+#ifdef CONFIG_OF
+static int mpu3050_parse_dt(struct device *dev,
+ struct mpu3050_gyro_platform_data *pdata)
+{
+ int rc = 0;
+
+ rc = of_property_read_u32(dev->of_node, "invn,poll-interval",
+ &pdata->poll_interval);
+ if (rc) {
+ dev_err(dev, "Failed to read poll-interval\n");
+ return rc;
+ }
+
+ /* check gpio_int later, if it is invalid, just use poll */
+ pdata->gpio_int = of_get_named_gpio_flags(dev->of_node,
+ "invn,gpio-int", 0, NULL);
+
+ pdata->gpio_en = of_get_named_gpio_flags(dev->of_node,
+ "invn,gpio-en", 0, NULL);
+ if (!gpio_is_valid(pdata->gpio_en))
+ return -EINVAL;
+
+ return 0;
+}
+#else
+static int mpu3050_parse_dt(struct device *dev,
+ struct mpu3050_gyro_platform_data *pdata)
+{
+ return -EINVAL;
+}
+#endif
/**
* mpu3050_probe - device detection callback
@@ -566,8 +639,10 @@
{
struct mpu3050_sensor *sensor;
struct input_dev *idev;
+ struct mpu3050_gyro_platform_data *pdata;
int ret;
int error;
+ u32 i;
sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL);
idev = input_allocate_device();
@@ -580,10 +655,29 @@
sensor->client = client;
sensor->dev = &client->dev;
sensor->idev = idev;
- sensor->platform_data = client->dev.platform_data;
i2c_set_clientdata(client, sensor);
+
+ if (client->dev.of_node) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct mpu3050_gyro_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Failed to allcated memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+ ret = mpu3050_parse_dt(&client->dev, pdata);
+ if (ret) {
+ dev_err(&client->dev, "Failed to parse device tree\n");
+ error = ret;
+ goto err_free_mem;
+ }
+ } else
+ pdata = client->dev.platform_data;
+ sensor->platform_data = pdata;
+
if (sensor->platform_data) {
u32 interval = sensor->platform_data->poll_interval;
+ sensor->enable_gpio = sensor->platform_data->gpio_en;
if ((interval < MPU3050_MIN_POLL_INTERVAL) ||
(interval > MPU3050_MAX_POLL_INTERVAL))
@@ -592,6 +686,12 @@
sensor->poll_interval = interval;
} else {
sensor->poll_interval = MPU3050_DEFAULT_POLL_INTERVAL;
+ sensor->enable_gpio = -EINVAL;
+ }
+
+ if (gpio_is_valid(sensor->enable_gpio)) {
+ ret = gpio_request(sensor->enable_gpio, "GYRO_EN_PM");
+ gpio_direction_output(sensor->enable_gpio, 1);
}
mpu3050_set_power_mode(client, 1);
@@ -604,7 +704,11 @@
goto err_free_mem;
}
- if (ret != MPU3050_CHIP_ID) {
+ for (i = 0; i < ARRAY_SIZE(mpu3050_chip_ids); i++)
+ if (ret == mpu3050_chip_ids[i])
+ break;
+
+ if (i == ARRAY_SIZE(mpu3050_chip_ids)) {
dev_err(&client->dev, "unsupported chip id\n");
error = -ENXIO;
goto err_free_mem;
@@ -617,7 +721,7 @@
idev->open = mpu3050_input_open;
idev->close = mpu3050_input_close;
- __set_bit(EV_ABS, idev->evbit);
+ input_set_capability(idev, EV_ABS, ABS_MISC);
input_set_abs_params(idev, ABS_X,
MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
input_set_abs_params(idev, ABS_Y,
@@ -657,6 +761,11 @@
__func__, sensor->platform_data->gpio_int);
goto err_free_gpio;
}
+ client->irq = gpio_to_irq(
+ sensor->platform_data->gpio_int);
+ } else {
+ ret = -EINVAL;
+ goto err_pm_set_suspended;
}
error = request_threaded_irq(client->irq,
@@ -677,14 +786,20 @@
goto err_free_irq;
}
- error = create_sysfs_interfaces(&client->dev);
+ error = create_sysfs_interfaces(&idev->dev);
if (error < 0) {
dev_err(&client->dev, "failed to create sysfs\n");
goto err_input_cleanup;
}
- pm_runtime_enable(&client->dev);
- pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);
+ if (sensor->use_poll)
+ schedule_delayed_work(&sensor->input_work,
+ msecs_to_jiffies(sensor->poll_interval));
+ else
+ i2c_smbus_write_byte_data(sensor->client, MPU3050_INT_CFG,
+ MPU3050_ACTIVE_LOW |
+ MPU3050_OPEN_DRAIN |
+ MPU3050_RAW_RDY_EN);
return 0;
@@ -722,6 +837,8 @@
free_irq(client->irq, sensor);
remove_sysfs_interfaces(&client->dev);
+ if (gpio_is_valid(sensor->enable_gpio))
+ gpio_free(sensor->enable_gpio);
input_unregister_device(sensor->idev);
kfree(sensor);
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 479b788..5415f4e 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -393,6 +393,9 @@
bool update_cfg;
const char *fw_name;
bool no_force_update;
+ bool lpm_support;
+ bool regs_enabled;
+
#if defined(CONFIG_SECURE_TOUCH)
atomic_t st_enabled;
atomic_t st_pending_irqs;
@@ -2158,6 +2161,11 @@
if (on == false)
goto power_off;
+ if (data->regs_enabled) {
+ dev_dbg(&data->client->dev, "regs are already enabled\n");
+ return 0;
+ }
+
rc = reg_set_optimum_mode_check(data->vcc_ana, MXT_ACTIVE_LOAD_UA);
if (rc < 0) {
dev_err(&data->client->dev,
@@ -2206,6 +2214,8 @@
}
}
+ data->regs_enabled = true;
+
msleep(130);
return 0;
@@ -2226,6 +2236,12 @@
return rc;
power_off:
+
+ if (!data->regs_enabled) {
+ dev_dbg(&data->client->dev, "regs are already disabled\n");
+ return 0;
+ }
+
reg_set_optimum_mode_check(data->vcc_ana, 0);
regulator_disable(data->vcc_ana);
if (data->pdata->digital_pwr_regulator) {
@@ -2236,6 +2252,9 @@
reg_set_optimum_mode_check(data->vcc_i2c, 0);
regulator_disable(data->vcc_i2c);
}
+
+ data->regs_enabled = false;
+
msleep(50);
return 0;
}
@@ -2435,8 +2454,9 @@
struct input_dev *input_dev = data->input_dev;
int error;
- mutex_lock(&input_dev->mutex);
+ disable_irq(data->irq);
+ mutex_lock(&input_dev->mutex);
if (input_dev->users) {
error = mxt_stop(data);
if (error < 0) {
@@ -2444,16 +2464,24 @@
mutex_unlock(&input_dev->mutex);
return error;
}
-
}
mutex_unlock(&input_dev->mutex);
+ mxt_release_all(data);
/* put regulators in low power mode */
- error = mxt_regulator_lpm(data, true);
- if (error < 0) {
- dev_err(dev, "failed to enter low power mode\n");
- return error;
+ if (data->lpm_support) {
+ error = mxt_regulator_lpm(data, true);
+ if (error < 0) {
+ dev_err(dev, "failed to enter low power mode\n");
+ return error;
+ }
+ } else {
+ error = mxt_power_on(data, false);
+ if (error < 0) {
+ dev_err(dev, "failed to disable regulators\n");
+ return error;
+ }
}
return 0;
@@ -2466,13 +2494,25 @@
struct input_dev *input_dev = data->input_dev;
int error;
- /* put regulators in high power mode */
- error = mxt_regulator_lpm(data, false);
- if (error < 0) {
- dev_err(dev, "failed to enter high power mode\n");
- return error;
+ /* put regulators back in active power mode */
+ if (data->lpm_support) {
+ error = mxt_regulator_lpm(data, false);
+ if (error < 0) {
+ dev_err(dev, "failed to enter high power mode\n");
+ return error;
+ }
+ } else {
+ error = mxt_power_on(data, true);
+ if (error < 0) {
+ dev_err(dev, "failed to enable regulators\n");
+ return error;
+ }
+ mxt_power_on_delay(data);
}
+ mxt_write_object(data, MXT_GEN_COMMAND_T6, MXT_COMMAND_RESET, 1);
+ mxt_reset_delay(data);
+
mutex_lock(&input_dev->mutex);
if (input_dev->users) {
@@ -2495,6 +2535,8 @@
mutex_unlock(&input_dev->mutex);
+ enable_irq(data->irq);
+
return 0;
}
@@ -2672,6 +2714,9 @@
pdata->no_force_update = of_property_read_bool(np,
"atmel,no-force-update");
+ pdata->no_lpm_support = of_property_read_bool(np,
+ "atmel,no-lpm-support");
+
/* reset, irq gpio info */
pdata->reset_gpio = of_get_named_gpio_flags(np, "atmel,reset-gpio",
0, &pdata->reset_gpio_flags);
@@ -2877,6 +2922,7 @@
data->input_dev = input_dev;
data->pdata = pdata;
data->no_force_update = pdata->no_force_update;
+ data->lpm_support = !pdata->no_lpm_support;
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index 8dbac64..8de6b1e 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -308,7 +308,7 @@
event->pressure = 0;
}
- input_mt_slot(data->input_dev, i);
+ input_mt_slot(data->input_dev, event->finger_id[i]);
input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,
!!event->pressure);
@@ -317,10 +317,6 @@
event->x[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
event->y[i]);
- input_report_abs(data->input_dev, ABS_MT_PRESSURE,
- event->pressure);
- input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
- event->finger_id[i]);
input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
event->pressure);
}
@@ -493,7 +489,7 @@
static int ft5x06_ts_suspend(struct device *dev)
{
struct ft5x06_ts_data *data = dev_get_drvdata(dev);
- char txbuf[2];
+ char txbuf[2], i;
if (data->loading_fw) {
dev_info(dev, "Firmware loading in process...\n");
@@ -507,6 +503,14 @@
disable_irq(data->client->irq);
+ /* release all touches */
+ for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) {
+ input_mt_slot(data->input_dev, i);
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0);
+ }
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+
if (gpio_is_valid(data->pdata->reset_gpio)) {
txbuf[0] = FT_REG_PMODE;
txbuf[1] = FT_PMODE_HIBERNATE;
@@ -1231,7 +1235,6 @@
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min,
pdata->y_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0);
err = input_register_device(input_dev);
if (err) {
@@ -1306,7 +1309,7 @@
err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1);
if (err < 0) {
dev_err(&client->dev, "version read failed");
- return err;
+ goto free_reset_gpio;
}
dev_info(&client->dev, "Device ID = 0x%x\n", reg_value);
diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c
index 349b020..2a5fea7 100644
--- a/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_fw_update.c
@@ -29,7 +29,6 @@
#include <linux/input/synaptics_dsx.h>
#include "synaptics_i2c_rmi4.h"
-#define DEBUG_FW_UPDATE
#define SHOW_PROGRESS
#define MAX_FIRMWARE_ID_LEN 10
#define FORCE_UPDATE false
@@ -53,7 +52,13 @@
#define BLOCK_NUMBER_OFFSET 0
#define BLOCK_DATA_OFFSET 2
-#define NAME_BUFFER_SIZE 128
+#define RMI4_INFO_MAX_LEN 200
+
+#define RMI4_STORE_TS_INFO(buf, id, rev, fw_ver) \
+ snprintf(buf, RMI4_INFO_MAX_LEN, \
+ "controller\t= synaptics\n" \
+ "model\t\t= %d rev %d\n" \
+ "fw_ver\t\t= %d\n", id, rev, fw_ver)
enum falsh_config_area {
UI_CONFIG_AREA = 0x00,
@@ -77,7 +82,8 @@
enum flash_area {
NONE,
UI_FIRMWARE,
- CONFIG_AREA
+ CONFIG_AREA,
+ MISMATCH
};
enum image_file_option {
@@ -99,53 +105,6 @@
#define SLEEP_TIME_US 50
-static ssize_t fwu_sysfs_show_image(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t fwu_sysfs_store_image(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_write_config_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_read_config_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_config_area_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_image_size_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_block_size_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_config_id_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
static int fwu_wait_for_idle(int timeout_ms);
struct image_header_data {
@@ -163,10 +122,10 @@
unsigned char config_size[4];
/* 0x10-0x1F */
unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE];
- unsigned char reserved_1a;
- unsigned char reserved_1b;
- unsigned char reserved_1c;
- unsigned char reserved_1d;
+ unsigned char pkg_id_lsb;
+ unsigned char pkg_id_msb;
+ unsigned char pkg_id_rev_lsb;
+ unsigned char pkg_id_rev_msb;
unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
/* 0x20-0x2F */
unsigned char reserved_20_2f[0x10];
@@ -178,7 +137,7 @@
/* 0x50-0x53*/
unsigned char firmware_id[4];
} __packed;
- unsigned char data[54];
+ unsigned char data[0x54];
};
};
@@ -190,6 +149,8 @@
unsigned char bootloader_version;
unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+ u16 package_id;
+ u16 package_revision_id;
unsigned int firmware_id;
bool is_contain_build_info;
};
@@ -290,59 +251,8 @@
struct f34_flash_properties flash_properties;
struct workqueue_struct *fwu_workqueue;
struct delayed_work fwu_work;
- char *firmware_name;
-};
-
-static struct bin_attribute dev_attr_data = {
- .attr = {
- .name = "data",
- .mode = (S_IRUGO | S_IWUGO),
- },
- .size = 0,
- .read = fwu_sysfs_show_image,
- .write = fwu_sysfs_store_image,
-};
-
-static struct device_attribute attrs[] = {
- __ATTR(force_update_fw, S_IWUGO,
- synaptics_rmi4_show_error,
- fwu_sysfs_force_reflash_store),
- __ATTR(update_fw, S_IWUGO,
- synaptics_rmi4_show_error,
- fwu_sysfs_do_reflash_store),
- __ATTR(writeconfig, S_IWUGO,
- synaptics_rmi4_show_error,
- fwu_sysfs_write_config_store),
- __ATTR(readconfig, S_IWUGO,
- synaptics_rmi4_show_error,
- fwu_sysfs_read_config_store),
- __ATTR(configarea, S_IWUGO,
- synaptics_rmi4_show_error,
- fwu_sysfs_config_area_store),
- __ATTR(imagesize, S_IWUGO,
- synaptics_rmi4_show_error,
- fwu_sysfs_image_size_store),
- __ATTR(blocksize, S_IRUGO,
- fwu_sysfs_block_size_show,
- synaptics_rmi4_store_error),
- __ATTR(fwblockcount, S_IRUGO,
- fwu_sysfs_firmware_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(configblockcount, S_IRUGO,
- fwu_sysfs_configuration_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(permconfigblockcount, S_IRUGO,
- fwu_sysfs_perm_config_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(blconfigblockcount, S_IRUGO,
- fwu_sysfs_bl_config_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(dispconfigblockcount, S_IRUGO,
- fwu_sysfs_disp_config_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(config_id, S_IRUGO,
- fwu_sysfs_config_id_show,
- synaptics_rmi4_store_error),
+ char firmware_name[NAME_BUFFER_SIZE];
+ char *ts_info;
};
static struct synaptics_rmi4_fwu_handle *fwu;
@@ -365,6 +275,26 @@
(unsigned int)ptr[0] * 0x1000000;
}
+static void synaptics_rmi4_update_debug_info(void)
+{
+ unsigned char pkg_id[4];
+ unsigned int build_id;
+ struct synaptics_rmi4_device_info *rmi;
+ /* read device package id */
+ fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f01_fd.query_base_addr + 17,
+ pkg_id,
+ sizeof(pkg_id));
+ rmi = &(fwu->rmi4_data->rmi4_mod_info);
+
+ build_id = (unsigned int)rmi->build_id[0] +
+ (unsigned int)rmi->build_id[1] * 0x100 +
+ (unsigned int)rmi->build_id[2] * 0x10000;
+
+ RMI4_STORE_TS_INFO(fwu->ts_info, pkg_id[1] << 8 | pkg_id[0],
+ pkg_id[3] << 8 | pkg_id[2], build_id);
+}
+
static void parse_header(struct image_header *header,
const unsigned char *fw_image)
{
@@ -375,25 +305,32 @@
header->config_size = extract_uint(data->config_size);
memcpy(header->product_id, data->product_id,
sizeof(data->product_id));
- header->product_id[sizeof(data->product_info)] = 0;
+ header->product_id[sizeof(data->product_id)] = 0;
+
memcpy(header->product_info, data->product_info,
sizeof(data->product_info));
header->is_contain_build_info =
(data->options_firmware_id == (1 << OPTION_BUILD_INFO));
if (header->is_contain_build_info) {
+ header->package_id = (data->pkg_id_rev_msb << 8) |
+ data->pkg_id_lsb;
+ header->package_revision_id = (data->pkg_id_rev_msb << 8) |
+ data->pkg_id_rev_lsb;
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s Package ID %d Rev %d\n", __func__,
+ header->package_id, header->package_revision_id);
+
header->firmware_id = extract_uint(data->firmware_id);
dev_info(&fwu->rmi4_data->i2c_client->dev,
"%s Firwmare build id %d\n", __func__,
header->firmware_id);
}
-#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev,
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
"Firwmare size %d, config size %d\n",
header->image_size,
header->config_size);
-#endif
return;
}
@@ -544,11 +481,9 @@
{
int retval;
-#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev,
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
"%s: Reset device\n",
__func__);
-#endif
retval = fwu->rmi4_data->reset_device(fwu->rmi4_data);
if (retval < 0) {
@@ -613,6 +548,7 @@
unsigned long imageFirmwareID;
unsigned char firmware_id[4];
unsigned char config_id[4];
+ unsigned char pkg_id[4];
char *strptr;
char *imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL);
enum flash_area flash_area = NONE;
@@ -624,6 +560,24 @@
goto exit;
}
+ if (header->is_contain_build_info) {
+ /* if package id does not match, do not update firmware */
+ fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f01_fd.query_base_addr + 17,
+ pkg_id,
+ sizeof(pkg_id));
+
+ if (header->package_id != ((pkg_id[1] << 8) | pkg_id[0])) {
+ flash_area = MISMATCH;
+ goto exit;
+ }
+ if (header->package_revision_id !=
+ ((pkg_id[3] << 8) | pkg_id[2])) {
+ flash_area = MISMATCH;
+ goto exit;
+ }
+ }
+
retval = fwu_read_f01_device_status(&f01_device_status);
if (retval < 0) {
flash_area = NONE;
@@ -734,10 +688,13 @@
flash_area = CONFIG_AREA;
goto exit;
}
-
exit:
kfree(imagePR);
- if (flash_area == NONE)
+ if (flash_area == MISMATCH)
+ dev_info(&i2c_client->dev,
+ "%s: Package ID indicates mismatch of firmware and" \
+ " controller compatibility\n", __func__);
+ else if (flash_area == NONE)
dev_info(&i2c_client->dev,
"%s: Nothing needs to be updated\n", __func__);
else
@@ -759,9 +716,7 @@
bool f34found = false;
struct synaptics_rmi4_fn_desc rmi_fd;
-#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Scan PDT\n");
-#endif
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev, "Scan PDT\n");
for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
retval = fwu->fn_ptr->read(fwu->rmi4_data,
@@ -824,13 +779,11 @@
10 : 100;
#endif
-#ifdef DEBUG_FW_UPDATE
- dev_info(&i2c_client->dev,
+ dev_dbg(&i2c_client->dev,
"%s: Start to update %s blocks\n",
__func__,
command == CMD_WRITE_CONFIG_BLOCK ?
"config" : "firmware");
-#endif
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
block_offset,
@@ -915,12 +868,11 @@
{
int retval;
-#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev,
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
"Write bootloader ID 0x%02X 0x%02X\n",
fwu->bootloader_id[0],
fwu->bootloader_id[1]);
-#endif
+
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
fwu->bootloader_id,
@@ -941,9 +893,8 @@
struct f01_device_status f01_device_status;
struct f01_device_control f01_device_control;
-#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Enter bootloader mode\n");
-#endif
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev, "Enter bootloader mode\n");
+
retval = fwu_read_f01_device_status(&f01_device_status);
if (retval < 0)
return retval;
@@ -1302,26 +1253,23 @@
pr_notice("%s: Start of reflash process\n", __func__);
- if (!fwu->rmi4_data->fw_image_name) {
- retval = 0;
+ if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) == 0) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Firmware image name not given, skipping update\n");
- goto exit;
+ return 0;
+ }
+
+ if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) ==
+ NAME_BUFFER_SIZE) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "Firmware image name exceeds max length (%d), " \
+ "skipping update\n", NAME_BUFFER_SIZE);
+ return 0;
}
if (fwu->ext_data_source)
fw_image = fwu->ext_data_source;
else {
- fwu->firmware_name = kcalloc(NAME_BUFFER_SIZE,
- sizeof(char), GFP_KERNEL);
- if (!fwu->firmware_name) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s Failed to allocate firmware name (%d).\n",
- __func__, NAME_BUFFER_SIZE);
- retval = -ENOMEM;
- goto memory_exit;
- }
-
snprintf(fwu->firmware_name, NAME_BUFFER_SIZE, "%s",
fwu->rmi4_data->fw_image_name);
dev_info(&fwu->rmi4_data->i2c_client->dev,
@@ -1336,8 +1284,7 @@
"%s: Firmware image %s not available\n",
__func__,
fwu->firmware_name);
- retval = -EINVAL;
- goto exit;
+ return -EINVAL;
}
dev_dbg(&fwu->rmi4_data->i2c_client->dev,
@@ -1363,6 +1310,8 @@
switch (flash_area) {
case NONE:
+ case MISMATCH:
+ retval = 0;
dev_info(&fwu->rmi4_data->i2c_client->dev,
"%s: No need to do reflash.\n",
__func__);
@@ -1408,13 +1357,11 @@
goto exit;
}
+exit:
if (fw_entry)
release_firmware(fw_entry);
pr_notice("%s: End of reflash process\n", __func__);
-exit:
- kfree(fwu->firmware_name);
-memory_exit:
return retval;
}
@@ -1428,10 +1375,21 @@
if (!fwu->initialized)
return -ENODEV;
+ fwu->rmi4_data->fw_updating = true;
+ if (fwu->rmi4_data->suspended == true) {
+ fwu->rmi4_data->fw_updating = false;
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "Cannot start fw upgrade while device is in suspend\n");
+ return -EBUSY;
+ }
+
fwu->ext_data_source = fw_data;
fwu->config_area = UI_CONFIG_AREA;
retval = fwu_start_reflash();
+ fwu->rmi4_data->fw_updating = false;
+
+ synaptics_rmi4_update_debug_info();
return retval;
}
@@ -1468,6 +1426,40 @@
return count;
}
+static ssize_t fwu_sysfs_fw_name_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ char *strptr;
+
+ if (count >= NAME_BUFFER_SIZE) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Input over %d characters long\n", NAME_BUFFER_SIZE);
+ return -EINVAL;
+ }
+
+ strptr = strnstr(buf, ".img",
+ count);
+ if (!strptr) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Input is not valid .img file\n");
+ return -EINVAL;
+ }
+
+ strlcpy(rmi4_data->fw_image_name, buf, count);
+ return count;
+}
+
+static ssize_t fwu_sysfs_fw_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) > 0)
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ fwu->rmi4_data->fw_image_name);
+ else
+ return snprintf(buf, PAGE_SIZE, "No firmware name given\n");
+}
+
static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
@@ -1683,6 +1675,41 @@
config_id[0], config_id[1], config_id[2], config_id[3]);
}
+static ssize_t fwu_sysfs_package_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char pkg_id[4];
+ /* read device package id */
+ fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f01_fd.query_base_addr + 17,
+ pkg_id,
+ sizeof(pkg_id));
+
+ return snprintf(buf, PAGE_SIZE, "%d rev %d\n",
+ (pkg_id[1] << 8) | pkg_id[0],
+ (pkg_id[3] << 8) | pkg_id[2]);
+}
+
+static int synaptics_rmi4_debug_dump_info(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%s\n", fwu->ts_info);
+
+ return 0;
+}
+
+static int debugfs_dump_info_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, synaptics_rmi4_debug_dump_info,
+ inode->i_private);
+}
+
+static const struct file_operations debug_dump_info_fops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_dump_info_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
{
@@ -1692,6 +1719,65 @@
return;
}
+static struct bin_attribute dev_attr_data = {
+ .attr = {
+ .name = "data",
+ .mode = (S_IRUGO | S_IWUSR | S_IWGRP),
+ },
+ .size = 0,
+ .read = fwu_sysfs_show_image,
+ .write = fwu_sysfs_store_image,
+};
+
+static struct device_attribute attrs[] = {
+ __ATTR(fw_name, S_IRUGO | S_IWUSR | S_IWGRP,
+ fwu_sysfs_fw_name_show,
+ fwu_sysfs_fw_name_store),
+ __ATTR(force_update_fw, S_IRUGO | S_IWUSR | S_IWGRP,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_force_reflash_store),
+ __ATTR(update_fw, S_IRUGO | S_IWUSR | S_IWGRP,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_do_reflash_store),
+ __ATTR(writeconfig, S_IRUGO | S_IWUSR | S_IWGRP,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_write_config_store),
+ __ATTR(readconfig, S_IRUGO | S_IWUSR | S_IWGRP,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_read_config_store),
+ __ATTR(configarea, S_IRUGO | S_IWUSR | S_IWGRP,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_config_area_store),
+ __ATTR(imagesize, S_IRUGO | S_IWUSR | S_IWGRP,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_image_size_store),
+ __ATTR(blocksize, S_IRUGO,
+ fwu_sysfs_block_size_show,
+ synaptics_rmi4_store_error),
+ __ATTR(fwblockcount, S_IRUGO,
+ fwu_sysfs_firmware_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(configblockcount, S_IRUGO,
+ fwu_sysfs_configuration_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(permconfigblockcount, S_IRUGO,
+ fwu_sysfs_perm_config_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(blconfigblockcount, S_IRUGO,
+ fwu_sysfs_bl_config_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(dispconfigblockcount, S_IRUGO,
+ fwu_sysfs_disp_config_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(config_id, S_IRUGO,
+ fwu_sysfs_config_id_show,
+ synaptics_rmi4_store_error),
+ __ATTR(package_id, S_IRUGO,
+ fwu_sysfs_package_id_show,
+ synaptics_rmi4_store_error),
+};
+
+
static void synaptics_rmi4_fwu_work(struct work_struct *work)
{
fwu_start_reflash();
@@ -1702,6 +1788,7 @@
int retval;
unsigned char attr_count;
struct pdt_properties pdt_props;
+ struct dentry *temp;
fwu = kzalloc(sizeof(*fwu), GFP_KERNEL);
if (!fwu) {
@@ -1765,7 +1852,7 @@
fwu->initialized = true;
fwu->force_update = FORCE_UPDATE;
- retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
+ retval = sysfs_create_bin_file(&rmi4_data->i2c_client->dev.kobj,
&dev_attr_data);
if (retval < 0) {
dev_err(&rmi4_data->i2c_client->dev,
@@ -1775,7 +1862,7 @@
}
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
+ retval = sysfs_create_file(&rmi4_data->i2c_client->dev.kobj,
&attrs[attr_count].attr);
if (retval < 0) {
dev_err(&rmi4_data->i2c_client->dev,
@@ -1786,6 +1873,25 @@
}
}
+ temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR,
+ fwu->rmi4_data->dir, fwu->rmi4_data,
+ &debug_dump_info_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to create debugfs dump info file\n",
+ __func__);
+ retval = PTR_ERR(temp);
+ goto exit_remove_attrs;
+ }
+
+ fwu->ts_info = kzalloc(RMI4_INFO_MAX_LEN, GFP_KERNEL);
+ if (!fwu->ts_info) {
+ dev_err(&rmi4_data->i2c_client->dev, "Not enough memory\n");
+ goto exit_free_ts_info;
+ }
+
+ synaptics_rmi4_update_debug_info();
+
#ifdef INSIDE_FIRMWARE_UPDATE
fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue");
INIT_DELAYED_WORK(&fwu->fwu_work, synaptics_rmi4_fwu_work);
@@ -1797,7 +1903,8 @@
init_completion(&remove_complete);
return 0;
-
+exit_free_ts_info:
+ debugfs_remove(temp);
exit_remove_attrs:
for (attr_count--; attr_count >= 0; attr_count--) {
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index b9dd4ae..ba0be2b 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -34,6 +34,7 @@
#define DRIVER_NAME "synaptics_rmi4_i2c"
#define INPUT_PHYS_NAME "synaptics_rmi4_i2c/input0"
+#define DEBUGFS_DIR_NAME "ts_debug"
#define RESET_DELAY 100
@@ -114,12 +115,6 @@
static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
-static ssize_t synaptics_rmi4_mode_suspend_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t synaptics_rmi4_mode_resume_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
#if defined(CONFIG_FB)
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data);
@@ -240,17 +235,11 @@
static struct device_attribute attrs[] = {
#ifdef CONFIG_PM
- __ATTR(full_pm_cycle, (S_IRUGO | S_IWUGO),
+ __ATTR(full_pm_cycle, (S_IRUGO | S_IWUSR | S_IWGRP),
synaptics_rmi4_full_pm_cycle_show,
synaptics_rmi4_full_pm_cycle_store),
- __ATTR(mode_suspend, S_IWUGO,
- synaptics_rmi4_show_error,
- synaptics_rmi4_mode_suspend_store),
- __ATTR(mode_resume, S_IWUGO,
- synaptics_rmi4_show_error,
- synaptics_rmi4_mode_resume_store),
#endif
- __ATTR(reset, S_IWUGO,
+ __ATTR(reset, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
synaptics_rmi4_f01_reset_store),
__ATTR(productinfo, S_IRUGO,
@@ -262,13 +251,13 @@
__ATTR(flashprog, S_IRUGO,
synaptics_rmi4_f01_flashprog_show,
synaptics_rmi4_store_error),
- __ATTR(0dbutton, (S_IRUGO | S_IWUGO),
+ __ATTR(0dbutton, (S_IRUGO | S_IWUSR | S_IWGRP),
synaptics_rmi4_0dbutton_show,
synaptics_rmi4_0dbutton_store),
- __ATTR(flipx, (S_IRUGO | S_IWUGO),
+ __ATTR(flipx, (S_IRUGO | S_IWUSR | S_IWGRP),
synaptics_rmi4_flipx_show,
synaptics_rmi4_flipx_store),
- __ATTR(flipy, (S_IRUGO | S_IWUGO),
+ __ATTR(flipy, (S_IRUGO | S_IWUSR | S_IWGRP),
synaptics_rmi4_flipy_show,
synaptics_rmi4_flipy_store),
};
@@ -300,34 +289,30 @@
return count;
}
-static ssize_t synaptics_rmi4_mode_suspend_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static int synaptics_rmi4_debug_suspend_set(void *_data, u64 val)
{
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ struct synaptics_rmi4_data *rmi4_data = _data;
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
+ if (val)
+ synaptics_rmi4_suspend(&rmi4_data->input_dev->dev);
+ else
+ synaptics_rmi4_resume(&rmi4_data->input_dev->dev);
- synaptics_rmi4_suspend(&(rmi4_data->input_dev->dev));
-
- return count;
+ return 0;
}
-static ssize_t synaptics_rmi4_mode_resume_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t synaptics_rmi4_debug_suspend_get(void *_data, u64 *val)
{
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ struct synaptics_rmi4_data *rmi4_data = _data;
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
+ *val = rmi4_data->suspended;
- synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
-
- return count;
+ return 0;
}
+DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, synaptics_rmi4_debug_suspend_get,
+ synaptics_rmi4_debug_suspend_set, "%lld\n");
+
#ifdef CONFIG_FB
static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
{
@@ -2061,6 +2046,7 @@
struct synaptics_rmi4_device_info *rmi;
struct synaptics_rmi4_platform_data *platform_data =
client->dev.platform_data;
+ struct dentry *temp;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -2118,6 +2104,8 @@
rmi4_data->touch_stopped = false;
rmi4_data->sensor_sleep = false;
rmi4_data->irq_enabled = false;
+ rmi4_data->fw_updating = false;
+ rmi4_data->suspended = false;
rmi4_data->i2c_read = synaptics_rmi4_i2c_read;
rmi4_data->i2c_write = synaptics_rmi4_i2c_write;
@@ -2127,7 +2115,9 @@
rmi4_data->flip_x = rmi4_data->board->x_flip;
rmi4_data->flip_y = rmi4_data->board->y_flip;
- rmi4_data->fw_image_name = rmi4_data->board->fw_image_name;
+ if (rmi4_data->board->fw_image_name)
+ snprintf(rmi4_data->fw_image_name, NAME_BUFFER_SIZE, "%s",
+ rmi4_data->board->fw_image_name);
rmi4_data->input_dev->name = DRIVER_NAME;
rmi4_data->input_dev->phys = INPUT_PHYS_NAME;
@@ -2292,8 +2282,27 @@
goto err_enable_irq;
}
+ rmi4_data->dir = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL);
+ if (rmi4_data->dir == NULL || IS_ERR(rmi4_data->dir)) {
+ dev_err(&client->dev,
+ "%s: Failed to create debugfs directory, rc = %ld\n",
+ __func__, PTR_ERR(rmi4_data->dir));
+ retval = PTR_ERR(rmi4_data->dir);
+ goto err_create_debugfs_dir;
+ }
+
+ temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, rmi4_data->dir,
+ rmi4_data, &debug_suspend_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ dev_err(&client->dev,
+ "%s: Failed to create suspend debugfs file, rc = %ld\n",
+ __func__, PTR_ERR(temp));
+ retval = PTR_ERR(temp);
+ goto err_create_debugfs_file;
+ }
+
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
+ retval = sysfs_create_file(&client->dev.kobj,
&attrs[attr_count].attr);
if (retval < 0) {
dev_err(&client->dev,
@@ -2317,7 +2326,10 @@
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
&attrs[attr_count].attr);
}
-
+err_create_debugfs_file:
+ debugfs_remove_recursive(rmi4_data->dir);
+err_create_debugfs_dir:
+ free_irq(rmi4_data->irq, rmi4_data);
err_enable_irq:
cancel_delayed_work_sync(&rmi4_data->det_work);
flush_workqueue(rmi4_data->det_workqueue);
@@ -2372,6 +2384,7 @@
rmi = &(rmi4_data->rmi4_mod_info);
+ debugfs_remove_recursive(rmi4_data->dir);
cancel_delayed_work_sync(&rmi4_data->det_work);
flush_workqueue(rmi4_data->det_workqueue);
destroy_workqueue(rmi4_data->det_workqueue);
@@ -2659,19 +2672,32 @@
struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
int retval;
- if (!rmi4_data->sensor_sleep) {
- rmi4_data->touch_stopped = true;
- wake_up(&rmi4_data->wait);
- synaptics_rmi4_irq_enable(rmi4_data, false);
- synaptics_rmi4_sensor_sleep(rmi4_data);
+ if (rmi4_data->suspended) {
+ dev_info(dev, "Already in suspend state\n");
+ return 0;
}
- retval = synaptics_rmi4_regulator_lpm(rmi4_data, true);
- if (retval < 0) {
- dev_err(dev, "failed to enter low power mode\n");
- return retval;
+ if (!rmi4_data->fw_updating) {
+ if (!rmi4_data->sensor_sleep) {
+ rmi4_data->touch_stopped = true;
+ wake_up(&rmi4_data->wait);
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+ synaptics_rmi4_sensor_sleep(rmi4_data);
+ }
+
+ retval = synaptics_rmi4_regulator_lpm(rmi4_data, true);
+ if (retval < 0) {
+ dev_err(dev, "failed to enter low power mode\n");
+ return retval;
+ }
+ } else {
+ dev_err(dev,
+ "Firmware updating, cannot go into suspend mode\n");
+ return 0;
}
+ rmi4_data->suspended = true;
+
return 0;
}
@@ -2690,6 +2716,11 @@
struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
int retval;
+ if (!rmi4_data->suspended) {
+ dev_info(dev, "Already in awake state\n");
+ return 0;
+ }
+
retval = synaptics_rmi4_regulator_lpm(rmi4_data, false);
if (retval < 0) {
dev_err(dev, "failed to enter active power mode\n");
@@ -2700,6 +2731,8 @@
rmi4_data->touch_stopped = false;
synaptics_rmi4_irq_enable(rmi4_data, true);
+ rmi4_data->suspended = false;
+
return 0;
}
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/drivers/input/touchscreen/synaptics_i2c_rmi4.h
index 681b95c..5f6d6ce 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.h
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.h
@@ -34,6 +34,7 @@
#elif defined CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
+#include <linux/debugfs.h>
#define PDT_PROPS (0x00EF)
#define PDT_START (0x00E9)
@@ -68,6 +69,8 @@
#define MASK_2BIT 0x03
#define MASK_1BIT 0x01
+#define NAME_BUFFER_SIZE 128
+
/*
* struct synaptics_rmi4_fn_desc - function descriptor fields in PDT
* @query_base_addr: base address for query registers
@@ -183,6 +186,7 @@
* @fingers_on_2d: flag to indicate presence of fingers in 2d area
* @flip_x: set to TRUE if desired to flip direction on x-axis
* @flip_y: set to TRUE if desired to flip direction on y-axis
+ * @fw_updating: firmware is updating flag
* @sensor_sleep: flag to indicate sleep state of sensor
* @wait: wait queue for touch data polling in interrupt thread
* @i2c_read: pointer to i2c read function
@@ -202,7 +206,8 @@
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
- const char *fw_image_name;
+ struct dentry *dir;
+ char fw_image_name[NAME_BUFFER_SIZE];
unsigned char current_page;
unsigned char button_0d_enabled;
unsigned char full_pm_cycle;
@@ -224,6 +229,8 @@
bool sensor_sleep;
bool flip_x;
bool flip_y;
+ bool fw_updating;
+ bool suspended;
wait_queue_head_t wait;
int (*i2c_read)(struct synaptics_rmi4_data *pdata, unsigned short addr,
unsigned char *data, unsigned short length);
diff --git a/drivers/input/touchscreen/synaptics_rmi_dev.c b/drivers/input/touchscreen/synaptics_rmi_dev.c
index c6b8a1c..c60ca23 100644
--- a/drivers/input/touchscreen/synaptics_rmi_dev.c
+++ b/drivers/input/touchscreen/synaptics_rmi_dev.c
@@ -73,19 +73,19 @@
};
static struct device_attribute attrs[] = {
- __ATTR(open, S_IWUGO,
+ __ATTR(open, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
rmidev_sysfs_open_store),
- __ATTR(release, S_IWUGO,
+ __ATTR(release, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
rmidev_sysfs_release_store),
- __ATTR(address, S_IWUGO,
+ __ATTR(address, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
rmidev_sysfs_address_store),
- __ATTR(length, S_IWUGO,
+ __ATTR(length, S_IRUGO | S_IWUSR | S_IWGRP,
synaptics_rmi4_show_error,
rmidev_sysfs_length_store),
- __ATTR(data, (S_IRUGO | S_IWUGO),
+ __ATTR(data, (S_IRUGO | S_IWUSR | S_IWGRP),
rmidev_sysfs_data_show,
rmidev_sysfs_data_store),
};
diff --git a/drivers/iommu/msm_iommu-v1.c b/drivers/iommu/msm_iommu-v1.c
index a400b58..b9c4cae 100644
--- a/drivers/iommu/msm_iommu-v1.c
+++ b/drivers/iommu/msm_iommu-v1.c
@@ -39,6 +39,7 @@
#define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
static DEFINE_MUTEX(msm_iommu_lock);
+struct dump_regs_tbl dump_regs_tbl[MAX_DUMP_REGS];
static int __enable_regulators(struct msm_iommu_drvdata *drvdata)
{
@@ -769,7 +770,7 @@
struct msm_iommu_priv *priv;
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
- unsigned int par;
+ u64 par;
void __iomem *base;
phys_addr_t ret = 0;
int ctx;
@@ -802,6 +803,23 @@
__disable_clocks(iommu_drvdata);
if (par & CB_PAR_F) {
+ unsigned int level = (par & CB_PAR_PLVL) >> CB_PAR_PLVL_SHIFT;
+ pr_err("IOMMU translation fault!\n");
+ pr_err("name = %s\n", iommu_drvdata->name);
+ pr_err("context = %s (%d)\n", ctx_drvdata->name,
+ ctx_drvdata->num);
+ pr_err("Interesting registers:\n");
+ pr_err("PAR = %16llx [%s%s%s%s%s%s%s%sPLVL%u %s]\n", par,
+ (par & CB_PAR_F) ? "F " : "",
+ (par & CB_PAR_TF) ? "TF " : "",
+ (par & CB_PAR_AFF) ? "AFF " : "",
+ (par & CB_PAR_PF) ? "PF " : "",
+ (par & CB_PAR_EF) ? "EF " : "",
+ (par & CB_PAR_TLBMCF) ? "TLBMCF " : "",
+ (par & CB_PAR_TLBLKF) ? "TLBLKF " : "",
+ (par & CB_PAR_ATOT) ? "ATOT " : "",
+ level,
+ (par & CB_PAR_STAGE) ? "S2 " : "S1 ");
ret = 0;
} else {
/* We are dealing with a supersection */
@@ -825,6 +843,7 @@
void print_ctx_regs(struct msm_iommu_context_reg regs[])
{
uint32_t fsr = regs[DUMP_REG_FSR].val;
+ u64 ttbr;
pr_err("FAR = %016llx\n",
COMBINE_DUMP_REG(
@@ -847,8 +866,22 @@
pr_err("FSYNR0 = %08x FSYNR1 = %08x\n",
regs[DUMP_REG_FSYNR0].val, regs[DUMP_REG_FSYNR1].val);
- pr_err("TTBR0 = %08x TTBR1 = %08x\n",
- regs[DUMP_REG_TTBR0].val, regs[DUMP_REG_TTBR1].val);
+
+ ttbr = COMBINE_DUMP_REG(regs[DUMP_REG_TTBR0_1].val,
+ regs[DUMP_REG_TTBR0_0].val);
+ if (regs[DUMP_REG_TTBR0_1].valid)
+ pr_err("TTBR0 = %016llx\n", ttbr);
+ else
+ pr_err("TTBR0 = %016llx (32b)\n", ttbr);
+
+ ttbr = COMBINE_DUMP_REG(regs[DUMP_REG_TTBR1_1].val,
+ regs[DUMP_REG_TTBR1_0].val);
+
+ if (regs[DUMP_REG_TTBR1_1].valid)
+ pr_err("TTBR1 = %016llx\n", ttbr);
+ else
+ pr_err("TTBR1 = %016llx (32b)\n", ttbr);
+
pr_err("SCTLR = %08x ACTLR = %08x\n",
regs[DUMP_REG_SCTLR].val, regs[DUMP_REG_ACTLR].val);
pr_err("PRRR = %08x NMRR = %08x\n",
@@ -857,49 +890,13 @@
static void __print_ctx_regs(void __iomem *base, int ctx, unsigned int fsr)
{
- struct msm_iommu_context_reg regs[MAX_DUMP_REGS] = {
- [DUMP_REG_FAR0] = {
- .val = GET_FAR(base, ctx)
- },
- [DUMP_REG_FAR1] = {
- /* TODO: make GET_FAR 64-bit and take this from that */
- .val = 0
- },
- [DUMP_REG_PAR0] = {
- .val = GET_PAR(base, ctx)
- },
- [DUMP_REG_PAR1] = {
- /* TODO: make GET_PAR 64-bit and take this from that */
- .val = 0
- },
- [DUMP_REG_FSR] = {
- .val = fsr
- },
- [DUMP_REG_FSYNR0] = {
- .val = GET_FSYNR0(base, ctx)
- },
- [DUMP_REG_FSYNR1] = {
- .val = GET_FSYNR1(base, ctx)
- },
- [DUMP_REG_TTBR0] = {
- .val = GET_TTBR0(base, ctx)
- },
- [DUMP_REG_TTBR1] = {
- .val = GET_TTBR1(base, ctx)
- },
- [DUMP_REG_SCTLR] = {
- .val = GET_SCTLR(base, ctx)
- },
- [DUMP_REG_ACTLR] = {
- .val = GET_ACTLR(base, ctx)
- },
- [DUMP_REG_PRRR] = {
- .val = GET_PRRR(base, ctx)
- },
- [DUMP_REG_NMRR] = {
- .val = GET_NMRR(base, ctx)
- },
- };
+ struct msm_iommu_context_reg regs[MAX_DUMP_REGS];
+ unsigned int i;
+
+ for (i = DUMP_REG_FIRST; i < MAX_DUMP_REGS; ++i) {
+ regs[i].val = GET_CTX_REG(dump_regs_tbl[i].key, base, ctx);
+ regs[i].valid = 1;
+ }
print_ctx_regs(regs);
}
@@ -976,6 +973,32 @@
return __pa(priv->pt.fl_table);
}
+#define DUMP_REG_INIT(dump_reg, cb_reg, mbp) \
+ do { \
+ dump_regs_tbl[dump_reg].key = cb_reg; \
+ dump_regs_tbl[dump_reg].name = #cb_reg; \
+ dump_regs_tbl[dump_reg].must_be_present = mbp; \
+ } while (0)
+
+static void msm_iommu_build_dump_regs_table(void)
+{
+ DUMP_REG_INIT(DUMP_REG_FAR0, CB_FAR, 1);
+ DUMP_REG_INIT(DUMP_REG_FAR1, CB_FAR + 4, 1);
+ DUMP_REG_INIT(DUMP_REG_PAR0, CB_PAR, 1);
+ DUMP_REG_INIT(DUMP_REG_PAR1, CB_PAR + 4, 1);
+ DUMP_REG_INIT(DUMP_REG_FSR, CB_FSR, 1);
+ DUMP_REG_INIT(DUMP_REG_FSYNR0, CB_FSYNR0, 1);
+ DUMP_REG_INIT(DUMP_REG_FSYNR1, CB_FSYNR1, 1);
+ DUMP_REG_INIT(DUMP_REG_TTBR0_0, CB_TTBR0, 1);
+ DUMP_REG_INIT(DUMP_REG_TTBR0_1, CB_TTBR0 + 4, 0);
+ DUMP_REG_INIT(DUMP_REG_TTBR1_0, CB_TTBR1, 1);
+ DUMP_REG_INIT(DUMP_REG_TTBR1_1, CB_TTBR1 + 4, 0);
+ DUMP_REG_INIT(DUMP_REG_SCTLR, CB_SCTLR, 1);
+ DUMP_REG_INIT(DUMP_REG_ACTLR, CB_ACTLR, 1);
+ DUMP_REG_INIT(DUMP_REG_PRRR, CB_PRRR, 1);
+ DUMP_REG_INIT(DUMP_REG_NMRR, CB_NMRR, 1);
+}
+
static struct iommu_ops msm_iommu_ops = {
.domain_init = msm_iommu_domain_init,
.domain_destroy = msm_iommu_domain_destroy,
@@ -995,6 +1018,8 @@
{
msm_iommu_pagetable_init();
bus_set_iommu(&platform_bus_type, &msm_iommu_ops);
+ msm_iommu_build_dump_regs_table();
+
return 0;
}
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index 119a126..bbbe77b 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -32,6 +32,14 @@
static struct of_device_id msm_iommu_v1_ctx_match_table[];
+#ifdef CONFIG_IOMMU_LPAE
+static const char *BFB_REG_NODE_NAME = "qcom,iommu-lpae-bfb-regs";
+static const char *BFB_DATA_NODE_NAME = "qcom,iommu-lpae-bfb-data";
+#else
+static const char *BFB_REG_NODE_NAME = "qcom,iommu-bfb-regs";
+static const char *BFB_DATA_NODE_NAME = "qcom,iommu-bfb-data";
+#endif
+
static int msm_iommu_parse_bfb_settings(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
{
@@ -40,17 +48,17 @@
int ret;
/*
- * It is not valid for a device to have the qcom,iommu-bfb-regs
- * property but not the qcom,iommu-bfb-data property, and vice versa.
+ * It is not valid for a device to have the BFB_REG_NODE_NAME
+ * property but not the BFB_DATA_NODE_NAME property, and vice versa.
*/
- if (!of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-regs", &nreg)) {
- if (of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-data",
+ if (!of_get_property(pdev->dev.of_node, BFB_REG_NODE_NAME, &nreg)) {
+ if (of_get_property(pdev->dev.of_node, BFB_DATA_NODE_NAME,
&nval))
return -EINVAL;
return 0;
}
- if (!of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-data", &nval))
+ if (!of_get_property(pdev->dev.of_node, BFB_DATA_NODE_NAME, &nval))
return -EINVAL;
if (nreg >= sizeof(bfb_settings->regs))
@@ -68,14 +76,14 @@
return -ENOMEM;
ret = of_property_read_u32_array(pdev->dev.of_node,
- "qcom,iommu-bfb-regs",
+ BFB_REG_NODE_NAME,
bfb_settings->regs,
nreg / sizeof(*bfb_settings->regs));
if (ret)
return ret;
ret = of_property_read_u32_array(pdev->dev.of_node,
- "qcom,iommu-bfb-data",
+ BFB_DATA_NODE_NAME,
bfb_settings->data,
nval / sizeof(*bfb_settings->data));
if (ret)
diff --git a/drivers/iommu/msm_iommu_pagetable.c b/drivers/iommu/msm_iommu_pagetable.c
index b871a5a..1e4bff8 100644
--- a/drivers/iommu/msm_iommu_pagetable.c
+++ b/drivers/iommu/msm_iommu_pagetable.c
@@ -24,6 +24,49 @@
#include <trace/events/kmem.h>
#include "msm_iommu_pagetable.h"
+#define NUM_FL_PTE 4096
+#define NUM_SL_PTE 256
+#define NUM_TEX_CLASS 8
+
+/* First-level page table bits */
+#define FL_BASE_MASK 0xFFFFFC00
+#define FL_TYPE_TABLE (1 << 0)
+#define FL_TYPE_SECT (2 << 0)
+#define FL_SUPERSECTION (1 << 18)
+#define FL_AP0 (1 << 10)
+#define FL_AP1 (1 << 11)
+#define FL_AP2 (1 << 15)
+#define FL_SHARED (1 << 16)
+#define FL_BUFFERABLE (1 << 2)
+#define FL_CACHEABLE (1 << 3)
+#define FL_TEX0 (1 << 12)
+#define FL_OFFSET(va) (((va) & 0xFFF00000) >> 20)
+#define FL_NG (1 << 17)
+
+/* Second-level page table bits */
+#define SL_BASE_MASK_LARGE 0xFFFF0000
+#define SL_BASE_MASK_SMALL 0xFFFFF000
+#define SL_TYPE_LARGE (1 << 0)
+#define SL_TYPE_SMALL (2 << 0)
+#define SL_AP0 (1 << 4)
+#define SL_AP1 (2 << 4)
+#define SL_AP2 (1 << 9)
+#define SL_SHARED (1 << 10)
+#define SL_BUFFERABLE (1 << 2)
+#define SL_CACHEABLE (1 << 3)
+#define SL_TEX0 (1 << 6)
+#define SL_OFFSET(va) (((va) & 0xFF000) >> 12)
+#define SL_NG (1 << 11)
+
+/* Memory type and cache policy attributes */
+#define MT_SO 0
+#define MT_DEV 1
+#define MT_NORMAL 2
+#define CP_NONCACHED 0
+#define CP_WB_WA 1
+#define CP_WT 2
+#define CP_WB_NWA 3
+
/* Sharability attributes of MSM IOMMU mappings */
#define MSM_IOMMU_ATTR_NON_SH 0x0
#define MSM_IOMMU_ATTR_SH 0x4
@@ -36,6 +79,13 @@
static int msm_iommu_tex_class[4];
+/* TEX Remap Registers */
+#define NMRR_ICP(nmrr, n) (((nmrr) & (3 << ((n) * 2))) >> ((n) * 2))
+#define NMRR_OCP(nmrr, n) (((nmrr) & (3 << ((n) * 2 + 16))) >> ((n) * 2 + 16))
+
+#define PRRR_NOS(prrr, n) ((prrr) & (1 << ((n) + 24)) ? 1 : 0)
+#define PRRR_MT(prrr, n) ((((prrr) & (3 << ((n) * 2))) >> ((n) * 2)))
+
static inline void clean_pte(unsigned long *start, unsigned long *end,
int redirect)
{
@@ -284,79 +334,8 @@
size_t msm_iommu_pagetable_unmap(struct msm_iommu_pt *pt, unsigned long va,
size_t len)
{
- unsigned long *fl_pte;
- unsigned long fl_offset;
- unsigned long *sl_table;
- unsigned long *sl_pte;
- unsigned long sl_offset;
- int i, ret = 0;
-
- if (len != SZ_16M && len != SZ_1M &&
- len != SZ_64K && len != SZ_4K) {
- pr_debug("Bad length: %d\n", len);
- ret = -EINVAL;
- goto fail;
- }
-
- if (!pt->fl_table) {
- pr_debug("Null page table\n");
- ret = -EINVAL;
- goto fail;
- }
-
- fl_offset = FL_OFFSET(va); /* Upper 12 bits */
- fl_pte = pt->fl_table + fl_offset; /* int pointers, 4 bytes */
-
- if (*fl_pte == 0) {
- pr_debug("First level PTE is 0\n");
- ret = -ENODEV;
- goto fail;
- }
-
- /* Unmap supersection */
- if (len == SZ_16M) {
- for (i = 0; i < 16; i++)
- *(fl_pte+i) = 0;
-
- clean_pte(fl_pte, fl_pte + 16, pt->redirect);
- }
-
- if (len == SZ_1M) {
- *fl_pte = 0;
- clean_pte(fl_pte, fl_pte + 1, pt->redirect);
- }
-
- sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
- sl_offset = SL_OFFSET(va);
- sl_pte = sl_table + sl_offset;
-
- if (len == SZ_64K) {
- for (i = 0; i < 16; i++)
- *(sl_pte+i) = 0;
-
- clean_pte(sl_pte, sl_pte + 16, pt->redirect);
- }
-
- if (len == SZ_4K) {
- *sl_pte = 0;
- clean_pte(sl_pte, sl_pte + 1, pt->redirect);
- }
-
- if (len == SZ_4K || len == SZ_64K) {
- int used = 0;
-
- for (i = 0; i < NUM_SL_PTE; i++)
- if (sl_table[i])
- used = 1;
- if (!used) {
- free_page((unsigned long)sl_table);
- *fl_pte = 0;
- clean_pte(fl_pte, fl_pte + 1, pt->redirect);
- }
- }
-
-fail:
- return ret;
+ msm_iommu_pagetable_unmap_range(pt, va, len);
+ return len;
}
static phys_addr_t get_phys_addr(struct scatterlist *sg)
diff --git a/drivers/iommu/msm_iommu_pagetable.h b/drivers/iommu/msm_iommu_pagetable.h
index 7513aa5..a5ea318 100644
--- a/drivers/iommu/msm_iommu_pagetable.h
+++ b/drivers/iommu/msm_iommu_pagetable.h
@@ -13,56 +13,6 @@
#ifndef __ARCH_ARM_MACH_MSM_IOMMU_PAGETABLE_H
#define __ARCH_ARM_MACH_MSM_IOMMU_PAGETABLE_H
-#define NUM_FL_PTE 4096
-#define NUM_SL_PTE 256
-#define NUM_TEX_CLASS 8
-
-/* First-level page table bits */
-#define FL_BASE_MASK 0xFFFFFC00
-#define FL_TYPE_TABLE (1 << 0)
-#define FL_TYPE_SECT (2 << 0)
-#define FL_SUPERSECTION (1 << 18)
-#define FL_AP0 (1 << 10)
-#define FL_AP1 (1 << 11)
-#define FL_AP2 (1 << 15)
-#define FL_SHARED (1 << 16)
-#define FL_BUFFERABLE (1 << 2)
-#define FL_CACHEABLE (1 << 3)
-#define FL_TEX0 (1 << 12)
-#define FL_OFFSET(va) (((va) & 0xFFF00000) >> 20)
-#define FL_NG (1 << 17)
-
-/* Second-level page table bits */
-#define SL_BASE_MASK_LARGE 0xFFFF0000
-#define SL_BASE_MASK_SMALL 0xFFFFF000
-#define SL_TYPE_LARGE (1 << 0)
-#define SL_TYPE_SMALL (2 << 0)
-#define SL_AP0 (1 << 4)
-#define SL_AP1 (2 << 4)
-#define SL_AP2 (1 << 9)
-#define SL_SHARED (1 << 10)
-#define SL_BUFFERABLE (1 << 2)
-#define SL_CACHEABLE (1 << 3)
-#define SL_TEX0 (1 << 6)
-#define SL_OFFSET(va) (((va) & 0xFF000) >> 12)
-#define SL_NG (1 << 11)
-
-/* Memory type and cache policy attributes */
-#define MT_SO 0
-#define MT_DEV 1
-#define MT_NORMAL 2
-#define CP_NONCACHED 0
-#define CP_WB_WA 1
-#define CP_WT 2
-#define CP_WB_NWA 3
-
-/* TEX Remap Registers */
-#define NMRR_ICP(nmrr, n) (((nmrr) & (3 << ((n) * 2))) >> ((n) * 2))
-#define NMRR_OCP(nmrr, n) (((nmrr) & (3 << ((n) * 2 + 16))) >> ((n) * 2 + 16))
-
-#define PRRR_NOS(prrr, n) ((prrr) & (1 << ((n) + 24)) ? 1 : 0)
-#define PRRR_MT(prrr, n) ((((prrr) & (3 << ((n) * 2))) >> ((n) * 2)))
-
#define MRC(reg, processor, op1, crn, crm, op2) \
__asm__ __volatile__ ( \
" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \
@@ -71,7 +21,10 @@
#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0)
#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1)
-struct iommu_pt;
+#define RCP15_MAIR0(reg) MRC(reg, p15, 0, c10, c2, 0)
+#define RCP15_MAIR1(reg) MRC(reg, p15, 0, c10, c2, 1)
+
+struct msm_iommu_pt;
void msm_iommu_pagetable_init(void);
int msm_iommu_pagetable_alloc(struct msm_iommu_pt *pt);
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
index 50f6df4..474efdf 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -124,41 +124,8 @@
return ret;
}
-static struct dump_regs_tbl {
- /*
- * To keep things context-bank-agnostic, we only store the CB
- * register offset in `key'
- */
- unsigned long key;
- const char *name;
- int offset;
-} dump_regs_tbl[MAX_DUMP_REGS];
-
#define EXTRACT_DUMP_REG_KEY(addr, ctx) (addr & ((1 << CTX_SHIFT) - 1))
-#define DUMP_REG_INIT(dump_reg, cb_reg) \
- do { \
- dump_regs_tbl[dump_reg].key = cb_reg; \
- dump_regs_tbl[dump_reg].name = #cb_reg; \
- } while (0)
-
-static void msm_iommu_sec_build_dump_regs_table(void)
-{
- DUMP_REG_INIT(DUMP_REG_FAR0, CB_FAR);
- DUMP_REG_INIT(DUMP_REG_FAR1, CB_FAR + 4);
- DUMP_REG_INIT(DUMP_REG_PAR0, CB_PAR);
- DUMP_REG_INIT(DUMP_REG_PAR1, CB_PAR + 4);
- DUMP_REG_INIT(DUMP_REG_FSR, CB_FSR);
- DUMP_REG_INIT(DUMP_REG_FSYNR0, CB_FSYNR0);
- DUMP_REG_INIT(DUMP_REG_FSYNR1, CB_FSYNR1);
- DUMP_REG_INIT(DUMP_REG_TTBR0, CB_TTBR0);
- DUMP_REG_INIT(DUMP_REG_TTBR1, CB_TTBR1);
- DUMP_REG_INIT(DUMP_REG_SCTLR, CB_SCTLR);
- DUMP_REG_INIT(DUMP_REG_ACTLR, CB_ACTLR);
- DUMP_REG_INIT(DUMP_REG_PRRR, CB_PRRR);
- DUMP_REG_INIT(DUMP_REG_NMRR, CB_NMRR);
-}
-
static int msm_iommu_reg_dump_to_regs(
struct msm_iommu_context_reg ctx_regs[],
struct msm_scm_fault_regs_dump *dump, int cb_num)
@@ -205,9 +172,13 @@
for (i = 0; i < MAX_DUMP_REGS; ++i) {
if (!ctx_regs[i].valid) {
- pr_err("Register missing from dump: %s, %lx\n",
- dump_regs_tbl[i].name, dump_regs_tbl[i].key);
- ret = 1;
+ if (dump_regs_tbl[i].must_be_present) {
+ pr_err("Register missing from dump: %s, %lx\n",
+ dump_regs_tbl[i].name,
+ dump_regs_tbl[i].key);
+ ret = 1;
+ }
+ ctx_regs[i].val = 0;
}
}
@@ -495,7 +466,8 @@
* Ensure that the buffer is in RAM by the time it gets to TZ
*/
clean_caches((unsigned long) flush_va,
- map.plist.size * map.plist.list_size, virt_to_phys(flush_va));
+ sizeof(unsigned long) * map.plist.list_size,
+ virt_to_phys(flush_va));
ret = scm_call(SCM_SVC_MP, IOMMU_SECURE_MAP2, &map, sizeof(map),
&scm_ret, sizeof(scm_ret));
@@ -799,9 +771,6 @@
bus_set_iommu(&msm_iommu_sec_bus_type, &msm_iommu_ops);
ret = msm_iommu_sec_ptbl_init();
- if (ret)
- goto fail;
- msm_iommu_sec_build_dump_regs_table();
fail:
return ret;
}
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index f13e55a..159c6f6 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -300,6 +300,16 @@
To compile this driver as a module, choose M here: the module will
be called leds-qpnp.
+config LEDS_MSM_GPIO_FLASH
+ tristate "Support for GPIO Flash LEDs"
+ depends on GPIO_MSM_V3
+ help
+ This driver supports the leds functionality of GPIO Flash LED. It
+ includes flash mode and torch mode.
+
+ To compile this driver as a module, choose M here: the module will
+ be called leds-gpio-flash.
+
config LEDS_WM831X_STATUS
tristate "LED support for status LEDs on WM831x PMICs"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index c688898..f20a0d0 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -54,6 +54,7 @@
obj-$(CONFIG_LEDS_QCIBL) += leds-qci-backlight.o
obj-$(CONFIG_LEDS_MSM_PDM) += leds-msm-pdm.o
obj-$(CONFIG_LEDS_MSM_TRICOLOR) += leds-msm-tricolor.o
+obj-$(CONFIG_LEDS_MSM_GPIO_FLASH) += leds-msm-gpio-flash.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-msm-gpio-flash.c b/drivers/leds/leds-msm-gpio-flash.c
new file mode 100644
index 0000000..60d4681
--- /dev/null
+++ b/drivers/leds/leds-msm-gpio-flash.c
@@ -0,0 +1,208 @@
+
+/* 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/printk.h>
+
+#define LED_GPIO_FLASH_DRIVER_NAME "qcom,leds-gpio-flash"
+#define LED_TRIGGER_DEFAULT "none"
+
+struct led_gpio_flash_data {
+ int flash_en;
+ int flash_now;
+ int brightness;
+ struct led_classdev cdev;
+};
+
+static struct of_device_id led_gpio_flash_of_match[] = {
+ {.compatible = LED_GPIO_FLASH_DRIVER_NAME,},
+ {},
+};
+
+static void led_gpio_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ int rc = 0;
+ struct led_gpio_flash_data *flash_led =
+ container_of(led_cdev, struct led_gpio_flash_data, cdev);
+
+ int brightness = value;
+ int flash_en = 0, flash_now = 0;
+
+ if (brightness > LED_HALF) {
+ flash_en = 0;
+ flash_now = 1;
+ } else if (brightness > LED_OFF) {
+ flash_en = 1;
+ flash_now = 0;
+ } else {
+ flash_en = 0;
+ flash_now = 0;
+ }
+
+ rc = gpio_direction_output(flash_led->flash_en, flash_en);
+ if (rc) {
+ pr_err("%s: Failed to set gpio %d\n", __func__,
+ flash_led->flash_en);
+ goto err;
+ }
+ rc = gpio_direction_output(flash_led->flash_now, flash_now);
+ if (rc) {
+ pr_err("%s: Failed to set gpio %d\n", __func__,
+ flash_led->flash_now);
+ goto err;
+ }
+
+ flash_led->brightness = brightness;
+err:
+ return;
+}
+
+static enum led_brightness led_gpio_brightness_get(struct led_classdev
+ *led_cdev)
+{
+ struct led_gpio_flash_data *flash_led =
+ container_of(led_cdev, struct led_gpio_flash_data, cdev);
+ return flash_led->brightness;
+}
+
+int __devinit led_gpio_flash_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ const char *temp_str;
+ struct led_gpio_flash_data *flash_led = NULL;
+ struct device_node *node = pdev->dev.of_node;
+
+ flash_led = devm_kzalloc(&pdev->dev, sizeof(struct led_gpio_flash_data),
+ GFP_KERNEL);
+ if (flash_led == NULL) {
+ dev_err(&pdev->dev, "%s:%d Unable to allocate memory\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ flash_led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
+ rc = of_property_read_string(node, "linux,default-trigger", &temp_str);
+ if (!rc)
+ flash_led->cdev.default_trigger = temp_str;
+
+ flash_led->flash_en = of_get_named_gpio(node, "qcom,flash-en", 0);
+
+ if (flash_led->flash_en < 0) {
+ dev_err(&pdev->dev,
+ "Looking up %s property in node %s failed. rc = %d\n",
+ "flash-en", node->full_name, flash_led->flash_en);
+ goto error;
+ } else {
+ rc = gpio_request(flash_led->flash_en, "FLASH_EN");
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: Failed to request gpio %d,rc = %d\n",
+ __func__, flash_led->flash_en, rc);
+
+ goto error;
+ }
+ }
+
+ flash_led->flash_now = of_get_named_gpio(node, "qcom,flash-now", 0);
+ if (flash_led->flash_now < 0) {
+ dev_err(&pdev->dev,
+ "Looking up %s property in node %s failed. rc = %d\n",
+ "flash-now", node->full_name, flash_led->flash_now);
+ goto error;
+ } else {
+ rc = gpio_request(flash_led->flash_now, "FLASH_NOW");
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: Failed to request gpio %d,rc = %d\n",
+ __func__, flash_led->flash_now, rc);
+
+ goto error;
+ }
+ }
+
+ gpio_tlmm_config(GPIO_CFG(flash_led->flash_en, 0,
+ GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
+ GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+ gpio_tlmm_config(GPIO_CFG(flash_led->flash_now, 0,
+ GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
+ GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+
+ rc = of_property_read_string(node, "linux,name", &flash_led->cdev.name);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Failed to read linux name. rc = %d\n",
+ __func__, rc);
+ goto error;
+ }
+
+ platform_set_drvdata(pdev, flash_led);
+ flash_led->cdev.max_brightness = LED_FULL;
+ flash_led->cdev.brightness_set = led_gpio_brightness_set;
+ flash_led->cdev.brightness_get = led_gpio_brightness_get;
+
+ rc = led_classdev_register(&pdev->dev, &flash_led->cdev);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Failed to register led dev. rc = %d\n",
+ __func__, rc);
+ goto error;
+ }
+ return 0;
+
+error:
+ devm_kfree(&pdev->dev, flash_led);
+ return rc;
+}
+
+int __devexit led_gpio_flash_remove(struct platform_device *pdev)
+{
+ struct led_gpio_flash_data *flash_led =
+ (struct led_gpio_flash_data *)platform_get_drvdata(pdev);
+
+ led_classdev_unregister(&flash_led->cdev);
+ devm_kfree(&pdev->dev, flash_led);
+ return 0;
+}
+
+static struct platform_driver led_gpio_flash_driver = {
+ .probe = led_gpio_flash_probe,
+ .remove = __devexit_p(led_gpio_flash_remove),
+ .driver = {
+ .name = LED_GPIO_FLASH_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = led_gpio_flash_of_match,
+ }
+};
+
+static int __init led_gpio_flash_init(void)
+{
+ return platform_driver_register(&led_gpio_flash_driver);
+}
+
+static void __exit led_gpio_flash_exit(void)
+{
+ return platform_driver_unregister(&led_gpio_flash_driver);
+}
+
+late_initcall(led_gpio_flash_init);
+module_exit(led_gpio_flash_exit);
+
+MODULE_DESCRIPTION("QCOM GPIO LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-msm-gpio-flash");
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index bac8678..e84b477 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -97,6 +97,7 @@
#define FLASH_FAULT_DETECT(base) (base + 0x51)
#define FLASH_MAX_LEVEL 0x4F
+#define TORCH_MAX_LEVEL 0x0F
#define FLASH_NO_MASK 0x00
#define FLASH_MASK_1 0x20
@@ -159,6 +160,7 @@
#define QPNP_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
#define QPNP_LUT_RAMP_STEP_DEFAULT 255
#define PWM_LUT_MAX_SIZE 63
+#define PWM_GPLED_LUT_MAX_SIZE 31
#define RGB_LED_DISABLE 0x00
#define MPP_MAX_LEVEL LED_FULL
@@ -168,11 +170,13 @@
#define LED_MPP_SINK_CTRL(base) (base + 0x4C)
#define LED_MPP_CURRENT_DEFAULT 5
+#define LED_MPP_VIN_CTRL_DEFAULT 0
#define LED_MPP_CURRENT_PER_SETTING 5
#define LED_MPP_SOURCE_SEL_DEFAULT LED_MPP_MODE_ENABLE
#define LED_MPP_SINK_MASK 0x07
#define LED_MPP_MODE_MASK 0x7F
+#define LED_MPP_VIN_MASK 0x03
#define LED_MPP_EN_MASK 0x80
#define LED_MPP_SRC_MASK 0x0F
#define LED_MPP_MODE_CTRL_MASK 0x70
@@ -299,6 +303,10 @@
* @pwm_channel - pwm channel to be configured for led
* @pwm_period_us - period for pwm, in us
* @mode - mode the led operates in
+ * @old_duty_pcts - storage for duty pcts that may need to be reused
+ * @default_mode - default mode of LED as set in device tree
+ * @use_blink - use blink sysfs entry
+ * @blinking - device is currently blinking w/LPG mode
*/
struct pwm_config_data {
struct lut_params lut_params;
@@ -306,9 +314,11 @@
int pwm_channel;
u32 pwm_period_us;
struct pwm_duty_cycles *duty_cycles;
+ int *old_duty_pcts;
u8 mode;
- u8 enable;
+ u8 default_mode;
bool use_blink;
+ bool blinking;
};
/**
@@ -340,6 +350,8 @@
* @current_setting - current setting, 5ma-40ma in 5ma increments
* @source_sel - source selection
* @mode_ctrl - mode control
+ * @vin_ctrl - input control
+ * @min_brightness - minimum brightness supported
* @pwm_mode - pwm mode in use
*/
struct mpp_config_data {
@@ -347,6 +359,8 @@
u8 current_setting;
u8 source_sel;
u8 mode_ctrl;
+ u8 vin_ctrl;
+ u8 min_brightness;
u8 pwm_mode;
};
@@ -414,6 +428,7 @@
* struct qpnp_led_data - internal led data structure
* @led_classdev - led class device
* @delayed_work - delayed work for turning off the LED
+ * @work - workqueue for led
* @id - led index
* @base_reg - base register given in device tree
* @lock - to protect the transactions
@@ -427,11 +442,12 @@
struct led_classdev cdev;
struct spmi_device *spmi_dev;
struct delayed_work dwork;
+ struct work_struct work;
int id;
u16 base;
u8 reg;
u8 num_leds;
- spinlock_t lock;
+ struct mutex lock;
struct wled_config_data *wled_cfg;
struct flash_config_data *flash_cfg;
struct kpdbl_config_data *kpdbl_cfg;
@@ -566,6 +582,21 @@
int duty_us;
if (led->cdev.brightness) {
+ if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
+ dev_warn(&led->spmi_dev->dev,
+ "brightness is less than supported..." \
+ "set to minimum supported\n");
+ led->cdev.brightness = led->mpp_cfg->min_brightness;
+ }
+
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
+ if (!led->mpp_cfg->pwm_cfg->blinking) {
+ led->mpp_cfg->pwm_cfg->mode =
+ led->mpp_cfg->pwm_cfg->default_mode;
+ led->mpp_cfg->pwm_mode =
+ led->mpp_cfg->pwm_cfg->default_mode;
+ }
+ }
if (led->mpp_cfg->pwm_mode == PWM_MODE) {
pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
@@ -606,8 +637,13 @@
return rc;
}
} else {
- if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
+ led->mpp_cfg->pwm_cfg->mode =
+ led->mpp_cfg->pwm_cfg->default_mode;
+ led->mpp_cfg->pwm_mode =
+ led->mpp_cfg->pwm_cfg->default_mode;
pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
+ }
rc = qpnp_led_masked_write(led,
LED_MPP_MODE_CTRL(led->base),
LED_MPP_MODE_MASK,
@@ -629,6 +665,8 @@
}
}
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+ led->mpp_cfg->pwm_cfg->blinking = false;
qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
return 0;
@@ -639,8 +677,12 @@
int rc;
int val = led->cdev.brightness;
- led->flash_cfg->current_prgm = (val * FLASH_MAX_LEVEL /
- led->max_current);
+ if (led->flash_cfg->torch_enable)
+ led->flash_cfg->current_prgm =
+ (val * TORCH_MAX_LEVEL / led->max_current);
+ else
+ led->flash_cfg->current_prgm =
+ (val * FLASH_MAX_LEVEL / led->max_current);
led->flash_cfg->current_prgm =
led->flash_cfg->current_prgm >> FLASH_CURRENT_PRGM_SHIFT;
@@ -691,7 +733,7 @@
qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
FLASH_CURRENT_MASK,
- led->max_current);
+ TORCH_MAX_LEVEL);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Max current reg write failed(%d)\n",
@@ -861,6 +903,9 @@
int rc;
if (led->cdev.brightness) {
+ if (!led->kpdbl_cfg->pwm_cfg->blinking)
+ led->kpdbl_cfg->pwm_cfg->mode =
+ led->kpdbl_cfg->pwm_cfg->default_mode;
rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
@@ -877,6 +922,8 @@
return rc;
}
} else {
+ led->kpdbl_cfg->pwm_cfg->mode =
+ led->kpdbl_cfg->pwm_cfg->default_mode;
pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
@@ -887,6 +934,7 @@
}
}
+ led->kpdbl_cfg->pwm_cfg->blinking = false;
qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
return 0;
@@ -898,6 +946,9 @@
int rc;
if (led->cdev.brightness) {
+ if (!led->rgb_cfg->pwm_cfg->blinking)
+ led->rgb_cfg->pwm_cfg->mode =
+ led->rgb_cfg->pwm_cfg->default_mode;
if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
led->cdev.brightness) / LED_FULL;
@@ -924,6 +975,8 @@
return rc;
}
} else {
+ led->rgb_cfg->pwm_cfg->mode =
+ led->rgb_cfg->pwm_cfg->default_mode;
pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
rc = qpnp_led_masked_write(led,
RGB_LED_EN_CTL(led->base),
@@ -935,6 +988,7 @@
}
}
+ led->rgb_cfg->pwm_cfg->blinking = false;
qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
return 0;
@@ -943,9 +997,7 @@
static void qpnp_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- int rc, i;
struct qpnp_led_data *led;
- struct qpnp_led_data *led_array;
led = container_of(led_cdev, struct qpnp_led_data, cdev);
if (value < LED_OFF || value > led->cdev.max_brightness) {
@@ -953,6 +1005,16 @@
return;
}
+ led->cdev.brightness = value;
+ schedule_work(&led->work);
+}
+
+static void __qpnp_led_work(struct qpnp_led_data *led,
+ enum led_brightness value)
+{
+ int rc, i;
+ struct qpnp_led_data *led_array;
+
if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) {
if (!led->flash_cfg->flash_on && value > 0) {
led_array = dev_get_drvdata(&led->spmi_dev->dev);
@@ -980,8 +1042,7 @@
}
}
- spin_lock(&led->lock);
- led->cdev.brightness = value;
+ mutex_lock(&led->lock);
switch (led->id) {
case QPNP_ID_WLED:
@@ -1021,7 +1082,7 @@
dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
break;
}
- spin_unlock(&led->lock);
+ mutex_unlock(&led->lock);
if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) {
if (led->flash_cfg->flash_on && !value) {
@@ -1050,6 +1111,16 @@
}
}
+static void qpnp_led_work(struct work_struct *work)
+{
+ struct qpnp_led_data *led = container_of(work,
+ struct qpnp_led_data, work);
+
+ __qpnp_led_work(led, led->cdev.brightness);
+
+ return;
+}
+
static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
{
switch (led->id) {
@@ -1341,28 +1412,424 @@
return 0;
}
+static ssize_t pwm_us_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 pwm_us;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_pwm_us;
+ struct pwm_config_data *pwm_cfg;
+
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ ret = kstrtou32(buf, 10, &pwm_us);
+ if (ret)
+ return ret;
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for pwm_us\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_pwm_us = pwm_cfg->pwm_period_us;
+
+ pwm_cfg->pwm_period_us = pwm_us;
+ pwm_free(pwm_cfg->pwm_dev);
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->pwm_period_us = previous_pwm_us;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new pwm_us value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t pause_lo_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 pause_lo;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_pause_lo;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &pause_lo);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for pause lo\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ pwm_cfg->lut_params.lut_pause_lo = pause_lo;
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new pause lo value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t pause_hi_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 pause_hi;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_pause_hi;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &pause_hi);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for pause hi\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ pwm_cfg->lut_params.lut_pause_hi = pause_hi;
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new pause hi value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t start_idx_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 start_idx;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_start_idx;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &start_idx);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for start idx\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_start_idx = pwm_cfg->duty_cycles->start_idx;
+ pwm_cfg->duty_cycles->start_idx = start_idx;
+ pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
+ pwm_free(pwm_cfg->pwm_dev);
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->duty_cycles->start_idx = previous_start_idx;
+ pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new start idx value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t ramp_step_ms_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 ramp_step_ms;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_ramp_step_ms;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &ramp_step_ms);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for ramp step\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new ramp step value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t lut_flags_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 lut_flags;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_lut_flags;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &lut_flags);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for lut flags\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_lut_flags = pwm_cfg->lut_params.flags;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ pwm_cfg->lut_params.flags = lut_flags;
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->lut_params.flags = previous_lut_flags;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new lut flags value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t duty_pcts_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ int num_duty_pcts = 0;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ char *buffer;
+ ssize_t ret;
+ int i = 0;
+ int max_duty_pcts;
+ struct pwm_config_data *pwm_cfg;
+ u32 previous_num_duty_pcts;
+ int value;
+ int *previous_duty_pcts;
+
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ max_duty_pcts = PWM_LUT_MAX_SIZE;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ max_duty_pcts = PWM_LUT_MAX_SIZE;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for duty pcts\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ buffer = (char *)buf;
+
+ for (i = 0; i < max_duty_pcts; i++) {
+ if (buffer == NULL)
+ break;
+ ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
+ pwm_cfg->old_duty_pcts[i] = value;
+ num_duty_pcts++;
+ if (ret <= 1)
+ break;
+ }
+
+ if (num_duty_pcts >= max_duty_pcts) {
+ dev_err(&led->spmi_dev->dev,
+ "Number of duty pcts given exceeds max (%d)\n",
+ max_duty_pcts);
+ return -EINVAL;
+ }
+
+ previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
+ previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
+
+ pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
+ pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
+ pwm_cfg->old_duty_pcts = previous_duty_pcts;
+ pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret)
+ goto restore;
+
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+
+restore:
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new duty pcts value\n");
+ pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
+ pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
+ pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
+ pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return ret;
+}
+
static void led_blink(struct qpnp_led_data *led,
struct pwm_config_data *pwm_cfg)
{
- u8 previous_mode;
-
- previous_mode = pwm_cfg->mode;
if (pwm_cfg->use_blink) {
if (led->cdev.brightness) {
+ pwm_cfg->blinking = true;
if (led->id == QPNP_ID_LED_MPP)
led->mpp_cfg->pwm_mode = LPG_MODE;
pwm_cfg->mode = LPG_MODE;
- pwm_free(pwm_cfg->pwm_dev);
- qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
- qpnp_led_set(&led->cdev, led->cdev.brightness);
- if (led->id == QPNP_ID_LED_MPP)
- led->mpp_cfg->pwm_mode = previous_mode;
- pwm_cfg->mode = previous_mode;
} else {
- pwm_free(pwm_cfg->pwm_dev);
- qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
- qpnp_led_set(&led->cdev, led->cdev.brightness);
+ pwm_cfg->blinking = false;
+ pwm_cfg->mode = pwm_cfg->default_mode;
+ if (led->id == QPNP_ID_LED_MPP)
+ led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
}
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
}
}
@@ -1399,6 +1866,13 @@
static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
+static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
+static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
+static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
+static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
+static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
+static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
+static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
static DEVICE_ATTR(blink, 0664, NULL, blink_store);
static struct attribute *led_attrs[] = {
@@ -1411,11 +1885,34 @@
.attrs = led_attrs,
};
+static struct attribute *pwm_attrs[] = {
+ &dev_attr_pwm_us.attr,
+ NULL
+};
+
+static struct attribute *lpg_attrs[] = {
+ &dev_attr_pause_lo.attr,
+ &dev_attr_pause_hi.attr,
+ &dev_attr_start_idx.attr,
+ &dev_attr_ramp_step_ms.attr,
+ &dev_attr_lut_flags.attr,
+ &dev_attr_duty_pcts.attr,
+ NULL
+};
+
static struct attribute *blink_attrs[] = {
&dev_attr_blink.attr,
NULL
};
+static const struct attribute_group pwm_attr_group = {
+ .attrs = pwm_attrs,
+};
+
+static const struct attribute_group lpg_attr_group = {
+ .attrs = lpg_attrs,
+};
+
static const struct attribute_group blink_attr_group = {
.attrs = blink_attrs,
};
@@ -1609,6 +2106,14 @@
if (val < 0)
val = 0;
+ rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base),
+ LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Failed to write led vin control reg\n");
+ return rc;
+ }
+
rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
LED_MPP_SINK_MASK, val);
if (rc) {
@@ -1840,11 +2345,18 @@
return -EINVAL;
}
+ led->flash_cfg->torch_enable =
+ of_property_read_bool(node, "qcom,torch-enable");
+
rc = of_property_read_u32(node, "qcom,current", &val);
- if (!rc)
- led->flash_cfg->current_prgm = (val *
+ if (!rc) {
+ if (led->flash_cfg->torch_enable)
+ led->flash_cfg->current_prgm = (val *
+ TORCH_MAX_LEVEL / led->max_current);
+ else
+ led->flash_cfg->current_prgm = (val *
FLASH_MAX_LEVEL / led->max_current);
- else
+ } else
return -EINVAL;
rc = of_property_read_u32(node, "qcom,headroom", &val);
@@ -1883,9 +2395,6 @@
led->flash_cfg->safety_timer =
of_property_read_bool(node, "qcom,safety-timer");
- led->flash_cfg->torch_enable =
- of_property_read_bool(node, "qcom,torch-enable");
-
return 0;
}
@@ -1942,12 +2451,23 @@
pwm_cfg->duty_cycles->duty_pcts =
devm_kzalloc(&spmi_dev->dev,
- sizeof(int) * pwm_cfg->duty_cycles->num_duty_pcts,
+ sizeof(int) * PWM_LUT_MAX_SIZE,
GFP_KERNEL);
if (!pwm_cfg->duty_cycles->duty_pcts) {
dev_err(&spmi_dev->dev,
"Unable to allocate memory\n");
- rc = -ENOMEM;
+ rc = -ENOMEM;
+ goto bad_lpg_params;
+ }
+
+ pwm_cfg->old_duty_pcts =
+ devm_kzalloc(&spmi_dev->dev,
+ sizeof(int) * PWM_LUT_MAX_SIZE,
+ GFP_KERNEL);
+ if (!pwm_cfg->old_duty_pcts) {
+ dev_err(&spmi_dev->dev,
+ "Unable to allocate memory\n");
+ rc = -ENOMEM;
goto bad_lpg_params;
}
@@ -2062,6 +2582,7 @@
return -ENOMEM;
}
led->kpdbl_cfg->pwm_cfg->mode = led_mode;
+ led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
} else
return rc;
@@ -2130,6 +2651,7 @@
return -ENOMEM;
}
led->rgb_cfg->pwm_cfg->mode = led_mode;
+ led->rgb_cfg->pwm_cfg->default_mode = led_mode;
} else
return rc;
@@ -2176,6 +2698,20 @@
else if (rc != -EINVAL)
return rc;
+ led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
+ if (!rc)
+ led->mpp_cfg->vin_ctrl = (u8) val;
+ else if (rc != -EINVAL)
+ return rc;
+
+ led->mpp_cfg->min_brightness = 0;
+ rc = of_property_read_u32(node, "qcom,min-brightness", &val);
+ if (!rc)
+ led->mpp_cfg->min_brightness = (u8) val;
+ else if (rc != -EINVAL)
+ return rc;
+
rc = of_property_read_string(node, "qcom,mode", &mode);
if (!rc) {
led_mode = qpnp_led_get_mode(mode);
@@ -2196,6 +2732,7 @@
return -ENOMEM;
}
led->mpp_cfg->pwm_cfg->mode = led_mode;
+ led->mpp_cfg->pwm_cfg->default_mode = led_mode;
} else
return rc;
@@ -2331,7 +2868,8 @@
goto fail_id_check;
}
- spin_lock_init(&led->lock);
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, qpnp_led_work);
rc = qpnp_led_initialize(led);
if (rc < 0)
@@ -2360,27 +2898,60 @@
if (led->id == QPNP_ID_LED_MPP) {
if (!led->mpp_cfg->pwm_cfg)
break;
+ if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &pwm_attr_group);
+ if (rc)
+ goto fail_id_check;
+ }
if (led->mpp_cfg->pwm_cfg->use_blink) {
rc = sysfs_create_group(&led->cdev.dev->kobj,
&blink_attr_group);
if (rc)
goto fail_id_check;
+
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &lpg_attr_group);
+ if (rc)
+ goto fail_id_check;
+ } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &lpg_attr_group);
+ if (rc)
+ goto fail_id_check;
}
} else if ((led->id == QPNP_ID_RGB_RED) ||
(led->id == QPNP_ID_RGB_GREEN) ||
(led->id == QPNP_ID_RGB_BLUE)) {
+ if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &pwm_attr_group);
+ if (rc)
+ goto fail_id_check;
+ }
if (led->rgb_cfg->pwm_cfg->use_blink) {
rc = sysfs_create_group(&led->cdev.dev->kobj,
&blink_attr_group);
if (rc)
goto fail_id_check;
+
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &lpg_attr_group);
+ if (rc)
+ goto fail_id_check;
+ } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &lpg_attr_group);
+ if (rc)
+ goto fail_id_check;
}
}
/* configure default state */
if (led->default_on) {
led->cdev.brightness = led->cdev.max_brightness;
- qpnp_led_set(&led->cdev, led->cdev.brightness);
+ __qpnp_led_work(led, led->cdev.brightness);
+ schedule_work(&led->work);
if (led->turn_off_delay_ms > 0)
qpnp_led_turn_off(led);
} else
@@ -2392,8 +2963,11 @@
return 0;
fail_id_check:
- for (i = 0; i < parsed_leds; i++)
+ for (i = 0; i < parsed_leds; i++) {
+ mutex_destroy(&led_array[i].lock);
led_classdev_unregister(&led_array[i].cdev);
+ }
+
return rc;
}
@@ -2403,6 +2977,8 @@
int i, parsed_leds = led_array->num_leds;
for (i = 0; i < parsed_leds; i++) {
+ cancel_work_sync(&led_array[i].work);
+ mutex_destroy(&led_array[i].lock);
led_classdev_unregister(&led_array[i].cdev);
switch (led_array[i].id) {
case QPNP_ID_WLED:
@@ -2418,6 +2994,35 @@
case QPNP_ID_RGB_RED:
case QPNP_ID_RGB_GREEN:
case QPNP_ID_RGB_BLUE:
+ if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &pwm_attr_group);
+ if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &blink_attr_group);
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &lpg_attr_group);
+ } else if (led_array[i].rgb_cfg->pwm_cfg->mode\
+ == LPG_MODE)
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &lpg_attr_group);
+ break;
+ case QPNP_ID_LED_MPP:
+ if (!led_array[i].mpp_cfg->pwm_cfg)
+ break;
+ if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &pwm_attr_group);
+ if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &blink_attr_group);
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &lpg_attr_group);
+ } else if (led_array[i].mpp_cfg->pwm_cfg->mode\
+ == LPG_MODE)
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &lpg_attr_group);
+ break;
default:
dev_err(&led_array[i].spmi_dev->dev,
"Invalid LED(%d)\n",
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index 2c2b339..189418a 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -246,6 +246,8 @@
enum dmx_tsp_format_t tsp_format);
int (*set_secure_mode)(struct dmx_ts_feed *feed,
struct dmx_secure_mode *sec_mode);
+ int (*set_cipher_ops)(struct dmx_ts_feed *feed,
+ struct dmx_cipher_operations *cipher_ops);
int (*oob_command) (struct dmx_ts_feed *feed,
struct dmx_oob_command *cmd);
int (*ts_insertion_init)(struct dmx_ts_feed *feed);
@@ -301,6 +303,8 @@
u32 bytes_num);
int (*set_secure_mode)(struct dmx_section_feed *feed,
struct dmx_secure_mode *sec_mode);
+ int (*set_cipher_ops)(struct dmx_section_feed *feed,
+ struct dmx_cipher_operations *cipher_ops);
int (*oob_command) (struct dmx_section_feed *feed,
struct dmx_oob_command *cmd);
int (*get_scrambling_bits)(struct dmx_section_feed *feed, u8 *value);
@@ -388,6 +392,7 @@
struct dmx_frontend* frontend; /* Front-end connected to the demux */
void* priv; /* Pointer to private data of the API client */
struct data_buffer dvr_input; /* DVR input buffer */
+ int dvr_input_protected;
struct dentry *debugfs_demux_dir; /* debugfs dir */
int (*open) (struct dmx_demux* demux);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 28e8092..a1cac54 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -566,6 +566,7 @@
int bytes_written = 0;
size_t split;
size_t tsp_size;
+ u8 *data_start;
struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
todo = dvr_cmd->cmd.data_feed_count;
@@ -578,15 +579,15 @@
/* wait for input */
ret = wait_event_interruptible(
src->queue,
- (dvb_ringbuffer_avail(src) >= tsp_size) || (!src->data)
- || (dmxdev->dvr_in_exit) || (src->error));
+ (dvb_ringbuffer_avail(src) >= tsp_size) ||
+ dmxdev->dvr_in_exit || src->error);
if (ret < 0)
break;
spin_lock(&dmxdev->dvr_in_lock);
- if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
+ if (dmxdev->exit || dmxdev->dvr_in_exit) {
spin_unlock(&dmxdev->dvr_in_lock);
ret = -ENODEV;
break;
@@ -609,12 +610,20 @@
* Lock on DVR buffer is released before calling to
* write, if DVR was released meanwhile, dvr_in_exit is
* prompted. Lock is acquired when updating the read pointer
- * again to preserve read/write pointers consistency
+ * again to preserve read/write pointers consistency.
+ *
+ * In protected input mode, DVR input buffer is not mapped
+ * to kernel memory. Underlying demux implementation
+ * should trigger HW to read from DVR input buffer
+ * based on current read offset.
*/
if (split > 0) {
+ data_start = (dmxdev->demux->dvr_input_protected) ?
+ NULL : (src->data + src->pread);
+
spin_unlock(&dmxdev->dvr_in_lock);
ret = dmxdev->demux->write(dmxdev->demux,
- src->data + src->pread,
+ data_start,
split);
if (ret < 0) {
@@ -641,9 +650,12 @@
}
}
+ data_start = (dmxdev->demux->dvr_input_protected) ?
+ NULL : (src->data + src->pread);
+
spin_unlock(&dmxdev->dvr_in_lock);
ret = dmxdev->demux->write(dmxdev->demux,
- src->data + src->pread, todo);
+ data_start, todo);
if (ret < 0) {
printk(KERN_ERR "dmxdev: dvr write error %d\n",
@@ -708,8 +720,7 @@
ret = dvb_dvr_feed_cmd(dmxdev, &dvr_cmd);
if (ret < 0) {
- printk(KERN_ERR
- "%s: DVR data feed failed, ret=%d\n",
+ dprintk("%s: DVR data feed failed, ret=%d\n",
__func__, ret);
continue;
}
@@ -825,6 +836,7 @@
dmxdev->demux->dvr_input.priv_handle = NULL;
dmxdev->demux->dvr_input.ringbuff = &dmxdev->dvr_input_buffer;
+ dmxdev->demux->dvr_input_protected = 0;
mem = vmalloc(DVR_CMDS_BUFFER_SIZE);
if (!mem) {
vfree(dmxdev->dvr_input_buffer.data);
@@ -936,7 +948,8 @@
if ((dmxdev->dvr_input_buffer_mode ==
DMX_BUFFER_MODE_EXTERNAL) &&
(dmxdev->demux->dvr_input.priv_handle)) {
- dmxdev->demux->unmap_buffer(dmxdev->demux,
+ if (!dmxdev->demux->dvr_input_protected)
+ dmxdev->demux->unmap_buffer(dmxdev->demux,
dmxdev->demux->dvr_input.priv_handle);
dmxdev->demux->dvr_input.priv_handle = NULL;
}
@@ -1130,8 +1143,8 @@
for (todo = count; todo > 0; todo -= ret) {
ret = wait_event_interruptible(src->queue,
(dvb_ringbuffer_free(src)) ||
- (!src->data) || (!cmdbuf->data) ||
- (src->error != 0) || (dmxdev->dvr_in_exit));
+ !src->data || !cmdbuf->data ||
+ (src->error != 0) || dmxdev->dvr_in_exit);
if (ret < 0)
return ret;
@@ -1314,6 +1327,7 @@
enum dmx_buffer_mode *buffer_mode;
void **buff_handle;
void *oldmem;
+ int *is_protected;
if ((mode != DMX_BUFFER_MODE_INTERNAL) &&
(mode != DMX_BUFFER_MODE_EXTERNAL))
@@ -1328,11 +1342,13 @@
lock = &dmxdev->lock;
buffer_mode = &dmxdev->dvr_buffer_mode;
buff_handle = &dmxdev->dvr_priv_buff_handle;
+ is_protected = NULL;
} else {
buf = &dmxdev->dvr_input_buffer;
lock = &dmxdev->dvr_in_lock;
buffer_mode = &dmxdev->dvr_input_buffer_mode;
buff_handle = &dmxdev->demux->dvr_input.priv_handle;
+ is_protected = &dmxdev->demux->dvr_input_protected;
}
if (mode == *buffer_mode)
@@ -1353,6 +1369,9 @@
*buff_handle = NULL;
}
+ if (is_protected)
+ *is_protected = 0;
+
/* set default internal buffer */
dvb_dvr_set_buffer_size(dmxdev, f_flags, DVR_BUFFER_SIZE);
} else if (oldmem) {
@@ -1372,31 +1391,56 @@
void **buff_handle;
void *newmem;
void *oldmem;
+ int *is_protected;
+ struct dmx_caps caps;
+
+ if (dmxdev->demux->get_caps)
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+ else
+ caps.caps = 0;
if ((f_flags & O_ACCMODE) == O_RDONLY) {
buf = &dmxdev->dvr_buffer;
lock = &dmxdev->lock;
buffer_mode = dmxdev->dvr_buffer_mode;
buff_handle = &dmxdev->dvr_priv_buff_handle;
+ is_protected = NULL;
} else {
buf = &dmxdev->dvr_input_buffer;
lock = &dmxdev->dvr_in_lock;
buffer_mode = dmxdev->dvr_input_buffer_mode;
buff_handle = &dmxdev->demux->dvr_input.priv_handle;
+ is_protected = &dmxdev->demux->dvr_input_protected;
+ if (!(caps.caps & DMX_CAP_SECURED_INPUT_PLAYBACK) &&
+ dmx_buffer->is_protected)
+ return -EINVAL;
}
- if ((!dmx_buffer->size) ||
+ if (!dmx_buffer->size ||
(buffer_mode == DMX_BUFFER_MODE_INTERNAL))
return -EINVAL;
oldmem = *buff_handle;
- if (dmxdev->demux->map_buffer(dmxdev->demux, dmx_buffer,
- buff_handle, &newmem))
- return -ENOMEM;
+
+ /*
+ * Protected buffer is relevant only for DVR input buffer
+ * when DVR device is opened for write. In such case,
+ * buffer is mapped only if the buffer is not protected one.
+ */
+ if (!is_protected || !dmx_buffer->is_protected) {
+ if (dmxdev->demux->map_buffer(dmxdev->demux, dmx_buffer,
+ buff_handle, &newmem))
+ return -ENOMEM;
+ } else {
+ newmem = NULL;
+ *buff_handle = NULL;
+ }
spin_lock_irq(lock);
buf->data = newmem;
buf->size = dmx_buffer->size;
+ if (is_protected)
+ *is_protected = dmx_buffer->is_protected;
dvb_ringbuffer_reset(buf);
spin_unlock_irq(lock);
@@ -1717,19 +1761,20 @@
static int dvb_dmxdev_set_source(struct dmxdev_filter *dmxdevfilter,
dmx_source_t *source)
{
+ int ret = 0;
struct dmxdev *dev;
if (dmxdevfilter->state == DMXDEV_STATE_GO)
return -EBUSY;
dev = dmxdevfilter->dev;
-
- dev->source = *source;
-
if (dev->demux->set_source)
- return dev->demux->set_source(dev->demux, source);
+ ret = dev->demux->set_source(dev->demux, source);
- return 0;
+ if (!ret)
+ dev->source = *source;
+
+ return ret;
}
static int dvb_dmxdev_reuse_decoder_buf(struct dmxdev_filter *dmxdevfilter,
@@ -3105,7 +3150,10 @@
tsfeed->set_tsp_out_format(tsfeed, filter->dmx_tsp_format);
if (tsfeed->set_secure_mode)
- tsfeed->set_secure_mode(tsfeed, &feed->sec_mode);
+ tsfeed->set_secure_mode(tsfeed, &filter->sec_mode);
+
+ if (tsfeed->set_cipher_ops)
+ tsfeed->set_cipher_ops(tsfeed, &feed->cipher_ops);
if ((para->pes_type == DMX_PES_VIDEO0) ||
(para->pes_type == DMX_PES_VIDEO1) ||
@@ -3265,7 +3313,11 @@
if ((*secfeed)->set_secure_mode)
(*secfeed)->set_secure_mode(*secfeed,
- &filter->feed.sec.sec_mode);
+ &filter->sec_mode);
+
+ if ((*secfeed)->set_cipher_ops)
+ (*secfeed)->set_cipher_ops(*secfeed,
+ &filter->feed.sec.cipher_ops);
} else {
dvb_dmxdev_feed_stop(filter);
}
@@ -3434,6 +3486,8 @@
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
init_timer(&dmxdevfilter->timer);
+ dmxdevfilter->sec_mode.is_secured = 0;
+
INIT_LIST_HEAD(&dmxdevfilter->insertion_buffers);
dmxdevfilter->dmx_tsp_format = DMX_TSP_FORMAT_188;
@@ -3513,7 +3567,7 @@
return -ENOMEM;
feed->pid = pid;
- feed->sec_mode.is_secured = 0;
+ feed->cipher_ops.operations_count = 0;
feed->idx_params.enable = 0;
list_add(&feed->next, &filter->feed.ts);
@@ -3568,7 +3622,7 @@
memcpy(&dmxdevfilter->params.sec,
params, sizeof(struct dmx_sct_filter_params));
invert_mode(&dmxdevfilter->params.sec.filter);
- dmxdevfilter->feed.sec.sec_mode.is_secured = 0;
+ dmxdevfilter->feed.sec.cipher_ops.operations_count = 0;
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
if (params->flags & DMX_IMMEDIATE_START)
@@ -3582,43 +3636,78 @@
struct dmxdev_filter *filter,
struct dmx_secure_mode *sec_mode)
{
+ if (!dmxdev || !filter || !sec_mode)
+ return -EINVAL;
+
+ if (filter->state == DMXDEV_STATE_GO) {
+ printk(KERN_ERR "%s: invalid filter state\n", __func__);
+ return -EBUSY;
+ }
+
+ dprintk(KERN_DEBUG "%s: secure=%d\n", __func__, sec_mode->is_secured);
+
+ filter->sec_mode = *sec_mode;
+
+ return 0;
+}
+
+static int dvb_dmxdev_set_cipher(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmx_cipher_operations *cipher_ops)
+{
struct dmxdev_feed *feed;
struct dmxdev_feed *ts_feed = NULL;
struct dmxdev_sec_feed *sec_feed = NULL;
+ struct dmx_caps caps;
- if (NULL == dmxdev || NULL == filter || NULL == sec_mode)
+ if (!dmxdev || !dmxdev->demux->get_caps)
return -EINVAL;
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+
+ if (!filter || !cipher_ops ||
+ (cipher_ops->operations_count > caps.num_cipher_ops) ||
+ (cipher_ops->operations_count >
+ DMX_MAX_CIPHER_OPERATIONS_COUNT))
+ return -EINVAL;
+
+ dprintk(KERN_DEBUG "%s: pid=%d, operations=%d\n", __func__,
+ cipher_ops->pid, cipher_ops->operations_count);
+
if (filter->state < DMXDEV_STATE_SET ||
filter->state > DMXDEV_STATE_GO) {
printk(KERN_ERR "%s: invalid filter state\n", __func__);
return -EPERM;
}
- dprintk(KERN_DEBUG "%s: key_id=%d, secure=%d, looking for pid=%d\n",
- __func__, sec_mode->key_ladder_id, sec_mode->is_secured,
- sec_mode->pid);
+
+ if (!filter->sec_mode.is_secured && cipher_ops->operations_count) {
+ printk(KERN_ERR "%s: secure mode must be enabled to set cipher ops\n",
+ __func__);
+ return -EPERM;
+ }
+
switch (filter->type) {
case DMXDEV_TYPE_PES:
list_for_each_entry(feed, &filter->feed.ts, next) {
- if (feed->pid == sec_mode->pid) {
+ if (feed->pid == cipher_ops->pid) {
ts_feed = feed;
- ts_feed->sec_mode = *sec_mode;
+ ts_feed->cipher_ops = *cipher_ops;
if (filter->state == DMXDEV_STATE_GO &&
- ts_feed->ts->set_secure_mode)
- ts_feed->ts->set_secure_mode(
- ts_feed->ts, sec_mode);
+ ts_feed->ts->set_cipher_ops)
+ ts_feed->ts->set_cipher_ops(
+ ts_feed->ts, cipher_ops);
break;
}
}
break;
case DMXDEV_TYPE_SEC:
- if (filter->params.sec.pid == sec_mode->pid) {
+ if (filter->params.sec.pid == cipher_ops->pid) {
sec_feed = &filter->feed.sec;
- sec_feed->sec_mode = *sec_mode;
+ sec_feed->cipher_ops = *cipher_ops;
if (filter->state == DMXDEV_STATE_GO &&
- sec_feed->feed->set_secure_mode)
- sec_feed->feed->set_secure_mode(sec_feed->feed,
- sec_mode);
+ sec_feed->feed->set_cipher_ops)
+ sec_feed->feed->set_cipher_ops(sec_feed->feed,
+ cipher_ops);
}
break;
@@ -3628,7 +3717,7 @@
if (!ts_feed && !sec_feed) {
printk(KERN_ERR "%s: pid %d is undefined for this filter\n",
- __func__, sec_mode->pid);
+ __func__, cipher_ops->pid);
return -EINVAL;
}
@@ -4029,6 +4118,15 @@
mutex_unlock(&dmxdevfilter->mutex);
break;
+ case DMX_SET_CIPHER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_set_cipher(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
case DMX_REUSE_DECODER_BUFFER:
if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
mutex_unlock(&dmxdev->mutex);
@@ -4384,13 +4482,12 @@
buffer_status.fullness);
seq_printf(s, "error: %d, ",
buffer_status.error);
- seq_printf(s, "scramble: %d\n",
- scrambling_bits.value);
-
- } else {
- seq_printf(s, "scramble: %d\n",
- scrambling_bits.value);
}
+
+ seq_printf(s, "scramble: %d, ",
+ scrambling_bits.value);
+ seq_printf(s, "secured: %d\n",
+ filter->sec_mode.is_secured);
}
}
@@ -4425,6 +4522,7 @@
return -ENOMEM;
dmxdev->playback_mode = DMX_PB_MODE_PUSH;
+ dmxdev->demux->dvr_input_protected = 0;
mutex_init(&dmxdev->mutex);
spin_lock_init(&dmxdev->lock);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index 4e306e8..6747ca7 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -58,15 +58,15 @@
struct dmxdev_feed {
u16 pid;
- struct dmx_secure_mode sec_mode;
struct dmx_indexing_params idx_params;
+ struct dmx_cipher_operations cipher_ops;
struct dmx_ts_feed *ts;
struct list_head next;
};
struct dmxdev_sec_feed {
- struct dmx_secure_mode sec_mode;
struct dmx_section_feed *feed;
+ struct dmx_cipher_operations cipher_ops;
};
#define DMX_EVENT_QUEUE_SIZE 500 /* number of events */
@@ -182,6 +182,8 @@
int todo;
u8 secheader[3];
+ struct dmx_secure_mode sec_mode;
+
/* Decoder buffer(s) related */
struct dmx_decoder_buffers decoder_buffers;
};
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index bd4344b..692a04e 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -1893,7 +1893,7 @@
(feed->idx_params.types &
(DMX_IDX_H264_IDR_START | DMX_IDX_H264_IDR_END |
DMX_IDX_H264_NON_IDR_END |
- DMX_IDX_H264_FIRST_SPS_FRAME_END |
+ DMX_IDX_H264_FIRST_SPS_FRAME_START |
DMX_IDX_H264_FIRST_SPS_FRAME_END))) {
feed->patterns[feed->pattern_num] =
dvb_dmx_get_pattern(DMX_IDX_H264_IDR_START);
@@ -1905,7 +1905,7 @@
(feed->idx_params.types &
(DMX_IDX_H264_NON_IDR_START | DMX_IDX_H264_NON_IDR_END |
DMX_IDX_H264_IDR_END |
- DMX_IDX_H264_FIRST_SPS_FRAME_END |
+ DMX_IDX_H264_FIRST_SPS_FRAME_START |
DMX_IDX_H264_FIRST_SPS_FRAME_END))) {
feed->patterns[feed->pattern_num] =
dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_START);
@@ -2262,19 +2262,36 @@
{
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (dvbdmxfeed->state == DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+
+ dvbdmxfeed->secure_mode = *secure_mode;
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dmx_ts_set_cipher_ops(struct dmx_ts_feed *feed,
+ struct dmx_cipher_operations *cipher_ops)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
int ret = 0;
- mutex_lock(&dvbdmx->mutex);
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
if ((dvbdmxfeed->state == DMX_STATE_GO) &&
- 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;
- }
+ dvbdmx->set_cipher_op)
+ ret = dvbdmx->set_cipher_op(dvbdmxfeed, cipher_ops);
+
+ if (!ret)
+ dvbdmxfeed->cipher_ops = *cipher_ops;
mutex_unlock(&dvbdmx->mutex);
return ret;
@@ -2481,6 +2498,7 @@
(*ts_feed)->data_ready_cb = dmx_ts_feed_data_ready_cb;
(*ts_feed)->notify_data_read = NULL;
(*ts_feed)->set_secure_mode = dmx_ts_set_secure_mode;
+ (*ts_feed)->set_cipher_ops = dmx_ts_set_cipher_ops;
(*ts_feed)->oob_command = dvbdmx_ts_feed_oob_cmd;
(*ts_feed)->get_scrambling_bits = dvbdmx_ts_get_scrambling_bits;
(*ts_feed)->ts_insertion_init = NULL;
@@ -2724,15 +2742,38 @@
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);
+ if (dvbdmxfeed->state == DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+ dvbdmxfeed->secure_mode = *secure_mode;
mutex_unlock(&dvbdmx->mutex);
return 0;
}
+static int dmx_section_set_cipher_ops(struct dmx_section_feed *feed,
+ struct dmx_cipher_operations *cipher_ops)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if ((dvbdmxfeed->state == DMX_STATE_GO) &&
+ dvbdmx->set_cipher_op) {
+ ret = dvbdmx->set_cipher_op(dvbdmxfeed, cipher_ops);
+ }
+
+ if (!ret)
+ dvbdmxfeed->cipher_ops = *cipher_ops;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+}
+
static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
struct dmx_section_filter *filter)
{
@@ -2875,6 +2916,7 @@
(*feed)->data_ready_cb = dmx_section_feed_data_ready_cb;
(*feed)->notify_data_read = NULL;
(*feed)->set_secure_mode = dmx_section_set_secure_mode;
+ (*feed)->set_cipher_ops = dmx_section_set_cipher_ops;
(*feed)->oob_command = dvbdmx_section_feed_oob_cmd;
(*feed)->get_scrambling_bits = dvbdmx_section_get_scrambling_bits;
@@ -2939,8 +2981,10 @@
{
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
- if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
+ if (!demux->frontend || !buf || demux->dvr_input_protected ||
+ (demux->frontend->source != DMX_MEMORY_FE)) {
return -EINVAL;
+ }
dvb_dmx_swfilter_format(dvbdemux, buf, count, dvbdemux->tsp_format);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index aeafa57..835e7b8 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -176,6 +176,7 @@
int buffer_size;
enum dmx_tsp_format_t tsp_out_format;
struct dmx_secure_mode secure_mode;
+ struct dmx_cipher_operations cipher_ops;
struct timespec timeout;
struct dvb_demux_filter *filter;
@@ -233,8 +234,8 @@
struct dmx_buffer_status *dmx_buffer_status);
int (*reuse_decoder_buffer)(struct dvb_demux_feed *feed,
int cookie);
- int (*set_secure_mode)(struct dvb_demux_feed *feed,
- struct dmx_secure_mode *secure_mode);
+ int (*set_cipher_op)(struct dvb_demux_feed *feed,
+ struct dmx_cipher_operations *cipher_ops);
u32 (*check_crc32)(struct dvb_demux_feed *feed,
const u8 *buf, size_t len);
void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index 3a30970..74ec99a 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -164,6 +164,15 @@
1280 * 270. It does not support auto focus. It supports
few special effects like mono.
+config GC0339
+ bool "Sensor GC0339 (BAYER .3M)"
+ depends on MSMB_CAMERA
+ ---help---
+ gc0339 is a Galaxycore .3 MP Bayer Sensor.
+ It supports 1 or 2 mipi lanes.
+ Preview and snapshot resolution shall be 640*480 at 30 fps,
+ It does not support auto focus.
+
config OV8825
bool "OmniVision OV8825 (BAYER 8MP)"
depends on MSMB_CAMERA
@@ -173,6 +182,15 @@
snapshot config = 3264 * 2448 at 18 fps.
2 lanes max fps is 18, 4 lanes max fps is 24.
+config s5k4e1
+ bool "Sensor s5k4e1 (BAYER 5MP)"
+ depends on MSMB_CAMERA
+ ---help---
+ Samsung 5 MP Bayer Sensor. It uses 2 mipi lanes,
+ supports 720P preview at 30 fps
+ and QSXGA snapshot at 15 fps.
+ This sensor driver does not support auto focus.
+
config MSM_V4L2_VIDEO_OVERLAY_DEVICE
tristate "Qualcomm MSM V4l2 video overlay device"
---help---
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 2ff70d3..2db25a6 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -34,11 +34,12 @@
#define VFE40_8974V2_VERSION 0x1001001A
#define VFE40_8974V3_VERSION 0x1001001B
#define VFE40_8x26_VERSION 0x20000013
+#define VFE40_8x26V2_VERSION 0x20010014
#define VFE40_BURST_LEN 3
#define VFE40_STATS_BURST_LEN 2
#define VFE40_UB_SIZE 1536
-#define VFE40_EQUAL_SLICE_UB 286
+#define VFE40_EQUAL_SLICE_UB 228
#define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx)
#define VFE40_RDI_BASE(idx) (0x2E8 + 0x4 * idx)
#define VFE40_XBAR_BASE(idx) (0x58 + 0x4 * (idx / 2))
@@ -93,8 +94,10 @@
static void msm_vfe40_init_qos_parms(struct vfe_device *vfe_dev)
{
void __iomem *vfebase = vfe_dev->vfe_base;
+
if (vfe_dev->vfe_hw_version == VFE40_8974V1_VERSION ||
- vfe_dev->vfe_hw_version == VFE40_8x26_VERSION) {
+ vfe_dev->vfe_hw_version == VFE40_8x26_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8x26V2_VERSION) {
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_0);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_1);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_2);
@@ -237,6 +240,7 @@
msm_vfe40_init_vbif_parms_8974_v2(vfe_dev);
break;
case VFE40_8x26_VERSION:
+ case VFE40_8x26V2_VERSION:
msm_vfe40_init_vbif_parms_8x26(vfe_dev);
break;
default:
@@ -1295,7 +1299,7 @@
}
static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
- .num_wm = 4,
+ .num_wm = 5,
.num_comp_mask = 3,
.num_rdi = 3,
.num_rdi_master = 3,
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 27aba5c..07f3b40 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -519,6 +519,7 @@
struct msm_session *session;
unsigned int session_id;
unsigned int stream_id;
+ unsigned long spin_flags = 0;
event_data = (struct msm_v4l2_event_data *)
((struct v4l2_event *)arg)->u.data;
@@ -564,9 +565,13 @@
break;
}
+ spin_lock_irqsave(&(session->command_ack_q.lock),
+ spin_flags);
ret_cmd->event = *(struct v4l2_event *)arg;
msm_enqueue(&cmd_ack->command_q, &ret_cmd->list);
wake_up(&cmd_ack->wait);
+ spin_unlock_irqrestore(&(session->command_ack_q.lock),
+ spin_flags);
}
break;
@@ -852,16 +857,19 @@
{
unsigned long flags;
struct v4l2_subdev *subdev = NULL;
+ struct v4l2_subdev *subdev_out = NULL;
spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
if (!list_empty(&msm_v4l2_dev->subdevs)) {
list_for_each_entry(subdev, &msm_v4l2_dev->subdevs, list)
- if (!strcmp(name, subdev->name))
+ if (!strcmp(name, subdev->name)) {
+ subdev_out = subdev;
break;
+ }
}
spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
- return subdev;
+ return subdev_out;
}
static void msm_sd_notify(struct v4l2_subdev *sd,
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 df72328..d238649 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
@@ -24,6 +24,9 @@
#include <linux/proc_fs.h>
#include <linux/msm_ion.h>
#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
#include <mach/iommu_domains.h>
#include <mach/iommu.h>
#include <mach/vreg.h>
@@ -36,6 +39,7 @@
#include "msm_cpp.h"
#include "msm_isp_util.h"
#include "msm_camera_io_util.h"
+#include <linux/debugfs.h>
#define MSM_CPP_DRV_NAME "msm_cpp"
@@ -43,6 +47,23 @@
#define CONFIG_MSM_CPP_DBG 0
+#define CPP_CMD_TIMEOUT_MS 300
+
+struct msm_cpp_timer_data_t {
+ struct cpp_device *cpp_dev;
+ struct msm_cpp_frame_info_t *processed_frame;
+};
+
+struct msm_cpp_timer_t {
+ uint8_t used;
+ struct msm_cpp_timer_data_t data;
+ struct timer_list cpp_timer;
+};
+
+struct msm_cpp_timer_t cpp_timers[2];
+static int del_timer_idx;
+static int set_timer_idx;
+
/* dump the frame command before writing to the hardware */
#define MSM_CPP_DUMP_FRM_CMD 0
@@ -109,6 +130,11 @@
{"micro_iface_clk", -1},
};
static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev);
+static void cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin);
+static void cpp_timer_callback(unsigned long data);
+
+uint8_t induce_error;
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev);
static void msm_cpp_write(u32 data, void __iomem *cpp_base)
{
@@ -472,7 +498,6 @@
tx_fifo[i] = msm_camera_io_r(cpp_dev->base +
MSM_CPP_MICRO_FIFO_TX_DATA);
}
-
spin_lock_irqsave(&cpp_dev->tasklet_lock, flags);
queue_cmd = &cpp_dev->tasklet_queue_cmd[cpp_dev->taskletq_idx];
if (queue_cmd->cmd_used) {
@@ -494,6 +519,36 @@
spin_unlock_irqrestore(&cpp_dev->tasklet_lock, flags);
tasklet_schedule(&cpp_dev->cpp_tasklet);
+ } else if (irq_status & 0x7C0) {
+ pr_err("%s: fatal error: 0x%x\n", __func__, irq_status);
+ pr_err("%s: DEBUG_SP: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x40));
+ pr_err("%s: DEBUG_T: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x44));
+ pr_err("%s: DEBUG_N: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x48));
+ pr_err("%s: DEBUG_R: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4C));
+ pr_err("%s: DEBUG_OPPC: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x50));
+ pr_err("%s: DEBUG_MO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x54));
+ pr_err("%s: DEBUG_TIMER0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x60));
+ pr_err("%s: DEBUG_TIMER1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x64));
+ pr_err("%s: DEBUG_GPI: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x70));
+ pr_err("%s: DEBUG_GPO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x74));
+ pr_err("%s: DEBUG_T0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x80));
+ pr_err("%s: DEBUG_R0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x84));
+ pr_err("%s: DEBUG_T1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x88));
+ pr_err("%s: DEBUG_R1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x8C));
}
msm_camera_io_w(irq_status, cpp_dev->base + MSM_CPP_MICRO_IRQGEN_CLR);
return IRQ_HANDLED;
@@ -509,6 +564,7 @@
uint32_t tx_fifo[MSM_CPP_TX_FIFO_LEVEL];
struct cpp_device *cpp_dev = (struct cpp_device *) data;
struct msm_cpp_tasklet_queue_cmd *queue_cmd;
+ struct msm_cpp_timer_t *timer = NULL;
while (atomic_read(&cpp_dev->irq_cnt)) {
spin_lock_irqsave(&cpp_dev->tasklet_lock, flags);
@@ -535,6 +591,25 @@
msg_id = tx_fifo[i+2];
if (msg_id == MSM_CPP_MSG_ID_FRAME_ACK) {
CPP_DBG("Frame done!!\n");
+ /* delete CPP timer */
+ CPP_DBG("delete timer %d.\n",
+ del_timer_idx);
+ timer = &cpp_timers[del_timer_idx];
+ del_timer(&timer->cpp_timer);
+ timer->used = 0;
+ timer->data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+ msm_cpp_notify_frame_done(cpp_dev);
+ } else if (msg_id ==
+ MSM_CPP_MSG_ID_FRAME_NACK) {
+ pr_err("NACK error from hw!!\n");
+ CPP_DBG("delete timer %d.\n",
+ del_timer_idx);
+ timer = &cpp_timers[del_timer_idx];
+ del_timer(&timer->cpp_timer);
+ timer->used = 0;
+ timer->data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
msm_cpp_notify_frame_done(cpp_dev);
}
i += cmd_len + 2;
@@ -543,48 +618,6 @@
}
}
-static void msm_cpp_boot_hw(struct cpp_device *cpp_dev)
-{
- disable_irq(cpp_dev->irq->start);
-
- msm_camera_io_w(0x1, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
- msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_BOOT_START);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
-
- /*Trigger MC to jump to start address*/
- msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
- msm_cpp_write(MSM_CPP_JUMP_ADDRESS, cpp_dev->base);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- msm_cpp_poll(cpp_dev->base, 0x1);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_JUMP_ACK);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
-
- /*Get Bootloader Version*/
- msm_cpp_write(MSM_CPP_CMD_GET_BOOTLOADER_VER, cpp_dev->base);
- pr_info("MC Bootloader Version: 0x%x\n",
- msm_cpp_read(cpp_dev->base));
-
- /*Get Firmware Version*/
- msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
- msm_cpp_write(MSM_CPP_MSG_ID_CMD, cpp_dev->base);
- msm_cpp_write(0x1, cpp_dev->base);
- msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
- msm_cpp_write(MSM_CPP_MSG_ID_TRAILER, cpp_dev->base);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- msm_cpp_poll(cpp_dev->base, 0x2);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_FW_VER);
- pr_info("CPP FW Version: 0x%x\n", msm_cpp_read(cpp_dev->base));
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
- enable_irq(cpp_dev->irq->start);
- msm_camera_io_w_mb(0x8, cpp_dev->base +
- MSM_CPP_MICRO_IRQGEN_MASK);
- msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
- MSM_CPP_MICRO_IRQGEN_CLR);
-}
-
static int cpp_init_hardware(struct cpp_device *cpp_dev)
{
int rc = 0;
@@ -663,8 +696,15 @@
cpp_dev->taskletq_idx = 0;
atomic_set(&cpp_dev->irq_cnt, 0);
msm_cpp_create_buff_queue(cpp_dev, MSM_CPP_MAX_BUFF_QUEUE);
- if (cpp_dev->is_firmware_loaded == 1)
- msm_cpp_boot_hw(cpp_dev);
+ if (cpp_dev->is_firmware_loaded == 1) {
+ disable_irq(cpp_dev->irq->start);
+ cpp_load_fw(cpp_dev, NULL);
+ enable_irq(cpp_dev->irq->start);
+ msm_camera_io_w_mb(0x7C8, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+ }
return rc;
req_irq_fail:
iounmap(cpp_dev->cpp_hw_base);
@@ -713,47 +753,44 @@
const struct firmware *fw = NULL;
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",
- fw_name_bin, dev, rc);
- }
-
- CPP_DBG("HW Ver:0x%x\n",
- msm_camera_io_r(cpp_dev->base +
- MSM_CPP_MICRO_HW_VERSION));
-
+ msm_camera_io_w(0x1, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_BOOT_START);
- /*Enable MC clock*/
- msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_CLKEN_CTL);
-
+ MSM_CPP_MICRO_BOOT_START);
msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- /*Start firmware loading*/
- msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
- msm_cpp_write(MSM_CPP_END_ADDRESS, cpp_dev->base);
- msm_cpp_write(MSM_CPP_START_ADDRESS, cpp_dev->base);
- if (NULL != fw)
- ptr_bin = (uint32_t *)fw->data;
+ if (fw_name_bin) {
+ pr_debug("%s: FW file: %s\n", __func__, fw_name_bin);
+ rc = request_firmware(&fw, fw_name_bin, dev);
+ if (rc) {
+ dev_err(dev,
+ "Fail to loc blob %s from dev %p, Error: %d\n",
+ fw_name_bin, dev, rc);
+ }
+ if (NULL != fw)
+ ptr_bin = (uint32_t *)fw->data;
- if (ptr_bin == NULL) {
- pr_err("ptr_bin is NULL\n");
- } else {
- for (i = 0; i < fw->size/4; i++) {
- if (ptr_bin) {
+ msm_camera_io_w(0x1, cpp_dev->base +
+ MSM_CPP_MICRO_BOOT_START);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+ msm_camera_io_w(0xFFFFFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+
+ /*Start firmware loading*/
+ msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_END_ADDRESS, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_START_ADDRESS, cpp_dev->base);
+
+ if (ptr_bin) {
+ for (i = 0; i < fw->size/4; i++) {
msm_cpp_write(*ptr_bin, cpp_dev->base);
ptr_bin++;
}
}
+ if (fw)
+ release_firmware(fw);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_OK);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
}
- if (fw)
- release_firmware(fw);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_OK);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
/*Trigger MC to jump to start address*/
msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
@@ -817,7 +854,6 @@
cpp_dev->cpp_open_cnt++;
if (cpp_dev->cpp_open_cnt == 1) {
cpp_init_hardware(cpp_dev);
- iommu_attach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
cpp_init_mem(cpp_dev);
cpp_dev->state = CPP_STATE_IDLE;
}
@@ -852,9 +888,38 @@
cpp_dev->cpp_open_cnt--;
if (cpp_dev->cpp_open_cnt == 0) {
+ pr_err("%s: irq_status: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4));
+ pr_err("%s: DEBUG_SP: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x40));
+ pr_err("%s: DEBUG_T: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x44));
+ pr_err("%s: DEBUG_N: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x48));
+ pr_err("%s: DEBUG_R: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4C));
+ pr_err("%s: DEBUG_OPPC: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x50));
+ pr_err("%s: DEBUG_MO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x54));
+ pr_err("%s: DEBUG_TIMER0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x60));
+ pr_err("%s: DEBUG_TIMER1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x64));
+ pr_err("%s: DEBUG_GPI: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x70));
+ pr_err("%s: DEBUG_GPO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x74));
+ pr_err("%s: DEBUG_T0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x80));
+ pr_err("%s: DEBUG_R0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x84));
+ pr_err("%s: DEBUG_T1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x88));
+ pr_err("%s: DEBUG_R1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x8C));
msm_camera_io_w(0x0, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
cpp_deinit_mem(cpp_dev);
- iommu_detach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
cpp_release_hardware(cpp_dev);
cpp_dev->state = CPP_STATE_OFF;
}
@@ -969,23 +1034,140 @@
}
#endif
+void msm_cpp_do_timeout_work(struct work_struct *work)
+{
+ int ret;
+ uint32_t i = 0;
+ struct msm_cpp_frame_info_t *this_frame =
+ cpp_timers[del_timer_idx].data.processed_frame;
+ struct msm_cpp_frame_info_t *second_frame = NULL;
+
+ pr_err("cpp_timer_callback called idx:%d. (jiffies=%lu)\n",
+ del_timer_idx, jiffies);
+ pr_err("fatal: cpp_timer expired for identity=0x%x, frame_id=%03d",
+ this_frame->identity, this_frame->frame_id);
+ cpp_timers[del_timer_idx].used = 0;
+ cpp_timers[del_timer_idx].data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+
+ if (cpp_timers[del_timer_idx].used == 1) {
+ pr_err("deleting cpp_timer %d.\n", del_timer_idx);
+ del_timer(&cpp_timers[del_timer_idx].cpp_timer);
+ cpp_timers[del_timer_idx].used = 0;
+ second_frame = cpp_timers[del_timer_idx].data.processed_frame;
+ cpp_timers[del_timer_idx].data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+ }
+
+ disable_irq(cpp_timers[del_timer_idx].data.cpp_dev->irq->start);
+ pr_err("Reloading firmware\n");
+ cpp_load_fw(cpp_timers[del_timer_idx].data.cpp_dev, NULL);
+ pr_err("Firmware loading done\n");
+ enable_irq(cpp_timers[del_timer_idx].data.cpp_dev->irq->start);
+ msm_camera_io_w_mb(0x8, cpp_timers[del_timer_idx].data.cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w_mb(0xFFFF,
+ cpp_timers[del_timer_idx].data.cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+
+ cpp_timers[set_timer_idx].data.processed_frame = this_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ pr_err("ReInstalling cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer, cpp_timer_callback,
+ (unsigned long)&cpp_timers[0]);
+ pr_err("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+ pr_err("Rescheduling for identity=0x%x, frame_id=%03d",
+ this_frame->identity, this_frame->frame_id);
+ msm_cpp_write(0x6, cpp_timers[set_timer_idx].data.cpp_dev->base);
+ msm_cpp_dump_frame_cmd(this_frame->cpp_cmd_msg,
+ this_frame->msg_len);
+ for (i = 0; i < this_frame->msg_len; i++)
+ msm_cpp_write(this_frame->cpp_cmd_msg[i],
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+
+
+ if (second_frame != NULL) {
+ cpp_timers[set_timer_idx].data.processed_frame = second_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ pr_err("ReInstalling cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ cpp_timer_callback, (unsigned long)&cpp_timers[0]);
+ pr_err("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+ pr_err("Rescheduling for identity=0x%x, frame_id=%03d",
+ second_frame->identity, second_frame->frame_id);
+ msm_cpp_write(0x6,
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+ msm_cpp_dump_frame_cmd(second_frame->cpp_cmd_msg,
+ second_frame->msg_len);
+ for (i = 0; i < second_frame->msg_len; i++)
+ msm_cpp_write(second_frame->cpp_cmd_msg[i],
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+ }
+}
+
+void cpp_timer_callback(unsigned long data)
+{
+ struct msm_cpp_work_t *work =
+ cpp_timers[set_timer_idx].data.cpp_dev->work;
+ queue_work(cpp_timers[set_timer_idx].data.cpp_dev->timer_wq,
+ (struct work_struct *)work);
+}
+
static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev,
struct msm_queue_cmd *frame_qcmd)
{
uint32_t i;
int32_t rc = -EAGAIN;
+ int ret;
struct msm_cpp_frame_info_t *process_frame;
if (cpp_dev->processing_q.len < MAX_CPP_PROCESSING_FRAME) {
process_frame = frame_qcmd->command;
msm_enqueue(&cpp_dev->processing_q,
&frame_qcmd->list_frame);
+
+ cpp_timers[set_timer_idx].data.processed_frame = process_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ /* install timer for cpp timeout */
+ CPP_DBG("Installing cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ cpp_timer_callback, (unsigned long)&cpp_timers[0]);
+ CPP_DBG("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+
msm_cpp_write(0x6, cpp_dev->base);
msm_cpp_dump_frame_cmd(process_frame->cpp_cmd_msg,
process_frame->msg_len);
- for (i = 0; i < process_frame->msg_len; i++)
- msm_cpp_write(process_frame->cpp_cmd_msg[i],
- cpp_dev->base);
+ for (i = 0; i < process_frame->msg_len; i++) {
+ if ((induce_error) && (i == 1)) {
+ pr_err("Induce error\n");
+ msm_cpp_write(process_frame->cpp_cmd_msg[i]-1,
+ cpp_dev->base);
+ induce_error--;
+ } else
+ msm_cpp_write(process_frame->cpp_cmd_msg[i],
+ cpp_dev->base);
+ }
do_gettimeofday(&(process_frame->in_time));
rc = 0;
}
@@ -1097,7 +1279,7 @@
&buff_mgr_info);
if (rc < 0) {
rc = -EAGAIN;
- pr_err("error getting buffer rc:%d\n", rc);
+ pr_debug("error getting buffer rc:%d\n", rc);
goto ERROR2;
}
new_frame->output_buffer_info[1].index = buff_mgr_info.index;
@@ -1184,7 +1366,6 @@
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;
if (ioctl_ptr == NULL) {
pr_err("ioctl_ptr is null\n");
@@ -1206,8 +1387,11 @@
case VIDIOC_MSM_CPP_LOAD_FIRMWARE: {
if (cpp_dev->is_firmware_loaded == 0) {
- fw_name_bin = kzalloc(ioctl_ptr->len+1, GFP_KERNEL);
- if (!fw_name_bin) {
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
+ cpp_dev->fw_name_bin = kzalloc(ioctl_ptr->len+1,
+ GFP_KERNEL);
+ if (!cpp_dev->fw_name_bin) {
pr_err("%s:%d: malloc error\n", __func__,
__LINE__);
mutex_unlock(&cpp_dev->mutex);
@@ -1222,24 +1406,24 @@
pr_err("ioctl_ptr->len is 0\n");
return -EINVAL;
}
- rc = (copy_from_user(fw_name_bin,
+ rc = (copy_from_user(cpp_dev->fw_name_bin,
(void __user *)ioctl_ptr->ioctl_ptr,
ioctl_ptr->len) ? -EFAULT : 0);
if (rc) {
ERR_COPY_FROM_USER();
- kfree(fw_name_bin);
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
mutex_unlock(&cpp_dev->mutex);
return -EINVAL;
}
- *(fw_name_bin+ioctl_ptr->len) = '\0';
+ *(cpp_dev->fw_name_bin+ioctl_ptr->len) = '\0';
if (cpp_dev == NULL) {
pr_err("cpp_dev is null\n");
return -EINVAL;
}
disable_irq(cpp_dev->irq->start);
- cpp_load_fw(cpp_dev, fw_name_bin);
- kfree(fw_name_bin);
+ cpp_load_fw(cpp_dev, cpp_dev->fw_name_bin);
enable_irq(cpp_dev->irq->start);
cpp_dev->is_firmware_loaded = 1;
}
@@ -1467,7 +1651,6 @@
return msm_register_domain(&cpp_fw_layout);
}
-
static int __devinit cpp_probe(struct platform_device *pdev)
{
struct cpp_device *cpp_dev;
@@ -1582,23 +1765,35 @@
cpp_dev->msm_sd.sd.entity.revision = cpp_dev->msm_sd.sd.devnode->num;
cpp_dev->state = CPP_STATE_BOOT;
cpp_init_hardware(cpp_dev);
+ iommu_attach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
msm_camera_io_w(0x0, cpp_dev->base +
MSM_CPP_MICRO_IRQGEN_MASK);
msm_camera_io_w(0xFFFF, cpp_dev->base +
MSM_CPP_MICRO_IRQGEN_CLR);
-
+ msm_camera_io_w(0x80000000, cpp_dev->base + 0xF0);
cpp_release_hardware(cpp_dev);
cpp_dev->state = CPP_STATE_OFF;
+ msm_cpp_enable_debugfs(cpp_dev);
msm_queue_init(&cpp_dev->eventData_q, "eventdata");
msm_queue_init(&cpp_dev->processing_q, "frame");
INIT_LIST_HEAD(&cpp_dev->tasklet_q);
tasklet_init(&cpp_dev->cpp_tasklet, msm_cpp_do_tasklet,
(unsigned long)cpp_dev);
+ cpp_dev->timer_wq = create_workqueue("msm_cpp_workqueue");
+ cpp_dev->work = kmalloc(sizeof(struct msm_cpp_work_t),
+ GFP_KERNEL);
+ INIT_WORK((struct work_struct *)cpp_dev->work, msm_cpp_do_timeout_work);
cpp_dev->cpp_open_cnt = 0;
cpp_dev->is_firmware_loaded = 0;
+ cpp_timers[0].data.cpp_dev = cpp_dev;
+ cpp_timers[1].data.cpp_dev = cpp_dev;
+ cpp_timers[0].used = 0;
+ cpp_timers[1].used = 0;
+ cpp_dev->fw_name_bin = NULL;
return rc;
+
ERROR3:
release_mem_region(cpp_dev->mem->start, resource_size(cpp_dev->mem));
ERROR2:
@@ -1628,6 +1823,7 @@
return 0;
}
+ 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,
@@ -1635,6 +1831,8 @@
release_mem_region(cpp_dev->cpp_hw_mem->start,
resource_size(cpp_dev->cpp_hw_mem));
mutex_destroy(&cpp_dev->mutex);
+ kfree(cpp_dev->work);
+ destroy_workqueue(cpp_dev->timer_wq);
kfree(cpp_dev->cpp_clk);
kfree(cpp_dev);
return 0;
@@ -1660,6 +1858,30 @@
platform_driver_unregister(&cpp_driver);
}
+static int msm_cpp_debugfs_error_s(void *data, u64 val)
+{
+ pr_err("setting error inducement");
+ induce_error = val;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cpp_debugfs_error, NULL,
+ msm_cpp_debugfs_error_s, "%llu\n");
+
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev)
+{
+ struct dentry *debugfs_base;
+ debugfs_base = debugfs_create_dir("msm_cpp", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("error", S_IRUGO | S_IWUSR, debugfs_base,
+ (void *)cpp_dev, &cpp_debugfs_error))
+ return -ENOMEM;
+
+ return 0;
+}
+
module_init(msm_cpp_init_module);
module_exit(msm_cpp_exit_module);
MODULE_DESCRIPTION("MSM CPP driver");
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 36a5fa5..0a70d37 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
@@ -147,6 +147,11 @@
struct list_head native_buff_head;
};
+struct msm_cpp_work_t {
+ struct work_struct my_work;
+ struct cpp_device *cpp_dev;
+};
+
struct cpp_device {
struct platform_device *pdev;
struct msm_sd_subdev msm_sd;
@@ -165,6 +170,9 @@
struct mutex mutex;
enum cpp_state state;
uint8_t is_firmware_loaded;
+ char *fw_name_bin;
+ struct workqueue_struct *timer_wq;
+ struct msm_cpp_work_t *work;
int domain_num;
struct iommu_domain *domain;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
index 5104bcb..18ac623 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -8,6 +8,10 @@
obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
obj-$(CONFIG_IMX135) += imx135.o
obj-$(CONFIG_OV8825) += ov8825.o
+obj-$(CONFIG_s5k4e1) += s5k4e1.o
obj-$(CONFIG_OV2720) += ov2720.o
obj-$(CONFIG_OV9724) += ov9724.o
+obj-$(CONFIG_HI256) += hi256.o
obj-$(CONFIG_MT9M114) += mt9m114.o
+obj-$(CONFIG_SP1628) += sp1628.o
+obj-$(CONFIG_GC0339) += gc0339.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 676862f..a27ca99 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -111,9 +111,30 @@
msm_camera_io_w(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR);
rc = wait_for_completion_interruptible_timeout(
&cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT);
- if (rc <= 0)
- pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
- __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s:%d wait failed\n", __func__, __LINE__);
+ } else if (rc == 0) {
+ pr_err("%s:%d wait timeout\n", __func__, __LINE__);
+
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+
+ /* Set proper mask to RESET CMD address based on MASTER */
+ if (master == MASTER_0)
+ msm_camera_io_w(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ msm_camera_io_w(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+
+ /* wait for reset done irq */
+ rc = wait_for_completion_interruptible_timeout(
+ &cci_dev->cci_master_info[master].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__, __LINE__,
+ rc);
+ }
return;
}
@@ -774,10 +795,7 @@
rc = msm_cci_config(sd, arg);
break;
case MSM_SD_SHUTDOWN: {
- struct msm_camera_cci_ctrl ctrl_cmd;
- ctrl_cmd.cmd = MSM_CCI_RELEASE;
- rc = msm_cci_config(sd, &ctrl_cmd);
- break;
+ return rc;
}
default:
rc = -ENOIOCTLCMD;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
index 3dd3a4e..4ebcfdf 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
@@ -130,12 +130,6 @@
pr_err("%s failed e_ctrl is NULL\n", __func__);
return -EINVAL;
}
- if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
- rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_util(
- &e_ctrl->i2c_client, MSM_CCI_INIT);
- if (rc < 0)
- pr_err("%s cci_init failed\n", __func__);
- }
CDBG("%s X\n", __func__);
return rc;
}
@@ -149,12 +143,6 @@
pr_err("%s failed e_ctrl is NULL\n", __func__);
return -EINVAL;
}
- if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
- rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_util(
- &e_ctrl->i2c_client, MSM_CCI_RELEASE);
- if (rc < 0)
- pr_err("%s cci_init failed\n", __func__);
- }
CDBG("%s X\n", __func__);
return rc;
}
@@ -445,21 +433,27 @@
struct msm_eeprom_board_info *eb_info;
struct msm_camera_power_ctrl_t *power_info =
&e_ctrl->eboard_info->power_info;
- struct spi_device *spi = e_ctrl->i2c_client.spi_client->spi_master;
- struct device_node *of_node = spi->dev.of_node;
+ struct device_node *of_node = NULL;
struct msm_camera_gpio_conf *gconf = NULL;
uint16_t gpio_array_size = 0;
uint16_t *gpio_array = NULL;
eb_info = e_ctrl->eboard_info;
- rc = msm_camera_get_dt_power_setting_data(spi->dev.of_node,
- &power_info->power_setting, &power_info->power_setting_size);
- if (rc)
- return rc;
+ if (e_ctrl->eeprom_device_type == MSM_CAMERA_SPI_DEVICE)
+ of_node = e_ctrl->i2c_client.
+ spi_client->spi_master->dev.of_node;
+ else if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ of_node = e_ctrl->pdev->dev.of_node;
rc = msm_camera_get_dt_vreg_data(of_node, &power_info->cam_vreg,
&power_info->num_vreg);
- if (rc)
+ if (rc < 0)
+ return rc;
+
+ rc = msm_camera_get_dt_power_setting_data(of_node,
+ power_info->cam_vreg, power_info->num_vreg,
+ &power_info->power_setting, &power_info->power_setting_size);
+ if (rc < 0)
goto ERROR1;
power_info->gpio_conf = kzalloc(sizeof(struct msm_camera_gpio_conf),
@@ -787,26 +781,35 @@
goto board_free;
}
- if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
- rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_util(
- &e_ctrl->i2c_client, MSM_CCI_INIT);
- if (rc < 0)
- pr_err("%s cci_init failed\n", __func__);
- }
+ rc = msm_eeprom_get_dt_data(e_ctrl);
+ if (rc)
+ goto board_free;
rc = msm_eeprom_alloc_memory_map(e_ctrl, of_node);
if (rc)
goto board_free;
+ rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("failed rc %d\n", rc);
+ goto memdata_free;
+ }
rc = read_eeprom_memory(e_ctrl);
if (rc < 0) {
pr_err("%s read_eeprom_memory failed\n", __func__);
- goto memdata_free;
+ goto power_down;
}
pr_err("%s line %d\n", __func__, __LINE__);
for (j = 0; j < e_ctrl->num_bytes; j++)
CDBG("memory_data[%d] = 0x%X\n", j, e_ctrl->memory_data[j]);
+ rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("failed rc %d\n", rc);
+ goto memdata_free;
+ }
v4l2_subdev_init(&e_ctrl->msm_sd.sd,
e_ctrl->eeprom_v4l2_subdev_ops);
v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl);
@@ -821,16 +824,13 @@
msm_sd_register(&e_ctrl->msm_sd);
- if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
- rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_util(
- &e_ctrl->i2c_client, MSM_CCI_RELEASE);
- if (rc < 0)
- pr_err("%s cci_init failed\n", __func__);
- }
e_ctrl->is_supported = 1;
CDBG("%s X\n", __func__);
return rc;
+power_down:
+ msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
memdata_free:
kfree(e_ctrl->memory_data);
kfree(eb_info->eeprom_map);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/gc0339.c b/drivers/media/platform/msm/camera_v2/sensor/gc0339.c
new file mode 100644
index 0000000..8cba04c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/gc0339.c
@@ -0,0 +1,676 @@
+/* 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 <mach/gpiomux.h>
+#include "msm_sensor.h"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_i2c_mux.h"
+
+
+#define GC0339_SENSOR_NAME "gc0339"
+DEFINE_MSM_MUTEX(gc0339_mut);
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+
+static struct msm_sensor_ctrl_t gc0339_s_ctrl;
+
+static struct msm_sensor_power_setting gc0339_power_setting[] = {
+
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .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_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 24000000,
+ .delay = 5,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 1,
+ },
+ {
+ .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 = 1,
+ },
+};
+
+static struct v4l2_subdev_info gc0339_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static int32_t msm_gc0339_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &gc0339_s_ctrl);
+}
+
+static const struct i2c_device_id gc0339_i2c_id[] = {
+ {GC0339_SENSOR_NAME, (kernel_ulong_t)&gc0339_s_ctrl},
+ { }
+};
+
+static struct i2c_driver gc0339_i2c_driver = {
+ .id_table = gc0339_i2c_id,
+ .probe = msm_gc0339_i2c_probe,
+ .driver = {
+ .name = GC0339_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client gc0339_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+int32_t gc0339_power_up(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0, index = 0;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting *power_setting = NULL;
+ struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ power_setting_array = &s_ctrl->power_setting_array;
+
+ if (data->gpio_conf->cam_gpiomux_conf_tbl != NULL) {
+ pr_err("%s:%d mux install\n", __func__, __LINE__);
+ msm_gpiomux_install(
+ (struct msm_gpiomux_config *)
+ data->gpio_conf->cam_gpiomux_conf_tbl,
+ data->gpio_conf->cam_gpiomux_conf_tbl_size);
+ }
+
+ rc = msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0) {
+ pr_err("%s: request gpio failed\n", __func__);
+ return rc;
+ }
+ for (index = 0; index < power_setting_array->size; index++) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ if (power_setting->seq_val >= s_ctrl->clk_info_size) {
+ pr_err("%s clk index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ s_ctrl->clk_info_size);
+ goto power_up_failed;
+ }
+ if (power_setting->config_val)
+ s_ctrl->clk_info[power_setting->seq_val].
+ clk_rate = power_setting->config_val;
+
+ rc = msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 1);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n",
+ __func__);
+ goto power_up_failed;
+ }
+ break;
+ case SENSOR_GPIO:
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !data->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ goto power_up_failed;
+ }
+ pr_debug("%s:%d gpio set val %d\n", __func__, __LINE__,
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val]);
+ if (data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val])
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val],
+ power_setting->config_val);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ goto power_up_failed;
+ }
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 1);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_INIT);
+ if (rc < 0) {
+ pr_err("%s cci_init failed\n", __func__);
+ goto power_up_failed;
+ }
+ }
+ if (s_ctrl->func_tbl->sensor_match_id)
+ rc = s_ctrl->func_tbl->sensor_match_id(s_ctrl);
+ else
+ rc = msm_sensor_match_id(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d match id failed rc %d\n", __func__, __LINE__, rc);
+ goto power_up_failed;
+ }
+
+ CDBG("%s exit\n", __func__);
+ return 0;
+power_up_failed:
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
+ }
+
+ for (index--; index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 0);
+ break;
+ case SENSOR_GPIO:
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 0);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+ msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 0);
+ return rc;
+}
+
+int32_t gc0339_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t index = 0;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting *power_setting = NULL;
+ struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ power_setting_array = &s_ctrl->power_setting_array;
+
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
+ }
+
+ for (index = (power_setting_array->size - 1); index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 0);
+ break;
+ case SENSOR_GPIO:
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !data->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 0);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+ msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 0);
+ CDBG("%s exit\n", __func__);
+ return 0;
+}
+
+int32_t gc0339_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write(
+ s_ctrl->sensor_i2c_client,
+ 0xfc,
+ 0x10, MSM_CAMERA_I2C_BYTE_DATA);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ s_ctrl->sensordata->slave_info->sensor_id_reg_addr,
+ &chipid, MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ if (chipid != s_ctrl->sensordata->slave_info->sensor_id) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+ return rc;
+}
+
+int32_t gc0339_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ long rc = 0;
+ int32_t i = 0;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ switch (cdata->cfgtype) {
+ case CFG_GET_SENSOR_INFO:
+ memcpy(cdata->cfg.sensor_info.sensor_name,
+ s_ctrl->sensordata->sensor_name,
+ sizeof(cdata->cfg.sensor_info.sensor_name));
+ cdata->cfg.sensor_info.session_id =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ cdata->cfg.sensor_info.subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.sensor_name);
+ CDBG("%s:%d session id %d\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.session_id);
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+ cdata->cfg.sensor_info.subdev_id[i]);
+
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params =
+ *s_ctrl->sensordata->sensor_init_params;
+ CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+ __LINE__,
+ cdata->cfg.sensor_init_params.modes_supported,
+ cdata->cfg.sensor_init_params.position,
+ cdata->cfg.sensor_init_params.sensor_mount_angle);
+ break;
+ case CFG_SET_SLAVE_INFO: {
+ struct msm_camera_sensor_slave_info sensor_slave_info;
+ struct msm_sensor_power_setting_array *power_setting_array;
+ int slave_index = 0;
+ if (copy_from_user(&sensor_slave_info,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_sensor_slave_info))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Update sensor slave address */
+ if (sensor_slave_info.slave_addr) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ sensor_slave_info.slave_addr >> 1;
+ }
+
+ /* Update sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type =
+ sensor_slave_info.addr_type;
+
+ /* Update power up / down sequence */
+ s_ctrl->power_setting_array =
+ sensor_slave_info.power_setting_array;
+ power_setting_array = &s_ctrl->power_setting_array;
+ power_setting_array->power_setting = kzalloc(
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+ if (!power_setting_array->power_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(power_setting_array->power_setting,
+ (void *)
+ sensor_slave_info.power_setting_array.power_setting,
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->free_power_setting = true;
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.slave_addr);
+ CDBG("%s sensor addr type %d\n", __func__,
+ sensor_slave_info.addr_type);
+ CDBG("%s sensor reg %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id);
+ for (slave_index = 0; slave_index <
+ power_setting_array->size; slave_index++) {
+ CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+ slave_index,
+ power_setting_array->power_setting[slave_index].
+ seq_type,
+ power_setting_array->power_setting[slave_index].
+ seq_val,
+ power_setting_array->power_setting[slave_index].
+ config_val,
+ power_setting_array->power_setting[slave_index].
+ delay);
+ }
+ break;
+ }
+ case CFG_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_seq_reg_array)),
+ GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+
+ case CFG_POWER_UP:
+ if (s_ctrl->func_tbl->sensor_power_up)
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->func_tbl->sensor_power_down)
+ rc = s_ctrl->func_tbl->sensor_power_down(
+ s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (copy_from_user(stop_setting,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = stop_setting->reg_setting;
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void *)reg_setting,
+ stop_setting->size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(stop_setting->reg_setting);
+ stop_setting->reg_setting = NULL;
+ stop_setting->size = 0;
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+ return rc;
+}
+
+static struct msm_sensor_fn_t gc0339_sensor_fn_t = {
+ .sensor_power_up = gc0339_power_up,
+ .sensor_power_down = gc0339_power_down,
+ .sensor_match_id = gc0339_match_id,
+ .sensor_config = gc0339_config,
+};
+
+
+static struct msm_sensor_ctrl_t gc0339_s_ctrl = {
+ .sensor_i2c_client = &gc0339_sensor_i2c_client,
+ .power_setting_array.power_setting = gc0339_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(gc0339_power_setting),
+ .msm_sensor_mutex = &gc0339_mut,
+ .sensor_v4l2_subdev_info = gc0339_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(gc0339_subdev_info),
+ .func_tbl = &gc0339_sensor_fn_t,
+};
+
+static const struct of_device_id gc0339_dt_match[] = {
+ {.compatible = "shinetech,gc0339", .data = &gc0339_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, gc0339_dt_match);
+
+static struct platform_driver gc0339_platform_driver = {
+ .driver = {
+ .name = "shinetech,gc0339",
+ .owner = THIS_MODULE,
+ .of_match_table = gc0339_dt_match,
+ },
+};
+
+static int32_t gc0339_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ const struct of_device_id *match;
+
+ match = of_match_device(gc0339_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init gc0339_init_module(void)
+{
+ int32_t rc = 0;
+
+ rc = platform_driver_probe(&gc0339_platform_driver,
+ gc0339_platform_probe);
+ if (!rc)
+ return rc;
+ return i2c_add_driver(&gc0339_i2c_driver);
+}
+
+static void __exit gc0339_exit_module(void)
+{
+ if (gc0339_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&gc0339_s_ctrl);
+ platform_driver_unregister(&gc0339_platform_driver);
+ } else
+ i2c_del_driver(&gc0339_i2c_driver);
+ return;
+}
+
+module_init(gc0339_init_module);
+module_exit(gc0339_exit_module);
+MODULE_DESCRIPTION("gc0339");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/hi256.c b/drivers/media/platform/msm/camera_v2/sensor/hi256.c
new file mode 100644
index 0000000..de651df
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/hi256.c
@@ -0,0 +1,1430 @@
+/* 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"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+#define HI256_SENSOR_NAME "hi256"
+#define PLATFORM_DRIVER_NAME "msm_camera_hi256"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+
+DEFINE_MSM_MUTEX(hi256_mut);
+static struct msm_sensor_ctrl_t hi256_s_ctrl;
+
+static struct msm_sensor_power_setting hi256_power_setting[] = {
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 20,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .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_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 24000000,
+ .delay = 10,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 20,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 10,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
+ .seq_type = SENSOR_I2C_MUX,
+ .seq_val = 0,
+ .config_val = 0,
+ .delay = 0,
+ },
+};
+
+static struct msm_camera_i2c_reg_conf hi256_uxga_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf1},
+ {0x03, 0x20},
+ {0x10, 0x1c},
+ {0x03, 0x22},
+ {0x10, 0x69},
+ {0x03, 0x00},
+ {0x12, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x0a},
+ {0x22, 0x00},
+ {0x23, 0x0a},
+ {0x40, 0x01},
+ {0x41, 0x68},
+ {0x42, 0x00},
+ {0x43, 0x12},
+ {0x03, 0x10},
+ {0x3f, 0x00},
+ {0x03, 0x12},
+ {0x20, 0x0f},
+ {0x21, 0x0f},
+ {0x90, 0x5d},
+ {0x03, 0x13},
+ {0x80, 0xfd},
+ {0x03, 0x00},
+ {0x10, 0x00},
+ {0x03, 0x48},
+ {0x72, 0x81},
+ {0x30, 0x0c},
+ {0x31, 0x80},
+ {0x03, 0x00},
+ {0x01, 0xf0},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_start_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf0},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_stop_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf1},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_recommend_settings[] = {
+ {0x01, 0xf1},
+ {0x01, 0xf3},
+ {0x01, 0xf1},
+
+ {0x08, 0x0f},
+ {0x0a, 0x00},
+
+ {0x03, 0x20},
+ {0x10, 0x1c},
+ {0x03, 0x22},
+ {0x10, 0x69},
+
+ {0x03, 0x00},
+ {0x10, 0x13},
+ {0x11, 0x93},
+ {0x12, 0x00},
+ {0x0b, 0xaa},
+ {0x0c, 0xaa},
+ {0x0d, 0xaa},
+ {0x20, 0x00},
+ {0x21, 0x06},
+ {0x22, 0x00},
+ {0x23, 0x05},
+ {0x24, 0x04},
+ {0x25, 0xb0},
+ {0x26, 0x06},
+ {0x27, 0x40},
+ {0x40, 0x01},
+ {0x41, 0x78},
+ {0x42, 0x00},
+ {0x43, 0x14},
+ {0x45, 0x04},
+ {0x46, 0x18},
+ {0x47, 0xd8},
+ {0x80, 0x2e},
+ {0x81, 0x7e},
+ {0x82, 0x90},
+ {0x83, 0x00},
+ {0x84, 0x0c},
+ {0x85, 0x00},
+ {0x90, 0x0c},
+ {0x91, 0x0c},
+ {0x92, 0x78},
+ {0x93, 0x70},
+ {0x94, 0xff},
+ {0x95, 0xff},
+ {0x96, 0xdc},
+ {0x97, 0xfe},
+ {0x98, 0x38},
+ {0xa0, 0x45},
+ {0xa2, 0x45},
+ {0xa4, 0x45},
+ {0xa6, 0x45},
+ {0xa8, 0x45},
+ {0xaa, 0x45},
+ {0xac, 0x45},
+ {0xae, 0x45},
+ {0x99, 0x43},
+ {0x9a, 0x43},
+ {0x9b, 0x43},
+ {0x9c, 0x43},
+ {0x03, 0x02},
+ {0x12, 0x03},
+ {0x13, 0x03},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x17, 0x8C},
+ {0x18, 0x4c},
+ {0x19, 0x00},
+ {0x1a, 0x39},
+ {0x1c, 0x09},
+ {0x1d, 0x40},
+ {0x1e, 0x30},
+ {0x1f, 0x10},
+ {0x20, 0x77},
+ {0x21, 0x6d},
+ {0x22, 0x77},
+ {0x23, 0x30},
+ {0x24, 0x77},
+ {0x27, 0x3c},
+ {0x2b, 0x80},
+ {0x2e, 0x00},
+ {0x2f, 0x00},
+ {0x30, 0x05},
+ {0x50, 0x20},
+ {0x52, 0x01},
+ {0x53, 0xc1},
+ {0x55, 0x1c},
+ {0x56, 0x11},
+ {0x58, 0x22},
+ {0x59, 0x20},
+ {0x5d, 0xa2},
+ {0x5e, 0x5a},
+ {0x60, 0x87},
+ {0x61, 0x99},
+ {0x62, 0x88},
+ {0x63, 0x97},
+ {0x64, 0x88},
+ {0x65, 0x97},
+ {0x67, 0x0c},
+ {0x68, 0x0c},
+ {0x69, 0x0c},
+ {0x72, 0x89},
+ {0x73, 0x96},
+ {0x74, 0x89},
+ {0x75, 0x96},
+ {0x76, 0x89},
+ {0x77, 0x96},
+ {0x7c, 0x85},
+ {0x7d, 0xaf},
+ {0x80, 0x01},
+ {0x81, 0x7f},
+ {0x82, 0x13},
+ {0x83, 0x24},
+ {0x84, 0x7d},
+ {0x85, 0x81},
+ {0x86, 0x7d},
+ {0x87, 0x81},
+ {0x92, 0x48},
+ {0x93, 0x54},
+ {0x94, 0x7d},
+ {0x95, 0x81},
+ {0x96, 0x7d},
+ {0x97, 0x81},
+ {0xa0, 0x02},
+ {0xa1, 0x7b},
+ {0xa2, 0x02},
+ {0xa3, 0x7b},
+ {0xa4, 0x7b},
+ {0xa5, 0x02},
+ {0xa6, 0x7b},
+ {0xa7, 0x02},
+ {0xa8, 0x85},
+ {0xa9, 0x8c},
+ {0xaa, 0x85},
+ {0xab, 0x8c},
+ {0xac, 0x10},
+ {0xad, 0x16},
+ {0xae, 0x10},
+ {0xaf, 0x16},
+ {0xb0, 0x99},
+ {0xb1, 0xa3},
+ {0xb2, 0xa4},
+ {0xb3, 0xae},
+ {0xb4, 0x9b},
+ {0xb5, 0xa2},
+ {0xb6, 0xa6},
+ {0xb7, 0xac},
+ {0xb8, 0x9b},
+ {0xb9, 0x9f},
+ {0xba, 0xa6},
+ {0xbb, 0xaa},
+ {0xbc, 0x9b},
+ {0xbd, 0x9f},
+ {0xbe, 0xa6},
+ {0xbf, 0xaa},
+ {0xc4, 0x2c},
+ {0xc5, 0x43},
+ {0xc6, 0x63},
+ {0xc7, 0x79},
+ {0xc8, 0x2d},
+ {0xc9, 0x42},
+ {0xca, 0x2d},
+ {0xcb, 0x42},
+ {0xcc, 0x64},
+ {0xcd, 0x78},
+ {0xce, 0x64},
+ {0xcf, 0x78},
+ {0xd0, 0x0a},
+ {0xd1, 0x09},
+ {0xd4, 0x0c},
+ {0xd5, 0x0c},
+ {0xd6, 0x78},
+ {0xd7, 0x70},
+ {0xe0, 0xc4},
+ {0xe1, 0xc4},
+ {0xe2, 0xc4},
+ {0xe3, 0xc4},
+ {0xe4, 0x00},
+ {0xe8, 0x80},
+ {0xe9, 0x40},
+ {0xea, 0x7f},
+ {0xf0, 0xc1},
+ {0xf1, 0xc1},
+ {0xf2, 0xc1},
+ {0xf3, 0xc1},
+ {0xf4, 0xc1},
+ {0x03, 0x03},
+ {0x10, 0x10},
+ {0x03, 0x10},
+ {0x10, 0x03},
+ {0x12, 0x30},
+ {0x13, 0x02},
+ {0x20, 0x00},
+ {0x30, 0x00},
+ {0x31, 0x00},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x30},
+ {0x35, 0x00},
+ {0x36, 0x00},
+ {0x38, 0x00},
+ {0x3e, 0x58},
+ {0x3f, 0x00},
+ {0x40, 0x80},
+ {0x41, 0x00},
+ {0x48, 0x95},
+ {0x60, 0x67},
+ {0x61, 0x88},
+ {0x62, 0x90},
+ {0x63, 0x50},
+ {0x64, 0x41},
+ {0x66, 0x42},
+ {0x67, 0x20},
+ {0x6a, 0x71},
+ {0x6b, 0x84},
+ {0x6c, 0x72},
+ {0x6d, 0x83},
+ {0x03, 0x11},
+ {0x10, 0x7f},
+ {0x11, 0x40},
+ {0x12, 0x0a},
+ {0x13, 0xbb},
+ {0x26, 0x31},
+ {0x27, 0x34},
+ {0x28, 0x0f},
+ {0x29, 0x10},
+ {0x2b, 0x30},
+ {0x2c, 0x32},
+ {0x30, 0x70},
+ {0x31, 0x10},
+ {0x32, 0x58},
+ {0x33, 0x09},
+ {0x34, 0x06},
+ {0x35, 0x03},
+ {0x36, 0x70},
+ {0x37, 0x18},
+ {0x38, 0x58},
+ {0x39, 0x09},
+ {0x3a, 0x06},
+ {0x3b, 0x03},
+ {0x3c, 0x80},
+ {0x3d, 0x18},
+ {0x3e, 0x80},
+ {0x3f, 0x0c},
+ {0x40, 0x05},
+ {0x41, 0x06},
+ {0x42, 0x80},
+ {0x43, 0x18},
+ {0x44, 0x80},
+ {0x45, 0x0c},
+ {0x46, 0x05},
+ {0x47, 0x06},
+ {0x48, 0x90},
+ {0x49, 0x40},
+ {0x4a, 0x80},
+ {0x4b, 0x13},
+ {0x4c, 0x10},
+ {0x4d, 0x11},
+ {0x4e, 0x80},
+ {0x4f, 0x30},
+ {0x50, 0x80},
+ {0x51, 0x13},
+ {0x52, 0x10},
+ {0x53, 0x13},
+ {0x54, 0x11},
+ {0x55, 0x17},
+ {0x56, 0x20},
+ {0x57, 0x01},
+ {0x58, 0x00},
+ {0x59, 0x00},
+ {0x5a, 0x18},
+ {0x5b, 0x00},
+ {0x5c, 0x00},
+ {0x60, 0x3f},
+ {0x62, 0x60},
+ {0x70, 0x06},
+ {0x03, 0x12},
+ {0x20, 0x00},
+ {0x21, 0x00},
+ {0x25, 0x00},
+ {0x28, 0x00},
+ {0x29, 0x00},
+ {0x2a, 0x00},
+ {0x30, 0x50},
+ {0x31, 0x18},
+ {0x32, 0x32},
+ {0x33, 0x40},
+ {0x34, 0x50},
+ {0x35, 0x70},
+ {0x36, 0xa0},
+ {0x40, 0xa0},
+ {0x41, 0x40},
+ {0x42, 0xa0},
+ {0x43, 0x90},
+ {0x44, 0x90},
+ {0x45, 0x80},
+ {0x46, 0xb0},
+ {0x47, 0x55},
+ {0x48, 0xa0},
+ {0x49, 0x90},
+ {0x4a, 0x90},
+ {0x4b, 0x80},
+ {0x4c, 0xb0},
+ {0x4d, 0x40},
+ {0x4e, 0x90},
+ {0x4f, 0x60},
+ {0x50, 0xa0},
+ {0x51, 0x80},
+ {0x52, 0xb0},
+ {0x53, 0x40},
+ {0x54, 0x90},
+ {0x55, 0x60},
+ {0x56, 0xa0},
+ {0x57, 0x80},
+ {0x58, 0x90},
+ {0x59, 0x40},
+ {0x5a, 0xd0},
+ {0x5b, 0xd0},
+ {0x5c, 0xe0},
+ {0x5d, 0x80},
+ {0x5e, 0x88},
+ {0x5f, 0x40},
+ {0x60, 0xe0},
+ {0x61, 0xe0},
+ {0x62, 0xe0},
+ {0x63, 0x80},
+ {0x70, 0x15},
+ {0x71, 0x01},
+ {0x72, 0x18},
+ {0x73, 0x01},
+ {0x74, 0x25},
+ {0x75, 0x15},
+ {0x80, 0x20},
+ {0x81, 0x40},
+ {0x82, 0x65},
+ {0x85, 0x1a},
+ {0x88, 0x00},
+ {0x89, 0x00},
+ {0x90, 0x5d},
+ {0xD0, 0x0c},
+ {0xD1, 0x80},
+ {0xD2, 0x17},
+ {0xD3, 0x00},
+ {0xD4, 0x00},
+ {0xD5, 0x0f},
+ {0xD6, 0xff},
+ {0xD7, 0xff},
+ {0x3b, 0x06},
+ {0x3c, 0x06},
+ {0xc5, 0x00},
+ {0xc6, 0x00},
+ {0x03, 0x13},
+ {0x10, 0xcb},
+ {0x11, 0x7b},
+ {0x12, 0x07},
+ {0x14, 0x00},
+ {0x20, 0x15},
+ {0x21, 0x13},
+ {0x22, 0x33},
+ {0x23, 0x05},
+ {0x24, 0x09},
+ {0x25, 0x0a},
+ {0x26, 0x18},
+ {0x27, 0x30},
+ {0x29, 0x12},
+ {0x2a, 0x50},
+ {0x2b, 0x02},
+ {0x2c, 0x02},
+ {0x25, 0x06},
+ {0x2d, 0x0c},
+ {0x2e, 0x12},
+ {0x2f, 0x12},
+ {0x50, 0x10},
+ {0x51, 0x14},
+ {0x52, 0x12},
+ {0x53, 0x0c},
+ {0x54, 0x0f},
+ {0x55, 0x0c},
+ {0x56, 0x10},
+ {0x57, 0x13},
+ {0x58, 0x12},
+ {0x59, 0x0c},
+ {0x5a, 0x0f},
+ {0x5b, 0x0c},
+ {0x5c, 0x25},
+ {0x5d, 0x25},
+ {0x5e, 0x25},
+ {0x5f, 0x25},
+ {0x60, 0x25},
+ {0x61, 0x25},
+ {0x62, 0x25},
+ {0x63, 0x25},
+ {0x64, 0x25},
+ {0x65, 0x25},
+ {0x66, 0x25},
+ {0x67, 0x25},
+ {0x68, 0x07},
+ {0x69, 0x07},
+ {0x6a, 0x07},
+ {0x6b, 0x05},
+ {0x6c, 0x05},
+ {0x6d, 0x05},
+ {0x6e, 0x07},
+ {0x6f, 0x07},
+ {0x70, 0x07},
+ {0x71, 0x05},
+ {0x72, 0x05},
+ {0x73, 0x05},
+ {0x80, 0x01},
+ {0x81, 0x1f},
+ {0x82, 0x05},
+ {0x83, 0x31},
+ {0x90, 0x05},
+ {0x91, 0x05},
+ {0x92, 0x33},
+ {0x93, 0x30},
+ {0x94, 0x03},
+ {0x95, 0x14},
+ {0x97, 0x20},
+ {0x99, 0x20},
+ {0xa0, 0x01},
+ {0xa1, 0x02},
+ {0xa2, 0x01},
+ {0xa3, 0x02},
+ {0xa4, 0x05},
+ {0xa5, 0x05},
+ {0xa6, 0x07},
+ {0xa7, 0x08},
+ {0xa8, 0x07},
+ {0xa9, 0x08},
+ {0xaa, 0x07},
+ {0xab, 0x08},
+ {0xb0, 0x22},
+ {0xb1, 0x2a},
+ {0xb2, 0x28},
+ {0xb3, 0x22},
+ {0xb4, 0x2a},
+ {0xb5, 0x28},
+ {0xb6, 0x22},
+ {0xb7, 0x2a},
+ {0xb8, 0x28},
+ {0xb9, 0x22},
+ {0xba, 0x2a},
+ {0xbb, 0x28},
+ {0xbc, 0x25},
+ {0xbd, 0x2a},
+ {0xbe, 0x27},
+ {0xbf, 0x25},
+ {0xc0, 0x2a},
+ {0xc1, 0x27},
+ {0xc2, 0x1e},
+ {0xc3, 0x24},
+ {0xc4, 0x20},
+ {0xc5, 0x1e},
+ {0xc6, 0x24},
+ {0xc7, 0x20},
+ {0xc8, 0x18},
+ {0xc9, 0x20},
+ {0xca, 0x1e},
+ {0xcb, 0x18},
+ {0xcc, 0x20},
+ {0xcd, 0x1e},
+ {0xce, 0x18},
+ {0xcf, 0x20},
+ {0xd0, 0x1e},
+ {0xd1, 0x18},
+ {0xd2, 0x20},
+ {0xd3, 0x1e},
+ {0x03, 0x14},
+ {0x10, 0x11},
+ {0x14, 0x80},
+ {0x15, 0x80},
+ {0x16, 0x80},
+ {0x17, 0x80},
+ {0x18, 0x80},
+ {0x19, 0x80},
+ {0x20, 0x80},
+ {0x21, 0x80},
+ {0x22, 0x80},
+ {0x23, 0x80},
+ {0x24, 0x80},
+ {0x30, 0xc8},
+ {0x31, 0x2b},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x90},
+ {0x40, 0x32},
+ {0x50, 0x21},
+ {0x60, 0x19},
+ {0x70, 0x21},
+ {0x03, 0x15},
+ {0x10, 0x0f},
+ {0x14, 0x46},
+ {0x15, 0x36},
+ {0x16, 0x26},
+ {0x17, 0x2f},
+ {0x30, 0x8f},
+ {0x31, 0x59},
+ {0x32, 0x0a},
+ {0x33, 0x15},
+ {0x34, 0x5b},
+ {0x35, 0x06},
+ {0x36, 0x07},
+ {0x37, 0x40},
+ {0x38, 0x87},
+ {0x40, 0x94},
+ {0x41, 0x20},
+ {0x42, 0x89},
+ {0x43, 0x84},
+ {0x44, 0x03},
+ {0x45, 0x01},
+ {0x46, 0x88},
+ {0x47, 0x9c},
+ {0x48, 0x28},
+ {0x50, 0x02},
+ {0x51, 0x82},
+ {0x52, 0x00},
+ {0x53, 0x07},
+ {0x54, 0x11},
+ {0x55, 0x98},
+ {0x56, 0x00},
+ {0x57, 0x0b},
+ {0x58, 0x8b},
+ {0x80, 0x03},
+ {0x85, 0x40},
+ {0x87, 0x02},
+ {0x88, 0x00},
+ {0x89, 0x00},
+ {0x8a, 0x00},
+ {0x03, 0x16},
+ {0x10, 0x31},
+ {0x18, 0x5e},
+ {0x19, 0x5d},
+ {0x1a, 0x0e},
+ {0x1b, 0x01},
+ {0x1c, 0xdc},
+ {0x1d, 0xfe},
+ {0x30, 0x00},
+ {0x31, 0x0a},
+ {0x32, 0x1f},
+ {0x33, 0x33},
+ {0x34, 0x53},
+ {0x35, 0x6c},
+ {0x36, 0x81},
+ {0x37, 0x94},
+ {0x38, 0xa4},
+ {0x39, 0xb3},
+ {0x3a, 0xc0},
+ {0x3b, 0xcb},
+ {0x3c, 0xd5},
+ {0x3d, 0xde},
+ {0x3e, 0xe6},
+ {0x3f, 0xee},
+ {0x40, 0xf5},
+ {0x41, 0xfc},
+ {0x42, 0xff},
+ {0x50, 0x00},
+ {0x51, 0x08},
+ {0x52, 0x1e},
+ {0x53, 0x36},
+ {0x54, 0x5a},
+ {0x55, 0x75},
+ {0x56, 0x8d},
+ {0x57, 0xa1},
+ {0x58, 0xb2},
+ {0x59, 0xbe},
+ {0x5a, 0xc9},
+ {0x5b, 0xd2},
+ {0x5c, 0xdb},
+ {0x5d, 0xe3},
+ {0x5e, 0xeb},
+ {0x5f, 0xf0},
+ {0x60, 0xf5},
+ {0x61, 0xf7},
+ {0x62, 0xf8},
+ {0x70, 0x00},
+ {0x71, 0x08},
+ {0x72, 0x17},
+ {0x73, 0x2f},
+ {0x74, 0x53},
+ {0x75, 0x6c},
+ {0x76, 0x81},
+ {0x77, 0x94},
+ {0x78, 0xa4},
+ {0x79, 0xb3},
+ {0x7a, 0xc0},
+ {0x7b, 0xcb},
+ {0x7c, 0xd5},
+ {0x7d, 0xde},
+ {0x7e, 0xe6},
+ {0x7f, 0xee},
+ {0x80, 0xf4},
+ {0x81, 0xfa},
+ {0x82, 0xff},
+ {0x03, 0x17},
+ {0x10, 0xf7},
+ {0xC4, 0x66},
+ {0xC5, 0x55},
+ {0x03, 0x20},
+ {0x11, 0x1c},
+ {0x18, 0x30},
+ {0x1a, 0x08},
+ {0x20, 0x05},
+ {0x21, 0x30},
+ {0x22, 0x10},
+ {0x23, 0x00},
+ {0x24, 0x00},
+ {0x28, 0xe7},
+ {0x29, 0x0d},
+ {0x2a, 0xf0},
+ {0x2b, 0x34},
+ {0x30, 0x78},
+ {0x2c, 0xc2},
+ {0x2d, 0xff},
+ {0x2e, 0x33},
+ {0x30, 0x78},
+ {0x32, 0x03},
+ {0x33, 0x2e},
+ {0x34, 0x30},
+ {0x35, 0xd4},
+ {0x36, 0xfe},
+ {0x37, 0x32},
+ {0x38, 0x04},
+ {0x39, 0x22},
+ {0x3a, 0xde},
+ {0x3b, 0x22},
+ {0x3c, 0xde},
+ {0x50, 0x45},
+ {0x51, 0x88},
+ {0x56, 0x03},
+ {0x57, 0xf7},
+ {0x58, 0x14},
+ {0x59, 0x88},
+ {0x5a, 0x04},
+ {0x60, 0xaa},
+ {0x61, 0xaa},
+ {0x62, 0xaa},
+ {0x63, 0xaa},
+ {0x64, 0xaa},
+ {0x65, 0xaa},
+ {0x66, 0xab},
+ {0x67, 0xEa},
+ {0x68, 0xab},
+ {0x69, 0xEa},
+ {0x6a, 0xaa},
+ {0x6b, 0xaa},
+ {0x6c, 0xaa},
+ {0x6d, 0xaa},
+ {0x6e, 0xaa},
+ {0x6f, 0xaa},
+ {0x70, 0x76},
+ {0x71, 0x80},
+ {0x76, 0x43},
+ {0x77, 0x04},
+ {0x78, 0x23},
+ {0x79, 0x46},
+ {0x7a, 0x23},
+ {0x7b, 0x22},
+ {0x7d, 0x23},
+ {0x83, 0x01},
+ {0x84, 0x5f},
+ {0x85, 0x90},
+ {0x86, 0x01},
+ {0x87, 0x2c},
+ {0x88, 0x05},
+ {0x89, 0x7e},
+ {0x8a, 0x40},
+ {0x8B, 0x75},
+ {0x8C, 0x30},
+ {0x8D, 0x61},
+ {0x8E, 0x44},
+ {0x9c, 0x08},
+ {0x9d, 0x34},
+ {0x9e, 0x01},
+ {0x9f, 0x2c},
+ {0xb0, 0x18},
+ {0xb1, 0x14},
+ {0xb2, 0x80},
+ {0xb3, 0x18},
+ {0xb4, 0x1a},
+ {0xb5, 0x44},
+ {0xb6, 0x2f},
+ {0xb7, 0x28},
+ {0xb8, 0x25},
+ {0xb9, 0x22},
+ {0xba, 0x21},
+ {0xbb, 0x20},
+ {0xbc, 0x32},
+ {0xbd, 0x30},
+ {0xc0, 0x10},
+ {0xc1, 0x2b},
+ {0xc2, 0x2b},
+ {0xc3, 0x2b},
+ {0xc4, 0x08},
+ {0xc8, 0x40},
+ {0xc9, 0x40},
+ {0x03, 0x22},
+ {0x10, 0xfd},
+ {0x11, 0x2e},
+ {0x19, 0x01},
+ {0x20, 0x10},
+ {0x21, 0x80},
+ {0x24, 0x01},
+ {0x30, 0x80},
+ {0x31, 0x80},
+ {0x38, 0x11},
+ {0x39, 0x34},
+ {0x40, 0xfa},
+ {0x41, 0x44},
+ {0x42, 0x43},
+ {0x43, 0xf6},
+ {0x44, 0x44},
+ {0x45, 0x33},
+ {0x46, 0x00},
+ {0x50, 0xb2},
+ {0x51, 0x81},
+ {0x52, 0x98},
+ {0x80, 0x38},
+ {0x81, 0x20},
+ {0x82, 0x38},
+ {0x83, 0x5e},
+ {0x84, 0x18},
+ {0x85, 0x58},
+ {0x86, 0x20},
+ {0x87, 0x49},
+ {0x88, 0x33},
+ {0x89, 0x37},
+ {0x8a, 0x2a},
+ {0x8b, 0x41},
+ {0x8c, 0x39},
+ {0x8d, 0x34},
+ {0x8e, 0x29},
+ {0x8f, 0x53},
+ {0x90, 0x52},
+ {0x91, 0x51},
+ {0x92, 0x4e},
+ {0x93, 0x46},
+ {0x94, 0x3d},
+ {0x95, 0x34},
+ {0x96, 0x2e},
+ {0x97, 0x29},
+ {0x98, 0x22},
+ {0x99, 0x1c},
+ {0x9a, 0x18},
+ {0x9b, 0x77},
+ {0x9c, 0x77},
+ {0x9d, 0x48},
+ {0x9e, 0x38},
+ {0x9f, 0x30},
+ {0xa0, 0x60},
+ {0xa1, 0x34},
+ {0xa2, 0x6f},
+ {0xa3, 0xff},
+ {0xa4, 0x14},
+ {0xa5, 0x2c},
+ {0xa6, 0xcf},
+ {0xad, 0x40},
+ {0xae, 0x4a},
+ {0xaf, 0x28},
+ {0xb0, 0x26},
+ {0xb1, 0x00},
+ {0xb4, 0xea},
+ {0xb8, 0xa0},
+ {0xb9, 0x00},
+ {0x03, 0x48},
+ {0x70, 0x03},
+ {0x71, 0x30},
+ {0x72, 0x81},
+ {0x73, 0x10},
+ {0x70, 0x85},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x70, 0x95},
+ {0x10, 0x1c},
+ {0x11, 0x10},
+ {0x12, 0x00},
+ {0x14, 0x00},
+ {0x16, 0x04},
+ {0x18, 0x80},
+ {0x19, 0x00},
+ {0x1a, 0xa0},
+ {0x1b, 0x0d},
+ {0x1c, 0x01},
+ {0x1d, 0x0a},
+ {0x1e, 0x07},
+ {0x1f, 0x0b},
+ {0x23, 0x01},
+ {0x24, 0x1e},
+ {0x25, 0x00},
+ {0x26, 0x00},
+ {0x27, 0x08},
+ {0x28, 0x00},
+ {0x30, 0x06},
+ {0x31, 0x40},
+ {0x32, 0x13},
+ {0x33, 0x0c},
+ {0x34, 0x04},
+ {0x35, 0x06},
+ {0x36, 0x01},
+ {0x37, 0x06},
+ {0x39, 0x4f},
+ {0x03, 0x20},
+ {0x10, 0x9c},
+ {0x03, 0x22},
+ {0x10, 0xe9},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x0e, 0x03},
+ {0x0e, 0x73},
+ {0x03, 0x00},
+ {0x01, 0xf0},
+};
+
+static struct v4l2_subdev_info hi256_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static struct msm_camera_i2c_reg_conf hi256_svga_settings[] = {
+ {0x03, 0x20},
+ {0x10, 0x1c},
+ {0x03, 0x22},
+ {0x10, 0x69},
+ {0x03, 0x00},
+ {0x10, 0x13},
+ {0x12, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x04},
+ {0x22, 0x00},
+ {0x23, 0x07},
+ {0x40, 0x01},
+ {0x41, 0x78},
+ {0x42, 0x00},
+ {0x43, 0x14},
+ {0x03, 0x10},
+ {0x3f, 0x02},
+ {0x03, 0x12},
+ {0x20, 0x0f},
+ {0x21, 0x0f},
+ {0x90, 0x5d},
+ {0x03, 0x13},
+ {0x80, 0x00},
+ {0x03, 0x48},
+ {0x72, 0x81},
+ {0x30, 0x06},
+ {0x31, 0x40},
+ {0x03, 0x20},
+ {0x88, 0x01},
+ {0x89, 0x5f},
+ {0x8a, 0x90},
+ {0x03, 0x20},
+ {0x10, 0x9c},
+ {0x03, 0x22},
+ {0x10, 0xe9},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_sleep_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf1},
+ {0x03, 0x02},
+ {0x55, 0x10},
+ {0x01, 0xf1},
+ {0x01, 0xf3},
+ {0x01, 0xf1},
+};
+
+
+static const struct i2c_device_id hi256_i2c_id[] = {
+ {HI256_SENSOR_NAME, (kernel_ulong_t)&hi256_s_ctrl},
+ { }
+};
+
+static int32_t msm_hi256_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &hi256_s_ctrl);
+}
+
+static struct i2c_driver hi256_i2c_driver = {
+ .id_table = hi256_i2c_id,
+ .probe = msm_hi256_i2c_probe,
+ .driver = {
+ .name = HI256_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client hi256_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+static const struct of_device_id hi256_dt_match[] = {
+ {.compatible = "shinetech,hi256", .data = &hi256_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, hi256_dt_match);
+
+static struct platform_driver hi256_platform_driver = {
+ .driver = {
+ .name = "shinetech,hi256",
+ .owner = THIS_MODULE,
+ .of_match_table = hi256_dt_match,
+ },
+};
+
+static void hi256_i2c_write_table(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_camera_i2c_reg_conf *table,
+ int num)
+{
+ int i = 0;
+ int rc = 0;
+ for (i = 0; i < num; ++i) {
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write(
+ s_ctrl->sensor_i2c_client, table->reg_addr,
+ table->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ msleep(100);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write(
+ s_ctrl->sensor_i2c_client, table->reg_addr,
+ table->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ }
+ table++;
+ }
+
+}
+
+static int32_t hi256_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ hi256_i2c_write_table(s_ctrl, &hi256_sleep_settings[0],
+ ARRAY_SIZE(hi256_sleep_settings));
+ return msm_sensor_power_down(s_ctrl);
+}
+
+static int32_t hi256_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc;
+ const struct of_device_id *match;
+ match = of_match_device(hi256_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init hi256_init_module(void)
+{
+ int32_t rc;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&hi256_platform_driver,
+ hi256_platform_probe);
+ if (!rc)
+ return rc;
+ return i2c_add_driver(&hi256_i2c_driver);
+}
+
+static void __exit hi256_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (hi256_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&hi256_s_ctrl);
+ platform_driver_unregister(&hi256_platform_driver);
+ } else
+ i2c_del_driver(&hi256_i2c_driver);
+ return;
+}
+
+static int32_t hi256_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ s_ctrl->sensordata->slave_info->sensor_id_reg_addr,
+ &chipid, MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: hi256 read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ CDBG("%s: read id: %x expected id %x:\n", __func__, chipid,
+ s_ctrl->sensordata->slave_info->sensor_id);
+ if (chipid != s_ctrl->sensordata->slave_info->sensor_id) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+ return rc;
+}
+
+int32_t hi256_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ long rc = 0;
+ int32_t i = 0;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__,
+ s_ctrl->sensordata->sensor_name, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_GET_SENSOR_INFO:
+ memcpy(cdata->cfg.sensor_info.sensor_name,
+ s_ctrl->sensordata->sensor_name,
+ sizeof(cdata->cfg.sensor_info.sensor_name));
+ cdata->cfg.sensor_info.session_id =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ cdata->cfg.sensor_info.subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.sensor_name);
+ CDBG("%s:%d session id %d\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.session_id);
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+ cdata->cfg.sensor_info.subdev_id[i]);
+ break;
+ case CFG_SET_INIT_SETTING:
+ CDBG("init setting");
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_recommend_settings[0],
+ ARRAY_SIZE(hi256_recommend_settings));
+ CDBG("init setting X");
+ break;
+ case CFG_SET_RESOLUTION: {
+ int val = 0;
+ if (copy_from_user(&val,
+ (void *)cdata->cfg.setting, sizeof(int))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ if (val == 0)
+ hi256_i2c_write_table(s_ctrl, &hi256_uxga_settings[0],
+ ARRAY_SIZE(hi256_uxga_settings));
+ else if (val == 1)
+ hi256_i2c_write_table(s_ctrl, &hi256_svga_settings[0],
+ ARRAY_SIZE(hi256_svga_settings));
+ break;
+ }
+ case CFG_SET_STOP_STREAM:
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_stop_settings[0],
+ ARRAY_SIZE(hi256_stop_settings));
+ break;
+ case CFG_SET_START_STREAM:
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_start_settings[0],
+ ARRAY_SIZE(hi256_start_settings));
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params =
+ *s_ctrl->sensordata->sensor_init_params;
+ CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+ __LINE__,
+ cdata->cfg.sensor_init_params.modes_supported,
+ cdata->cfg.sensor_init_params.position,
+ cdata->cfg.sensor_init_params.sensor_mount_angle);
+ break;
+ case CFG_SET_SLAVE_INFO: {
+ struct msm_camera_sensor_slave_info sensor_slave_info;
+ struct msm_sensor_power_setting_array *power_setting_array;
+ int slave_index = 0;
+ if (copy_from_user(&sensor_slave_info,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_sensor_slave_info))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Update sensor slave address */
+ if (sensor_slave_info.slave_addr) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ sensor_slave_info.slave_addr >> 1;
+ }
+
+ /* Update sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type =
+ sensor_slave_info.addr_type;
+
+ /* Update power up / down sequence */
+ s_ctrl->power_setting_array =
+ sensor_slave_info.power_setting_array;
+ power_setting_array = &s_ctrl->power_setting_array;
+ power_setting_array->power_setting = kzalloc(
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+ if (!power_setting_array->power_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(power_setting_array->power_setting,
+ (void *)
+ sensor_slave_info.power_setting_array.power_setting,
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting))) {
+ kfree(power_setting_array->power_setting);
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->free_power_setting = true;
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.slave_addr);
+ CDBG("%s sensor addr type %d\n", __func__,
+ sensor_slave_info.addr_type);
+ CDBG("%s sensor reg %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id);
+ for (slave_index = 0; slave_index <
+ power_setting_array->size; slave_index++) {
+ CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+ slave_index,
+ power_setting_array->power_setting[slave_index].
+ seq_type,
+ power_setting_array->power_setting[slave_index].
+ seq_val,
+ power_setting_array->power_setting[slave_index].
+ config_val,
+ power_setting_array->power_setting[slave_index].
+ delay);
+ }
+ kfree(power_setting_array->power_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_seq_reg_array)),
+ GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+
+ case CFG_POWER_UP:
+ if (s_ctrl->func_tbl->sensor_power_up)
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->func_tbl->sensor_power_down)
+ rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (copy_from_user(stop_setting, (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = stop_setting->reg_setting;
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void *)reg_setting, stop_setting->size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(stop_setting->reg_setting);
+ stop_setting->reg_setting = NULL;
+ stop_setting->size = 0;
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+
+static struct msm_sensor_fn_t hi256_sensor_func_tbl = {
+ .sensor_config = hi256_sensor_config,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = hi256_sensor_power_down,
+ .sensor_match_id = hi256_sensor_match_id,
+};
+
+static struct msm_sensor_ctrl_t hi256_s_ctrl = {
+ .sensor_i2c_client = &hi256_sensor_i2c_client,
+ .power_setting_array.power_setting = hi256_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(hi256_power_setting),
+ .msm_sensor_mutex = &hi256_mut,
+ .sensor_v4l2_subdev_info = hi256_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(hi256_subdev_info),
+ .func_tbl = &hi256_sensor_func_tbl,
+};
+
+module_init(hi256_init_module);
+module_exit(hi256_exit_module);
+MODULE_DESCRIPTION("Hi256 2MP YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
index 80b1ccb..3792247 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
@@ -64,7 +64,7 @@
}
int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte)
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
{
int32_t rc = -EFAULT;
unsigned char *buf = NULL;
@@ -137,7 +137,7 @@
}
int32_t msm_camera_cci_i2c_write_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte)
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
{
int32_t rc = -EFAULT;
uint8_t i = 0;
@@ -338,17 +338,11 @@
enum msm_camera_i2c_data_type data_type)
{
int32_t rc;
- int i;
S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n",
__func__, addr, data, data_type);
- for (i = 0; i < I2C_POLL_MAX_ITERATION; i++) {
- rc = msm_camera_cci_i2c_compare(client,
- addr, data, data_type);
- if (rc == 0 || rc < 0)
- break;
- usleep_range(10000, 11000);
- }
+ rc = msm_camera_cci_i2c_compare(client,
+ addr, data, data_type);
return rc;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
index 2511651..3898bd8 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
@@ -23,12 +23,13 @@
#define CDBG(fmt, args...) do { } while (0)
#endif
-int32_t msm_camera_get_dt_power_setting_data(struct device_node *of_node,
+int msm_camera_get_dt_power_setting_data(struct device_node *of_node,
+ struct camera_vreg_t *cam_vreg, int num_vreg,
struct msm_sensor_power_setting **power_setting,
uint16_t *power_setting_size)
{
- int32_t rc = 0, i = 0;
- int32_t count = 0;
+ int rc = 0, i, j;
+ int count = 0;
const char *seq_name = NULL;
uint32_t *array = NULL;
struct msm_sensor_power_setting *ps;
@@ -43,8 +44,7 @@
if (count <= 0)
return 0;
- ps = kzalloc(sizeof(struct msm_sensor_power_setting) * count,
- GFP_KERNEL);
+ ps = kzalloc(sizeof(*ps) * count, GFP_KERNEL);
if (!ps) {
pr_err("%s failed %d\n", __func__, __LINE__);
return -ENOMEM;
@@ -77,6 +77,10 @@
ps[i].seq_type = SENSOR_I2C_MUX;
CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__,
i, ps[i].seq_type);
+ } else {
+ CDBG("%s: unrecognized seq-type\n", __func__);
+ rc = -EILSEQ;
+ goto ERROR1;
}
}
@@ -91,24 +95,47 @@
pr_err("%s failed %d\n", __func__, __LINE__);
goto ERROR1;
}
- if (!strcmp(seq_name, "cam_vdig"))
- ps[i].seq_val = CAM_VDIG;
- else if (!strcmp(seq_name, "cam_vio"))
- ps[i].seq_val = CAM_VIO;
- else if (!strcmp(seq_name, "cam_vana"))
- ps[i].seq_val = CAM_VANA;
- else if (!strcmp(seq_name, "cam_vaf"))
- ps[i].seq_val = CAM_VAF;
- else if (!strcmp(seq_name, "sensor_gpio_reset"))
- ps[i].seq_val = SENSOR_GPIO_RESET;
- else if (!strcmp(seq_name, "sensor_gpio_standby"))
- ps[i].seq_val = SENSOR_GPIO_STANDBY;
- else if (!strcmp(seq_name, "sensor_cam_mclk"))
- ps[i].seq_val = SENSOR_CAM_MCLK;
- else if (!strcmp(seq_name, "sensor_cam_clk"))
- ps[i].seq_val = SENSOR_CAM_CLK;
- else if (!strcmp(seq_name, "none"))
- ps[i].seq_val = 0;
+ switch (ps[i].seq_type) {
+ case SENSOR_VREG:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(seq_name, cam_vreg[j].reg_name))
+ break;
+ }
+ if (j < num_vreg)
+ ps[i].seq_val = j;
+ else
+ rc = -EILSEQ;
+ break;
+ case SENSOR_GPIO:
+ if (!strcmp(seq_name, "sensor_gpio_reset"))
+ ps[i].seq_val = SENSOR_GPIO_RESET;
+ else if (!strcmp(seq_name, "sensor_gpio_standby"))
+ ps[i].seq_val = SENSOR_GPIO_STANDBY;
+ else
+ rc = -EILSEQ;
+ break;
+ case SENSOR_CLK:
+ if (!strcmp(seq_name, "sensor_cam_mclk"))
+ ps[i].seq_val = SENSOR_CAM_MCLK;
+ else if (!strcmp(seq_name, "sensor_cam_clk"))
+ ps[i].seq_val = SENSOR_CAM_CLK;
+ else
+ rc = -EILSEQ;
+ break;
+ case SENSOR_I2C_MUX:
+ if (!strcmp(seq_name, "none"))
+ ps[i].seq_val = 0;
+ else
+ rc = -EILSEQ;
+ break;
+ default:
+ rc = -EILSEQ;
+ break;
+ }
+ if (rc < 0) {
+ CDBG("%s: unrecognized seq-val\n", __func__);
+ goto ERROR1;
+ }
}
array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
@@ -151,6 +178,7 @@
}
kfree(array);
return rc;
+
ERROR2:
kfree(array);
ERROR1:
@@ -159,11 +187,11 @@
return rc;
}
-int32_t msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node,
+int msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node,
struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
uint16_t gpio_array_size)
{
- int32_t rc = 0, i = 0;
+ int rc = 0, i = 0;
uint32_t count = 0;
uint32_t *val_array = NULL;
@@ -247,8 +275,7 @@
struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
uint16_t gpio_array_size)
{
- int32_t rc = 0;
- int32_t val = 0;
+ int rc = 0, val = 0;
gconf->gpio_num_info = kzalloc(sizeof(struct msm_camera_gpio_num_info),
GFP_KERNEL);
@@ -299,10 +326,10 @@
return rc;
}
-int32_t msm_camera_get_dt_vreg_data(struct device_node *of_node,
+int msm_camera_get_dt_vreg_data(struct device_node *of_node,
struct camera_vreg_t **cam_vreg, int *num_vreg)
{
- int32_t rc = 0, i = 0;
+ int rc = 0, i = 0;
uint32_t count = 0;
uint32_t *vreg_array = NULL;
struct camera_vreg_t *vreg = NULL;
@@ -313,8 +340,7 @@
if (!count)
return 0;
- vreg = kzalloc(sizeof(struct camera_vreg_t) * count,
- GFP_KERNEL);
+ vreg = kzalloc(sizeof(*vreg) * count, GFP_KERNEL);
if (!vreg) {
pr_err("%s failed %d\n", __func__, __LINE__);
return -ENOMEM;
@@ -398,7 +424,7 @@
return rc;
}
-static int32_t msm_camera_enable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
+static int msm_camera_enable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
{
struct v4l2_subdev *i2c_mux_sd =
dev_get_drvdata(&i2c_conf->mux_dev->dev);
@@ -409,7 +435,7 @@
return 0;
}
-static int32_t msm_camera_disable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
+static int msm_camera_disable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
{
struct v4l2_subdev *i2c_mux_sd =
dev_get_drvdata(&i2c_conf->mux_dev->dev);
@@ -418,11 +444,11 @@
return 0;
}
-int32_t msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl,
+int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl,
enum msm_camera_device_type_t device_type,
struct msm_camera_i2c_client *sensor_i2c_client)
{
- int32_t rc = 0, index = 0, no_gpio = 0;
+ int rc = 0, index = 0, no_gpio = 0;
struct msm_sensor_power_setting *power_setting = NULL;
CDBG("%s:%d\n", __func__, __LINE__);
@@ -484,7 +510,7 @@
SENSOR_GPIO_MAX);
goto power_up_failed;
}
- pr_debug("%s:%d gpio set val %d\n", __func__, __LINE__,
+ CDBG("%s:%d gpio set val %d\n", __func__, __LINE__,
ctrl->gpio_conf->gpio_num_info->gpio_num
[power_setting->seq_val]);
gpio_set_value_cansleep(
@@ -584,11 +610,11 @@
return rc;
}
-int32_t msm_camera_power_down(struct msm_camera_power_ctrl_t *ctrl,
+int msm_camera_power_down(struct msm_camera_power_ctrl_t *ctrl,
enum msm_camera_device_type_t device_type,
struct msm_camera_i2c_client *sensor_i2c_client)
{
- int32_t index = 0;
+ int index = 0;
struct msm_sensor_power_setting *power_setting = NULL;
CDBG("%s:%d\n", __func__, __LINE__);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h
index 5a35747..fee2a4c 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h
@@ -19,6 +19,7 @@
#include "msm_camera_i2c.h"
int32_t msm_camera_get_dt_power_setting_data(struct device_node *of_node,
+ struct camera_vreg_t *cam_vreg, int num_vreg,
struct msm_sensor_power_setting **power_setting,
uint16_t *power_setting_size);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
index b1331ab..389e9d9 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
@@ -35,11 +35,11 @@
int (*i2c_read) (struct msm_camera_i2c_client *, uint32_t, uint16_t *,
enum msm_camera_i2c_data_type);
int32_t (*i2c_read_seq)(struct msm_camera_i2c_client *, uint32_t,
- uint8_t *, uint16_t);
+ uint8_t *, uint32_t);
int (*i2c_write) (struct msm_camera_i2c_client *, uint32_t, uint16_t,
enum msm_camera_i2c_data_type);
int (*i2c_write_seq) (struct msm_camera_i2c_client *, uint32_t ,
- uint8_t *, uint16_t);
+ uint8_t *, uint32_t);
int32_t (*i2c_write_table)(struct msm_camera_i2c_client *,
struct msm_camera_i2c_reg_setting *);
int32_t (*i2c_write_seq_table)(struct msm_camera_i2c_client *,
@@ -62,14 +62,14 @@
enum msm_camera_i2c_data_type data_type);
int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte);
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client,
uint32_t addr, uint16_t data,
enum msm_camera_i2c_data_type data_type);
int32_t msm_camera_cci_i2c_write_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte);
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
int32_t msm_camera_cci_i2c_write_table(
struct msm_camera_i2c_client *client,
@@ -101,14 +101,14 @@
enum msm_camera_i2c_data_type data_type);
int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte);
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
uint32_t addr, uint16_t data,
enum msm_camera_i2c_data_type data_type);
int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte);
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
int32_t msm_camera_qup_i2c_write_table(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_reg_setting *write_setting);
@@ -121,4 +121,9 @@
struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
enum msm_camera_i2c_data_type data_type);
+int32_t msm_camera_qup_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+
#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
index 9222bb5..60d1509 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
@@ -22,6 +22,10 @@
#define S_I2C_DBG(fmt, args...) do { } while (0)
#endif
+#define I2C_COMPARE_MATCH 0
+#define I2C_COMPARE_MISMATCH 1
+#define I2C_POLL_MAX_ITERATION 20
+
static int32_t msm_camera_qup_i2c_rxdata(
struct msm_camera_i2c_client *dev_client, unsigned char *rxdata,
int data_length)
@@ -103,7 +107,7 @@
}
int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte)
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
{
int32_t rc = -EFAULT;
unsigned char buf[client->addr_type+num_byte];
@@ -184,7 +188,7 @@
}
int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte)
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
{
int32_t rc = -EFAULT;
unsigned char buf[client->addr_type+num_byte];
@@ -330,3 +334,210 @@
}
return rc;
}
+
+static int32_t msm_camera_qup_i2c_compare(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc;
+ uint16_t reg_data = 0;
+ int data_len = 0;
+ switch (data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ data_len = data_type;
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ data_len = MSM_CAMERA_I2C_BYTE_DATA;
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ data_len = MSM_CAMERA_I2C_WORD_DATA;
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+ break;
+ }
+
+ rc = msm_camera_qup_i2c_read(client, addr, ®_data, data_len);
+ if (rc < 0)
+ return rc;
+
+ rc = I2C_COMPARE_MISMATCH;
+ switch (data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ if (data == reg_data)
+ rc = I2C_COMPARE_MATCH;
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ if ((reg_data & data) == data)
+ rc = I2C_COMPARE_MATCH;
+ break;
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ if (!(reg_data & data))
+ rc = I2C_COMPARE_MATCH;
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+ break;
+ }
+
+ S_I2C_DBG("%s: Register and data match result %d\n", __func__,
+ rc);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc;
+ int i;
+ S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n",
+ __func__, addr, data, data_type);
+
+ for (i = 0; i < I2C_POLL_MAX_ITERATION; i++) {
+ rc = msm_camera_qup_i2c_compare(client,
+ addr, data, data_type);
+ if (rc == 0 || rc < 0)
+ break;
+ usleep_range(10000, 11000);
+ }
+ return rc;
+}
+
+static int32_t msm_camera_qup_i2c_set_mask(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t mask,
+ enum msm_camera_i2c_data_type data_type, uint16_t set_mask)
+{
+ int32_t rc;
+ uint16_t reg_data;
+
+ rc = msm_camera_qup_i2c_read(client, addr, ®_data, data_type);
+ if (rc < 0) {
+ S_I2C_DBG("%s read fail\n", __func__);
+ return rc;
+ }
+ S_I2C_DBG("%s addr: 0x%x data: 0x%x setmask: 0x%x\n",
+ __func__, addr, reg_data, mask);
+
+ if (set_mask)
+ reg_data |= mask;
+ else
+ reg_data &= ~mask;
+ S_I2C_DBG("%s write: 0x%x\n", __func__, reg_data);
+
+ rc = msm_camera_qup_i2c_write(client, addr, reg_data, data_type);
+ if (rc < 0)
+ S_I2C_DBG("%s write fail\n", __func__);
+
+ return rc;
+}
+
+static int32_t msm_camera_qup_i2c_set_write_mask_data(
+ struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data, int16_t mask,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc;
+ uint16_t reg_data;
+ CDBG("%s\n", __func__);
+ if (mask == -1)
+ return 0;
+ if (mask == 0) {
+ rc = msm_camera_qup_i2c_write(client, addr, data, data_type);
+ } else {
+ rc = msm_camera_qup_i2c_read(client, addr, ®_data,
+ data_type);
+ if (rc < 0) {
+ CDBG("%s read fail\n", __func__);
+ return rc;
+ }
+ reg_data &= ~mask;
+ reg_data |= (data & mask);
+ rc = msm_camera_qup_i2c_write(client, addr, reg_data,
+ data_type);
+ if (rc < 0)
+ CDBG("%s write fail\n", __func__);
+ }
+ return rc;
+}
+
+
+int32_t msm_camera_qup_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ pr_err("%s, E. ", __func__);
+ for (i = 0; i < size; i++) {
+ enum msm_camera_i2c_data_type dt;
+ if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) {
+ rc = msm_camera_qup_i2c_poll(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ reg_conf_tbl->dt);
+ } else {
+ if (reg_conf_tbl->dt == 0)
+ dt = data_type;
+ else
+ dt = reg_conf_tbl->dt;
+ switch (dt) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ rc = msm_camera_qup_i2c_write(
+ client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data, dt);
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ rc = msm_camera_qup_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA, 1);
+ break;
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ rc = msm_camera_qup_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA, 0);
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ rc = msm_camera_qup_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_WORD_DATA, 1);
+ break;
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ rc = msm_camera_qup_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_WORD_DATA, 0);
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA:
+ rc = msm_camera_qup_i2c_set_write_mask_data(
+ client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ reg_conf_tbl->mask,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n",
+ __func__, dt);
+ break;
+ }
+ }
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.c
index d1d5f23..75b0063 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.c
@@ -67,50 +67,86 @@
}
-static int32_t msm_camera_spi_read_helper(struct msm_camera_i2c_client *client,
- struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
- uint16_t num_byte)
+/**
+ * msm_camera_spi_tx_helper() - wrapper for SPI transaction
+ * @client: io client
+ * @inst: inst of this transaction
+ * @addr: device addr following the inst
+ * @data: output byte array (could be NULL)
+ * @num_byte: size of @data
+ * @tx, rx: optional transfer buffer. It must be at least header
+ * + @num_byte long.
+ *
+ * This is the core function for SPI transaction, except for writes. It first
+ * checks address type, then allocates required memory for tx/rx buffers.
+ * It sends out <opcode><addr>, and optionally receives @num_byte of response,
+ * if @data is not NULL. This function does not check for wait conditions,
+ * and will return immediately once bus transaction finishes.
+ *
+ * This function will allocate buffers of header + @num_byte long. For
+ * large transfers, the allocation could fail. External buffer @tx, @rx
+ * should be passed in to bypass allocation. The size of buffer should be
+ * at least header + num_byte long. Since buffer is managed externally,
+ * @data will be ignored, and read results will be in @rx.
+ * @tx, @rx also can be used for repeated transfers to improve performance.
+ */
+int32_t msm_camera_spi_tx_helper(struct msm_camera_i2c_client *client,
+ struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
+ uint32_t num_byte, char *tx, char *rx)
{
- int32_t rc = -EFAULT;
+ int32_t rc = -EINVAL;
struct spi_device *spi = client->spi_client->spi_master;
- char *tx, *rx;
- uint16_t len;
- int8_t retries = client->spi_client->retries;
+ char *ctx = NULL, *crx = NULL;
+ uint32_t len, hlen;
+ uint8_t retries = client->spi_client->retries;
if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
return rc;
- len = sizeof(inst->opcode) + inst->addr_len + inst->dummy_len
- + num_byte;
+ hlen = msm_camera_spi_get_hlen(inst);
+ len = hlen + num_byte;
- tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
- if (!tx)
+ if (tx)
+ ctx = tx;
+ else
+ ctx = kzalloc(len, GFP_KERNEL);
+ if (!ctx)
return -ENOMEM;
- rx = kmalloc(len, GFP_KERNEL | GFP_DMA);
- if (!rx) {
- kfree(tx);
- return -ENOMEM;
+
+ if (num_byte) {
+ if (rx)
+ crx = rx;
+ else
+ crx = kzalloc(len, GFP_KERNEL);
+ if (!crx) {
+ if (!tx)
+ kfree(ctx);
+ return -ENOMEM;
+ }
+ } else {
+ crx = NULL;
}
- memset(tx, 0, len);
- memset(rx, 0, len);
- tx[0] = inst->opcode;
- msm_camera_set_addr(addr, inst->addr_len, client->addr_type, tx + 1);
- while ((rc = msm_camera_spi_txfr(spi, tx, rx, len)) && retries) {
+ ctx[0] = inst->opcode;
+ msm_camera_set_addr(addr, inst->addr_len, client->addr_type, ctx + 1);
+ while ((rc = msm_camera_spi_txfr(spi, ctx, crx, len)) && retries) {
retries--;
msleep(client->spi_client->retry_delay);
}
- if (rc) {
+ if (rc < 0) {
SPIDBG("%s: failed %d\n", __func__, rc);
goto out;
}
- len = sizeof(inst->opcode) + inst->addr_len + inst->dummy_len;
- memcpy(data, rx + len, num_byte);
+ if (data && num_byte && !rx)
+ memcpy(data, crx + hlen, num_byte);
+
out:
- kfree(tx);
- kfree(rx);
+ if (!tx)
+ kfree(ctx);
+ if (!rx)
+ kfree(crx);
return rc;
}
@@ -118,16 +154,17 @@
uint32_t addr, uint16_t *data,
enum msm_camera_i2c_data_type data_type)
{
- int32_t rc = -EFAULT;
+ int32_t rc = -EINVAL;
uint8_t temp[2];
if ((data_type != MSM_CAMERA_I2C_BYTE_DATA)
&& (data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
- rc = msm_camera_spi_read_helper(client,
- &client->spi_client->cmd_tbl.read, addr, &temp[0], data_type);
- if (rc)
+ rc = msm_camera_spi_tx_helper(client,
+ &client->spi_client->cmd_tbl.read, addr, &temp[0],
+ data_type, NULL, NULL);
+ if (rc < 0)
return rc;
if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
@@ -140,15 +177,38 @@
}
int32_t msm_camera_spi_read_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte)
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
{
- return msm_camera_spi_read_helper(client,
- &client->spi_client->cmd_tbl.read_seq, addr, data, num_byte);
+ return msm_camera_spi_tx_helper(client,
+ &client->spi_client->cmd_tbl.read_seq, addr, data, num_byte,
+ NULL, NULL);
+}
+
+/**
+ * msm_camera_spi_read_seq_l()- function for large SPI reads
+ * @client: io client
+ * @addr: device address to read
+ * @num_byte: read length
+ * @tx,rx: pre-allocated SPI buffer. Its size must be at least
+ * header + num_byte
+ *
+ * This function is used for large transactions. Instead of allocating SPI
+ * buffer each time, caller is responsible for pre-allocating memory buffers.
+ * Memory buffer must be at least header + num_byte. Header length can be
+ * obtained by msm_camera_spi_get_hlen().
+ */
+int32_t msm_camera_spi_read_seq_l(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint32_t num_byte, char *tx, char *rx)
+{
+ return msm_camera_spi_tx_helper(client,
+ &client->spi_client->cmd_tbl.read_seq, addr, NULL, num_byte,
+ tx, rx);
}
int32_t msm_camera_spi_query_id(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte)
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
{
- return msm_camera_spi_read_helper(client,
- &client->spi_client->cmd_tbl.query_id, addr, data, num_byte);
+ return msm_camera_spi_tx_helper(client,
+ &client->spi_client->cmd_tbl.query_id, addr, data, num_byte,
+ NULL, NULL);
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.h
index 564e470..0aefa50 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.h
@@ -17,6 +17,12 @@
#include <media/msm_cam_sensor.h>
#include "msm_camera_i2c.h"
+/**
+ * Common SPI communication scheme
+ * tx: <opcode>[addr][wait][write buffer]
+ * rx: [read buffer]
+ * Some inst require polling busy reg until it's done
+ */
struct msm_camera_spi_inst {
uint8_t opcode; /* one-byte opcode */
uint8_t addr_len; /* addr len in bytes */
@@ -38,14 +44,23 @@
uint8_t retries; /* retry times upon failure */
};
+static __always_inline
+uint16_t msm_camera_spi_get_hlen(struct msm_camera_spi_inst *inst)
+{
+ return sizeof(inst->opcode) + inst->addr_len + inst->dummy_len;
+}
+
int32_t msm_camera_spi_read(struct msm_camera_i2c_client *client,
uint32_t addr, uint16_t *data,
enum msm_camera_i2c_data_type data_type);
int32_t msm_camera_spi_read_seq(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte);
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_spi_read_seq_l(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint32_t num_byte, char *tx, char *rx);
int32_t msm_camera_spi_query_id(struct msm_camera_i2c_client *client,
- uint32_t addr, uint8_t *data, uint16_t num_byte);
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index c834925..adbfbe7 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -569,7 +569,92 @@
CDBG("%s qcom,gpio-reset %d\n", __func__,
gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY]);
}
- return rc;
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vio", &val);
+ if (!rc) {
+ if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vio invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VIO] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-vio %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VIO]);
+ } else if (rc != -EINVAL) {
+ pr_err("%s:%d read qcom,gpio-vio failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vana", &val);
+ if (!rc) {
+ if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vana invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VANA] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-vana %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VANA]);
+ } else if (rc != -EINVAL) {
+ pr_err("%s:%d read qcom,gpio-vana failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vdig", &val);
+ if (!rc) {
+ if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vdig invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VDIG] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-vdig %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VDIG]);
+ } else if (rc != -EINVAL) {
+ pr_err("%s:%d read qcom,gpio-vdig failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vaf", &val);
+ if (!rc) {
+ if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vaf invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VAF] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-vaf %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VAF]);
+ } else if (rc != -EINVAL) {
+ pr_err("%s:%d read qcom,gpio-vaf failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-af-pwdm", &val);
+ if (!rc) {
+ if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-af-pwdm invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_AF_PWDM] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-af-pwdm %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_AF_PWDM]);
+ } else if (rc != -EINVAL) {
+ pr_err("%s:%d read qcom,gpio-af-pwdm failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ return 0;
ERROR:
kfree(gconf->gpio_num_info);
@@ -1162,6 +1247,15 @@
return;
}
+static int msm_sensor_get_af_status(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ /* TO-DO: Need to set AF status register address and expected value
+ We need to check the AF status in the sensor register and
+ set the status in the *status variable accordingly*/
+ return 0;
+}
+
static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
@@ -1174,10 +1268,13 @@
switch (cmd) {
case VIDIOC_MSM_SENSOR_CFG:
return s_ctrl->func_tbl->sensor_config(s_ctrl, argp);
+ case VIDIOC_MSM_SENSOR_GET_AF_STATUS:
+ return msm_sensor_get_af_status(s_ctrl, argp);
case VIDIOC_MSM_SENSOR_RELEASE:
- case MSM_SD_SHUTDOWN:
msm_sensor_stop_stream(s_ctrl);
return 0;
+ case MSM_SD_SHUTDOWN:
+ return 0;
default:
return -ENOIOCTLCMD;
}
@@ -1660,6 +1757,7 @@
.i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table,
.i2c_write_table_w_microdelay =
msm_camera_qup_i2c_write_table_w_microdelay,
+ .i2c_write_conf_tbl = msm_camera_qup_i2c_write_conf_tbl,
};
int32_t msm_sensor_platform_probe(struct platform_device *pdev, void *data)
diff --git a/drivers/media/platform/msm/camera_v2/sensor/mt9m114.c b/drivers/media/platform/msm/camera_v2/sensor/mt9m114.c
index c1cf862..de4fcd0 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/mt9m114.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/mt9m114.c
@@ -1424,8 +1424,51 @@
break;
}
break;
- }
- default:
+ }
+ case CFG_SET_SATURATION: {
+ int32_t sat_lev;
+ if (copy_from_user(&sat_lev, (void *)cdata->cfg.setting,
+ sizeof(int32_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("%s: Saturation Value is %d", __func__, sat_lev);
+ break;
+ }
+ case CFG_SET_CONTRAST: {
+ int32_t con_lev;
+ if (copy_from_user(&con_lev, (void *)cdata->cfg.setting,
+ sizeof(int32_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("%s: Contrast Value is %d", __func__, con_lev);
+ break;
+ }
+ case CFG_SET_SHARPNESS: {
+ int32_t shp_lev;
+ if (copy_from_user(&shp_lev, (void *)cdata->cfg.setting,
+ sizeof(int32_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("%s: Sharpness Value is %d", __func__, shp_lev);
+ break;
+ }
+ case CFG_SET_AUTOFOCUS: {
+ /* TO-DO: set the Auto Focus */
+ pr_debug("%s: Setting Auto Focus", __func__);
+ break;
+ }
+ case CFG_CANCEL_AUTOFOCUS: {
+ /* TO-DO: Cancel the Auto Focus */
+ pr_debug("%s: Cancelling Auto Focus", __func__);
+ break;
+ }
+ default:
rc = -EFAULT;
break;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/s5k4e1.c b/drivers/media/platform/msm/camera_v2/sensor/s5k4e1.c
new file mode 100644
index 0000000..5c70df2
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/s5k4e1.c
@@ -0,0 +1,167 @@
+/* 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 s5k4e1_SENSOR_NAME "s5k4e1"
+DEFINE_MSM_MUTEX(s5k4e1_mut);
+
+static struct msm_sensor_ctrl_t s5k4e1_s_ctrl;
+
+static struct msm_sensor_power_setting s5k4e1_power_setting[] = {
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .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_VDIG,
+ .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_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 24000000,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_I2C_MUX,
+ .seq_val = 0,
+ .config_val = 0,
+ .delay = 1,
+ },
+};
+
+static struct v4l2_subdev_info s5k4e1_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static const struct i2c_device_id s5k4e1_i2c_id[] = {
+ {s5k4e1_SENSOR_NAME, (kernel_ulong_t)&s5k4e1_s_ctrl},
+ { }
+};
+
+static int32_t msm_s5k4e1_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &s5k4e1_s_ctrl);
+}
+
+static struct i2c_driver s5k4e1_i2c_driver = {
+ .id_table = s5k4e1_i2c_id,
+ .probe = msm_s5k4e1_i2c_probe,
+ .driver = {
+ .name = s5k4e1_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client s5k4e1_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static const struct of_device_id s5k4e1_dt_match[] = {
+ {.compatible = "shinetech,s5k4e1", .data = &s5k4e1_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, s5k4e1_dt_match);
+
+static struct platform_driver s5k4e1_platform_driver = {
+ .driver = {
+ .name = "shinetech,s5k4e1",
+ .owner = THIS_MODULE,
+ .of_match_table = s5k4e1_dt_match,
+ },
+};
+
+static int32_t s5k4e1_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ const struct of_device_id *match;
+ match = of_match_device(s5k4e1_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init s5k4e1_init_module(void)
+{
+ int32_t rc = 0;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&s5k4e1_platform_driver,
+ s5k4e1_platform_probe);
+ if (!rc)
+ return rc;
+ pr_err("%s:%d rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&s5k4e1_i2c_driver);
+}
+
+static void __exit s5k4e1_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (s5k4e1_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&s5k4e1_s_ctrl);
+ platform_driver_unregister(&s5k4e1_platform_driver);
+ } else
+ i2c_del_driver(&s5k4e1_i2c_driver);
+ return;
+}
+
+static struct msm_sensor_ctrl_t s5k4e1_s_ctrl = {
+ .sensor_i2c_client = &s5k4e1_sensor_i2c_client,
+ .power_setting_array.power_setting = s5k4e1_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(s5k4e1_power_setting),
+ .msm_sensor_mutex = &s5k4e1_mut,
+ .sensor_v4l2_subdev_info = s5k4e1_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(s5k4e1_subdev_info),
+};
+
+module_init(s5k4e1_init_module);
+module_exit(s5k4e1_exit_module);
+MODULE_DESCRIPTION("s5k4e1");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/sp1628.c b/drivers/media/platform/msm/camera_v2/sensor/sp1628.c
new file mode 100644
index 0000000..82e4b7c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/sp1628.c
@@ -0,0 +1,946 @@
+/* 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
+ * 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"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+#define SP1628_SENSOR_NAME "sp1628"
+DEFINE_MSM_MUTEX(sp1628_mut);
+
+static struct msm_sensor_ctrl_t sp1628_s_ctrl;
+
+static struct msm_sensor_power_setting sp1628_power_setting[] = {
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VANA,
+ .config_val = 0,
+ .delay = 5,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 50,
+ },
+
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 50,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 50,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 50,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 50,
+ },
+ {
+ .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 = 5,
+ },
+};
+
+static struct msm_camera_i2c_reg_conf sp1628_start_settings[] = {
+ {0x92, 0x81},
+};
+
+static struct msm_camera_i2c_reg_conf sp1628_stop_settings[] = {
+ {0x92, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf sp1628_recommend_settings[] = {
+ {0xfd, 0x00,},
+ {0x91, 0x00,},
+ {0x92, 0x81,},
+ {0x98, 0x2a,},
+ {0x96, 0xd0,}, /* c0*/
+ {0x97, 0x02,}, /* 03*/
+ {0x2f, 0x20,}, /* 24M*3=72M*/
+ {0x0b, 0x48,}, /* analog*/
+ {0x30, 0x80,}, /* 00*/
+ {0x0c, 0x66,}, /* analog*/
+ {0x0d, 0x12,},
+ {0x13, 0x0f,}, /* 10*/
+ {0x14, 0x00,},
+ {0x12, 0x00,},
+ {0x6b, 0x10,}, /* 11*/
+ {0x6c, 0x00,},
+ {0x6d, 0x00,},
+ {0x6e, 0x00,},
+ {0x6f, 0x10,}, /* 11*/
+ {0x73, 0x11,}, /* 12*/
+ {0x7a, 0x10,}, /* 11*/
+ {0x15, 0x17,}, /* 18*/
+ {0x71, 0x18,}, /* 19*/
+ {0x76, 0x18,}, /* 19*/
+ {0x29, 0x08,},
+ {0x18, 0x01,},
+ {0x19, 0x10,},
+ {0x1a, 0xc3,}, /* c1*/
+ {0x1b, 0x6f,},
+ {0x1d, 0x11,}, /* 01*/
+ {0x1e, 0x00,}, /* 1e*/
+ {0x1f, 0x80,},
+ {0x20, 0x7f,},
+ {0x22, 0x3c,}, /* 1b*/
+ {0x25, 0xff,},
+ {0x2b, 0x88,},
+ {0x2c, 0x85,},
+ {0x2d, 0x00,},
+ {0x2e, 0x80,},
+ {0x27, 0x38,},
+ {0x28, 0x03,},
+ {0x70, 0x1a,},
+ {0x72, 0x18,}, /* 1a*/
+ {0x74, 0x18,},
+ {0x75, 0x18,},
+ {0x77, 0x16,}, /* 18*/
+ {0x7f, 0x19,},
+ {0x31, 0x71,}, /*70 mirror/flip 720P*/
+ {0xfd, 0x01,},
+ {0x5d, 0x11,}, /* position*/
+ {0x5f, 0x00,},
+ {0x36, 0x08,},
+ {0x2f, 0xff,},
+ {0xfb, 0x25,}, /* blacklevl*/
+ {0x48, 0x00,}, /* dp*/
+ {0x49, 0x99,},
+ {0xf2, 0x0A,},
+ {0xfd, 0x02,}, /* AE*/
+ {0x52, 0x34,},
+ {0x53, 0x02,},
+ {0x54, 0x0c,},
+ {0x55, 0x08,},
+ {0x86, 0x0c,},
+ {0x87, 0x10,},
+ {0x8b, 0x10,},
+
+ /* 12-30 50Hz*/
+ {0xfd, 0x00,}, /* ae setting*/
+ {0x03, 0x05,},
+ {0x04, 0x64,},
+ {0x05, 0x00,},
+ {0x06, 0x00,},
+ {0x09, 0x00,},
+ {0x0a, 0x02,},
+ {0xfd, 0x01,},
+ {0xf0, 0x00,},
+ {0xf7, 0xe6,},
+ {0xf8, 0xc1,},
+ {0x02, 0x08,},
+ {0x03, 0x01,},
+ {0x06, 0xe6,},
+ {0x07, 0x00,},
+ {0x08, 0x01,},
+ {0x09, 0x00,},
+ {0xfd, 0x02,},
+ {0x40, 0x0a,},
+ {0x41, 0xc1,},
+ {0x42, 0x00,},
+ {0x88, 0x37,},
+ {0x89, 0xa7,},
+ {0x8a, 0x22,},
+ {0xfd, 0x02,}, /* Status*/
+ {0xbe, 0x30,},
+ {0xbf, 0x07,},
+ {0xd0, 0x30,},
+ {0xd1, 0x07,},
+ {0xfd, 0x01,},
+ {0x5b, 0x07,},
+ {0x5c, 0x30,},
+ {0xfd, 0x00,},
+
+ /* 12-30 60Hz*/
+ {0xfd, 0x00,}, /* ae setting*/
+ {0x03, 0x04,},
+ {0x04, 0x80,},
+ {0x05, 0x00,},
+ {0x06, 0x00,},
+ {0x09, 0x00,},
+ {0x0a, 0x01,},
+ {0xfd, 0x01,},
+ {0xf0, 0x00,},
+ {0xf7, 0xc0,},
+ {0xf8, 0xc1,},
+ {0x02, 0x0a,},
+ {0x03, 0x01,},
+ {0x06, 0xc0,},
+ {0x07, 0x00,},
+ {0x08, 0x01,},
+ {0x09, 0x00,},
+ {0xfd, 0x02,},
+ {0x40, 0x0a,},
+ {0x41, 0xc1,},
+ {0x42, 0x00,},
+ {0x88, 0x37,},
+ {0x89, 0xa7,},
+ {0x8a, 0x22,},
+ {0xfd, 0x02,}, /* Status*/
+ {0xbe, 0x80,},
+ {0xbf, 0x07,},
+ {0xd0, 0x80,},
+ {0xd1, 0x07,},
+ {0xfd, 0x01,},
+ {0x5b, 0x07,},
+ {0x5c, 0x80,},
+ {0xfd, 0x00,},
+
+ {0xfd, 0x01,}, /* fix status*/
+ {0x5a, 0x38,}, /* DP_gain*/
+ {0xfd, 0x02,},
+ {0xba, 0x30,}, /* mean_dummy_low*/
+ {0xbb, 0x50,}, /* mean_low_dummy*/
+ {0xbc, 0xc0,}, /* rpc_heq_low*/
+ {0xbd, 0xa0,}, /* rpc_heq_dummy*/
+ {0xb8, 0x80,}, /* mean_nr_dummy*/
+ {0xb9, 0x90,}, /* mean_dummy_nr*/
+ {0xfd, 0x01,}, /* rpc*/
+ {0xe0, 0x54,},
+ {0xe1, 0x40,},
+ {0xe2, 0x38,},
+ {0xe3, 0x34,},
+ {0xe4, 0x34,},
+ {0xe5, 0x30,},
+ {0xe6, 0x30,},
+ {0xe7, 0x2e,},
+ {0xe8, 0x2e,},
+ {0xe9, 0x2e,},
+ {0xea, 0x2c,},
+ {0xf3, 0x2c,},
+ {0xf4, 0x2c,},
+ {0xfd, 0x01,}, /* min gain*/
+ {0x04, 0xc0,}, /* rpc_max_indr*/
+ {0x05, 0x2c,}, /* rpc_min_indr*/
+ {0x0a, 0xc0,}, /* rpc_max_outdr*/
+ {0x0b, 0x2c,}, /* rpc_min_outdr*/
+ {0xfd, 0x01,}, /* ae target*/
+ {0xeb, 0x78,},
+ {0xec, 0x78,},
+ {0xed, 0x05,},
+ {0xee, 0x0a,},
+ {0xfd, 0x01,}, /* lsc*/
+ {0x26, 0x30,},
+ {0x27, 0xdc,},
+ {0x28, 0x05,},
+ {0x29, 0x08,},
+ {0x2a, 0x00,},
+ {0x2b, 0x03,},
+ {0x2c, 0x00,},
+ {0x2d, 0x2f,},
+ {0xfd, 0x01,}, /* RGainf*/
+ {0xa1, 0x37,}, /* left*/
+ {0xa2, 0x26,}, /* right*/
+ {0xa3, 0x32,}, /* up*/
+ {0xa4, 0x2b,}, /* down*/
+ {0xad, 0x0f,}, /* lu*/
+ {0xae, 0x0a,}, /* ru*/
+ {0xaf, 0x0a,}, /* ld*/
+ {0xb0, 0x0a,}, /* rd*/
+ {0x18, 0x2f,}, /* left*/
+ {0x19, 0x30,}, /* right*/
+ {0x1a, 0x32,}, /* up*/
+ {0x1b, 0x30,}, /* down*/
+ {0xbf, 0xa5,}, /* lu*/
+ {0xc0, 0x12,}, /* ru*/
+ {0xc1, 0x08,}, /* ld*/
+ {0xfa, 0x00,}, /* rd*/
+ {0xa5, 0x35,}, /* GGain*/
+ {0xa6, 0x24,},
+ {0xa7, 0x2e,},
+ {0xa8, 0x25,},
+ {0xb1, 0x00,},
+ {0xb2, 0x04,},
+ {0xb3, 0x00,},
+ {0xb4, 0x00,},
+ {0x1c, 0x24,},
+ {0x1d, 0x23,},
+ {0x1e, 0x2c,},
+ {0xb9, 0x25,},
+ {0x21, 0xa0,},
+ {0x22, 0x13,},
+ {0x23, 0x1c,},
+ {0x24, 0x0d,},
+ {0xa9, 0x2f,}, /* BGain*/
+ {0xaa, 0x24,},
+ {0xab, 0x2d,},
+ {0xac, 0x24,},
+ {0xb5, 0x00,},
+ {0xb6, 0x00,},
+ {0xb7, 0x00,},
+ {0xb8, 0x00,},
+ {0xba, 0x22,},
+ {0xbc, 0x24,},
+ {0xbd, 0x31,},
+ {0xbe, 0x24,},
+ {0x25, 0xa0,},
+ {0x45, 0x08,},
+ {0x46, 0x12,},
+ {0x47, 0x09,},
+ {0xfd, 0x01,}, /* awb*/
+ {0x32, 0x15,},
+ {0xfd, 0x02,},
+ {0x26, 0xc9,},
+ {0x27, 0x8b,},
+ {0x1b, 0x80,},
+ {0x1a, 0x80,},
+ {0x18, 0x27,},
+ {0x19, 0x26,},
+ {0x2a, 0x01,},
+ {0x2b, 0x10,},
+ {0x28, 0xf8,},
+ {0x29, 0x08,},
+
+ /* d65*/
+ {0x66, 0x35,},
+ {0x67, 0x60,},
+ {0x68, 0xb0,},
+ {0x69, 0xe0,},
+ {0x6a, 0xa5,},
+
+ /* indoor*/
+ {0x7c, 0x38,},
+ {0x7d, 0x58,},
+ {0x7e, 0xdb,},
+ {0x7f, 0x13,},
+ {0x80, 0xa6,},
+
+ /* cwftl84*/
+ {0x70, 0x18,}, /* 2f*/
+ {0x71, 0x4a,},
+ {0x72, 0x08,},
+ {0x73, 0x32,}, /* 24*/
+ {0x74, 0xaa,},
+
+ /* tl84--F*/
+ {0x6b, 0x02,}, /* 18*/
+ {0x6c, 0x2a,}, /* 34*/
+ {0x6d, 0x1e,}, /* 17*/
+ {0x6e, 0x49,}, /* 32*/
+ {0x6f, 0xaa,},
+
+ /* f--H*/
+ {0x61, 0xea,}, /* 02*/
+ {0x62, 0xf8,}, /* 2a*/
+ {0x63, 0x4f,}, /* 1e*/
+ {0x64, 0x5f,}, /* 49*/
+ {0x65, 0x5a,}, /* aa*/
+
+ {0x75, 0x80,},
+ {0x76, 0x09,},
+ {0x77, 0x02,},
+ {0x24, 0x25,},
+ {0x0e, 0x16,},
+ {0x3b, 0x09,},
+ {0xfd, 0x02,}, /* sharp*/
+ {0xde, 0x0f,},
+ {0xd2, 0x0c,}, /* control black-white edge; 0 - bolder, f - thinner*/
+ {0xd3, 0x0a,},
+ {0xd4, 0x08,},
+ {0xd5, 0x08,},
+ {0xd7, 0x10,}, /* outline judgement*/
+ {0xd8, 0x1d,},
+ {0xd9, 0x32,},
+ {0xda, 0x48,},
+ {0xdb, 0x08,},
+ {0xe8, 0x38,}, /* outline strength*/
+ {0xe9, 0x38,},
+ {0xea, 0x38,}, /* 30*/
+ {0xeb, 0x38,}, /* 2*/
+ {0xec, 0x60,},
+ {0xed, 0x40,},
+ {0xee, 0x38,}, /* 30*/
+ {0xef, 0x38,}, /* 20*/
+ {0xf3, 0x00,}, /* sharpness level of flat area*/
+ {0xf4, 0x00,},
+ {0xf5, 0x00,},
+ {0xf6, 0x00,},
+ {0xfd, 0x02,}, /* skin sharpen*/
+ {0xdc, 0x04,}, /* skin de-sharpen*/
+ {0x05, 0x6f,},
+ {0x09, 0x10,},
+ {0xfd, 0x01,}, /* dns*/
+ {0x64, 0x22,}, /* 0 - max, 8 - min*/
+ {0x65, 0x22,},
+ {0x86, 0x20,}, /* threshold, 0 - min*/
+ {0x87, 0x20,},
+ {0x88, 0x20,},
+ {0x89, 0x20,},
+ {0x6d, 0x0f,},
+ {0x6e, 0x0f,},
+ {0x6f, 0x10,},
+ {0x70, 0x10,},
+ {0x71, 0x0d,},
+ {0x72, 0x23,},
+ {0x73, 0x23,}, /* 28*/
+ {0x74, 0x23,}, /* 2a*/
+ {0x75, 0x46,}, /* [7:4] strength of flat area,
+ [3:0]strength of un-flat area;
+ 0-max, 8-min*/
+ {0x76, 0x36,},
+ {0x77, 0x36,}, /* 25*/
+ {0x78, 0x36,}, /* 12*/
+ {0x81, 0x1d,}, /* 2x*/
+ {0x82, 0x2b,}, /* 4x*/
+ {0x83, 0x2b,}, /* 50; 8x*/
+ {0x84, 0x2b,}, /* 80; 16x*/
+ {0x85, 0x0a,}, /* 12/8reg0x81*/
+ {0xfd, 0x01,}, /* gamma*/
+ {0x8b, 0x00,}, /* 00; 00; 00;*/
+ {0x8c, 0x0d,}, /* 02; 0b; 0b;*/
+ {0x8d, 0x1f,}, /* 0a; 19; 17;*/
+ {0x8e, 0x2d,}, /* 13; 2a; 27;*/
+ {0x8f, 0x3a,}, /* 1d; 37; 35;*/
+ {0x90, 0x4b,}, /* 30; 4b; 51;*/
+ {0x91, 0x59,}, /* 40; 5e; 64;*/
+ {0x92, 0x64,}, /* 4e; 6c; 74;*/
+ {0x93, 0x70,}, /* 5a; 78; 80;*/
+ {0x94, 0x83,}, /* 71; 92; 92;*/
+ {0x95, 0x92,}, /* 85; a6; a2;*/
+ {0x96, 0xa1,}, /* 96; b5; af;*/
+ {0x97, 0xae,}, /* a6; bf; bb;*/
+ {0x98, 0xba,}, /* b3; ca; c6;*/
+ {0x99, 0xc4,}, /* c0; d2; d0;*/
+ {0x9a, 0xcf,}, /* cb; d9; d9;*/
+ {0x9b, 0xdb,}, /* d5; e1; e0;*/
+ {0x9c, 0xe5,}, /* df; e8; e8;*/
+ {0x9d, 0xec,}, /* e9; ee; ee;*/
+ {0x9e, 0xf3,}, /* f2; f4; f4;*/
+ {0x9f, 0xfa,}, /* fa; fa; fa;*/
+ {0xa0, 0xff,}, /* ff; ff; ff;*/
+ {0xfd, 0x02,}, /* CCM*/
+ {0x15, 0xc8,}, /* b>th ab*/
+ {0x16, 0x95,}, /* r<th 87*/
+
+ {0xa0, 0x8c,}, /* !F*/
+ {0xa1, 0xfa,},
+ {0xa2, 0xfa,},
+ {0xa3, 0xf4,},
+ {0xa4, 0x99,},
+ {0xa5, 0xf4,},
+ {0xa6, 0x00,},
+ {0xa7, 0xb4,},
+ {0xa8, 0xcc,},
+ {0xa9, 0x3c,},
+ {0xaa, 0x33,},
+ {0xab, 0x0c,},
+
+ {0xac, 0x80,}, /* F*/
+ {0xad, 0x00,},
+ {0xae, 0x00,},
+ {0xaf, 0xe7,},
+ {0xb0, 0xc0,},
+ {0xb1, 0xda,},
+ {0xb2, 0xe7,},
+ {0xb3, 0xb4,},
+ {0xb4, 0xe6,},
+ {0xb5, 0x00,},
+ {0xb6, 0x33,},
+ {0xb7, 0x0f,},
+ {0xfd, 0x01,}, /* sat u*/
+ {0xd3, 0x8a,}, /* 90 105%*/
+ {0xd4, 0x8a,}, /* 90*/
+ {0xd5, 0x88,},
+ {0xd6, 0x80,},
+ {0xd7, 0x8a,}, /* 90; sat v*/
+ {0xd8, 0x8a,}, /* 90*/
+ {0xd9, 0x88,},
+ {0xda, 0x80,},
+ {0xfd, 0x01,}, /* auto_sat*/
+ {0xd2, 0x00,}, /* autosa_en*/
+ {0xfd, 0x01,}, /* uv_th*/
+ {0xc2, 0xee,},
+ {0xc3, 0xee,},
+ {0xc4, 0xdd,},
+ {0xc5, 0xbb,},
+ {0xfd, 0x01,}, /* low_lum_offset*/
+ {0xcd, 0x10,},
+ {0xce, 0x1f,},
+ {0xfd, 0x02,}, /* gw*/
+ {0x35, 0x6f,},
+ {0x37, 0x13,},
+ {0xfd, 0x01,}, /* heq*/
+ {0xdb, 0x00,},
+ {0x10, 0x00,},
+ {0x14, 0x25,},
+ {0x11, 0x10,},
+ {0x15, 0x25,},
+ {0x16, 0x15,},
+ {0xfd, 0x02,}, /* cnr*/
+ {0x8e, 0x10,},
+ {0x90, 0x20,},
+ {0x91, 0x20,},
+ {0x92, 0x60,},
+ {0x93, 0x80,},
+ {0xfd, 0x02,}, /* auto*/
+ {0x85, 0x00,},
+ {0xfd, 0x01,},
+ {0x00, 0x00,}, /* fix mode*/
+ {0x32, 0x15,}, /* ae en*/
+ {0x33, 0xef,}, /* lsc\bpc en*/
+ {0x34, 0xc7,}, /* ynr\cnr\gamma\color en*/
+ {0x35, 0x40,}, /* YUYV*/
+ {0xfd, 0x00,},
+
+};
+
+static struct v4l2_subdev_info sp1628_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static const struct i2c_device_id sp1628_i2c_id[] = {
+ {SP1628_SENSOR_NAME, (kernel_ulong_t)&sp1628_s_ctrl},
+ { }
+};
+
+static int32_t msm_sp1628_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ CDBG("%s, E.", __func__);
+
+ return msm_sensor_i2c_probe(client, id, &sp1628_s_ctrl);
+}
+
+static struct i2c_driver sp1628_i2c_driver = {
+ .id_table = sp1628_i2c_id,
+ .probe = msm_sp1628_i2c_probe,
+ .driver = {
+ .name = SP1628_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client sp1628_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+static const struct of_device_id sp1628_dt_match[] = {
+ {.compatible = "qcom,sp1628", .data = &sp1628_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, sp1628_dt_match);
+
+static struct platform_driver sp1628_platform_driver = {
+ .driver = {
+ .name = "qcom,sp1628",
+ .owner = THIS_MODULE,
+ .of_match_table = sp1628_dt_match,
+ },
+};
+
+static int32_t sp1628_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc;
+ const struct of_device_id *match;
+ CDBG("%s, E.", __func__);
+ match = of_match_device(sp1628_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init sp1628_init_module(void)
+{
+ int32_t rc;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&sp1628_platform_driver,
+ sp1628_platform_probe);
+ if (!rc)
+ return rc;
+ pr_err("%s:%d rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&sp1628_i2c_driver);
+}
+
+static void __exit sp1628_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (sp1628_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&sp1628_s_ctrl);
+ platform_driver_unregister(&sp1628_platform_driver);
+ } else
+ i2c_del_driver(&sp1628_i2c_driver);
+ return;
+}
+
+int32_t sp1628_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ long rc = 0;
+ int32_t i = 0;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__,
+ s_ctrl->sensordata->sensor_name, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_GET_SENSOR_INFO:
+ memcpy(cdata->cfg.sensor_info.sensor_name,
+ s_ctrl->sensordata->sensor_name,
+ sizeof(cdata->cfg.sensor_info.sensor_name));
+ cdata->cfg.sensor_info.session_id =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ cdata->cfg.sensor_info.subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.sensor_name);
+ CDBG("%s:%d session id %d\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.session_id);
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+ cdata->cfg.sensor_info.subdev_id[i]);
+
+ break;
+ case CFG_SET_INIT_SETTING:
+ /* Write Recommend settings */
+ pr_err("%s, sensor write init setting!!", __func__);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_conf_tbl(s_ctrl->sensor_i2c_client,
+ sp1628_recommend_settings,
+ ARRAY_SIZE(sp1628_recommend_settings),
+ MSM_CAMERA_I2C_BYTE_DATA);
+ break;
+ case CFG_SET_RESOLUTION:
+ break;
+ case CFG_SET_STOP_STREAM:
+ pr_err("%s, sensor stop stream!!", __func__);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_conf_tbl(s_ctrl->sensor_i2c_client,
+ sp1628_stop_settings,
+ ARRAY_SIZE(sp1628_stop_settings),
+ MSM_CAMERA_I2C_BYTE_DATA);
+ break;
+ case CFG_SET_START_STREAM:
+ pr_err("%s, sensor start stream!!", __func__);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_conf_tbl(s_ctrl->sensor_i2c_client,
+ sp1628_start_settings,
+ ARRAY_SIZE(sp1628_start_settings),
+ MSM_CAMERA_I2C_BYTE_DATA);
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params =
+ *s_ctrl->sensordata->sensor_init_params;
+ CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+ __LINE__,
+ cdata->cfg.sensor_init_params.modes_supported,
+ cdata->cfg.sensor_init_params.position,
+ cdata->cfg.sensor_init_params.sensor_mount_angle);
+ break;
+ case CFG_SET_SLAVE_INFO: {
+ struct msm_camera_sensor_slave_info sensor_slave_info;
+ struct msm_sensor_power_setting_array *power_setting_array;
+ int slave_index = 0;
+ if (copy_from_user(&sensor_slave_info,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_sensor_slave_info))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Update sensor slave address */
+ if (sensor_slave_info.slave_addr) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ sensor_slave_info.slave_addr >> 1;
+ }
+
+ /* Update sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type =
+ sensor_slave_info.addr_type;
+
+ /* Update power up / down sequence */
+ s_ctrl->power_setting_array =
+ sensor_slave_info.power_setting_array;
+ power_setting_array = &s_ctrl->power_setting_array;
+ power_setting_array->power_setting = kzalloc(
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+ if (!power_setting_array->power_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(power_setting_array->power_setting,
+ (void *)sensor_slave_info.power_setting_array.power_setting,
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting))) {
+ kfree(power_setting_array->power_setting);
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->free_power_setting = true;
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.slave_addr);
+ CDBG("%s sensor addr type %d\n", __func__,
+ sensor_slave_info.addr_type);
+ CDBG("%s sensor reg %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id);
+ for (slave_index = 0; slave_index <
+ power_setting_array->size; slave_index++) {
+ CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+ slave_index,
+ power_setting_array->power_setting[slave_index].
+ seq_type,
+ power_setting_array->power_setting[slave_index].
+ seq_val,
+ power_setting_array->power_setting[slave_index].
+ config_val,
+ power_setting_array->power_setting[slave_index].
+ delay);
+ }
+ kfree(power_setting_array->power_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_seq_reg_array)),
+ GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+
+ case CFG_POWER_UP:
+ if (s_ctrl->func_tbl->sensor_power_up)
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->func_tbl->sensor_power_down)
+ rc = s_ctrl->func_tbl->sensor_power_down(
+ s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (copy_from_user(stop_setting, (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = stop_setting->reg_setting;
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void *)reg_setting, stop_setting->size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(stop_setting->reg_setting);
+ stop_setting->reg_setting = NULL;
+ stop_setting->size = 0;
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+
+int32_t sp1628_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+
+ CDBG("%s, E. calling i2c_read:, i2c_addr:%d, id_reg_addr:%d",
+ __func__,
+ s_ctrl->sensordata->slave_info->sensor_slave_addr,
+ s_ctrl->sensordata->slave_info->sensor_id_reg_addr);
+
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ 0x02,
+ &chipid, MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ CDBG("%s: read id: %x expected id 0x16:\n", __func__, chipid);
+ if (chipid != 0x16) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+
+ chipid = 0;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ 0xa0,
+ &chipid, MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ CDBG("%s: read id: %x expected id 0x28:\n", __func__, chipid);
+ if (chipid != 0x28) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+
+ return rc;
+}
+
+
+static struct msm_sensor_fn_t sp1628_sensor_func_tbl = {
+ .sensor_config = sp1628_sensor_config,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_match_id = sp1628_match_id,
+};
+
+static struct msm_sensor_ctrl_t sp1628_s_ctrl = {
+ .sensor_i2c_client = &sp1628_sensor_i2c_client,
+ .power_setting_array.power_setting = sp1628_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(sp1628_power_setting),
+ .msm_sensor_mutex = &sp1628_mut,
+ .sensor_v4l2_subdev_info = sp1628_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(sp1628_subdev_info),
+ .func_tbl = &sp1628_sensor_func_tbl,
+};
+
+module_init(sp1628_init_module);
+module_exit(sp1628_exit_module);
+MODULE_DESCRIPTION("Aptina 1.26MP YUV sensor driver");
+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 85fdff6..a3d6dd8 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
@@ -692,6 +692,7 @@
memset(feed, 0, sizeof(*feed));
feed->sdmx_filter_handle = SDMX_INVALID_FILTER_HANDLE;
feed->mpq_demux = mpq_demux;
+ feed->session_id = 0;
}
/*
@@ -1634,7 +1635,17 @@
__func__, ret);
goto failed_unmap_metadata_buf;
}
- metadata_buff_desc->base_addr = (void *)temp;
+
+ /*
+ * NOTE: the following casting to u32 must be done
+ * as long as TZ does not support LPAE. Once TZ supports
+ * LPAE SDMX interface needs to be updated accordingly.
+ */
+ if (temp > 0xFFFFFFFF)
+ MPQ_DVB_ERR_PRINT(
+ "%s: WARNNING - physical address %pa is larger than 32bits!\n",
+ __func__, &temp);
+ metadata_buff_desc->base_addr = (void *)(u32)temp;
dvb_ringbuffer_init(&feed->metadata_buf, metadata_buff_base,
SDMX_METADATA_BUFFER_SIZE);
@@ -2272,7 +2283,16 @@
return ret;
}
- buf_desc->base_addr = (void *)phys_addr;
+ /*
+ * NOTE: the following casting to u32 must be done
+ * as long as TZ does not support LPAE. Once TZ supports
+ * LPAE SDMX interface needs to be updated accordingly.
+ */
+ if (phys_addr > 0xFFFFFFFF)
+ MPQ_DVB_ERR_PRINT(
+ "%s: WARNNING - physical address %pa is larger than 32bits!\n",
+ __func__, &phys_addr);
+ buf_desc->base_addr = (void *)(u32)phys_addr;
buf_desc->size = rbuf->size;
return 0;
@@ -3404,8 +3424,19 @@
sg = sg_ptr->sgl;
for (i = 0; i < sg_ptr->nents; i++) {
+ /*
+ * NOTE: the following casting to u32 must be done
+ * as long as TZ does not support LPAE. Once TZ supports
+ * LPAE SDMX interface needs to be updated accordingly.
+ */
+ if (sg_dma_address(sg) > 0xFFFFFFFF)
+ MPQ_DVB_ERR_PRINT(
+ "%s: WARNNING - physical address %pa is larger than 32bits!\n",
+ __func__, &sg_dma_address(sg));
+
buff_chunks[i].base_addr =
- (void *)sg_dma_address(sg);
+ (void *)(u32)sg_dma_address(sg);
+
if (sg->length > actual_buff_size)
chunk_size = actual_buff_size;
else
@@ -3649,16 +3680,18 @@
* If pid has a key ladder id associated, we need to
* set it to SDMX.
*/
- if (dvbdmx_feed->secure_mode.is_secured) {
+ if (dvbdmx_feed->secure_mode.is_secured &&
+ dvbdmx_feed->cipher_ops.operations_count) {
MPQ_DVB_DBG_PRINT(
- "%s: set key-ladder %d to PID %d\n",
- __func__,
- dvbdmx_feed->secure_mode.key_ladder_id,
- dvbdmx_feed->secure_mode.pid);
- ret = sdmx_set_kl_ind(
- mpq_demux->sdmx_session_handle,
- dvbdmx_feed->secure_mode.pid,
- dvbdmx_feed->secure_mode.key_ladder_id);
+ "%s: set key-ladder %d to PID %d\n",
+ __func__,
+ dvbdmx_feed->cipher_ops.operations[0].key_ladder_id,
+ dvbdmx_feed->cipher_ops.pid);
+
+ ret = sdmx_set_kl_ind(mpq_demux->sdmx_session_handle,
+ dvbdmx_feed->cipher_ops.pid,
+ dvbdmx_feed->cipher_ops.operations[0].key_ladder_id);
+
if (ret) {
MPQ_DVB_ERR_PRINT(
"%s: FAILED to set key ladder, ret=%d\n",
@@ -3776,8 +3809,10 @@
}
init_mpq_feed_end:
- if (!ret)
+ if (!ret) {
mpq_demux->num_active_feeds++;
+ mpq_feed->session_id++;
+ }
mutex_unlock(&mpq_demux->mutex);
return ret;
}
@@ -3786,14 +3821,14 @@
/**
* 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)
+int mpq_dmx_set_cipher_ops(struct dvb_demux_feed *feed,
+ struct dmx_cipher_operations *cipher_ops)
{
struct mpq_feed *mpq_feed;
struct mpq_demux *mpq_demux;
int ret = 0;
- if (!feed || !feed->priv || !sec_mode) {
+ if (!feed || !feed->priv || !cipher_ops) {
MPQ_DVB_ERR_PRINT(
"%s: invalid parameters\n",
__func__);
@@ -3801,37 +3836,45 @@
}
MPQ_DVB_DBG_PRINT("%s(%d, %d, %d)\n",
- __func__, sec_mode->pid,
- sec_mode->is_secured,
- sec_mode->key_ladder_id);
+ __func__, cipher_ops->pid,
+ cipher_ops->operations_count,
+ cipher_ops->operations[0].key_ladder_id);
+
+ if ((cipher_ops->operations_count > 1) ||
+ (cipher_ops->operations_count &&
+ cipher_ops->operations[0].encrypt)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid cipher operations, count=%d, encrypt=%d\n",
+ __func__, cipher_ops->operations_count,
+ cipher_ops->operations[0].encrypt);
+ return -EINVAL;
+ }
+
+ if (!feed->secure_mode.is_secured) {
+ /*
+ * Filter is not configured as secured, setting cipher
+ * operations is not allowed.
+ */
+ MPQ_DVB_ERR_PRINT(
+ "%s: Cannot set cipher operations to non-secure filter\n",
+ __func__);
+ return -EPERM;
+ }
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) {
+ if ((mpq_demux->sdmx_session_handle != SDMX_INVALID_SESSION_HANDLE) &&
+ cipher_ops->operations_count) {
ret = sdmx_set_kl_ind(mpq_demux->sdmx_session_handle,
- sec_mode->pid,
- sec_mode->key_ladder_id);
+ cipher_ops->pid,
+ cipher_ops->operations[0].key_ladder_id);
if (ret) {
MPQ_DVB_ERR_PRINT(
"%s: FAILED to set key ladder, ret=%d\n",
@@ -3844,7 +3887,7 @@
return ret;
}
-EXPORT_SYMBOL(mpq_dmx_set_secure_mode);
+EXPORT_SYMBOL(mpq_dmx_set_cipher_ops);
static void mpq_sdmx_prepare_filter_status(struct mpq_demux *mpq_demux,
struct sdmx_filter_status *filter_sts,
@@ -4525,19 +4568,14 @@
static void mpq_sdmx_process_results(struct mpq_demux *mpq_demux)
{
int i;
- int j;
int sdmx_filters;
struct sdmx_filter_status *sts;
struct mpq_feed *mpq_feed;
+ u8 mpq_feed_idx;
sdmx_filters = mpq_demux->sdmx_filter_count;
for (i = 0; i < sdmx_filters; i++) {
- /*
- * MPQ_TODO: review lookup optimization
- * Can have the related mpq_feed index already associated with
- * the filter status.
- */
- sts = &mpq_demux->filters_status[i];
+ sts = &mpq_demux->sdmx_filters_state.status[i];
MPQ_DVB_DBG_PRINT(
"%s: Filter: handle=%d, status=0x%x, errors=0x%x\n",
__func__, sts->filter_handle, sts->status_indicators,
@@ -4548,16 +4586,13 @@
MPQ_DVB_DBG_PRINT("%s: Data fill count=%d (write=%d)\n",
__func__, sts->data_fill_count, sts->data_write_offset);
- for (j = 0; j < MPQ_MAX_DMX_FILES; j++) {
- mpq_feed = &mpq_demux->feeds[j];
- if ((mpq_feed->dvb_demux_feed->state == DMX_STATE_GO) &&
- (sts->filter_handle ==
- mpq_feed->sdmx_filter_handle) &&
- (!mpq_feed->secondary_feed))
- break;
- }
-
- if (j == MPQ_MAX_DMX_FILES)
+ mpq_feed_idx = mpq_demux->sdmx_filters_state.mpq_feed_idx[i];
+ mpq_feed = &mpq_demux->feeds[mpq_feed_idx];
+ if ((mpq_feed->dvb_demux_feed->state != DMX_STATE_GO) ||
+ (sts->filter_handle != mpq_feed->sdmx_filter_handle) ||
+ mpq_feed->secondary_feed ||
+ (mpq_demux->sdmx_filters_state.session_id[i] !=
+ mpq_feed->session_id))
continue;
if (sts->error_indicators & SDMX_FILTER_ERR_MD_BUF_FULL)
@@ -4636,9 +4671,14 @@
if ((mpq_feed->sdmx_filter_handle != SDMX_INVALID_FILTER_HANDLE)
&& (mpq_feed->dvb_demux_feed->state == DMX_STATE_GO)
&& (!mpq_feed->secondary_feed)) {
- sts = &mpq_demux->filters_status[filter_index];
+ sts = mpq_demux->sdmx_filters_state.status +
+ filter_index;
mpq_sdmx_prepare_filter_status(mpq_demux, sts,
mpq_feed);
+ mpq_demux->sdmx_filters_state.mpq_feed_idx[filter_index]
+ = i;
+ mpq_demux->sdmx_filters_state.session_id[filter_index] =
+ mpq_feed->session_id;
filter_index++;
}
}
@@ -4662,7 +4702,8 @@
prev_fill_count = fill_count;
sdmx_res = sdmx_process(mpq_demux->sdmx_session_handle, flags, input,
&fill_count, &read_offset, &errors, &status,
- mpq_demux->sdmx_filter_count, mpq_demux->filters_status);
+ mpq_demux->sdmx_filter_count,
+ mpq_demux->sdmx_filters_state.status);
process_end_time = current_kernel_time();
bytes_read = prev_fill_count - fill_count;
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 ca7c15a..f095e00 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
@@ -322,6 +322,8 @@
* other feeds
* @metadata_buf: Ring buffer object for managing the metadata buffer
* @metadata_buf_handle: Allocation handle for the metadata buffer
+ * @session_id: Counter that is incremented every time feed is initialized
+ * through mpq_dmx_init_mpq_feed
* @sdmx_buf: Ring buffer object for intermediate output data from the sdmx
* @sdmx_buf_handle: Allocation handle for the sdmx intermediate data buffer
* @video_info: Video feed specific information
@@ -338,6 +340,7 @@
struct dvb_ringbuffer metadata_buf;
struct ion_handle *metadata_buf_handle;
+ u8 session_id;
struct dvb_ringbuffer sdmx_buf;
struct ion_handle *sdmx_buf_handle;
@@ -355,23 +358,24 @@
* @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
+ * @plugin_priv: Underlying plugin's own private data
* @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
* @sdmx_filter_count: Number of active secure demux filters
* @sdmx_eos: End-of-stream indication flag for current sdmx session
- * @plugin_priv: Underlying plugin's own private data
+ * @sdmx_filters_state: Array holding buffers status for each secure
+ * demux filter.
* @hw_notification_interval: Notification interval in msec,
- * exposed in debugfs.
+ * exposed in debugfs.
* @hw_notification_min_interval: Minimum notification internal in msec,
* exposed in debugfs.
* @hw_notification_count: Notification count, exposed in debugfs.
* @hw_notification_size: Notification size in bytes, exposed in debugfs.
* @hw_notification_min_size: Minimum notification size in bytes,
- * exposed in debugfs.
+ * exposed in debugfs.
* @decoder_drop_count: Accumulated number of bytes dropped due to decoder
* buffer fullness, exposed in debugfs.
* @decoder_out_count: Counter incremeneted for each video frame output by
@@ -403,14 +407,31 @@
struct ion_client *ion_client;
struct mutex mutex;
struct mpq_feed feeds[MPQ_MAX_DMX_FILES];
+ void *plugin_priv;
+
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;
int sdmx_filter_count;
int sdmx_eos;
- void *plugin_priv;
+ struct {
+ /* SDMX filters status */
+ struct sdmx_filter_status status[MPQ_MAX_DMX_FILES];
+
+ /* Index of the feed respective to SDMX filter */
+ u8 mpq_feed_idx[MPQ_MAX_DMX_FILES];
+
+ /*
+ * Snapshot of session_id of the feed
+ * when SDMX process was called. This is used
+ * to identify whether the feed has been
+ * restarted when processing SDMX results.
+ * May happen when demux is stalled in playback
+ * from memory with PULL mode.
+ */
+ u8 session_id[MPQ_MAX_DMX_FILES];
+ } sdmx_filters_state;
/* debug-fs */
u32 hw_notification_interval;
@@ -633,15 +654,19 @@
void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux);
/**
- * mpq_dmx_set_secure_mode - Handles set secure mode command from demux device
+ * mpq_dmx_set_cipher_ops - Handles setting of cipher operations
*
- * @feed: The feed to set its secure mode
- * @sec_mode: Secure mode details (key ladder info)
+ * @feed: The feed to set its cipher operations
+ * @cipher_ops: Cipher operations to be set
+ *
+ * This common function handles only the case when working with
+ * secure-demux. When working with secure demux a single decrypt cipher
+ * operation is allowed.
*
* Return error code
-*/
-int mpq_dmx_set_secure_mode(struct dvb_demux_feed *feed,
- struct dmx_secure_mode *secure_mode);
+ */
+int mpq_dmx_set_cipher_ops(struct dvb_demux_feed *feed,
+ struct dmx_cipher_operations *cipher_ops);
/**
* mpq_dmx_convert_tts - Convert timestamp attached by HW to each TS
@@ -652,7 +677,7 @@
* @timestampIn27Mhz: Timestamp result in 27MHz
*
* Return error code
-*/
+ */
void mpq_dmx_convert_tts(struct dvb_demux_feed *feed,
const u8 timestamp[TIMESTAMP_LEN],
u64 *timestampIn27Mhz);
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 ef3f57f..40445b0 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
@@ -561,6 +561,10 @@
caps->max_bitrate = 144;
caps->demod_input_max_bitrate = 72;
caps->memory_input_max_bitrate = 72;
+ caps->num_cipher_ops = 0;
+
+ /* TSIF reports 3 bytes STC at unit of 27MHz/256 */
+ caps->max_stc = (u64)0xFFFFFF * 256;
/* Buffer requirements */
caps->section.flags =
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 8e628f6..8179061 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
@@ -195,7 +195,7 @@
} mpq_dmx_tspp_info;
static void *tspp_mem_allocator(int channel_id, u32 size,
- u32 *phys_base, void *user)
+ phys_addr_t *phys_base, void *user)
{
void *virt_addr = NULL;
int i = TSPP_GET_TSIF_NUM(channel_id);
@@ -218,7 +218,7 @@
}
static void tspp_mem_free(int channel_id, u32 size,
- void *virt_base, u32 phys_base, void *user)
+ void *virt_base, phys_addr_t phys_base, void *user)
{
int i = TSPP_GET_TSIF_NUM(channel_id);
@@ -372,7 +372,18 @@
buff_start_addr_phys =
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base;
- input.base_addr = (void *)buff_start_addr_phys;
+
+ /*
+ * NOTE: the following casting to u32 must be done
+ * as long as TZ does not support LPAE. Once TZ supports
+ * LPAE SDMX interface needs to be updated accordingly.
+ */
+ if (buff_start_addr_phys > 0xFFFFFFFF)
+ MPQ_DVB_ERR_PRINT(
+ "%s: WARNNING - physical address %pa is larger than 32bits!\n",
+ __func__, &buff_start_addr_phys);
+
+ input.base_addr = (void *)(u32)buff_start_addr_phys;
input.size = mpq_dmx_tspp_info.tsif[tsif].buffer_count *
TSPP_DESCRIPTOR_SIZE;
@@ -381,7 +392,7 @@
"%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);
+ (int)(buff_current_addr_phys - buff_start_addr_phys));
mpq_sdmx_process(mpq_demux, &input, aggregate_len,
buff_current_addr_phys - buff_start_addr_phys,
@@ -1627,6 +1638,10 @@
caps->max_bitrate = 192;
caps->demod_input_max_bitrate = 96;
caps->memory_input_max_bitrate = 96;
+ caps->num_cipher_ops = 1;
+
+ /* TSIF reports 3 bytes STC at unit of 27MHz/256 */
+ caps->max_stc = (u64)0xFFFFFF * 256;
/* Buffer requirements */
caps->section.flags =
@@ -1744,7 +1759,7 @@
mpq_dmx_decoder_fullness_abort;
mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status;
mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
- mpq_demux->demux.set_secure_mode = mpq_dmx_set_secure_mode;
+ mpq_demux->demux.set_cipher_op = mpq_dmx_set_cipher_ops;
mpq_demux->demux.oob_command = mpq_dmx_oob_command;
mpq_demux->demux.convert_ts = mpq_dmx_convert_tts;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
index 1ab9da1..c2e37a4 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
@@ -73,7 +73,8 @@
}
caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_INDEXING |
- DMX_CAP_VIDEO_DECODER_DATA | DMX_CAP_TS_INSERTION;
+ DMX_CAP_VIDEO_DECODER_DATA | DMX_CAP_TS_INSERTION |
+ DMX_CAP_SECURED_INPUT_PLAYBACK;
caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM;
@@ -85,6 +86,10 @@
caps->max_bitrate = 320;
caps->demod_input_max_bitrate = 96;
caps->memory_input_max_bitrate = 80;
+ caps->num_cipher_ops = DMX_MAX_CIPHER_OPERATIONS_COUNT;
+
+ /* TSIF reports 7 bytes STC at unit of 27MHz */
+ caps->max_stc = 0x00FFFFFFFFFFFFFF;
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/Makefile b/drivers/media/platform/msm/vidc/Makefile
index 6977165..ff6274c 100644
--- a/drivers/media/platform/msm/vidc/Makefile
+++ b/drivers/media/platform/msm/vidc/Makefile
@@ -5,6 +5,7 @@
msm_venc.o \
msm_smem.o \
msm_vidc_debug.o \
+ msm_vidc_res_parse.o \
venus_hfi.o \
hfi_response_handler.o \
hfi_packetization.o \
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 6e8c809..d92a9c1 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -360,6 +360,25 @@
return ret;
}
+static u32 get_hfi_buf_mode(enum buffer_mode_type hal_buf_mode)
+{
+ u32 buf_mode;
+ switch (hal_buf_mode) {
+ case HAL_BUFFER_MODE_STATIC:
+ buf_mode = HFI_BUFFER_MODE_STATIC;
+ break;
+ case HAL_BUFFER_MODE_RING:
+ buf_mode = HFI_BUFFER_MODE_RING;
+ break;
+ default:
+ dprintk(VIDC_ERR, "Invalid buffer mode :0x%x\n",
+ hal_buf_mode);
+ buf_mode = 0;
+ break;
+ }
+ return buf_mode;
+}
+
int create_pkt_cmd_session_set_buffers(
struct hfi_cmd_session_set_buffers_packet *pkt,
u32 session_id,
@@ -1277,6 +1296,39 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
+ case HAL_PARAM_BUFFER_ALLOC_MODE:
+ {
+ u32 buffer_type;
+ u32 buffer_mode;
+ struct hfi_buffer_alloc_mode *hfi;
+ struct hal_buffer_alloc_mode *alloc_info = pdata;
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE;
+ hfi = (struct hfi_buffer_alloc_mode *)
+ &pkt->rg_property_data[1];
+ buffer_type = get_hfi_buffer(alloc_info->buffer_type);
+ if (buffer_type)
+ hfi->buffer_type = buffer_type;
+ else
+ return -EINVAL;
+ buffer_mode = get_hfi_buf_mode(alloc_info->buffer_mode);
+ if (buffer_mode)
+ hfi->buffer_mode = buffer_mode;
+ else
+ return -EINVAL;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_buffer_alloc_mode);
+ break;
+ }
+ case HAL_PARAM_VDEC_FRAME_ASSEMBLY:
+ {
+ struct hfi_enable *hfi;
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY;
+ hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+ hfi->enable = ((struct hfi_enable *) pdata)->enable;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
@@ -1343,3 +1395,17 @@
pkt->trigger_type = get_hfi_ssr_type(type);
return 0;
}
+
+int create_pkt_cmd_sys_image_version(
+ struct hfi_cmd_sys_get_property_packet *pkt)
+{
+ if (!pkt) {
+ dprintk(VIDC_ERR, "%s invalid param :%p\n", __func__, pkt);
+ return -EINVAL;
+ }
+ pkt->size = sizeof(struct hfi_cmd_sys_get_property_packet);
+ pkt->packet_type = HFI_CMD_SYS_GET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->rg_property_data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION;
+ return 0;
+}
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.h b/drivers/media/platform/msm/vidc/hfi_packetization.h
index df93906..20619c0 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.h
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.h
@@ -89,4 +89,7 @@
int create_pkt_ssr_cmd(enum hal_ssr_trigger_type type,
struct hfi_cmd_sys_test_ssr_packet *pkt);
+
+int create_pkt_cmd_sys_image_version(
+ struct hfi_cmd_sys_get_property_packet *pkt);
#endif
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 859345f..44105ad 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/interrupt.h>
+#include <mach/msm_smem.h>
#include "vidc_hfi_helper.h"
#include "vidc_hfi_io.h"
#include "msm_vidc_debug.h"
@@ -1089,6 +1090,44 @@
callback(SESSION_GET_SEQ_HDR_DONE, &data_done);
}
+void hfi_process_sys_property_info(
+ struct hfi_property_sys_image_version_info_type *pkt)
+{
+ int i = 0;
+ u32 smem_block_size = 0;
+ u8 *smem_table_ptr;
+ char version[256];
+ const u32 smem_image_index_venus = 14 * 128;
+
+ if (!pkt || !pkt->string_size) {
+ dprintk(VIDC_ERR, "%s: invalid param\n", __func__);
+ return;
+ }
+
+ if (pkt->string_size < sizeof(version)) {
+ /*
+ * The version string returned by firmware includes null
+ * characters at the start and in between. Replace the null
+ * characters with space, to print the version info.
+ */
+ for (i = 0; i < pkt->string_size; i++) {
+ if (pkt->str_image_version[i] != '\0')
+ version[i] = pkt->str_image_version[i];
+ else
+ version[i] = ' ';
+ }
+ version[i] = '\0';
+ dprintk(VIDC_INFO, "F/W version: %s\n", version);
+ }
+
+ smem_table_ptr = smem_get_entry(SMEM_IMAGE_VERSION_TABLE,
+ &smem_block_size);
+ if (smem_table_ptr &&
+ ((smem_image_index_venus + 128) <= smem_block_size))
+ memcpy(smem_table_ptr + smem_image_index_venus,
+ (u8 *)pkt->str_image_version, 128);
+}
+
u32 hfi_process_msg_packet(
msm_vidc_callback callback, u32 device_id,
struct vidc_hal_msg_pkt_hdr *msg_hdr)
@@ -1121,6 +1160,11 @@
(struct hfi_msg_sys_session_init_done_packet *)
msg_hdr);
break;
+ case HFI_MSG_SYS_PROPERTY_INFO:
+ hfi_process_sys_property_info(
+ (struct hfi_property_sys_image_version_info_type *)
+ msg_hdr);
+ break;
case HFI_MSG_SYS_SESSION_END_DONE:
hfi_process_session_end_done(callback, device_id,
(struct hfi_msg_sys_session_end_done_packet *)
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 687bd71..96d95989 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -20,7 +20,6 @@
#include <linux/debugfs.h>
#include <linux/version.h>
#include <linux/slab.h>
-#include <linux/of.h>
#include <mach/board.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
@@ -31,6 +30,7 @@
#include "msm_smem.h"
#include "vidc_hfi_api.h"
#include "msm_vidc_resources.h"
+#include "msm_vidc_res_parse.h"
#define BASE_DEVICE_NUMBER 32
@@ -54,50 +54,6 @@
struct list_head registered_bufs;
};
-struct master_slave {
- int masters_ocmem[2];
- int masters_ddr[2];
- int slaves_ocmem[2];
- int slaves_ddr[2];
-};
-
-struct bus_pdata_config {
- int *masters;
- int *slaves;
- char *name;
-};
-
-static struct master_slave bus_vectors_masters_slaves = {
- .masters_ocmem = {MSM_BUS_MASTER_VIDEO_P0_OCMEM,
- MSM_BUS_MASTER_VIDEO_P1_OCMEM},
- .masters_ddr = {MSM_BUS_MASTER_VIDEO_P0, MSM_BUS_MASTER_VIDEO_P1},
- .slaves_ocmem = {MSM_BUS_SLAVE_OCMEM, MSM_BUS_SLAVE_OCMEM},
- .slaves_ddr = {MSM_BUS_SLAVE_EBI_CH0, MSM_BUS_SLAVE_EBI_CH0},
-};
-
-static struct bus_pdata_config bus_pdata_config_vector[] = {
-{
- .masters = bus_vectors_masters_slaves.masters_ocmem,
- .slaves = bus_vectors_masters_slaves.slaves_ocmem,
- .name = "qcom,enc-ocmem-ab-ib",
-},
-{
- .masters = bus_vectors_masters_slaves.masters_ocmem,
- .slaves = bus_vectors_masters_slaves.slaves_ocmem,
- .name = "qcom,dec-ocmem-ab-ib",
-},
-{
- .masters = bus_vectors_masters_slaves.masters_ddr,
- .slaves = bus_vectors_masters_slaves.slaves_ddr,
- .name = "qcom,enc-ddr-ab-ib",
-},
-{
- .masters = bus_vectors_masters_slaves.masters_ddr,
- .slaves = bus_vectors_masters_slaves.slaves_ddr,
- .name = "qcom,dec-ddr-ab-ib",
-},
-};
-
static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh)
{
return container_of(filp->private_data,
@@ -736,578 +692,6 @@
{
}
-static size_t get_u32_array_num_elements(struct platform_device *pdev,
- char *name)
-{
- struct device_node *np = pdev->dev.of_node;
- int len;
- size_t num_elements = 0;
- if (!of_get_property(np, name, &len)) {
- dprintk(VIDC_ERR, "Failed to read %s from device tree\n",
- name);
- goto fail_read;
- }
-
- num_elements = len / sizeof(u32);
- if (num_elements <= 0) {
- dprintk(VIDC_ERR, "%s not specified in device tree\n",
- name);
- goto fail_read;
- }
- return num_elements / 2;
-
-fail_read:
- return 0;
-}
-
-static int read_hfi_type(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- int rc = 0;
- const char *hfi_name = NULL;
-
- if (np) {
- rc = of_property_read_string(np, "qcom,hfi", &hfi_name);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to read hfi from device tree\n");
- goto err_hfi_read;
- }
- if (!strcmp(hfi_name, "venus"))
- rc = VIDC_HFI_VENUS;
- else if (!strcmp(hfi_name, "q6"))
- rc = VIDC_HFI_Q6;
- else
- rc = -EINVAL;
- } else
- rc = VIDC_HFI_Q6;
-
-err_hfi_read:
- return rc;
-}
-
-static inline void msm_vidc_free_freq_table(
- struct msm_vidc_platform_resources *res)
-{
- kfree(res->load_freq_tbl);
- res->load_freq_tbl = NULL;
-}
-
-static inline void msm_vidc_free_reg_table(
- struct msm_vidc_platform_resources *res)
-{
- kfree(res->reg_set.reg_tbl);
- res->reg_set.reg_tbl = NULL;
-}
-
-static inline void msm_vidc_free_bus_vectors(
- struct msm_vidc_platform_resources *res)
-{
- int i, j;
- if (res->bus_pdata) {
- for (i = 0; i < ARRAY_SIZE(bus_pdata_config_vector); i++) {
- for (j = 0; j < res->bus_pdata[i].num_usecases; j++) {
- kfree(res->bus_pdata[i].usecase[j].vectors);
- res->bus_pdata[i].usecase[j].vectors = NULL;
- }
- kfree(res->bus_pdata[i].usecase);
- res->bus_pdata[i].usecase = NULL;
- }
- kfree(res->bus_pdata);
- res->bus_pdata = NULL;
- }
-}
-
-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;
- int num_elements = 0;
- struct platform_device *pdev = res->pdev;
-
- num_elements = get_u32_array_num_elements(pdev, "qcom,load-freq-tbl");
- if (num_elements == 0) {
- dprintk(VIDC_ERR, "no elements in frequency table\n");
- return rc;
- }
-
- res->load_freq_tbl = kzalloc(num_elements * sizeof(*res->load_freq_tbl),
- GFP_KERNEL);
- if (!res->load_freq_tbl) {
- dprintk(VIDC_ERR,
- "%s Failed to alloc load_freq_tbl\n",
- __func__);
- return -ENOMEM;
- }
-
- if (of_property_read_u32_array(pdev->dev.of_node,
- "qcom,load-freq-tbl", (u32 *)res->load_freq_tbl,
- num_elements * 2)) {
- dprintk(VIDC_ERR, "Failed to read frequency table\n");
- msm_vidc_free_freq_table(res);
- return -EINVAL;
- }
-
- res->load_freq_tbl_size = num_elements;
- return rc;
-}
-
-static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res)
-{
- struct reg_set *reg_set;
- struct platform_device *pdev = res->pdev;
- int i;
- int rc = 0;
-
- reg_set = &res->reg_set;
- reg_set->count = get_u32_array_num_elements(pdev, "qcom,reg-presets");
- if (reg_set->count == 0) {
- dprintk(VIDC_DBG, "no elements in reg set\n");
- return rc;
- }
-
- 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 register table\n",
- __func__);
- return -ENOMEM;
- }
-
- if (of_property_read_u32_array(pdev->dev.of_node, "qcom,reg-presets",
- (u32 *)reg_set->reg_tbl, reg_set->count * 2)) {
- dprintk(VIDC_ERR, "Failed to read register table\n");
- msm_vidc_free_reg_table(res);
- return -EINVAL;
- }
- for (i = 0; i < reg_set->count; i++) {
- dprintk(VIDC_DBG,
- "reg = %x, value = %x\n",
- reg_set->reg_tbl[i].reg,
- reg_set->reg_tbl[i].value
- );
- }
- return rc;
-}
-
-static void msm_vidc_free_bus_vector(struct msm_bus_scale_pdata *bus_pdata)
-{
- int i;
- for (i = 0; i < bus_pdata->num_usecases; i++) {
- kfree(bus_pdata->usecase[i].vectors);
- bus_pdata->usecase[i].vectors = NULL;
- }
-
- kfree(bus_pdata->usecase);
- bus_pdata->usecase = NULL;
-}
-
-static int msm_vidc_load_bus_vector(struct platform_device *pdev,
- struct msm_bus_scale_pdata *bus_pdata, u32 num_ports,
- struct bus_pdata_config *bus_pdata_config)
-{
- struct bus_values {
- u32 ab;
- u32 ib;
- };
- struct bus_values *values;
- int i, j;
- int rc = 0;
-
- values = kzalloc(sizeof(*values) * bus_pdata->num_usecases, GFP_KERNEL);
- if (!values) {
- dprintk(VIDC_ERR, "%s Failed to alloc bus_values\n", __func__);
- rc = -ENOMEM;
- goto err_mem_alloc;
- }
-
- if (of_property_read_u32_array(pdev->dev.of_node,
- bus_pdata_config->name, (u32 *)values,
- bus_pdata->num_usecases * (sizeof(*values)/sizeof(u32)))) {
- dprintk(VIDC_ERR, "%s Failed to read bus values\n", __func__);
- rc = -EINVAL;
- goto err_parse_dt;
- }
-
- bus_pdata->usecase = kzalloc(sizeof(*bus_pdata->usecase) *
- bus_pdata->num_usecases, GFP_KERNEL);
- if (!bus_pdata->usecase) {
- dprintk(VIDC_ERR,
- "%s Failed to alloc bus_pdata usecase\n", __func__);
- rc = -ENOMEM;
- goto err_parse_dt;
- }
- bus_pdata->name = bus_pdata_config->name;
- for (i = 0; i < bus_pdata->num_usecases; i++) {
- bus_pdata->usecase[i].vectors = kzalloc(
- sizeof(*bus_pdata->usecase[i].vectors) * num_ports,
- GFP_KERNEL);
- if (!bus_pdata->usecase) {
- dprintk(VIDC_ERR,
- "%s Failed to alloc bus_pdata usecase\n",
- __func__);
- break;
- }
- for (j = 0; j < num_ports; j++) {
- bus_pdata->usecase[i].vectors[j].ab = (u64)values[i].ab
- * 1000;
- bus_pdata->usecase[i].vectors[j].ib = (u64)values[i].ib
- * 1000;
- bus_pdata->usecase[i].vectors[j].src =
- bus_pdata_config->masters[j];
- bus_pdata->usecase[i].vectors[j].dst =
- bus_pdata_config->slaves[j];
- dprintk(VIDC_DBG,
- "ab = %llu, ib = %llu, src = %d, dst = %d\n",
- bus_pdata->usecase[i].vectors[j].ab,
- bus_pdata->usecase[i].vectors[j].ib,
- bus_pdata->usecase[i].vectors[j].src,
- bus_pdata->usecase[i].vectors[j].dst);
- }
- bus_pdata->usecase[i].num_paths = num_ports;
- }
- if (i < bus_pdata->num_usecases) {
- for (--i; i >= 0; i--) {
- kfree(bus_pdata->usecase[i].vectors);
- bus_pdata->usecase[i].vectors = NULL;
- }
- kfree(bus_pdata->usecase);
- bus_pdata->usecase = NULL;
- rc = -EINVAL;
- }
-err_parse_dt:
- kfree(values);
-err_mem_alloc:
- return rc;
-}
-
-static int msm_vidc_load_bus_vectors(struct msm_vidc_platform_resources *res)
-{
- u32 num_ports = 0;
- int rc = 0;
- int i;
- struct platform_device *pdev = res->pdev;
- u32 num_bus_pdata = ARRAY_SIZE(bus_pdata_config_vector);
-
- if (of_property_read_u32_array(pdev->dev.of_node, "qcom,bus-ports",
- (u32 *)&num_ports, 1) || (num_ports == 0))
- goto err_mem_alloc;
-
- res->bus_pdata = kzalloc(sizeof(*res->bus_pdata) * num_bus_pdata,
- GFP_KERNEL);
- if (!res->bus_pdata) {
- dprintk(VIDC_ERR, "Failed to alloc memory\n");
- rc = -ENOMEM;
- goto err_mem_alloc;
- }
- for (i = 0; i < num_bus_pdata; i++) {
- if (!res->has_ocmem &&
- (!strcmp(bus_pdata_config_vector[i].name,
- "qcom,enc-ocmem-ab-ib")
- || !strcmp(bus_pdata_config_vector[i].name,
- "qcom,dec-ocmem-ab-ib"))) {
- continue;
- }
- res->bus_pdata[i].num_usecases = get_u32_array_num_elements(
- pdev, bus_pdata_config_vector[i].name);
- if (res->bus_pdata[i].num_usecases == 0) {
- dprintk(VIDC_ERR, "no elements in %s\n",
- bus_pdata_config_vector[i].name);
- rc = -EINVAL;
- break;
- }
-
- rc = msm_vidc_load_bus_vector(pdev, &res->bus_pdata[i],
- num_ports, &bus_pdata_config_vector[i]);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to load bus vector: %d\n", i);
- break;
- }
- }
- if (i < num_bus_pdata) {
- for (--i; i >= 0; i--)
- msm_vidc_free_bus_vector(&res->bus_pdata[i]);
- kfree(res->bus_pdata);
- res->bus_pdata = NULL;
- }
-err_mem_alloc:
- 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_DBG, "iommu_groups property not present\n");
- iommu_group_set->count = 0;
- return 0;
- }
-
- 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)
-{
- struct platform_device *pdev = res->pdev;
- struct resource *kres = NULL;
- int rc = 0;
-
- if (!pdev->dev.of_node) {
- dprintk(VIDC_ERR, "DT node not found\n");
- return -ENOENT;
- }
-
- res->fw_base_addr = 0x0;
-
- kres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- res->register_base = kres ? kres->start : -1;
- res->register_size = kres ? (kres->end + 1 - kres->start) : -1;
-
- kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- res->irq = kres ? kres->start : -1;
-
- res->has_ocmem = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-ocmem");
-
- rc = msm_vidc_load_freq_table(res);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to load freq table: %d\n", rc);
- goto err_load_freq_table;
- }
- 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;
- }
-
- rc = of_property_read_u32(pdev->dev.of_node, "qcom,max-hw-load",
- &res->max_load);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to determine max load supported: %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_freq_table(res);
-err_load_freq_table:
- return rc;
-}
-
-static int read_platform_resources_from_board(
- struct msm_vidc_platform_resources *res)
-{
- struct resource *kres = NULL;
- struct platform_device *pdev = res->pdev;
- struct msm_vidc_v4l2_platform_data *pdata = pdev->dev.platform_data;
- int c = 0, rc = 0;
-
- if (!pdata) {
- dprintk(VIDC_ERR, "Platform data not found\n");
- return -ENOENT;
- }
-
- res->fw_base_addr = 0x0;
-
- kres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- res->register_base = kres ? kres->start : -1;
- res->register_size = kres ? (kres->end + 1 - kres->start) : -1;
-
- kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- res->irq = kres ? kres->start : -1;
-
- res->load_freq_tbl = kzalloc(pdata->num_load_table *
- sizeof(*res->load_freq_tbl), GFP_KERNEL);
-
- if (!res->load_freq_tbl) {
- dprintk(VIDC_ERR, "%s Failed to alloc load_freq_tbl\n",
- __func__);
- return -ENOMEM;
- }
-
- res->load_freq_tbl_size = pdata->num_load_table;
- for (c = 0; c > pdata->num_load_table; ++c) {
- res->load_freq_tbl[c].load = pdata->load_table[c][0];
- res->load_freq_tbl[c].freq = pdata->load_table[c][1];
- }
-
- res->max_load = pdata->max_load;
- return rc;
-}
-
static int read_platform_resources(struct msm_vidc_core *core,
struct platform_device *pdev)
{
@@ -1521,11 +905,7 @@
video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
v4l2_device_unregister(&core->v4l2_dev);
- msm_vidc_free_freq_table(&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);
+ msm_vidc_free_platform_resources(&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 7547464..96bf9f4 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -68,6 +68,10 @@
"Extradata aspect ratio",
"Extradata mpeg2 seqdisp",
};
+static const char *const mpeg_vidc_video_alloc_mode_type[] = {
+ "Buffer Allocation Static",
+ "Buffer Allocation Ring Buffer",
+};
static const char *const perf_level[] = {
"Nominal",
@@ -249,6 +253,33 @@
.qmenu = perf_level,
.step = 0,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE,
+ .name = "Buffer allocation mode",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDC_VIDEO_STATIC,
+ .maximum = V4L2_MPEG_VIDC_VIDEO_RING,
+ .default_value = V4L2_MPEG_VIDC_VIDEO_STATIC,
+ .menu_skip_mask = ~(
+ (1 << V4L2_MPEG_VIDC_VIDEO_STATIC) |
+ (1 << V4L2_MPEG_VIDC_VIDEO_RING)
+ ),
+ .qmenu = mpeg_vidc_video_alloc_mode_type,
+ .step = 0,
+ .cluster = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_ASSEMBLY,
+ .name = "Video frame assembly",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = V4L2_MPEG_VIDC_FRAME_ASSEMBLY_DISABLE,
+ .maximum = V4L2_MPEG_VIDC_FRAME_ASSEMBLY_ENABLE,
+ .default_value = V4L2_MPEG_VIDC_FRAME_ASSEMBLY_DISABLE,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ .cluster = 0,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
@@ -934,6 +965,8 @@
struct hal_buffer_requirements *bufreq;
int extra_idx = 0;
struct hfi_device *hdev;
+ struct hal_buffer_count_actual new_buf_count;
+ enum hal_property property_id;
if (!q || !num_buffers || !num_planes
|| !sizes || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %p, %p, %p\n",
@@ -960,6 +993,16 @@
i, inst->capability.height.max,
inst->capability.width.max);
}
+ property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
+ new_buf_count.buffer_type = HAL_BUFFER_INPUT;
+ new_buf_count.buffer_count_actual = *num_buffers;
+ rc = call_hfi_op(hdev, session_set_property,
+ inst->session, property_id, &new_buf_count);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to set new buffer count(%d) on FW, err: %d\n",
+ new_buf_count.buffer_count_actual, rc);
+ }
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
dprintk(VIDC_DBG, "Getting bufreqs on capture plane\n");
@@ -987,15 +1030,11 @@
}
if (*num_buffers && *num_buffers >
bufreq->buffer_count_actual) {
- struct hal_buffer_count_actual new_buf_count;
- enum hal_property property_id =
- HAL_PARAM_BUFFER_COUNT_ACTUAL;
-
+ property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
new_buf_count.buffer_type = HAL_BUFFER_OUTPUT;
new_buf_count.buffer_count_actual = *num_buffers;
rc = call_hfi_op(hdev, session_set_property,
inst->session, property_id, &new_buf_count);
-
}
if (bufreq->buffer_count_actual > *num_buffers)
*num_buffers = bufreq->buffer_count_actual;
@@ -1349,13 +1388,29 @@
}
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE:
+ {
+ struct hal_buffer_alloc_mode mode;
+ property_id = HAL_PARAM_BUFFER_ALLOC_MODE;
+ mode.buffer_mode = ctrl->val;
+ mode.buffer_type = HAL_BUFFER_INPUT;
+ pdata = &mode;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_ASSEMBLY:
+ {
+ property_id = HAL_PARAM_VDEC_FRAME_ASSEMBLY;
+ hal_property.enable = ctrl->val;
+ pdata = &hal_property;
+ break;
+ }
default:
break;
}
if (!rc && property_id) {
dprintk(VIDC_DBG,
- "Control: HAL property = %d, ctrl_id = 0x%x, ctrl_value = %d\n",
+ "Control: HAL property=0x%x,ctrl: id=0x%x,value=0x%x\n",
property_id, ctrl->id, ctrl->val);
rc = call_hfi_op(hdev, session_set_property, (void *)
inst->session, property_id, pdata);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index bd05180..5f0ec4e 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -69,6 +69,20 @@
return false;
}
+static bool is_thumbnail_session(struct msm_vidc_inst *inst)
+{
+ if (inst->session_type == MSM_VIDC_DECODER) {
+ int rc = 0;
+ struct v4l2_control ctrl = {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE
+ };
+ rc = v4l2_g_ctrl(&inst->ctrl_handler, &ctrl);
+ if (!rc && ctrl.value)
+ return true;
+ }
+ return false;
+}
+
static int msm_comm_get_load(struct msm_vidc_core *core,
enum session_type type)
{
@@ -83,7 +97,9 @@
if (inst->session_type == type &&
inst->state >= MSM_VIDC_OPEN_DONE &&
inst->state < MSM_VIDC_STOP_DONE) {
- num_mbs_per_sec += NUM_MBS_PER_SEC(inst->prop.height,
+ if (!is_thumbnail_session(inst))
+ num_mbs_per_sec += NUM_MBS_PER_SEC(
+ inst->prop.height,
inst->prop.width, inst->prop.fps);
}
mutex_unlock(&inst->lock);
@@ -728,6 +744,15 @@
vb = response->clnt_data;
inst = (struct msm_vidc_inst *)response->session_id;
if (vb) {
+ vb->v4l2_planes[0].bytesused = response->input_done.filled_len;
+ vb->v4l2_planes[0].data_offset = response->input_done.offset;
+ if (vb->v4l2_planes[0].data_offset > vb->v4l2_planes[0].length)
+ dprintk(VIDC_ERR, "Error: data_offset overflow\n");
+ if (vb->v4l2_planes[0].bytesused > vb->v4l2_planes[0].length)
+ dprintk(VIDC_ERR, "Error: buffer overflow\n");
+ if ((u8 *)vb->v4l2_planes[0].m.userptr !=
+ response->input_done.packet_buffer)
+ dprintk(VIDC_ERR, "Error: unexpected buffer address\n");
mutex_lock(&inst->bufq[OUTPUT_PORT].lock);
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
@@ -1305,6 +1330,25 @@
return rc;
}
+static void msm_vidc_print_running_insts(struct msm_vidc_core *core)
+{
+ struct msm_vidc_inst *temp;
+ dprintk(VIDC_ERR, "Running instances:\n");
+ dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s\n", "type", "w", "h", "fps");
+ list_for_each_entry(temp, &core->instances, list) {
+ mutex_lock(&temp->lock);
+ if (temp->state >= MSM_VIDC_OPEN_DONE &&
+ temp->state < MSM_VIDC_STOP_DONE) {
+ dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d\n",
+ temp->session_type,
+ temp->prop.width,
+ temp->prop.height,
+ temp->prop.fps);
+ }
+ mutex_unlock(&temp->lock);
+ }
+}
+
static int msm_vidc_load_resources(int flipped_state,
struct msm_vidc_inst *inst)
{
@@ -1327,24 +1371,9 @@
num_mbs_per_sec = msm_comm_get_load(inst->core, MSM_VIDC_DECODER);
num_mbs_per_sec += msm_comm_get_load(inst->core, MSM_VIDC_ENCODER);
if (num_mbs_per_sec > inst->core->resources.max_load) {
- struct msm_vidc_inst *temp;
-
dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n",
num_mbs_per_sec, inst->core->resources.max_load);
- dprintk(VIDC_ERR, "Running instances:\n");
- dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s\n", "type", "w", "h", "fps");
- list_for_each_entry(temp, &inst->core->instances, list) {
- mutex_lock(&temp->lock);
- if (temp->state >= MSM_VIDC_OPEN_DONE &&
- temp->state < MSM_VIDC_STOP_DONE) {
- dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d\n",
- temp->session_type,
- temp->prop.width,
- temp->prop.height,
- temp->prop.fps);
- }
- mutex_unlock(&temp->lock);
- }
+ msm_vidc_print_running_insts(inst->core);
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_recover_from_session_error(inst);
return -ENOMEM;
@@ -1885,10 +1914,19 @@
memset(&frame_data, 0 , sizeof(struct vidc_frame_data));
frame_data.alloc_len = vb->v4l2_planes[0].length;
frame_data.filled_len = vb->v4l2_planes[0].bytesused;
+ frame_data.offset = vb->v4l2_planes[0].data_offset;
frame_data.device_addr = vb->v4l2_planes[0].m.userptr;
frame_data.timestamp = time_usec;
frame_data.flags = 0;
frame_data.clnt_data = (u32)vb;
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ (frame_data.filled_len > frame_data.alloc_len ||
+ frame_data.offset > frame_data.alloc_len)) {
+ dprintk(VIDC_ERR,
+ "Buffer will overflow, not queueing it\n");
+ rc = -EINVAL;
+ goto err_bad_input;
+ }
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
frame_data.buffer_type = HAL_BUFFER_INPUT;
if (vb->v4l2_buf.flags & V4L2_BUF_FLAG_EOS) {
@@ -1907,8 +1945,11 @@
V4L2_QCOM_BUF_TIMESTAMP_INVALID)
frame_data.timestamp = LLONG_MAX;
dprintk(VIDC_DBG,
- "Sending etb to hal: Alloc: %d :filled: %d\n",
- frame_data.alloc_len, frame_data.filled_len);
+ "Sending etb to hal: device_addr: 0x%x"
+ "Alloc: %d, filled: %d, offset: %d\n",
+ frame_data.device_addr,
+ frame_data.alloc_len, frame_data.filled_len,
+ frame_data.offset);
rc = call_hfi_op(hdev, session_etb, (void *)
inst->session, &frame_data);
if (!rc)
@@ -1961,6 +2002,7 @@
rc = -EINVAL;
}
}
+err_bad_input:
if (rc)
dprintk(VIDC_ERR, "Failed to queue buffer\n");
err_no_mem:
@@ -2479,6 +2521,29 @@
return rc;
}
+static int msm_vidc_load_supported(struct msm_vidc_inst *inst)
+{
+ int num_mbs_per_sec = 0;
+
+ if (inst->state == MSM_VIDC_OPEN_DONE) {
+ num_mbs_per_sec = msm_comm_get_load(inst->core,
+ MSM_VIDC_DECODER);
+ num_mbs_per_sec += msm_comm_get_load(inst->core,
+ MSM_VIDC_ENCODER);
+ if (num_mbs_per_sec > inst->core->resources.max_load) {
+ dprintk(VIDC_ERR,
+ "H/w is overloaded. needed: %d max: %d\n",
+ num_mbs_per_sec,
+ inst->core->resources.max_load);
+ mutex_lock(&inst->sync_lock);
+ msm_vidc_print_running_insts(inst->core);
+ mutex_unlock(&inst->sync_lock);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
{
struct msm_vidc_core_capability *capability;
@@ -2492,7 +2557,8 @@
capability = &inst->capability;
hdev = inst->core->device;
- if (inst->capability.capability_set) {
+ rc = msm_vidc_load_supported(inst);
+ if (!rc && inst->capability.capability_set) {
rc = call_hfi_op(hdev, capability_check,
inst->fmts[OUTPUT_PORT]->fourcc,
inst->prop.width, &capability->width.max,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
new file mode 100644
index 0000000..cb08da7
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -0,0 +1,642 @@
+/* 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/of.h>
+#include <mach/board.h>
+#include "msm_vidc_resources.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_res_parse.h"
+
+struct master_slave {
+ int masters_ocmem[2];
+ int masters_ddr[2];
+ int slaves_ocmem[2];
+ int slaves_ddr[2];
+};
+
+static struct master_slave bus_vectors_masters_slaves = {
+ .masters_ocmem = {MSM_BUS_MASTER_VIDEO_P0_OCMEM,
+ MSM_BUS_MASTER_VIDEO_P1_OCMEM},
+ .masters_ddr = {MSM_BUS_MASTER_VIDEO_P0, MSM_BUS_MASTER_VIDEO_P1},
+ .slaves_ocmem = {MSM_BUS_SLAVE_OCMEM, MSM_BUS_SLAVE_OCMEM},
+ .slaves_ddr = {MSM_BUS_SLAVE_EBI_CH0, MSM_BUS_SLAVE_EBI_CH0},
+};
+
+struct bus_pdata_config {
+ int *masters;
+ int *slaves;
+ char *name;
+};
+
+static struct bus_pdata_config bus_pdata_config_vector[] = {
+ {
+ .masters = bus_vectors_masters_slaves.masters_ocmem,
+ .slaves = bus_vectors_masters_slaves.slaves_ocmem,
+ .name = "qcom,enc-ocmem-ab-ib",
+ },
+ {
+ .masters = bus_vectors_masters_slaves.masters_ocmem,
+ .slaves = bus_vectors_masters_slaves.slaves_ocmem,
+ .name = "qcom,dec-ocmem-ab-ib",
+ },
+ {
+ .masters = bus_vectors_masters_slaves.masters_ddr,
+ .slaves = bus_vectors_masters_slaves.slaves_ddr,
+ .name = "qcom,enc-ddr-ab-ib",
+ },
+ {
+ .masters = bus_vectors_masters_slaves.masters_ddr,
+ .slaves = bus_vectors_masters_slaves.slaves_ddr,
+ .name = "qcom,dec-ddr-ab-ib",
+ },
+};
+
+static size_t get_u32_array_num_elements(struct platform_device *pdev,
+ char *name)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int len;
+ size_t num_elements = 0;
+ if (!of_get_property(np, name, &len)) {
+ dprintk(VIDC_ERR, "Failed to read %s from device tree\n",
+ name);
+ goto fail_read;
+ }
+
+ num_elements = len / sizeof(u32);
+ if (num_elements <= 0) {
+ dprintk(VIDC_ERR, "%s not specified in device tree\n",
+ name);
+ goto fail_read;
+ }
+ return num_elements / 2;
+
+fail_read:
+ return 0;
+}
+
+int read_hfi_type(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int rc = 0;
+ const char *hfi_name = NULL;
+
+ if (np) {
+ rc = of_property_read_string(np, "qcom,hfi", &hfi_name);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to read hfi from device tree\n");
+ goto err_hfi_read;
+ }
+ if (!strcmp(hfi_name, "venus"))
+ rc = VIDC_HFI_VENUS;
+ else if (!strcmp(hfi_name, "q6"))
+ rc = VIDC_HFI_Q6;
+ else
+ rc = -EINVAL;
+ } else
+ rc = VIDC_HFI_Q6;
+
+err_hfi_read:
+ return rc;
+}
+
+static inline void msm_vidc_free_freq_table(
+ struct msm_vidc_platform_resources *res)
+{
+ kfree(res->load_freq_tbl);
+ res->load_freq_tbl = NULL;
+}
+
+static inline void msm_vidc_free_reg_table(
+ struct msm_vidc_platform_resources *res)
+{
+ kfree(res->reg_set.reg_tbl);
+ res->reg_set.reg_tbl = NULL;
+}
+
+static inline void msm_vidc_free_bus_vectors(
+ struct msm_vidc_platform_resources *res)
+{
+ int i, j;
+ if (res->bus_pdata) {
+ for (i = 0; i < ARRAY_SIZE(bus_pdata_config_vector); i++) {
+ for (j = 0; j < res->bus_pdata[i].num_usecases; j++) {
+ kfree(res->bus_pdata[i].usecase[j].vectors);
+ res->bus_pdata[i].usecase[j].vectors = NULL;
+ }
+ kfree(res->bus_pdata[i].usecase);
+ res->bus_pdata[i].usecase = NULL;
+ }
+ kfree(res->bus_pdata);
+ res->bus_pdata = NULL;
+ }
+}
+
+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;
+}
+
+void msm_vidc_free_platform_resources(
+ struct msm_vidc_platform_resources *res)
+{
+ msm_vidc_free_freq_table(res);
+ msm_vidc_free_reg_table(res);
+ msm_vidc_free_bus_vectors(res);
+ msm_vidc_free_iommu_groups(res);
+ msm_vidc_free_buffer_usage_table(res);
+}
+
+static void msm_vidc_free_bus_vector(struct msm_bus_scale_pdata *bus_pdata)
+{
+ int i;
+ for (i = 0; i < bus_pdata->num_usecases; i++) {
+ kfree(bus_pdata->usecase[i].vectors);
+ bus_pdata->usecase[i].vectors = NULL;
+ }
+
+ kfree(bus_pdata->usecase);
+ bus_pdata->usecase = NULL;
+}
+
+static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res)
+{
+ struct reg_set *reg_set;
+ struct platform_device *pdev = res->pdev;
+ int i;
+ int rc = 0;
+
+ reg_set = &res->reg_set;
+ reg_set->count = get_u32_array_num_elements(pdev, "qcom,reg-presets");
+ if (reg_set->count == 0) {
+ dprintk(VIDC_DBG, "no elements in reg set\n");
+ return rc;
+ }
+
+ 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 register table\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (of_property_read_u32_array(pdev->dev.of_node, "qcom,reg-presets",
+ (u32 *)reg_set->reg_tbl, reg_set->count * 2)) {
+ dprintk(VIDC_ERR, "Failed to read register table\n");
+ msm_vidc_free_reg_table(res);
+ return -EINVAL;
+ }
+ for (i = 0; i < reg_set->count; i++) {
+ dprintk(VIDC_DBG,
+ "reg = %x, value = %x\n",
+ reg_set->reg_tbl[i].reg,
+ reg_set->reg_tbl[i].value
+ );
+ }
+ return rc;
+}
+static int msm_vidc_load_freq_table(struct msm_vidc_platform_resources *res)
+{
+ int rc = 0;
+ int num_elements = 0;
+ struct platform_device *pdev = res->pdev;
+
+ num_elements = get_u32_array_num_elements(pdev, "qcom,load-freq-tbl");
+ if (num_elements == 0) {
+ dprintk(VIDC_ERR, "no elements in frequency table\n");
+ return rc;
+ }
+
+ res->load_freq_tbl = kzalloc(num_elements * sizeof(*res->load_freq_tbl),
+ GFP_KERNEL);
+ if (!res->load_freq_tbl) {
+ dprintk(VIDC_ERR,
+ "%s Failed to alloc load_freq_tbl\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,load-freq-tbl", (u32 *)res->load_freq_tbl,
+ num_elements * 2)) {
+ dprintk(VIDC_ERR, "Failed to read frequency table\n");
+ msm_vidc_free_freq_table(res);
+ return -EINVAL;
+ }
+
+ res->load_freq_tbl_size = num_elements;
+ return rc;
+}
+
+static int msm_vidc_load_bus_vector(struct platform_device *pdev,
+ struct msm_bus_scale_pdata *bus_pdata, u32 num_ports,
+ struct bus_pdata_config *bus_pdata_config)
+{
+ struct bus_values {
+ u32 ab;
+ u32 ib;
+ };
+ struct bus_values *values;
+ int i, j;
+ int rc = 0;
+
+ values = kzalloc(sizeof(*values) * bus_pdata->num_usecases, GFP_KERNEL);
+ if (!values) {
+ dprintk(VIDC_ERR, "%s Failed to alloc bus_values\n", __func__);
+ rc = -ENOMEM;
+ goto err_mem_alloc;
+ }
+
+ if (of_property_read_u32_array(pdev->dev.of_node,
+ bus_pdata_config->name, (u32 *)values,
+ bus_pdata->num_usecases * (sizeof(*values)/sizeof(u32)))) {
+ dprintk(VIDC_ERR, "%s Failed to read bus values\n", __func__);
+ rc = -EINVAL;
+ goto err_parse_dt;
+ }
+
+ bus_pdata->usecase = kzalloc(sizeof(*bus_pdata->usecase) *
+ bus_pdata->num_usecases, GFP_KERNEL);
+ if (!bus_pdata->usecase) {
+ dprintk(VIDC_ERR,
+ "%s Failed to alloc bus_pdata usecase\n", __func__);
+ rc = -ENOMEM;
+ goto err_parse_dt;
+ }
+ bus_pdata->name = bus_pdata_config->name;
+ for (i = 0; i < bus_pdata->num_usecases; i++) {
+ bus_pdata->usecase[i].vectors = kzalloc(
+ sizeof(*bus_pdata->usecase[i].vectors) * num_ports,
+ GFP_KERNEL);
+ if (!bus_pdata->usecase) {
+ dprintk(VIDC_ERR,
+ "%s Failed to alloc bus_pdata usecase\n",
+ __func__);
+ break;
+ }
+ for (j = 0; j < num_ports; j++) {
+ bus_pdata->usecase[i].vectors[j].ab = (u64)values[i].ab
+ * 1000;
+ bus_pdata->usecase[i].vectors[j].ib = (u64)values[i].ib
+ * 1000;
+ bus_pdata->usecase[i].vectors[j].src =
+ bus_pdata_config->masters[j];
+ bus_pdata->usecase[i].vectors[j].dst =
+ bus_pdata_config->slaves[j];
+ dprintk(VIDC_DBG,
+ "ab = %llu, ib = %llu, src = %d, dst = %d\n",
+ bus_pdata->usecase[i].vectors[j].ab,
+ bus_pdata->usecase[i].vectors[j].ib,
+ bus_pdata->usecase[i].vectors[j].src,
+ bus_pdata->usecase[i].vectors[j].dst);
+ }
+ bus_pdata->usecase[i].num_paths = num_ports;
+ }
+ if (i < bus_pdata->num_usecases) {
+ for (--i; i >= 0; i--) {
+ kfree(bus_pdata->usecase[i].vectors);
+ bus_pdata->usecase[i].vectors = NULL;
+ }
+ kfree(bus_pdata->usecase);
+ bus_pdata->usecase = NULL;
+ rc = -EINVAL;
+ }
+err_parse_dt:
+ kfree(values);
+err_mem_alloc:
+ return rc;
+}
+
+static int msm_vidc_load_bus_vectors(struct msm_vidc_platform_resources *res)
+{
+ u32 num_ports = 0;
+ int rc = 0;
+ int i;
+ struct platform_device *pdev = res->pdev;
+ u32 num_bus_pdata = ARRAY_SIZE(bus_pdata_config_vector);
+
+ if (of_property_read_u32_array(pdev->dev.of_node, "qcom,bus-ports",
+ (u32 *)&num_ports, 1) || (num_ports == 0))
+ goto err_mem_alloc;
+
+ res->bus_pdata = kzalloc(sizeof(*res->bus_pdata) * num_bus_pdata,
+ GFP_KERNEL);
+ if (!res->bus_pdata) {
+ dprintk(VIDC_ERR, "Failed to alloc memory\n");
+ rc = -ENOMEM;
+ goto err_mem_alloc;
+ }
+ for (i = 0; i < num_bus_pdata; i++) {
+ if (!res->has_ocmem &&
+ (!strcmp(bus_pdata_config_vector[i].name,
+ "qcom,enc-ocmem-ab-ib")
+ || !strcmp(bus_pdata_config_vector[i].name,
+ "qcom,dec-ocmem-ab-ib"))) {
+ continue;
+ }
+ res->bus_pdata[i].num_usecases = get_u32_array_num_elements(
+ pdev, bus_pdata_config_vector[i].name);
+ if (res->bus_pdata[i].num_usecases == 0) {
+ dprintk(VIDC_ERR, "no elements in %s\n",
+ bus_pdata_config_vector[i].name);
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = msm_vidc_load_bus_vector(pdev, &res->bus_pdata[i],
+ num_ports, &bus_pdata_config_vector[i]);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to load bus vector: %d\n", i);
+ break;
+ }
+ }
+ if (i < num_bus_pdata) {
+ for (--i; i >= 0; i--)
+ msm_vidc_free_bus_vector(&res->bus_pdata[i]);
+ kfree(res->bus_pdata);
+ res->bus_pdata = NULL;
+ }
+err_mem_alloc:
+ 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_DBG, "iommu_groups property not present\n");
+ iommu_group_set->count = 0;
+ return 0;
+ }
+
+ 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;
+}
+
+int read_platform_resources_from_dt(
+ struct msm_vidc_platform_resources *res)
+{
+ struct platform_device *pdev = res->pdev;
+ struct resource *kres = NULL;
+ int rc = 0;
+
+ if (!pdev->dev.of_node) {
+ dprintk(VIDC_ERR, "DT node not found\n");
+ return -ENOENT;
+ }
+
+ res->fw_base_addr = 0x0;
+
+ kres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res->register_base = kres ? kres->start : -1;
+ res->register_size = kres ? (kres->end + 1 - kres->start) : -1;
+
+ kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ res->irq = kres ? kres->start : -1;
+
+ res->has_ocmem = of_property_read_bool(pdev->dev.of_node,
+ "qcom,has-ocmem");
+
+ rc = msm_vidc_load_freq_table(res);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to load freq table: %d\n", rc);
+ goto err_load_freq_table;
+ }
+ 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;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,max-hw-load",
+ &res->max_load);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to determine max load supported: %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_freq_table(res);
+err_load_freq_table:
+ return rc;
+}
+
+int read_platform_resources_from_board(
+ struct msm_vidc_platform_resources *res)
+{
+ struct resource *kres = NULL;
+ struct platform_device *pdev = res->pdev;
+ struct msm_vidc_v4l2_platform_data *pdata = pdev->dev.platform_data;
+ int c = 0, rc = 0;
+
+ if (!pdata) {
+ dprintk(VIDC_ERR, "Platform data not found\n");
+ return -ENOENT;
+ }
+
+ res->fw_base_addr = 0x0;
+
+ kres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res->register_base = kres ? kres->start : -1;
+ res->register_size = kres ? (kres->end + 1 - kres->start) : -1;
+
+ kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ res->irq = kres ? kres->start : -1;
+
+ res->load_freq_tbl = kzalloc(pdata->num_load_table *
+ sizeof(*res->load_freq_tbl), GFP_KERNEL);
+
+ if (!res->load_freq_tbl) {
+ dprintk(VIDC_ERR, "%s Failed to alloc load_freq_tbl\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ res->load_freq_tbl_size = pdata->num_load_table;
+ for (c = 0; c > pdata->num_load_table; ++c) {
+ res->load_freq_tbl[c].load = pdata->load_table[c][0];
+ res->load_freq_tbl[c].freq = pdata->load_table[c][1];
+ }
+
+ res->max_load = pdata->max_load;
+ return rc;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h
new file mode 100644
index 0000000..9fe6b58
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h
@@ -0,0 +1,30 @@
+
+/* 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.
+ *
+ */
+
+#ifndef DT_PARSE
+#define DT_PARSE
+#include <linux/of.h>
+#include <mach/board.h>
+#include "msm_vidc_resources.h"
+void msm_vidc_free_platform_resources(
+ struct msm_vidc_platform_resources *res);
+
+int read_hfi_type(struct platform_device *pdev);
+
+int read_platform_resources_from_dt(
+ struct msm_vidc_platform_resources *res);
+
+int read_platform_resources_from_board(
+ struct msm_vidc_platform_resources *res);
+#endif
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c
index 0678fc2..bc3b93d 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.c
+++ b/drivers/media/platform/msm/vidc/q6_hfi.c
@@ -400,9 +400,11 @@
static int q6_hfi_apr_callback(struct apr_client_data *data, void *priv)
{
struct q6_hfi_device *device = priv;
+ struct hfi_msg_event_notify_packet pkt = {0};
+ void *payload = NULL;
int rc = 0;
- if (!data || !device || !data->payload_size) {
+ if (!data || !device) {
dprintk(VIDC_ERR, "%s - Invalid arguments", __func__);
return -EINVAL;
}
@@ -410,7 +412,23 @@
dprintk(VIDC_DBG, "%s opcode = %u payload size = %u", __func__,
data->opcode, data->payload_size);
- rc = q6_hfi_iface_eventq_write(device, data->payload);
+ if (data->opcode == RESET_EVENTS) {
+ dprintk(VIDC_ERR, "%s Received subsystem reset event: %d",
+ __func__, data->reset_event);
+ pkt.packet_type = HFI_MSG_EVENT_NOTIFY;
+ pkt.size = sizeof(pkt);
+ pkt.event_id = HFI_EVENT_SYS_ERROR;
+ pkt.event_data1 = data->opcode;
+ pkt.event_data2 = data->reset_event;
+ payload = &pkt;
+ } else if (data->payload_size > 0) {
+ payload = data->payload;
+ } else {
+ dprintk(VIDC_ERR, "%s - Invalid payload size", __func__);
+ return -EINVAL;
+ }
+
+ rc = q6_hfi_iface_eventq_write(device, payload);
if (rc) {
dprintk(VIDC_ERR, "%s failed to write to event queue",
__func__);
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 15ac493..47b88db 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1030,6 +1030,7 @@
static int venus_hfi_core_init(void *device)
{
struct hfi_cmd_sys_init_packet pkt;
+ struct hfi_cmd_sys_get_property_packet version_pkt;
int rc = 0;
struct venus_hfi_device *dev;
@@ -1088,6 +1089,10 @@
rc = -ENOTEMPTY;
goto err_core_init;
}
+ rc = create_pkt_cmd_sys_image_version(&version_pkt);
+ if (rc || venus_hfi_iface_cmdq_write(dev, &version_pkt))
+ dprintk(VIDC_WARN, "Failed to send image version pkt to f/w");
+
return rc;
err_core_init:
disable_irq_nosync(dev->hal_data->irq);
@@ -1418,6 +1423,10 @@
break;
case HAL_SYS_DEBUG_CONFIG:
break;
+ case HAL_PARAM_BUFFER_ALLOC_MODE:
+ break;
+ case HAL_PARAM_VDEC_FRAME_ASSEMBLY:
+ break;
/*FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET*/
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
@@ -2852,8 +2861,8 @@
venus_hfi_disable_clks(device);
fail_enable_clks:
subsystem_put(device->resources.fw.cookie);
- device->resources.fw.cookie = NULL;
fail_load_fw:
+ device->resources.fw.cookie = NULL;
venus_hfi_iommu_detach(device);
fail_iommu_attach:
return rc;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index bb72da7..ac6cb02 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -335,7 +335,6 @@
#define HFI_MSG_SYS_OX_START \
(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x0000)
#define HFI_MSG_SYS_PING_ACK (HFI_MSG_SYS_OX_START + 0x2)
-#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_SYS_OX_START + 0x3)
#define HFI_MSG_SYS_SESSION_ABORT_DONE (HFI_MSG_SYS_OX_START + 0x4)
#define HFI_MSG_SESSION_OX_START \
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 389c13f..288b386 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -172,6 +172,8 @@
HAL_PARAM_VENC_H264_VUI_TIMING_INFO,
HAL_PARAM_VENC_H264_GENERATE_AUDNAL,
HAL_PARAM_VENC_MAX_NUM_B_FRAMES,
+ HAL_PARAM_BUFFER_ALLOC_MODE,
+ HAL_PARAM_VDEC_FRAME_ASSEMBLY,
};
enum hal_domain {
@@ -860,6 +862,16 @@
HAL_UNUSED_SEQCHG = 0x10000000,
};
+enum buffer_mode_type {
+ HAL_BUFFER_MODE_STATIC = 0x00000000,
+ HAL_BUFFER_MODE_RING,
+};
+
+struct hal_buffer_alloc_mode {
+ enum hal_buffer buffer_type;
+ enum buffer_mode_type buffer_mode;
+};
+
/* HAL Response */
enum command_response {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 2b6d6bb..0f1e896 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -197,6 +197,10 @@
(HFI_PROPERTY_SYS_COMMON_START + 0x003)
#define HFI_PROPERTY_SYS_IDLE_INDICATOR \
(HFI_PROPERTY_SYS_COMMON_START + 0x004)
+#define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL \
+ (HFI_PROPERTY_SYS_COMMON_START + 0x005)
+#define HFI_PROPERTY_SYS_IMAGE_VERSION \
+ (HFI_PROPERTY_SYS_COMMON_START + 0x006)
#define HFI_PROPERTY_PARAM_COMMON_START \
(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000)
@@ -615,6 +619,11 @@
struct hfi_resource_ocmem_requirement rg_requirements[1];
};
+struct hfi_property_sys_image_version_info_type {
+ u32 string_size;
+ u8 str_image_version[1];
+};
+
struct hfi_venc_config_advanced {
u8 pipe2d;
u8 hw_mode;
@@ -703,6 +712,7 @@
#define HFI_MSG_SYS_SESSION_INIT_DONE (HFI_MSG_SYS_COMMON_START + 0x6)
#define HFI_MSG_SYS_SESSION_END_DONE (HFI_MSG_SYS_COMMON_START + 0x7)
#define HFI_MSG_SYS_IDLE (HFI_MSG_SYS_COMMON_START + 0x8)
+#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_SYS_COMMON_START + 0xA)
#define HFI_MSG_SESSION_COMMON_START \
(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + \
diff --git a/drivers/media/platform/msm/wfd/wfd-ioctl.c b/drivers/media/platform/msm/wfd/wfd-ioctl.c
index 30a666d..6554947 100644
--- a/drivers/media/platform/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/platform/msm/wfd/wfd-ioctl.c
@@ -715,6 +715,11 @@
if (rc)
WFD_MSG_ERR("Failed to stop MDP\n");
+ rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+ ENCODE_FLUSH, (void *)inst->venc_inst);
+ if (rc)
+ WFD_MSG_ERR("Failed to flush encoder\n");
+
WFD_MSG_DBG("vsg stop\n");
rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
VSG_STOP, NULL);
@@ -723,10 +728,6 @@
complete(&inst->stop_mdp_thread);
kthread_stop(inst->mdp_task);
- rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
- ENCODE_FLUSH, (void *)inst->venc_inst);
- if (rc)
- WFD_MSG_ERR("Failed to flush encoder\n");
WFD_MSG_DBG("enc stop\n");
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
ENCODE_STOP, (void *)inst->venc_inst);
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index d673713..50a1ea1 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -2385,7 +2385,7 @@
iris_q_event(radio, IRIS_EVT_NEW_RT_RDS);
- while ((skb->data[len+RDS_OFFSET] != 0x0d) && (len < RX_RT_DATA_LENGTH))
+ while ((skb->data[len+RDS_OFFSET] != 0x0d) && (len < MAX_RT_LENGTH))
len++;
data = kmalloc(len+RDS_OFFSET, GFP_ATOMIC);
if (!data) {
@@ -2397,7 +2397,7 @@
data[1] = skb->data[RDS_PTYPE];
data[2] = skb->data[RDS_PID_LOWER];
data[3] = skb->data[RDS_PID_HIGHER];
- data[4] = 0;
+ data[4] = skb->data[RT_A_B_FLAG_OFFSET];
memcpy(data+RDS_OFFSET, &skb->data[RDS_OFFSET], len);
data[len+RDS_OFFSET] = 0x00;
diff --git a/drivers/mfd/pm8xxx-pwm.c b/drivers/mfd/pm8xxx-pwm.c
index eb8320f..24fd5c1 100644
--- a/drivers/mfd/pm8xxx-pwm.c
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -35,7 +35,7 @@
*/
#define PM8XXX_LPG_V0_PWM_CHANNELS 8
#define PM8XXX_LPG_V1_PWM_CHANNELS 6
-#define PM8XXX_LPG_CTL_REGS 7
+#define PM8XXX_LPG_CTL_REGS 8
/* PM8XXX PWM */
#define SSBI_REG_ADDR_PWM1_CTRL1 0x88
@@ -66,6 +66,7 @@
#define SSBI_REG_ADDR_LPG_LUT_CFG0 0x145
#define SSBI_REG_ADDR_LPG_LUT_CFG1 0x146
#define SSBI_REG_ADDR_LPG_TEST 0x147
+#define SSBI_REG_ADDR_LPG_CTL_7 0x14D
/* LPG Control 0 */
#define PM8XXX_PWM_1KHZ_COUNT_MASK 0xF0
@@ -126,6 +127,7 @@
#define PM8XXX_PWM_PAUSE_ENABLE_HIGH 0x02
#define PM8XXX_PWM_SIZE_9_BIT 0x01
+#define PM8XXX_PWM_SIZE_7_BIT 0x04
/* LPG Control 6 */
#define PM8XXX_PWM_PAUSE_COUNT_LO_MASK 0xFC
@@ -369,17 +371,22 @@
}
static void pm8xxx_pwm_calc_period(unsigned int period_us,
- struct pm8xxx_pwm_period *period)
+ struct pwm_device *pwm)
{
int n, m, clk, div;
int best_m, best_div, best_clk;
unsigned int last_err, cur_err, min_err;
unsigned int tmp_p, period_n;
+ struct pm8xxx_pwm_period *period = &pwm->period;
+
+ if (pwm->banks == PM_PWM_BANK_LO)
+ n = 7;
+ else
+ n = 6;
/* PWM Period / N */
if (period_us < ((unsigned)(-1) / NSEC_PER_USEC)) {
- period_n = (period_us * NSEC_PER_USEC) >> 6;
- n = 6;
+ period_n = (period_us * NSEC_PER_USEC) >> n;
} else {
period_n = (period_us >> 9) * NSEC_PER_USEC;
n = 9;
@@ -458,6 +465,9 @@
int rc = 0;
pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
+ if (pwm->period.pwm_size == 7)
+ pwm_size = 7;
+
max_pwm_value = (1 << pwm_size) - 1;
for (i = 0; i < len; i++) {
if (raw_value)
@@ -512,9 +522,16 @@
PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
- val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
- mask = PM8XXX_PWM_SIZE_9_BIT;
- pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+ if (pwm->period.pwm_size == 7) {
+ val = PM8XXX_PWM_SIZE_7_BIT;
+ mask = PM8XXX_PWM_SIZE_7_BIT;
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[7], mask, val);
+ } else {
+ val = (pwm->period.pwm_size > 6) ?
+ PM8XXX_PWM_SIZE_9_BIT : 0;
+ mask = PM8XXX_PWM_SIZE_9_BIT;
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+ }
} else {
val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
& PM8XXX_PWM_CLK_SEL_MASK;
@@ -639,8 +656,18 @@
{
int i, rc;
+ if (end == 7) {
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_CTL_7,
+ pwm->pwm_lpg_ctl[end]);
+ if (rc) {
+ pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[7])\n", rc);
+ return rc;
+ }
+ }
+
/* Write in reverse way so 0 would be the last */
- for (i = end - 1; i >= start; i--) {
+ for (i = end - 2; i >= start; i--) {
rc = pm8xxx_writeb(pwm->chip->dev->parent,
SSBI_REG_ADDR_LPG_CTL(i),
pwm->pwm_lpg_ctl[i]);
@@ -788,7 +815,7 @@
}
if (pwm->pwm_period != period_us) {
- pm8xxx_pwm_calc_period(period_us, period);
+ pm8xxx_pwm_calc_period(period_us, pwm);
pm8xxx_pwm_save_period(pwm);
pwm->pwm_period = period_us;
}
@@ -801,7 +828,7 @@
PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
pm8xxx_pwm_bank_sel(pwm);
- rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+ rc = pm8xxx_lpg_pwm_write(pwm, 1, 7);
} else {
rc = pm8xxx_pwm_write(pwm);
}
@@ -851,7 +878,7 @@
* PWM mode.
*/
if (pwm->chip->is_pwm_enable_sync_workaround_needed)
- rc = pm8xxx_lpg_pwm_write(pwm, 3, 4);
+ rc = pm8xxx_lpg_pwm_write(pwm, 3, 5);
} else {
pm8xxx_pwm_enable(pwm);
@@ -921,7 +948,7 @@
if (pwm_chip->is_lpg_supported) {
pm8xxx_pwm_bank_sel(pwm);
- rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
+ rc = pm8xxx_lpg_pwm_write(pwm, 4, 7);
} else {
rc = pm8xxx_pwm_write(pwm);
}
@@ -965,7 +992,7 @@
pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
pm8xxx_pwm_bank_sel(pwm);
- rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+ rc = pm8xxx_lpg_pwm_write(pwm, 1, 7);
} else {
rc = pm8xxx_pwm_write(pwm);
}
@@ -996,7 +1023,6 @@
int idx_len, int pause_lo, int pause_hi, int flags)
{
struct pm8xxx_pwm_lut lut;
- struct pm8xxx_pwm_period *period;
int len;
int rc;
@@ -1032,7 +1058,6 @@
return -EINVAL;
}
- period = &pwm->period;
mutex_lock(&pwm->chip->pwm_mutex);
if (flags & PM_PWM_BANK_HI)
@@ -1052,7 +1077,7 @@
}
if (pwm->pwm_period != period_us) {
- pm8xxx_pwm_calc_period(period_us, period);
+ pm8xxx_pwm_calc_period(period_us, pwm);
pm8xxx_pwm_save_period(pwm);
pwm->pwm_period = period_us;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index c60537a..9d0bc61 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -41,6 +41,7 @@
#include <mach/subsystem_restart.h>
#include <mach/socinfo.h>
#include <mach/qseecomi.h>
+#include <asm/cacheflush.h>
#include "qseecom_legacy.h"
#include "qseecom_kernel.h"
@@ -515,7 +516,10 @@
qseecom.send_resp_flag = 0;
send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
send_data_rsp.listener_id = lstnr ;
-
+ if (ptr_svc)
+ msm_ion_do_cache_op(qseecom.ion_clnt, ptr_svc->ihandle,
+ ptr_svc->sb_virt, ptr_svc->sb_length,
+ ION_IOC_CLEAN_INV_CACHES);
ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
(const void *)&send_data_rsp,
sizeof(send_data_rsp), resp,
@@ -601,11 +605,12 @@
memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
ret = __qseecom_check_app_exists(req);
- if (ret < 0)
+ if (ret < 0) {
+ qsee_disable_clock_vote(data, CLK_SFPB);
return ret;
- else
- app_id = ret;
+ }
+ app_id = ret;
if (app_id) {
pr_debug("App id %d (%s) already exists\n", app_id,
(char *)(req.app_name));
@@ -641,6 +646,8 @@
load_req.mdt_len = load_img_req.mdt_len;
load_req.img_len = load_img_req.img_len;
load_req.phy_addr = pa;
+ msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len,
+ ION_IOC_CLEAN_INV_CACHES);
/* SCM_CALL to load the app and get the app_id back */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
@@ -880,10 +887,15 @@
pr_err("Unsupported cmd_id %d\n", req.cmd_id);
return -EINVAL;
}
-
+ msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+ data->client.sb_virt, data->client.sb_length,
+ ION_IOC_CLEAN_INV_CACHES);
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_svc_ireq,
sizeof(send_svc_ireq),
&resp, sizeof(resp));
+ msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+ data->client.sb_virt, data->client.sb_length,
+ ION_IOC_INV_CACHES);
if (ret) {
pr_err("qseecom_scm_call failed with err: %d\n", ret);
return ret;
@@ -952,6 +964,11 @@
(uint32_t)req->resp_buf));
send_data_req.rsp_len = req->resp_len;
+ msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+ data->client.sb_virt,
+ (req->cmd_req_len + req->resp_len),
+ ION_IOC_CLEAN_INV_CACHES);
+
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
sizeof(send_data_req),
&resp, sizeof(resp));
@@ -974,6 +991,9 @@
ret = -EINVAL;
}
}
+ msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+ data->client.sb_virt, data->client.sb_length,
+ ION_IOC_INV_CACHES);
return ret;
}
@@ -1005,6 +1025,8 @@
char *field;
int ret = 0;
int i = 0;
+ uint32_t len = 0;
+ struct scatterlist *sg;
for (i = 0; i < MAX_ION_FD; i++) {
struct sg_table *sg_ptr = NULL;
@@ -1035,6 +1057,7 @@
sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
goto err;
}
+ sg = sg_ptr->sgl;
if (sg_ptr->nents == 1) {
uint32_t *update;
update = (uint32_t *) field;
@@ -1043,12 +1066,11 @@
else
*update = (uint32_t)sg_dma_address(
sg_ptr->sgl);
+ len += (uint32_t)sg->length;
} else {
struct qseecom_sg_entry *update;
- struct scatterlist *sg;
int j = 0;
update = (struct qseecom_sg_entry *) field;
- sg = sg_ptr->sgl;
for (j = 0; j < sg_ptr->nents; j++) {
if (cleanup) {
update->phys_addr = 0;
@@ -1058,10 +1080,19 @@
sg_dma_address(sg);
update->len = sg->length;
}
+ len += sg->length;
update++;
sg = sg_next(sg);
}
}
+ if (cleanup)
+ msm_ion_do_cache_op(qseecom.ion_clnt,
+ ihandle, NULL, len,
+ ION_IOC_INV_CACHES);
+ else
+ msm_ion_do_cache_op(qseecom.ion_clnt,
+ ihandle, NULL, len,
+ ION_IOC_CLEAN_INV_CACHES);
/* Deallocate the handle */
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
@@ -1288,6 +1319,7 @@
return -EIO;
}
+ __cpuc_flush_dcache_area((void *)img_data, fw_size);
/* SCM_CALL to load the image */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
sizeof(struct qseecom_load_app_ireq),
@@ -1354,6 +1386,7 @@
return -EIO;
}
+ __cpuc_flush_dcache_area((void *)img_data, fw_size);
/* SCM_CALL to load the image */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
sizeof(struct qseecom_load_lib_image_ireq),
@@ -1976,16 +2009,18 @@
pr_err("set_cpus_allowed_ptr failed : ret %d\n",
set_cpu_ret);
ret = -EFAULT;
- goto qseecom_load_external_elf_set_cpu_err;
+ goto exit_ion_free;
}
+
/* Vote for the SFPB clock */
ret = qsee_vote_for_clock(data, CLK_SFPB);
if (ret) {
pr_err("Unable to vote for SFPB clock: ret = %d", ret);
ret = -EIO;
- goto qseecom_load_external_elf_set_cpu_err;
+ goto exit_cpu_restore;
}
-
+ msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len,
+ ION_IOC_CLEAN_INV_CACHES);
/* SCM_CALL to load the external elf */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
sizeof(struct qseecom_load_app_ireq),
@@ -1994,23 +2029,32 @@
pr_err("scm_call to load failed : ret %d\n",
ret);
ret = -EFAULT;
- goto qseecom_load_external_elf_scm_err;
+ goto exit_disable_clock;
}
- if (resp.result == QSEOS_RESULT_INCOMPLETE) {
+ switch (resp.result) {
+ case QSEOS_RESULT_SUCCESS:
+ break;
+ case QSEOS_RESULT_INCOMPLETE:
+ pr_err("%s: qseos result incomplete\n", __func__);
ret = __qseecom_process_incomplete_cmd(data, &resp);
if (ret)
- pr_err("process_incomplete_cmd failed err: %d\n",
- ret);
- } else {
- if (resp.result != QSEOS_RESULT_SUCCESS) {
- pr_err("scm_call to load image failed resp.result =%d\n",
- resp.result);
- ret = -EFAULT;
- }
+ pr_err("process_incomplete_cmd failed: err: %d\n", ret);
+ break;
+ case QSEOS_RESULT_FAILURE:
+ pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
+ ret = -EFAULT;
+ break;
+ default:
+ pr_err("scm_call response result %d not supported\n",
+ resp.result);
+ ret = -EFAULT;
+ break;
}
-qseecom_load_external_elf_scm_err:
+exit_disable_clock:
+ qsee_disable_clock_vote(data, CLK_SFPB);
+exit_cpu_restore:
/* Restore the CPU mask */
mask = CPU_MASK_ALL;
set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
@@ -2019,12 +2063,10 @@
set_cpu_ret);
ret = -EFAULT;
}
-
-qseecom_load_external_elf_set_cpu_err:
+exit_ion_free:
/* Deallocate the handle */
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
- qsee_disable_clock_vote(data, CLK_SFPB);
return ret;
}
@@ -2498,7 +2540,7 @@
ret = scm_call(SCM_SVC_ES, SCM_SAVE_PARTITION_HASH_ID,
(void *) &req, sizeof(req), NULL, 0);
if (ret) {
- pr_err("scm_call failed");
+ pr_err("qseecom_scm_call failed");
return ret;
}
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index a395b7c..36bdf45 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -617,8 +617,8 @@
break;
if (iovec.addr != channel->waiting->sps.phys_base)
- pr_err("tspp: buffer mismatch 0x%08x",
- channel->waiting->sps.phys_base);
+ pr_err("tspp: buffer mismatch %pa",
+ &channel->waiting->sps.phys_base);
complete = 1;
channel->waiting->state = TSPP_BUF_STATE_DATA;
@@ -802,7 +802,7 @@
if (device->tsif_vreg) {
regulator_set_voltage(device->tsif_vreg,
- RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_NONE,
RPM_REGULATOR_CORNER_SUPER_TURBO);
}
@@ -818,7 +818,7 @@
clk_disable_unprepare(device->tsif_pclk);
if (device->tsif_vreg) {
regulator_set_voltage(device->tsif_vreg,
- RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_NONE,
RPM_REGULATOR_CORNER_SUPER_TURBO);
}
@@ -848,7 +848,7 @@
if (device->tsif_vreg) {
rc = regulator_set_voltage(device->tsif_vreg,
- RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_NONE,
RPM_REGULATOR_CORNER_SUPER_TURBO);
if (rc)
pr_err("Unable to set CX voltage.\n");
@@ -2945,7 +2945,7 @@
/* Set an initial voltage and enable the regulator */
rc = regulator_set_voltage(device->tsif_vreg,
- RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_NONE,
RPM_REGULATOR_CORNER_SUPER_TURBO);
if (rc) {
dev_err(&pdev->dev, "Unable to set CX voltage.\n");
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index f01ddab..da07947 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1363,7 +1363,7 @@
pr_debug("%s: %s - SANITIZE IN PROGRESS...\n",
mmc_hostname(card->host), __func__);
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ err = mmc_switch_ignore_timeout(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_SANITIZE_START, 1,
MMC_SANITIZE_REQ_TIMEOUT);
@@ -3130,11 +3130,11 @@
/* Silent the block layer */
if (md) {
- rc = mmc_queue_suspend(&md->queue);
+ rc = mmc_queue_suspend(&md->queue, 1);
if (rc)
goto suspend_error;
list_for_each_entry(part_md, &md->part, part) {
- rc = mmc_queue_suspend(&part_md->queue);
+ rc = mmc_queue_suspend(&part_md->queue, 1);
if (rc)
goto suspend_error;
}
@@ -3161,11 +3161,11 @@
int rc = 0;
if (md) {
- rc = mmc_queue_suspend(&md->queue);
+ rc = mmc_queue_suspend(&md->queue, 0);
if (rc)
goto out;
list_for_each_entry(part_md, &md->part, part) {
- rc = mmc_queue_suspend(&part_md->queue);
+ rc = mmc_queue_suspend(&part_md->queue, 0);
if (rc)
goto out_resume;
}
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 7a4d19e..e9ac2fc 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -20,7 +20,6 @@
#include <linux/mmc/host.h>
#include <linux/delay.h>
#include <linux/test-iosched.h>
-#include <linux/jiffies.h>
#include "queue.h"
#include <linux/mmc/mmc.h>
@@ -2787,7 +2786,7 @@
if (ret)
break;
- mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+ mtime = ktime_to_ms(mbtd->test_info.test_duration);
test_pr_info("%s: time is %lu msec, size is %u.%u MiB",
__func__, mtime,
@@ -2946,7 +2945,7 @@
if (ret)
break;
- mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+ mtime = ktime_to_ms(mbtd->test_info.test_duration);
byte_count = mbtd->test_info.test_byte_count;
test_pr_info("%s: time is %lu msec, size is %lu.%lu MiB",
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 0e024dd..507cd5b 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -438,12 +438,13 @@
/**
* mmc_queue_suspend - suspend a MMC request queue
* @mq: MMC queue to suspend
+ * @wait: Wait till MMC request queue is empty
*
* Stop the block request queue, and wait for our thread to
* complete any outstanding requests. This ensures that we
* won't suspend while a request is being processed.
*/
-int mmc_queue_suspend(struct mmc_queue *mq)
+int mmc_queue_suspend(struct mmc_queue *mq, int wait)
{
struct request_queue *q = mq->queue;
unsigned long flags;
@@ -457,7 +458,7 @@
spin_unlock_irqrestore(q->queue_lock, flags);
rc = down_trylock(&mq->thread_sem);
- if (rc) {
+ if (rc && !wait) {
/*
* Failed to take the lock so better to abort the
* suspend because mmcqd thread is processing requests.
@@ -467,6 +468,9 @@
blk_start_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
rc = -EBUSY;
+ } else if (rc && wait) {
+ down(&mq->thread_sem);
+ rc = 0;
}
}
return rc;
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 9280d1b..d1fe01c 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 int mmc_queue_suspend(struct mmc_queue *);
+extern int mmc_queue_suspend(struct mmc_queue *, int);
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 04687fa..064d5ec 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -59,6 +59,9 @@
*/
#define MMC_BKOPS_MAX_TIMEOUT (30 * 1000) /* max time to wait in ms */
+/* Flushing a large amount of cached data may take a long time. */
+#define MMC_FLUSH_REQ_TIMEOUT_MS 30000 /* msec */
+
static struct workqueue_struct *workqueue;
/*
@@ -785,21 +788,13 @@
cmd = mrq->cmd;
/*
- * If host has timed out waiting for the blocking BKOPs
- * to complete, card might be still in programming state
- * so let's try to bring the card out of programming state.
+ * If host has timed out waiting for the commands which can be
+ * HPIed then let the caller handle the timeout error as it may
+ * want to send the HPI command to bring the card out of
+ * programming state.
*/
- if (cmd->bkops_busy && cmd->error == -ETIMEDOUT) {
- if (!mmc_interrupt_hpi(host->card)) {
- pr_warning("%s: %s: Interrupted blocking bkops\n",
- mmc_hostname(host), __func__);
- cmd->error = 0;
- break;
- } else {
- pr_err("%s: %s: Failed to interrupt blocking bkops\n",
- mmc_hostname(host), __func__);
- }
- }
+ if (cmd->ignore_timeout && cmd->error == -ETIMEDOUT)
+ break;
if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card))
@@ -1064,8 +1059,6 @@
}
err = mmc_send_hpi_cmd(card, &status);
- if (err)
- goto out;
prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time);
do {
@@ -2594,7 +2587,7 @@
if (mmc_card_sdio(card))
return 0;
- if (mmc_card_mmc(card)) {
+ if (mmc_card_mmc(card) && (card->host->caps & MMC_CAP_HW_RESET)) {
rst_n_function = card->ext_csd.rst_n_function;
if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) !=
EXT_CSD_RST_N_ENABLED)
@@ -2611,9 +2604,6 @@
if (!host->bus_ops->power_restore)
return -EOPNOTSUPP;
- if (!(host->caps & MMC_CAP_HW_RESET))
- return -EOPNOTSUPP;
-
if (!card)
return -EINVAL;
@@ -2623,10 +2613,10 @@
mmc_host_clk_hold(host);
mmc_set_clock(host, host->f_init);
- if (mmc_card_sd(card))
- mmc_power_cycle(host);
- else if (host->ops->hw_reset)
+ if (mmc_card_mmc(card) && host->ops->hw_reset)
host->ops->hw_reset(host);
+ else
+ mmc_power_cycle(host);
/* If the reset has happened, then a status command will fail */
if (check) {
@@ -3336,7 +3326,7 @@
int mmc_flush_cache(struct mmc_card *card)
{
struct mmc_host *host = card->host;
- int err = 0;
+ int err = 0, rc;
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL))
return err;
@@ -3344,11 +3334,20 @@
if (mmc_card_mmc(card) &&
(card->ext_csd.cache_size > 0) &&
(card->ext_csd.cache_ctrl & 1)) {
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_FLUSH_CACHE, 1, 0);
- if (err)
+ err = mmc_switch_ignore_timeout(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_FLUSH_CACHE, 1,
+ MMC_FLUSH_REQ_TIMEOUT_MS);
+ if (err == -ETIMEDOUT) {
+ pr_debug("%s: cache flush timeout\n",
+ mmc_hostname(card->host));
+ rc = mmc_interrupt_hpi(card);
+ if (rc)
+ pr_err("%s: mmc_interrupt_hpi() failed (%d)\n",
+ mmc_hostname(host), rc);
+ } else if (err) {
pr_err("%s: cache flush error %d\n",
mmc_hostname(card->host), err);
+ }
}
return err;
@@ -3364,8 +3363,8 @@
int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
{
struct mmc_card *card = host->card;
- unsigned int timeout;
- int err = 0;
+ unsigned int timeout = card->ext_csd.generic_cmd6_time;
+ int err = 0, rc;
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
mmc_card_is_removable(host))
@@ -3376,16 +3375,28 @@
enable = !!enable;
if (card->ext_csd.cache_ctrl ^ enable) {
- timeout = enable ? card->ext_csd.generic_cmd6_time : 0;
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ if (!enable)
+ timeout = MMC_FLUSH_REQ_TIMEOUT_MS;
+
+ err = mmc_switch_ignore_timeout(card,
+ EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_CACHE_CTRL, enable, timeout);
- if (err)
+
+ if (err == -ETIMEDOUT && !enable) {
+ pr_debug("%s:cache disable operation timeout\n",
+ mmc_hostname(card->host));
+ rc = mmc_interrupt_hpi(card);
+ if (rc)
+ pr_err("%s: mmc_interrupt_hpi() failed (%d)\n",
+ mmc_hostname(host), rc);
+ } else if (err) {
pr_err("%s: cache %s error %d\n",
mmc_hostname(card->host),
enable ? "on" : "off",
err);
- else
+ } else {
card->ext_csd.cache_ctrl = enable;
+ }
}
}
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 9b9c1ed..2e9db1f 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -892,9 +892,12 @@
MMC_SEND_TUNING_BLOCK_HS200);
mmc_host_clk_release(card->host);
- if (err)
- pr_warn("%s: %s: tuning execution failed %d\n",
- mmc_hostname(card->host), __func__, err);
+ if (err) {
+ pr_warn("%s: %s: tuning execution failed %d. Restoring to previous clock %lu\n",
+ mmc_hostname(card->host), __func__, err,
+ host->clk_scaling.curr_freq);
+ mmc_set_clock(host, host->clk_scaling.curr_freq);
+ }
}
out:
mmc_release_host(host);
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 8087ea6..164c418 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -378,13 +378,13 @@
* @timeout_ms: timeout (ms) for operation performed by register write,
* timeout of zero implies maximum possible timeout
* @use_busy_signal: use the busy signal as response type
- * @bkops_busy: set this to indicate that we are starting blocking bkops
+ * @ignore_timeout: set this flag only for commands which can be HPIed
*
* Modifies the EXT_CSD register for selected card.
*/
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, bool use_busy_signal,
- bool bkops_busy)
+ bool ignore_timeout)
{
int err;
struct mmc_command cmd = {0};
@@ -407,7 +407,7 @@
cmd.cmd_timeout_ms = timeout_ms;
- cmd.bkops_busy = bkops_busy;
+ cmd.ignore_timeout = ignore_timeout;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
@@ -458,6 +458,13 @@
}
EXPORT_SYMBOL_GPL(mmc_switch);
+int mmc_switch_ignore_timeout(struct mmc_card *card, u8 set, u8 index, u8 value,
+ unsigned int timeout_ms)
+{
+ return __mmc_switch(card, set, index, value, timeout_ms, true, true);
+}
+EXPORT_SYMBOL(mmc_switch_ignore_timeout);
+
int mmc_send_status(struct mmc_card *card, u32 *status)
{
int err;
@@ -608,7 +615,7 @@
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
- pr_warn("%s: error %d interrupting operation. "
+ pr_debug("%s: error %d interrupting operation. "
"HPI command response %#x\n", mmc_hostname(card->host),
err, cmd.resp[0]);
return err;
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ddf9a87..a4498d2 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -666,9 +666,12 @@
MMC_SEND_TUNING_BLOCK);
mmc_host_clk_release(card->host);
- if (err)
- pr_warn("%s: %s: tuning execution failed %d\n",
- mmc_hostname(card->host), __func__, err);
+ if (err) {
+ pr_warn("%s: %s: tuning execution failed %d. Restoring to previous clock %lu\n",
+ mmc_hostname(card->host), __func__, err,
+ host->clk_scaling.curr_freq);
+ mmc_set_clock(host, host->clk_scaling.curr_freq);
+ }
}
out:
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
index b81af11..d8cff35 100644
--- a/drivers/mmc/core/sdio_cis.c
+++ b/drivers/mmc/core/sdio_cis.c
@@ -55,7 +55,7 @@
for (i = 0; i < nr_strings; i++) {
buffer[i] = string;
- strlcpy(string, buf, strlen(buf));
+ strlcpy(string, buf, strlen(buf) + 1);
string += strlen(string) + 1;
buf += strlen(buf) + 1;
}
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index e285bfa..89e3472 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1656,6 +1656,13 @@
sg_miter_stop(&host->pio.sg_miter);
}
+static inline void msmsdcc_clear_pio_irq_mask(struct msmsdcc_host *host)
+{
+ writel_relaxed(readl_relaxed(host->base + MMCIMASK0) & ~MCI_IRQ_PIO,
+ host->base + MMCIMASK0);
+ mb();
+}
+
static irqreturn_t
msmsdcc_pio_irq(int irq, void *dev_id)
{
@@ -1668,7 +1675,7 @@
spin_lock(&host->lock);
- if (!atomic_read(&host->clks_on)) {
+ if (!atomic_read(&host->clks_on) || !host->curr.data) {
spin_unlock(&host->lock);
return IRQ_NONE;
}
@@ -1719,25 +1726,19 @@
msmsdcc_sg_stop(host);
local_irq_restore(flags);
+ if (!host->curr.xfer_remain) {
+ msmsdcc_clear_pio_irq_mask(host);
+ goto out_unlock;
+ }
+
if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE) {
writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
- (~(MCI_IRQ_PIO))) | MCI_RXDATAAVLBLMASK,
- host->base + MMCIMASK0);
- if (!host->curr.xfer_remain) {
- /*
- * back to back write to MASK0 register don't need
- * synchronization delay.
- */
- writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
- (~(MCI_IRQ_PIO))) | 0, host->base + MMCIMASK0);
- }
- mb();
- } else if (!host->curr.xfer_remain) {
- writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
- (~(MCI_IRQ_PIO))) | 0, host->base + MMCIMASK0);
+ ~MCI_IRQ_PIO) | MCI_RXDATAAVLBLMASK,
+ host->base + MMCIMASK0);
mb();
}
+out_unlock:
spin_unlock(&host->lock);
return IRQ_HANDLED;
@@ -1831,6 +1832,7 @@
msmsdcc_sps_exit_curr_xfer(host);
}
else if (host->curr.data) { /* Non DMA */
+ msmsdcc_clear_pio_irq_mask(host);
msmsdcc_reset_and_restore(host);
msmsdcc_stop_data(host);
msmsdcc_request_end(host, cmd->mrq);
@@ -2002,6 +2004,7 @@
/* Stop current SPS transfer */
msmsdcc_sps_exit_curr_xfer(host);
} else {
+ msmsdcc_clear_pio_irq_mask(host);
msmsdcc_reset_and_restore(host);
if (host->curr.data)
msmsdcc_stop_data(host);
@@ -4316,56 +4319,6 @@
return rc;
}
-/*
- * Work around of the unavailability of a power_reset functionality in SD cards
- * by turning the OFF & back ON the regulators supplying the SD card.
- */
-void msmsdcc_hw_reset(struct mmc_host *mmc)
-{
- struct mmc_card *card = mmc->card;
- struct msmsdcc_host *host = mmc_priv(mmc);
- int rc;
-
- /* Write-protection bits would be lost on a hardware reset in emmc */
- if (!card || !mmc_card_sd(card))
- return;
-
- pr_debug("%s: Starting h/w reset\n", mmc_hostname(host->mmc));
-
- if (host->plat->translate_vdd || host->plat->vreg_data) {
-
- /* Disable the regulators */
- if (host->plat->translate_vdd)
- rc = host->plat->translate_vdd(mmc_dev(mmc), 0);
- else if (host->plat->vreg_data)
- rc = msmsdcc_setup_vreg(host, false, false);
-
- if (rc) {
- pr_err("%s: Failed to disable voltage regulator\n",
- mmc_hostname(host->mmc));
- BUG_ON(rc);
- }
-
- /* 10ms delay for supply to reach the desired voltage level */
- usleep_range(10000, 12000);
-
- /* Enable the regulators */
- if (host->plat->translate_vdd)
- rc = host->plat->translate_vdd(mmc_dev(mmc), 1);
- else if (host->plat->vreg_data)
- rc = msmsdcc_setup_vreg(host, true, false);
-
- if (rc) {
- pr_err("%s: Failed to enable voltage regulator\n",
- mmc_hostname(host->mmc));
- BUG_ON(rc);
- }
-
- /* 10ms delay for supply to reach the desired voltage level */
- usleep_range(10000, 12000);
- }
-}
-
/**
* msmsdcc_stop_request - stops ongoing request
* @mmc: MMC host, running the request
@@ -4473,7 +4426,6 @@
.enable_sdio_irq = msmsdcc_enable_sdio_irq,
.start_signal_voltage_switch = msmsdcc_switch_io_voltage,
.execute_tuning = msmsdcc_execute_tuning,
- .hw_reset = msmsdcc_hw_reset,
.stop_request = msmsdcc_stop_request,
.get_xfer_remain = msmsdcc_get_xfer_remain,
.notify_load = msmsdcc_notify_load,
@@ -5361,7 +5313,7 @@
mrq = host->curr.mrq;
if (mrq && mrq->cmd) {
- if (!mrq->cmd->bkops_busy) {
+ if (!mrq->cmd->ignore_timeout) {
pr_info("%s: CMD%d: Request timeout\n",
mmc_hostname(host->mmc), mrq->cmd->opcode);
msmsdcc_dump_sdcc_state(host);
@@ -5380,6 +5332,7 @@
/* Stop current SPS transfer */
msmsdcc_sps_exit_curr_xfer(host);
} else {
+ msmsdcc_clear_pio_irq_mask(host);
msmsdcc_reset_and_restore(host);
msmsdcc_stop_data(host);
if (mrq->data && mrq->data->stop)
@@ -6112,6 +6065,10 @@
msmsdcc_msm_bus_cancel_work_and_set_vote(host, &mmc->ios);
+ /* Disable SDHCi mode if supported */
+ if (is_sdhci_supported(host))
+ writel_relaxed(0, (host->base + MCI_CORE_HC_MODE));
+
/* Apply Hard reset to SDCC to put it in power on default state */
msmsdcc_hard_reset(host);
@@ -6157,7 +6114,6 @@
mmc->caps |= plat->mmc_bus_width;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
- mmc->caps |= MMC_CAP_HW_RESET;
/*
* If we send the CMD23 before multi block write/read command
* then we need not to send CMD12 at the end of the transfer.
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index bcfde57..3668d75 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -197,6 +197,8 @@
#define MAX_TESTBUS 8
#define MCI_TESTBUS_ENA (1 << 3)
+#define MCI_CORE_HC_MODE 0x78
+
#define MCI_SDCC_DEBUG_REG 0x124
#define MCI_IRQENABLE \
@@ -456,6 +458,7 @@
#define MSMSDCC_SW_RST_CFG_BROKEN (1 << 11)
#define MSMSDCC_DATA_PEND_FOR_CMD53 (1 << 12)
#define MSMSDCC_TESTBUS_DEBUG (1 << 13)
+#define MSMSDCC_SDHCI_MODE_SUPPORTED (1 << 14)
#define set_hw_caps(h, val) ((h)->hw_caps |= val)
#define is_sps_mode(h) ((h)->hw_caps & MSMSDCC_SPS_BAM_SUP)
@@ -473,6 +476,7 @@
((h)->hw_caps & MSMSDCC_SW_RST_CFG_BROKEN)
#define is_data_pend_for_cmd53(h) ((h)->hw_caps & MSMSDCC_DATA_PEND_FOR_CMD53)
#define is_testbus_debug(h) ((h)->hw_caps & MSMSDCC_TESTBUS_DEBUG)
+#define is_sdhci_supported(h) ((h)->hw_caps & MSMSDCC_SDHCI_MODE_SUPPORTED)
/* Set controller capabilities based on version */
static inline void set_default_hw_caps(struct msmsdcc_host *host)
@@ -511,7 +515,8 @@
MSMSDCC_AUTO_CMD21 |
MSMSDCC_DATA_PEND_FOR_CMD53 |
MSMSDCC_TESTBUS_DEBUG |
- MSMSDCC_SW_RST_CFG_BROKEN;
+ MSMSDCC_SW_RST_CFG_BROKEN |
+ MSMSDCC_SDHCI_MODE_SUPPORTED;
}
int msmsdcc_set_pwrsave(struct mmc_host *mmc, int pwrsave);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 4ff8fea..3495f4d 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -89,6 +89,9 @@
#define CORE_TESTBUS_ENA (1 << 3)
#define CORE_TESTBUS_SEL2 (1 << 4)
+#define CORE_MCI_VERSION 0x050
+#define CORE_VERSION_310 0x10000011
+
/*
* Waiting until end of potential AHB access for data:
* 16 AHB cycles (160ns for 100MHz and 320ns for 50MHz) +
@@ -264,6 +267,7 @@
u32 curr_io_level;
struct completion pwr_irq_completion;
struct sdhci_msm_bus_vote msm_bus_vote;
+ struct device_attribute polling;
u32 clk_rate; /* Keeps track of current clock rate that is set */
};
@@ -593,6 +597,7 @@
int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
{
unsigned long flags;
+ int tuning_seq_cnt = 3;
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 */
@@ -619,17 +624,18 @@
}
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;
}
+retry:
+ /* first of all reset the tuning block */
+ rc = msm_init_cm_dll(host);
+ if (rc)
+ goto kfree;
+
phase = 0;
do {
struct mmc_command cmd = {0};
@@ -686,10 +692,12 @@
pr_debug("%s: %s: finally setting the tuning phase to %d\n",
mmc_hostname(mmc), __func__, phase);
} else {
+ if (--tuning_seq_cnt)
+ goto retry;
/* tuning failed */
pr_err("%s: %s: no tuning point found\n",
mmc_hostname(mmc), __func__);
- rc = -EAGAIN;
+ rc = -EIO;
}
kfree:
@@ -1838,6 +1846,42 @@
else
return 0;
}
+
+static ssize_t
+show_polling(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ int poll;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ poll = !!(host->mmc->caps & MMC_CAP_NEEDS_POLL);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", poll);
+}
+
+static ssize_t
+store_polling(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ int value;
+ unsigned long flags;
+
+ if (!kstrtou32(buf, 0, &value)) {
+ spin_lock_irqsave(&host->lock, flags);
+ if (value) {
+ host->mmc->caps |= MMC_CAP_NEEDS_POLL;
+ mmc_detect_change(host->mmc, 0);
+ } else {
+ host->mmc->caps &= ~MMC_CAP_NEEDS_POLL;
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ return count;
+}
+
static ssize_t
show_sdhci_max_bus_bw(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -2129,6 +2173,12 @@
struct sdhci_msm_host *msm_host = pltfm_host->priv;
u32 value;
int ret;
+ u32 version;
+
+ version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION);
+ /* Core version 3.1.0 doesn't need this workaround */
+ if (version == CORE_VERSION_310)
+ return;
value = readl_relaxed(msm_host->core_mem + CORE_MCI_DATA_CTRL);
value &= ~(u32)CORE_MCI_DPSM_ENABLE;
@@ -2410,7 +2460,6 @@
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_CORE_RUNTIME_PM;
msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR;
@@ -2464,6 +2513,16 @@
if (ret)
goto remove_host;
+ if (!gpio_is_valid(msm_host->pdata->status_gpio)) {
+ msm_host->polling.show = show_polling;
+ msm_host->polling.store = store_polling;
+ sysfs_attr_init(&msm_host->polling.attr);
+ msm_host->polling.attr.name = "polling";
+ msm_host->polling.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&pdev->dev, &msm_host->polling);
+ if (ret)
+ goto remove_max_bus_bw_file;
+ }
ret = pm_runtime_set_active(&pdev->dev);
if (ret)
pr_err("%s: %s: pm_runtime_set_active failed: err: %d\n",
@@ -2474,6 +2533,8 @@
/* Successful initialization */
goto out;
+remove_max_bus_bw_file:
+ device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
remove_host:
dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
@@ -2512,6 +2573,8 @@
0xffffffff);
pr_debug("%s: %s\n", dev_name(&pdev->dev), __func__);
+ if (!gpio_is_valid(msm_host->pdata->status_gpio))
+ device_remove_file(&pdev->dev, &msm_host->polling);
device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
sdhci_remove_host(host, dead);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index ce1c536..9527249 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1932,7 +1932,7 @@
if (host->ops->execute_tuning) {
spin_unlock(&host->lock);
enable_irq(host->irq);
- host->ops->execute_tuning(host, opcode);
+ err = host->ops->execute_tuning(host, opcode);
disable_irq(host->irq);
spin_lock(&host->lock);
goto out;
@@ -2312,9 +2312,11 @@
spin_lock_irqsave(&host->lock, flags);
if (host->mrq) {
- pr_err("%s: Timeout waiting for hardware "
- "interrupt.\n", mmc_hostname(host->mmc));
- sdhci_dumpregs(host);
+ if (!host->mrq->cmd->ignore_timeout) {
+ pr_err("%s: Timeout waiting for hardware interrupt.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ }
if (host->data) {
pr_info("%s: bytes to transfer: %d transferred: %d\n",
diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c
index efa09f6..7f02187 100644
--- a/drivers/mtd/devices/msm_qpic_nand.c
+++ b/drivers/mtd/devices/msm_qpic_nand.c
@@ -694,7 +694,7 @@
dma_addr_t dma_addr_param_info = 0;
struct onfi_param_page *onfi_param_page_ptr;
struct msm_nand_flash_onfi_data data;
- uint32_t onfi_signature;
+ uint32_t onfi_signature = 0;
/* SPS command/data descriptors */
uint32_t total_cnt = 13;
@@ -2229,7 +2229,8 @@
return err;
}
-#define BAM_APPS_PIPE_LOCK_GRP 0
+#define BAM_APPS_PIPE_LOCK_GRP0 0
+#define BAM_APPS_PIPE_LOCK_GRP1 1
/*
* This function allocates, configures, connects an end point and
* also registers event notification for an end point. It also allocates
@@ -2273,7 +2274,13 @@
}
sps_config->options = SPS_O_AUTO_ENABLE | SPS_O_DESC_DONE;
- sps_config->lock_group = BAM_APPS_PIPE_LOCK_GRP;
+
+ if (pipe_index == SPS_DATA_PROD_PIPE_INDEX ||
+ pipe_index == SPS_DATA_CONS_PIPE_INDEX)
+ sps_config->lock_group = BAM_APPS_PIPE_LOCK_GRP0;
+ else if (pipe_index == SPS_CMD_CONS_PIPE_INDEX)
+ sps_config->lock_group = BAM_APPS_PIPE_LOCK_GRP1;
+
/*
* Descriptor FIFO is a cyclic FIFO. If SPS_MAX_DESC_NUM descriptors
* are allowed to be submitted before we get any ack for any of them,
diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c
index f000df7..644a751 100644
--- a/drivers/net/ethernet/msm/ecm_ipa.c
+++ b/drivers/net/ethernet/msm/ecm_ipa.c
@@ -1307,6 +1307,8 @@
next_state = ECM_IPA_INITIALIZED;
else if (operation == ECM_IPA_CONNECT)
next_state = ECM_IPA_CONNECTED_AND_UP;
+ else if (operation == ECM_IPA_CLEANUP)
+ next_state = ECM_IPA_UNLOADED;
break;
case ECM_IPA_CONNECTED_AND_UP:
if (operation == ECM_IPA_STOP)
diff --git a/drivers/net/ethernet/msm/msm_rmnet_wwan.c b/drivers/net/ethernet/msm/msm_rmnet_wwan.c
index 98bdccc..3e4605f 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_wwan.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_wwan.c
@@ -188,6 +188,9 @@
__func__, skb);
netif_wake_queue(dev);
}
+ if (a2_mux_is_ch_empty(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]))
+ ipa_rm_inactivity_timer_release_resource(
+ ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
spin_unlock_irqrestore(&wwan_ptr->lock, flags);
}
@@ -533,6 +536,7 @@
__func__, skb);
}
spin_unlock_irqrestore(&wwan_ptr->lock, flags);
+ return ret;
exit:
ipa_rm_inactivity_timer_release_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
@@ -548,6 +552,7 @@
static void wwan_tx_timeout(struct net_device *dev)
{
pr_warning("[%s] wwan_tx_timeout(), data stall in UL\n", dev->name);
+ ipa_bam_reg_dump();
}
/**
diff --git a/drivers/net/ethernet/msm/qfec.c b/drivers/net/ethernet/msm/qfec.c
index 164b734..e68c395 100644
--- a/drivers/net/ethernet/msm/qfec.c
+++ b/drivers/net/ethernet/msm/qfec.c
@@ -2921,6 +2921,8 @@
priv->mii.dev = dev;
priv->mii.mdio_read = qfec_mdio_read;
priv->mii.mdio_write = qfec_mdio_write;
+ /* initialize mdio clock */
+ priv->mdio_clk = GMII_ADR_REG_CR_62;
/* map register regions */
ret = qfec_map_resource(
diff --git a/drivers/net/ethernet/msm/qfec.h b/drivers/net/ethernet/msm/qfec.h
index 525fd9c..a25436f 100644
--- a/drivers/net/ethernet/msm/qfec.h
+++ b/drivers/net/ethernet/msm/qfec.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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
@@ -411,6 +411,14 @@
# define GMII_ADR_REG_GR 0x000007c0 /* addr bits */
# define GMII_ADR_REG_RSVRD1 0x00000020 /* */
# define GMII_ADR_REG_CR 0x0000001c /* csr clock range */
+
+# define GMII_ADR_REG_CR_42 0x00000000 /* csr clock 42 */
+# define GMII_ADR_REG_CR_62 0x00000001 /* csr clock 62 */
+# define GMII_ADR_REG_CR_16 0x00000002 /* csr clock 16 */
+# define GMII_ADR_REG_CR_26 0x00000003 /* csr clock 26 */
+# define GMII_ADR_REG_CR_102 0x00000004 /* csr clock 102 */
+# define GMII_ADR_REG_CR_124 0x00000005 /* csr clock 124 */
+
# define GMII_ADR_REG_GW 0x00000002 /* gmii write */
# define GMII_ADR_REG_GB 0x00000001 /* gmii busy */
diff --git a/drivers/net/usb/rmnet_usb_ctrl.h b/drivers/net/usb/rmnet_usb.h
similarity index 61%
rename from drivers/net/usb/rmnet_usb_ctrl.h
rename to drivers/net/usb/rmnet_usb.h
index f0c169f..36002df 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.h
+++ b/drivers/net/usb/rmnet_usb.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
@@ -10,8 +10,8 @@
* GNU General Public License for more details.
*/
-#ifndef __RMNET_USB_CTRL_H
-#define __RMNET_USB_CTRL_H
+#ifndef __RMNET_USB_H
+#define __RMNET_USB_H
#include <linux/mutex.h>
#include <linux/usb.h>
@@ -19,7 +19,36 @@
#include <linux/usb/ch9.h>
#include <linux/usb/cdc.h>
-#define CTRL_DEV_MAX_LEN 10
+#define MAX_RMNET_DEVS 4
+#define MAX_RMNET_INSTS_PER_DEV 17
+#define TOTAL_RMNET_DEV_COUNT (MAX_RMNET_DEVS * MAX_RMNET_INSTS_PER_DEV)
+
+#define CTRL_DEV_MAX_LEN 10
+
+#define RMNET_CTRL_DEV_OPEN 0
+#define RMNET_CTRL_DEV_READY 1
+#define RMNET_CTRL_DEV_MUX_EN 2
+
+/*MUX header bit masks*/
+#define MUX_CTRL_MASK 0x1
+#define MUX_PAD_SHIFT 0x2
+
+/*max padding bytes for n byte alignment*/
+#define MAX_PAD_BYTES(n) (n-1)
+
+/*
+ *MUX Header Format
+ *BIT 0 : Mux type 0: Data, 1: control
+ *BIT 1: Reserved
+ *BIT 2-7: Pad bytes
+ *BIT 8-15: Mux ID
+ *BIT 16-31: PACKET_LEN_WITH_PADDING (Bytes)
+ */
+struct mux_hdr {
+ __u8 padding_info;
+ __u8 mux_id;
+ __le16 pkt_len_w_padding;
+} __packed;
struct rmnet_ctrl_dev {
@@ -28,6 +57,10 @@
struct cdev cdev;
struct device *devicep;
+ unsigned ch_id;
+
+ /*to identify the usb device*/
+ unsigned id;
struct usb_interface *intf;
unsigned int int_pipe;
@@ -48,24 +81,17 @@
struct workqueue_struct *wq;
struct work_struct get_encap_work;
- unsigned is_opened;
+ unsigned long status;
- bool is_connected;
+ bool claimed;
+
+ unsigned int mdm_wait_timeout;
/*input control lines (DSR, CTS, CD, RI)*/
unsigned int cbits_tolocal;
-
/*output control lines (DTR, RTS)*/
unsigned int cbits_tomdm;
- /*
- * track first resp available from mdm when it boots up
- * to avoid bigger timeout value used by qmuxd
- */
- bool resp_available;
-
- unsigned int mdm_wait_timeout;
-
/*counters*/
unsigned int snd_encap_cmd_cnt;
unsigned int get_encap_resp_cnt;
@@ -76,15 +102,16 @@
unsigned int zlp_cnt;
};
-extern struct rmnet_ctrl_dev *ctrl_dev[];
+extern struct workqueue_struct *usbnet_wq;
extern int rmnet_usb_ctrl_start_rx(struct rmnet_ctrl_dev *);
extern int rmnet_usb_ctrl_suspend(struct rmnet_ctrl_dev *dev);
-extern int rmnet_usb_ctrl_init(void);
-extern void rmnet_usb_ctrl_exit(void);
+extern int rmnet_usb_ctrl_init(int num_devs, int insts_per_dev);
+extern void rmnet_usb_ctrl_exit(int num_devs, int insts_per_dev);
extern int rmnet_usb_ctrl_probe(struct usb_interface *intf,
- struct usb_host_endpoint *status,
- struct rmnet_ctrl_dev *dev);
+ struct usb_host_endpoint *int_in,
+ unsigned long rmnet_devnum,
+ unsigned long *data);
extern void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *);
#endif /* __RMNET_USB_H*/
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
index 283c6b0..d1f3748 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.c
+++ b/drivers/net/usb/rmnet_usb_ctrl.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
@@ -19,10 +19,11 @@
#include <linux/poll.h>
#include <linux/ratelimit.h>
#include <linux/debugfs.h>
-#include "rmnet_usb_ctrl.h"
+#include "rmnet_usb.h"
-#define DEVICE_NAME "hsicctl"
-#define NUM_CTRL_CHANNELS 4
+static char *rmnet_dev_names[MAX_RMNET_DEVS] = {"hsicctl"};
+module_param_array(rmnet_dev_names, charp, NULL, S_IRUGO | S_IWUSR);
+
#define DEFAULT_READ_URB_LENGTH 0x1000
#define UNLINK_TIMEOUT_MS 500 /*random value*/
@@ -93,13 +94,19 @@
pr_info(x); \
} while (0)
-struct rmnet_ctrl_dev *ctrl_dev[NUM_CTRL_CHANNELS];
-struct class *ctrldev_classp;
-static dev_t ctrldev_num;
+/* passed in rmnet_usb_ctrl_init */
+static int num_devs;
+static int insts_per_dev;
+
+/* dynamically allocated 2-D array of num_devs*insts_per_dev ctrl_devs */
+static struct rmnet_ctrl_dev **ctrl_devs;
+static struct class *ctrldev_classp[MAX_RMNET_DEVS];
+static dev_t ctrldev_num[MAX_RMNET_DEVS];
struct ctrl_pkt {
size_t data_size;
void *data;
+ void *ctxt;
};
struct ctrl_pkt_list_elem {
@@ -109,18 +116,56 @@
static void resp_avail_cb(struct urb *);
-static int is_dev_connected(struct rmnet_ctrl_dev *dev)
+static int rmnet_usb_ctrl_dmux(struct ctrl_pkt_list_elem *clist)
{
- if (dev) {
- mutex_lock(&dev->dev_lock);
- if (!dev->is_connected) {
- mutex_unlock(&dev->dev_lock);
- return 0;
- }
- mutex_unlock(&dev->dev_lock);
- return 1;
+ struct mux_hdr *hdr;
+ size_t pad_len;
+ size_t total_len;
+ unsigned int mux_id;
+
+ hdr = (struct mux_hdr *)clist->cpkt.data;
+ pad_len = hdr->padding_info >> MUX_PAD_SHIFT;
+ if (pad_len > MAX_PAD_BYTES(4)) {
+ pr_err_ratelimited("%s: Invalid pad len %d\n", __func__,
+ pad_len);
+ return -EINVAL;
}
- return 0;
+
+ mux_id = hdr->mux_id;
+ if (!mux_id || mux_id > insts_per_dev) {
+ pr_err_ratelimited("%s: Invalid mux id %d\n", __func__, mux_id);
+ return -EINVAL;
+ }
+
+ total_len = le16_to_cpu(hdr->pkt_len_w_padding);
+ if (!total_len || !(total_len - pad_len)) {
+ pr_err_ratelimited("%s: Invalid pkt length %d\n", __func__,
+ total_len);
+ return -EINVAL;
+ }
+
+ clist->cpkt.data_size = total_len - pad_len;
+
+ return mux_id - 1;
+}
+
+static void rmnet_usb_ctrl_mux(unsigned int id, struct ctrl_pkt *cpkt)
+{
+ struct mux_hdr *hdr;
+ size_t len;
+ size_t pad_len = 0;
+
+ hdr = (struct mux_hdr *)cpkt->data;
+ hdr->mux_id = id + 1;
+ len = cpkt->data_size - sizeof(struct mux_hdr) - MAX_PAD_BYTES(4);
+
+ /*add padding if len is not 4 byte aligned*/
+ pad_len = ALIGN(len, 4) - len;
+
+ hdr->pkt_len_w_padding = cpu_to_le16(len + pad_len);
+ hdr->padding_info = (pad_len << MUX_PAD_SHIFT) | MUX_CTRL_MASK;
+
+ cpkt->data_size = sizeof(struct mux_hdr) + hdr->pkt_len_w_padding;
}
static void get_encap_work(struct work_struct *w)
@@ -130,6 +175,9 @@
container_of(w, struct rmnet_ctrl_dev, get_encap_work);
int status;
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
+ return;
+
udev = interface_to_usbdev(dev->intf);
status = usb_autopm_get_interface(dev->intf);
@@ -152,8 +200,10 @@
dev->get_encap_failure_cnt++;
usb_unanchor_urb(dev->rcvurb);
usb_autopm_put_interface(dev->intf);
- dev_err(dev->devicep,
- "%s: Error submitting Read URB %d\n", __func__, status);
+ if (status != -ENODEV)
+ dev_err(dev->devicep,
+ "%s: Error submitting Read URB %d\n",
+ __func__, status);
goto resubmit_int_urb;
}
@@ -166,7 +216,9 @@
status = usb_submit_urb(dev->inturb, GFP_KERNEL);
if (status) {
usb_unanchor_urb(dev->inturb);
- dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
+ if (status != -ENODEV)
+ dev_err(dev->devicep,
+ "%s: Error re-submitting Int URB %d\n",
__func__, status);
}
}
@@ -216,14 +268,17 @@
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
dev->resp_avail_cnt++;
- usb_mark_last_busy(udev);
- queue_work(dev->wq, &dev->get_encap_work);
-
- if (!dev->resp_available) {
- dev->resp_available = true;
+ /* If MUX is not enabled, wakeup up the open process
+ * upon first notify response available.
+ */
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
+ set_bit(RMNET_CTRL_DEV_READY, &dev->status);
wake_up(&dev->open_wait_queue);
}
+ usb_mark_last_busy(udev);
+ queue_work(dev->wq, &dev->get_encap_work);
+
return;
default:
dev_err(dev->devicep,
@@ -235,8 +290,10 @@
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
usb_unanchor_urb(urb);
- dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
- __func__, status);
+ if (status != -ENODEV)
+ dev_err(dev->devicep,
+ "%s: Error re-submitting Int URB %d\n",
+ __func__, status);
}
return;
@@ -246,9 +303,9 @@
{
struct usb_device *udev;
struct ctrl_pkt_list_elem *list_elem = NULL;
- struct rmnet_ctrl_dev *dev = urb->context;
+ struct rmnet_ctrl_dev *rx_dev, *dev = urb->context;
void *cpkt;
- int status = 0;
+ int ch_id, status = 0;
size_t cpkt_size = 0;
udev = interface_to_usbdev(dev->intf);
@@ -258,7 +315,6 @@
switch (urb->status) {
case 0:
/*success*/
- dev->get_encap_resp_cnt++;
break;
/*do not resubmit*/
@@ -303,11 +359,27 @@
}
memcpy(list_elem->cpkt.data, cpkt, cpkt_size);
list_elem->cpkt.data_size = cpkt_size;
- spin_lock(&dev->rx_lock);
- list_add_tail(&list_elem->list, &dev->rx_list);
- spin_unlock(&dev->rx_lock);
- wake_up(&dev->read_wait_queue);
+ rx_dev = dev;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ ch_id = rmnet_usb_ctrl_dmux(list_elem);
+ if (ch_id < 0) {
+ kfree(list_elem->cpkt.data);
+ kfree(list_elem);
+ goto resubmit_int_urb;
+ }
+
+ rx_dev = &ctrl_devs[dev->id][ch_id];
+ }
+
+ rx_dev->get_encap_resp_cnt++;
+
+ spin_lock(&rx_dev->rx_lock);
+ list_add_tail(&list_elem->list, &rx_dev->rx_list);
+ spin_unlock(&rx_dev->rx_lock);
+
+ wake_up(&rx_dev->read_wait_queue);
resubmit_int_urb:
/*check if it is already submitted in resume*/
@@ -317,7 +389,9 @@
status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
if (status) {
usb_unanchor_urb(dev->inturb);
- dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
+ if (status != -ENODEV)
+ dev_err(dev->devicep,
+ "%s: Error re-submitting Int URB %d\n",
__func__, status);
}
}
@@ -331,8 +405,9 @@
retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
if (retval < 0) {
usb_unanchor_urb(dev->inturb);
- dev_err(dev->devicep, "%s Intr submit %d\n", __func__,
- retval);
+ if (retval != -ENODEV)
+ dev_err(dev->devicep,
+ "%s Intr submit %d\n", __func__, retval);
}
return retval;
@@ -340,8 +415,6 @@
static int rmnet_usb_ctrl_alloc_rx(struct rmnet_ctrl_dev *dev)
{
- int retval = -ENOMEM;
-
dev->rcvurb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->rcvurb) {
pr_err("%s: Error allocating read urb\n", __func__);
@@ -367,14 +440,14 @@
kfree(dev->rcvbuf);
kfree(dev->in_ctlreq);
- return retval;
+ return -ENOMEM;
}
static int rmnet_usb_ctrl_write_cmd(struct rmnet_ctrl_dev *dev)
{
struct usb_device *udev;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENODEV;
udev = interface_to_usbdev(dev->intf);
@@ -389,7 +462,8 @@
static void ctrl_write_callback(struct urb *urb)
{
- struct rmnet_ctrl_dev *dev = urb->context;
+ struct ctrl_pkt *cpkt = urb->context;
+ struct rmnet_ctrl_dev *dev = cpkt->ctxt;
if (urb->status) {
dev->tx_ctrl_err_cnt++;
@@ -400,18 +474,19 @@
kfree(urb->setup_packet);
kfree(urb->transfer_buffer);
usb_free_urb(urb);
+ kfree(cpkt);
usb_autopm_put_interface_async(dev->intf);
}
-static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
- size_t size)
+static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev,
+ struct ctrl_pkt *cpkt, size_t size)
{
int result;
struct urb *sndurb;
struct usb_ctrlrequest *out_ctlreq;
struct usb_device *udev;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENETRESET;
udev = interface_to_usbdev(dev->intf);
@@ -435,12 +510,12 @@
out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
out_ctlreq->wValue = 0;
out_ctlreq->wIndex = dev->intf->cur_altsetting->desc.bInterfaceNumber;
- out_ctlreq->wLength = cpu_to_le16(size);
+ out_ctlreq->wLength = cpu_to_le16(cpkt->data_size);
usb_fill_control_urb(sndurb, udev,
usb_sndctrlpipe(udev, 0),
- (unsigned char *)out_ctlreq, (void *)buf, size,
- ctrl_write_callback, dev);
+ (unsigned char *)out_ctlreq, (void *)cpkt->data,
+ cpkt->data_size, ctrl_write_callback, cpkt);
result = usb_autopm_get_interface(dev->intf);
if (result < 0) {
@@ -461,7 +536,9 @@
dev->snd_encap_cmd_cnt++;
result = usb_submit_urb(sndurb, GFP_KERNEL);
if (result < 0) {
- dev_err(dev->devicep, "%s: Submit URB error %d\n",
+ if (result != -ENODEV)
+ dev_err(dev->devicep,
+ "%s: Submit URB error %d\n",
__func__, result);
dev->snd_encap_cmd_cnt--;
usb_autopm_put_interface(dev->intf);
@@ -483,16 +560,15 @@
if (!dev)
return -ENODEV;
- if (dev->is_opened)
+ if (test_bit(RMNET_CTRL_DEV_OPEN, &dev->status))
goto already_opened;
- /*block open to get first response available from mdm*/
- if (dev->mdm_wait_timeout && !dev->resp_available) {
+ if (dev->mdm_wait_timeout &&
+ !test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
retval = wait_event_interruptible_timeout(
- dev->open_wait_queue,
- dev->resp_available,
- msecs_to_jiffies(dev->mdm_wait_timeout *
- 1000));
+ dev->open_wait_queue,
+ test_bit(RMNET_CTRL_DEV_READY, &dev->status),
+ msecs_to_jiffies(dev->mdm_wait_timeout * 1000));
if (retval == 0) {
dev_err(dev->devicep, "%s: Timeout opening %s\n",
__func__, dev->name);
@@ -504,15 +580,13 @@
}
}
- if (!dev->resp_available) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Connection timedout opening %s\n",
__func__, dev->name);
return -ETIMEDOUT;
}
- mutex_lock(&dev->dev_lock);
- dev->is_opened = 1;
- mutex_unlock(&dev->dev_lock);
+ set_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
file->private_data = dev;
@@ -547,9 +621,7 @@
}
spin_unlock_irqrestore(&dev->rx_lock, flag);
- mutex_lock(&dev->dev_lock);
- dev->is_opened = 0;
- mutex_unlock(&dev->dev_lock);
+ clear_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
time = usb_wait_anchor_empty_timeout(&dev->tx_submitted,
UNLINK_TIMEOUT_MS);
@@ -571,7 +643,7 @@
return POLLERR;
poll_wait(file, &dev->read_wait_queue, wait);
- if (!is_dev_connected(dev)) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Device not connected\n",
__func__);
return POLLERR;
@@ -588,6 +660,7 @@
{
int retval = 0;
int bytes_to_read;
+ unsigned int hdr_len = 0;
struct rmnet_ctrl_dev *dev;
struct ctrl_pkt_list_elem *list_elem = NULL;
unsigned long flags;
@@ -599,7 +672,7 @@
DBG("%s: Read from %s\n", __func__, dev->name);
ctrl_read:
- if (!is_dev_connected(dev)) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Device not connected\n",
__func__);
return -ENETRESET;
@@ -609,8 +682,8 @@
spin_unlock_irqrestore(&dev->rx_lock, flags);
retval = wait_event_interruptible(dev->read_wait_queue,
- !list_empty(&dev->rx_list) ||
- !is_dev_connected(dev));
+ !list_empty(&dev->rx_list) ||
+ !test_bit(RMNET_CTRL_DEV_READY, &dev->status));
if (retval < 0)
return retval;
@@ -628,7 +701,10 @@
}
spin_unlock_irqrestore(&dev->rx_lock, flags);
- if (copy_to_user(buf, list_elem->cpkt.data, bytes_to_read)) {
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status))
+ hdr_len = sizeof(struct mux_hdr);
+
+ if (copy_to_user(buf, list_elem->cpkt.data + hdr_len, bytes_to_read)) {
dev_err(dev->devicep,
"%s: copy_to_user failed for %s\n",
__func__, dev->name);
@@ -651,7 +727,10 @@
size_t size, loff_t *pos)
{
int status;
+ size_t total_len;
void *wbuf;
+ void *actual_data;
+ struct ctrl_pkt *cpkt;
struct rmnet_ctrl_dev *dev = file->private_data;
if (!dev)
@@ -660,26 +739,46 @@
if (size <= 0)
return -EINVAL;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENETRESET;
DBG("%s: Writing %i bytes on %s\n", __func__, size, dev->name);
- wbuf = kmalloc(size , GFP_KERNEL);
+ total_len = size;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status))
+ total_len += sizeof(struct mux_hdr) + MAX_PAD_BYTES(4);
+
+ wbuf = kmalloc(total_len , GFP_KERNEL);
if (!wbuf)
return -ENOMEM;
- status = copy_from_user(wbuf , buf, size);
+ cpkt = kmalloc(sizeof(struct ctrl_pkt), GFP_KERNEL);
+ if (!cpkt) {
+ kfree(wbuf);
+ return -ENOMEM;
+ }
+ actual_data = cpkt->data = wbuf;
+ cpkt->data_size = total_len;
+ cpkt->ctxt = dev;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ actual_data = wbuf + sizeof(struct mux_hdr);
+ rmnet_usb_ctrl_mux(dev->ch_id, cpkt);
+ }
+
+ status = copy_from_user(actual_data, buf, size);
if (status) {
dev_err(dev->devicep,
"%s: Unable to copy data from userspace %d\n",
__func__, status);
kfree(wbuf);
+ kfree(cpkt);
return status;
}
DUMP_BUFFER("Write: ", size, buf);
- status = rmnet_usb_ctrl_write(dev, wbuf, size);
+ status = rmnet_usb_ctrl_write(dev, cpkt, size);
if (status == size)
return size;
@@ -780,51 +879,43 @@
};
int rmnet_usb_ctrl_probe(struct usb_interface *intf,
- struct usb_host_endpoint *int_in, struct rmnet_ctrl_dev *dev)
+ struct usb_host_endpoint *int_in,
+ unsigned long rmnet_devnum,
+ unsigned long *data)
{
+ struct rmnet_ctrl_dev *dev = NULL;
u16 wMaxPacketSize;
struct usb_endpoint_descriptor *ep;
- struct usb_device *udev;
+ struct usb_device *udev = interface_to_usbdev(intf);
int interval;
- int ret = 0;
+ int ret = 0, n;
- udev = interface_to_usbdev(intf);
+ /* Find next available ctrl_dev */
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[rmnet_devnum][n];
+ if (!dev->claimed)
+ break;
+ }
- if (!dev) {
- pr_err("%s: Ctrl device not found\n", __func__);
+ if (!dev || n == insts_per_dev) {
+ pr_err("%s: No available ctrl devices for %lu\n", __func__,
+ rmnet_devnum);
return -ENODEV;
}
+
dev->int_pipe = usb_rcvintpipe(udev,
int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
- mutex_lock(&dev->dev_lock);
dev->intf = intf;
- /*TBD: for now just update CD status*/
- dev->cbits_tolocal = ACM_CTRL_CD;
+ dev->id = rmnet_devnum;
- /*send DTR high to modem*/
- dev->cbits_tomdm = ACM_CTRL_DTR;
- mutex_unlock(&dev->dev_lock);
-
- dev->resp_available = false;
dev->snd_encap_cmd_cnt = 0;
dev->get_encap_resp_cnt = 0;
dev->resp_avail_cnt = 0;
dev->tx_ctrl_err_cnt = 0;
dev->set_ctrl_line_state_cnt = 0;
- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- USB_CDC_REQ_SET_CONTROL_LINE_STATE,
- (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
- dev->cbits_tomdm,
- dev->intf->cur_altsetting->desc.bInterfaceNumber,
- NULL, 0, USB_CTRL_SET_TIMEOUT);
- if (ret < 0)
- return ret;
-
- dev->set_ctrl_line_state_cnt++;
-
dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->inturb) {
dev_err(dev->devicep, "Error allocating int urb\n");
@@ -861,22 +952,39 @@
usb_mark_last_busy(udev);
ret = rmnet_usb_ctrl_start_rx(dev);
- if (!ret)
- dev->is_connected = true;
+ if (ret) {
+ usb_free_urb(dev->inturb);
+ kfree(dev->intbuf);
+ return ret;
+ }
- return ret;
+ dev->claimed = true;
+
+ /*mux info is passed to data parameter*/
+ if (*data)
+ set_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status);
+
+ *data = (unsigned long)dev;
+
+ /* If MUX is enabled, wakeup the open process here */
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ set_bit(RMNET_CTRL_DEV_READY, &dev->status);
+ wake_up(&dev->open_wait_queue);
+ }
+
+ return 0;
}
void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
{
+ dev->claimed = false;
+
+ clear_bit(RMNET_CTRL_DEV_READY, &dev->status);
mutex_lock(&dev->dev_lock);
-
/*TBD: for now just update CD status*/
dev->cbits_tolocal = ~ACM_CTRL_CD;
-
dev->cbits_tomdm = ~ACM_CTRL_DTR;
- dev->is_connected = false;
mutex_unlock(&dev->dev_lock);
wake_up(&dev->read_wait_queue);
@@ -901,50 +1009,53 @@
struct rmnet_ctrl_dev *dev;
char *buf;
int ret;
- int i;
+ int i, n;
int temp = 0;
buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
- dev = ctrl_dev[i];
- if (!dev)
- continue;
-
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
- "\n#ctrl_dev: %p Name: %s#\n"
- "snd encap cmd cnt %u\n"
- "resp avail cnt: %u\n"
- "get encap resp cnt: %u\n"
- "set ctrl line state cnt: %u\n"
- "tx_err_cnt: %u\n"
- "cbits_tolocal: %d\n"
- "cbits_tomdm: %d\n"
- "mdm_wait_timeout: %u\n"
- "zlp_cnt: %u\n"
- "get_encap_failure_cnt %u\n"
- "dev opened: %s\n",
- dev, dev->name,
- dev->snd_encap_cmd_cnt,
- dev->resp_avail_cnt,
- dev->get_encap_resp_cnt,
- dev->set_ctrl_line_state_cnt,
- dev->tx_ctrl_err_cnt,
- dev->cbits_tolocal,
- dev->cbits_tomdm,
- dev->mdm_wait_timeout,
- dev->zlp_cnt,
- dev->get_encap_failure_cnt,
- dev->is_opened ? "OPEN" : "CLOSE");
-
+ for (i = 0; i < num_devs; i++) {
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\n#ctrl_dev: %p Name: %s#\n"
+ "snd encap cmd cnt %u\n"
+ "resp avail cnt: %u\n"
+ "get encap resp cnt: %u\n"
+ "set ctrl line state cnt: %u\n"
+ "tx_err_cnt: %u\n"
+ "cbits_tolocal: %d\n"
+ "cbits_tomdm: %d\n"
+ "mdm_wait_timeout: %u\n"
+ "zlp_cnt: %u\n"
+ "get_encap_failure_cnt %u\n"
+ "RMNET_CTRL_DEV_MUX_EN: %d\n"
+ "RMNET_CTRL_DEV_OPEN: %d\n"
+ "RMNET_CTRL_DEV_READY: %d\n",
+ dev, dev->name,
+ dev->snd_encap_cmd_cnt,
+ dev->resp_avail_cnt,
+ dev->get_encap_resp_cnt,
+ dev->set_ctrl_line_state_cnt,
+ dev->tx_ctrl_err_cnt,
+ dev->cbits_tolocal,
+ dev->cbits_tomdm,
+ dev->mdm_wait_timeout,
+ dev->zlp_cnt,
+ dev->get_encap_failure_cnt,
+ test_bit(RMNET_CTRL_DEV_MUX_EN,
+ &dev->status),
+ test_bit(RMNET_CTRL_DEV_OPEN,
+ &dev->status),
+ test_bit(RMNET_CTRL_DEV_READY,
+ &dev->status));
+ }
}
ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
-
kfree(buf);
-
return ret;
}
@@ -952,19 +1063,19 @@
buf, size_t count, loff_t *ppos)
{
struct rmnet_ctrl_dev *dev;
- int i;
+ int i, n;
- for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
- dev = ctrl_dev[i];
- if (!dev)
- continue;
+ for (i = 0; i < num_devs; i++) {
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
- dev->snd_encap_cmd_cnt = 0;
- dev->resp_avail_cnt = 0;
- dev->get_encap_resp_cnt = 0;
- dev->set_ctrl_line_state_cnt = 0;
- dev->tx_ctrl_err_cnt = 0;
- dev->zlp_cnt = 0;
+ dev->snd_encap_cmd_cnt = 0;
+ dev->resp_avail_cnt = 0;
+ dev->get_encap_resp_cnt = 0;
+ dev->set_ctrl_line_state_cnt = 0;
+ dev->tx_ctrl_err_cnt = 0;
+ dev->zlp_cnt = 0;
+ }
}
return count;
}
@@ -999,144 +1110,152 @@
static void rmnet_usb_ctrl_debugfs_exit(void) { }
#endif
-int rmnet_usb_ctrl_init(void)
+int rmnet_usb_ctrl_init(int no_rmnet_devs, int no_rmnet_insts_per_dev)
{
struct rmnet_ctrl_dev *dev;
- int n;
+ int i, n;
int status;
- for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+ num_devs = no_rmnet_devs;
+ insts_per_dev = no_rmnet_insts_per_dev;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- status = -ENOMEM;
- goto error0;
- }
- /*for debug purpose*/
- snprintf(dev->name, CTRL_DEV_MAX_LEN, "hsicctl%d", n);
+ ctrl_devs = kzalloc(num_devs * sizeof(*ctrl_devs), GFP_KERNEL);
+ if (!ctrl_devs)
+ return -ENOMEM;
- dev->wq = create_singlethread_workqueue(dev->name);
- if (!dev->wq) {
- pr_err("unable to allocate workqueue");
- kfree(dev);
- goto error0;
- }
+ for (i = 0; i < num_devs; i++) {
+ ctrl_devs[i] = kzalloc(insts_per_dev * sizeof(*ctrl_devs[i]),
+ GFP_KERNEL);
+ if (!ctrl_devs[i])
+ return -ENOMEM;
- mutex_init(&dev->dev_lock);
- spin_lock_init(&dev->rx_lock);
- init_waitqueue_head(&dev->read_wait_queue);
- init_waitqueue_head(&dev->open_wait_queue);
- INIT_LIST_HEAD(&dev->rx_list);
- init_usb_anchor(&dev->tx_submitted);
- init_usb_anchor(&dev->rx_submitted);
- INIT_WORK(&dev->get_encap_work, get_encap_work);
-
- status = rmnet_usb_ctrl_alloc_rx(dev);
- if (status < 0) {
- kfree(dev);
- goto error0;
- }
-
- ctrl_dev[n] = dev;
- }
-
- status = alloc_chrdev_region(&ctrldev_num, 0, NUM_CTRL_CHANNELS,
- DEVICE_NAME);
- if (IS_ERR_VALUE(status)) {
- pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
- __func__, status);
- goto error0;
- }
-
- ctrldev_classp = class_create(THIS_MODULE, DEVICE_NAME);
- if (IS_ERR(ctrldev_classp)) {
- pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
- status = -ENOMEM;
- goto error1;
- }
- for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
- cdev_init(&ctrl_dev[n]->cdev, &ctrldev_fops);
- ctrl_dev[n]->cdev.owner = THIS_MODULE;
-
- status = cdev_add(&ctrl_dev[n]->cdev, (ctrldev_num + n), 1);
-
+ status = alloc_chrdev_region(&ctrldev_num[i], 0, insts_per_dev,
+ rmnet_dev_names[i]);
if (IS_ERR_VALUE(status)) {
- pr_err("%s: cdev_add() ret %i\n", __func__, status);
- kfree(ctrl_dev[n]);
- goto error2;
+ pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
+ __func__, status);
+ return status;
}
- ctrl_dev[n]->devicep =
- device_create(ctrldev_classp, NULL,
- (ctrldev_num + n), NULL,
- DEVICE_NAME "%d", n);
+ ctrldev_classp[i] = class_create(THIS_MODULE,
+ rmnet_dev_names[i]);
+ if (IS_ERR(ctrldev_classp[i])) {
+ pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
+ status = PTR_ERR(ctrldev_classp[i]);
+ return status;
+ }
- if (IS_ERR(ctrl_dev[n]->devicep)) {
- pr_err("%s: device_create() ENOMEM\n", __func__);
- status = -ENOMEM;
- cdev_del(&ctrl_dev[n]->cdev);
- kfree(ctrl_dev[n]);
- goto error2;
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
+
+ /*for debug purpose*/
+ snprintf(dev->name, CTRL_DEV_MAX_LEN, "%s%d",
+ rmnet_dev_names[i], n);
+
+ dev->wq = create_singlethread_workqueue(dev->name);
+ if (!dev->wq) {
+ pr_err("unable to allocate workqueue");
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ dev->ch_id = n;
+
+ mutex_init(&dev->dev_lock);
+ spin_lock_init(&dev->rx_lock);
+ init_waitqueue_head(&dev->read_wait_queue);
+ init_waitqueue_head(&dev->open_wait_queue);
+ INIT_LIST_HEAD(&dev->rx_list);
+ init_usb_anchor(&dev->tx_submitted);
+ init_usb_anchor(&dev->rx_submitted);
+ INIT_WORK(&dev->get_encap_work, get_encap_work);
+
+ cdev_init(&dev->cdev, &ctrldev_fops);
+ dev->cdev.owner = THIS_MODULE;
+
+ status = cdev_add(&dev->cdev, (ctrldev_num[i] + n), 1);
+ if (status) {
+ pr_err("%s: cdev_add() ret %i\n", __func__,
+ status);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
+
+ dev->devicep = device_create(ctrldev_classp[i], NULL,
+ (ctrldev_num[i] + n), NULL,
+ "%s%d", rmnet_dev_names[i],
+ n);
+ if (IS_ERR(dev->devicep)) {
+ pr_err("%s: device_create() returned %ld\n",
+ __func__, PTR_ERR(dev->devicep));
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return PTR_ERR(dev->devicep);
+ }
+
+ /*create /sys/class/hsicctl/hsicctlx/modem_wait*/
+ status = device_create_file(dev->devicep,
+ &dev_attr_modem_wait);
+ if (status) {
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
+ dev_set_drvdata(dev->devicep, dev);
+
+ status = rmnet_usb_ctrl_alloc_rx(dev);
+ if (status) {
+ device_remove_file(dev->devicep,
+ &dev_attr_modem_wait);
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
}
- /*create /sys/class/hsicctl/hsicctlx/modem_wait*/
- status = device_create_file(ctrl_dev[n]->devicep,
- &dev_attr_modem_wait);
- if (status) {
- device_destroy(ctrldev_classp,
- MKDEV(MAJOR(ctrldev_num), n));
- cdev_del(&ctrl_dev[n]->cdev);
- kfree(ctrl_dev[n]);
- goto error2;
- }
- dev_set_drvdata(ctrl_dev[n]->devicep, ctrl_dev[n]);
}
rmnet_usb_ctrl_debugfs_init();
pr_info("rmnet usb ctrl Initialized.\n");
return 0;
-
-error2:
- while (--n >= 0) {
- cdev_del(&ctrl_dev[n]->cdev);
- device_destroy(ctrldev_classp,
- MKDEV(MAJOR(ctrldev_num), n));
- }
-
- class_destroy(ctrldev_classp);
- n = NUM_CTRL_CHANNELS;
-error1:
- unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
-error0:
- while (--n >= 0)
- kfree(ctrl_dev[n]);
-
- return status;
}
-void rmnet_usb_ctrl_exit(void)
+static void free_rmnet_ctrl_dev(struct rmnet_ctrl_dev *dev)
{
- int i;
+ kfree(dev->in_ctlreq);
+ kfree(dev->rcvbuf);
+ kfree(dev->intbuf);
+ usb_free_urb(dev->rcvurb);
+ usb_free_urb(dev->inturb);
+ device_remove_file(dev->devicep, &dev_attr_modem_wait);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+}
- for (i = 0; i < NUM_CTRL_CHANNELS; ++i) {
- if (!ctrl_dev[i])
- return;
+void rmnet_usb_ctrl_exit(int no_rmnet_devs, int no_rmnet_insts_per_dev)
+{
+ int i, n;
- kfree(ctrl_dev[i]->in_ctlreq);
- kfree(ctrl_dev[i]->rcvbuf);
- kfree(ctrl_dev[i]->intbuf);
- usb_free_urb(ctrl_dev[i]->rcvurb);
- usb_free_urb(ctrl_dev[i]->inturb);
-#if defined(DEBUG)
- device_remove_file(ctrl_dev[i]->devicep, &dev_attr_modem_wait);
-#endif
- cdev_del(&ctrl_dev[i]->cdev);
- destroy_workqueue(ctrl_dev[i]->wq);
- kfree(ctrl_dev[i]);
- ctrl_dev[i] = NULL;
- device_destroy(ctrldev_classp, MKDEV(MAJOR(ctrldev_num), i));
+ for (i = 0; i < no_rmnet_devs; i++) {
+ for (n = 0; n < no_rmnet_insts_per_dev; n++)
+ free_rmnet_ctrl_dev(&ctrl_devs[i][n]);
+
+ kfree(ctrl_devs[i]);
+
+ class_destroy(ctrldev_classp[i]);
+ if (ctrldev_num[i])
+ unregister_chrdev_region(ctrldev_num[i], insts_per_dev);
}
- class_destroy(ctrldev_classp);
- unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+ kfree(ctrl_devs);
rmnet_usb_ctrl_debugfs_exit();
}
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index 2a57b36..f9bb5c8 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.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
@@ -17,14 +17,48 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/usb.h>
+#include <linux/ratelimit.h>
#include <linux/usb/usbnet.h>
#include <linux/msm_rmnet.h>
-#include "rmnet_usb_ctrl.h"
+#include "rmnet_usb.h"
#define RMNET_DATA_LEN 2000
-#define HEADROOM_FOR_QOS 8
+#define RMNET_HEADROOM_W_MUX (sizeof(struct mux_hdr) + \
+ sizeof(struct QMI_QOS_HDR_S))
+#define RMNET_HEADROOM sizeof(struct QMI_QOS_HDR_S)
+#define RMNET_TAILROOM MAX_PAD_BYTES(4);
+static unsigned int no_rmnet_devs = 1;
+module_param(no_rmnet_devs, uint, S_IRUGO | S_IWUSR);
+
+unsigned int no_rmnet_insts_per_dev = 4;
+module_param(no_rmnet_insts_per_dev, uint, S_IRUGO | S_IWUSR);
+
+/*
+ * To support mux on multiple devices, bit position represents device
+ * and value represnts if mux is enabled or disabled.
+ * e.g. bit 0: mdm over HSIC, bit1: mdm over hsusb
+ */
+static unsigned long mux_enabled;
+module_param(mux_enabled, ulong, S_IRUGO | S_IWUSR);
+
+static unsigned int no_fwd_rmnet_links;
+module_param(no_fwd_rmnet_links, uint, S_IRUGO | S_IWUSR);
+
+struct usbnet *unet_list[TOTAL_RMNET_DEV_COUNT];
+
+/* net device name prefixes, indexed by driver_info->data */
+static const char * const rmnet_names[] = {
+ "rmnet_usb%d",
+ "rmnet2_usb%d",
+};
+
+/* net device reverse link name prefixes, indexed by driver_info->data */
+static const char * const rev_rmnet_names[] = {
+ "rev_rmnet_usb%d",
+ "rev_rmnet2_usb%d",
+};
static int data_msg_dbg_mask;
enum {
@@ -80,43 +114,121 @@
#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
-static void rmnet_usb_setup(struct net_device *);
+static int rmnet_data_start(void);
+static bool rmnet_data_init;
+
+static int rmnet_init(const char *val, const struct kernel_param *kp)
+{
+ int ret = 0;
+
+ if (rmnet_data_init) {
+ pr_err("dynamic setting rmnet params currently unsupported\n");
+ return -EINVAL;
+ }
+
+ ret = param_set_bool(val, kp);
+ if (ret)
+ return ret;
+
+ rmnet_data_start();
+
+ return ret;
+}
+
+static struct kernel_param_ops rmnet_init_ops = {
+ .set = rmnet_init,
+ .get = param_get_bool,
+};
+module_param_cb(rmnet_data_init, &rmnet_init_ops, &rmnet_data_init,
+ S_IRUGO | S_IWUSR);
+
+static void rmnet_usb_setup(struct net_device *, int mux_enabled);
static int rmnet_ioctl(struct net_device *, struct ifreq *, int);
static int rmnet_usb_suspend(struct usb_interface *iface, pm_message_t message)
{
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(iface);
struct rmnet_ctrl_dev *dev;
+ int i, n, rdev_cnt, unet_id;
+ int retval = 0;
- unet = usb_get_intfdata(iface);
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
- if (work_busy(&dev->get_encap_work))
- return -EBUSY;
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ spin_lock_irq(&unet->txq.lock);
+ if (work_busy(&dev->get_encap_work) || unet->txq.qlen) {
+ spin_unlock_irq(&unet->txq.lock);
+ retval = -EBUSY;
+ goto abort_suspend;
+ }
- if (usbnet_suspend(iface, message))
- return -EBUSY;
+ set_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
- usb_kill_anchored_urbs(&dev->rx_submitted);
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+ if (work_busy(&dev->get_encap_work)) {
+ spin_lock_irq(&unet->txq.lock);
+ clear_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
+ retval = -EBUSY;
+ goto abort_suspend;
+ }
+ }
+
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ netif_device_detach(unet->net);
+ usbnet_terminate_urbs(unet);
+ netif_device_attach(unet->net);
+ }
return 0;
+
+abort_suspend:
+ for (i = 0; i < n; i++) {
+ unet_id = i + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_start_rx(dev);
+ spin_lock_irq(&unet->txq.lock);
+ clear_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
+ }
+ return retval;
}
static int rmnet_usb_resume(struct usb_interface *iface)
{
- int retval = 0;
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(iface);
struct rmnet_ctrl_dev *dev;
+ int n, rdev_cnt, unet_id;
- unet = usb_get_intfdata(iface);
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
- usbnet_resume(iface);
- retval = rmnet_usb_ctrl_start_rx(dev);
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_start_rx(dev);
+ usb_set_intfdata(iface, unet);
+ unet->suspend_count = 1;
+ usbnet_resume(iface);
+ }
- return retval;
+ return 0;
}
static int rmnet_usb_bind(struct usbnet *usbnet, struct usb_interface *iface)
@@ -125,9 +237,13 @@
struct usb_host_endpoint *bulk_in = NULL;
struct usb_host_endpoint *bulk_out = NULL;
struct usb_host_endpoint *int_in = NULL;
+ struct driver_info *info = usbnet->driver_info;
int status = 0;
int i;
int numends;
+ bool mux;
+
+ mux = test_bit(info->data, &mux_enabled);
numends = iface->cur_altsetting->desc.bNumEndpoints;
for (i = 0; i < numends; i++) {
@@ -158,13 +274,72 @@
usbnet->status = int_in;
/*change name of net device to rmnet_usbx here*/
- strlcpy(usbnet->net->name, "rmnet_usb%d", IFNAMSIZ);
+ if (mux && (info->in > no_fwd_rmnet_links))
+ strlcpy(usbnet->net->name, rev_rmnet_names[info->data],
+ IFNAMSIZ);
+ else
+ strlcpy(usbnet->net->name, rmnet_names[info->data],
+ IFNAMSIZ);
- /*TBD: update rx_urb_size, curently set to eth frame len by usbnet*/
+ if (mux)
+ usbnet->rx_urb_size = usbnet->hard_mtu + sizeof(struct mux_hdr)
+ + MAX_PAD_BYTES(4);
out:
return status;
}
+static int rmnet_usb_data_dmux(struct sk_buff *skb, struct urb *rx_urb)
+{
+ struct mux_hdr *hdr;
+ size_t pad_len;
+ size_t total_len;
+ unsigned int mux_id;
+
+ hdr = (struct mux_hdr *)skb->data;
+ mux_id = hdr->mux_id;
+ if (!mux_id || mux_id > no_rmnet_insts_per_dev) {
+ pr_err_ratelimited("%s: Invalid data channel id %u.\n",
+ __func__, mux_id);
+ return -EINVAL;
+ }
+
+ pad_len = hdr->padding_info >> MUX_PAD_SHIFT;
+ if (pad_len > MAX_PAD_BYTES(4)) {
+ pr_err_ratelimited("%s: Invalid pad len %d\n",
+ __func__, pad_len);
+ return -EINVAL;
+ }
+
+ total_len = le16_to_cpu(hdr->pkt_len_w_padding);
+ if (!total_len || !(total_len - pad_len)) {
+ pr_err_ratelimited("%s: Invalid pkt length %d\n", __func__,
+ total_len);
+ return -EINVAL;
+ }
+
+ skb->data = (unsigned char *)(hdr + 1);
+ skb_reset_tail_pointer(skb);
+ rx_urb->actual_length = total_len - pad_len;
+
+ return mux_id - 1;
+}
+
+static void rmnet_usb_data_mux(struct sk_buff *skb, unsigned int id)
+{
+ struct mux_hdr *hdr;
+ size_t len;
+
+ hdr = (struct mux_hdr *)skb_push(skb, sizeof(struct mux_hdr));
+ hdr->mux_id = id + 1;
+ len = skb->len - sizeof(struct mux_hdr);
+
+ /*add padding if len is not 4 byte aligned*/
+ skb_put(skb, ALIGN(len, 4) - len);
+
+ hdr->pkt_len_w_padding = cpu_to_le16(skb->len - sizeof(struct mux_hdr));
+ hdr->padding_info = (ALIGN(len, 4) - len) << MUX_PAD_SHIFT;
+}
+
static struct sk_buff *rmnet_usb_tx_fixup(struct usbnet *dev,
struct sk_buff *skb, gfp_t flags)
{
@@ -178,6 +353,9 @@
qmih->flow_id = skb->mark;
}
+ if (dev->data[4])
+ rmnet_usb_data_mux(skb, dev->data[3]);
+
DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
dev->net->name, dev->net->stats.tx_packets, skb->len, skb->mark);
@@ -206,10 +384,35 @@
return protocol;
}
-static int rmnet_usb_rx_fixup(struct usbnet *dev,
- struct sk_buff *skb)
+static void rmnet_usb_rx_complete(struct urb *rx_urb)
{
+ struct sk_buff *skb = (struct sk_buff *) rx_urb->context;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
+ struct usbnet *dev = entry->dev;
+ unsigned int unet_offset;
+ unsigned int unet_id;
+ int mux_id;
+ unet_offset = dev->driver_info->data * no_rmnet_insts_per_dev;
+
+ if (!rx_urb->status && dev->data[4]) {
+ mux_id = rmnet_usb_data_dmux(skb, rx_urb);
+ if (mux_id < 0) {
+ /*resubmit urb and free skb in rx_complete*/
+ rx_urb->status = -EINVAL;
+ } else {
+ /*map urb to actual network iface based on mux id*/
+ unet_id = unet_offset + mux_id;
+ skb->dev = unet_list[unet_id]->net;
+ entry->dev = unet_list[unet_id];
+ }
+ }
+
+ rx_complete(rx_urb);
+}
+
+static int rmnet_usb_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
if (test_bit(RMNET_MODE_LLP_IP, &dev->data[0]))
skb->protocol = rmnet_ip_type_trans(skb, dev->net);
else /*set zero for eth mode*/
@@ -270,7 +473,6 @@
.ndo_validate_addr = 0,
};
-
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct usbnet *unet = netdev_priv(dev);
@@ -306,7 +508,6 @@
dev->mtu = prev_mtu;
dev->addr_len = 0;
dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
- dev->needed_headroom = HEADROOM_FOR_QOS;
dev->netdev_ops = &rmnet_usb_ops_ip;
clear_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
set_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
@@ -365,7 +566,7 @@
return rc;
}
-static void rmnet_usb_setup(struct net_device *dev)
+static void rmnet_usb_setup(struct net_device *dev, int mux_enabled)
{
/* Using Ethernet mode by default */
dev->netdev_ops = &rmnet_usb_ops_ether;
@@ -373,7 +574,15 @@
/* set this after calling ether_setup */
dev->mtu = RMNET_DATA_LEN;
- dev->needed_headroom = HEADROOM_FOR_QOS;
+ if (mux_enabled) {
+ dev->needed_headroom = RMNET_HEADROOM_W_MUX;
+
+ /*max pad bytes for 4 byte alignment*/
+ dev->needed_tailroom = RMNET_TAILROOM;
+ } else {
+ dev->needed_headroom = RMNET_HEADROOM;
+ }
+
random_ether_addr(dev->dev_addr);
dev->watchdog_timeo = 1000; /* 10 seconds? */
}
@@ -403,7 +612,6 @@
seq_printf(s, "tx errors: %lu\n", unet->net->stats.tx_errors);
seq_printf(s, "tx packets: %lu\n", unet->net->stats.tx_packets);
seq_printf(s, "tx bytes: %lu\n", unet->net->stats.tx_bytes);
- seq_printf(s, "suspend count: %d\n", unet->suspend_count);
seq_printf(s, "EVENT_DEV_OPEN: %d\n",
test_bit(EVENT_DEV_OPEN, &unet->flags));
seq_printf(s, "EVENT_TX_HALT: %d\n",
@@ -458,21 +666,25 @@
{
struct dentry *root = (struct dentry *)unet->data[2];
- debugfs_remove_recursive(root);
- unet->data[2] = 0;
+ if (root) {
+ debugfs_remove_recursive(root);
+ unet->data[2] = 0;
+ }
}
static int rmnet_usb_probe(struct usb_interface *iface,
const struct usb_device_id *prod)
{
struct usbnet *unet;
- struct driver_info *info;
+ struct driver_info *info = (struct driver_info *)prod->driver_info;
struct usb_device *udev;
- unsigned int iface_num;
- static int first_rmnet_iface_num = -EINVAL;
int status = 0;
+ unsigned int i, unet_id, rdev_cnt, n = 0;
+ bool mux;
+ struct rmnet_ctrl_dev *dev;
- iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+ udev = interface_to_usbdev(iface);
+
if (iface->num_altsetting != 1) {
dev_err(&iface->dev, "%s invalid num_altsetting %u\n",
__func__, iface->num_altsetting);
@@ -480,45 +692,61 @@
goto out;
}
- info = (struct driver_info *)prod->driver_info;
- if (!test_bit(iface_num, &info->data))
- return -ENODEV;
+ mux = test_bit(info->data, &mux_enabled);
+ rdev_cnt = mux ? no_rmnet_insts_per_dev : 1;
+ info->in = 0;
- status = usbnet_probe(iface, prod);
- if (status < 0) {
- dev_err(&iface->dev, "usbnet_probe failed %d\n", status);
- goto out;
+ for (n = 0; n < rdev_cnt; n++) {
+
+ /* Use this filed to increment device count this will be
+ * used by bind to determin the forward link and reverse
+ * link network interface names.
+ */
+ info->in++;
+ status = usbnet_probe(iface, prod);
+ if (status < 0) {
+ dev_err(&iface->dev, "usbnet_probe failed %d\n",
+ status);
+ goto out;
+ }
+
+ unet_id = n + info->data * no_rmnet_insts_per_dev;
+
+ unet_list[unet_id] = unet = usb_get_intfdata(iface);
+
+ /*store mux id for later access*/
+ unet->data[3] = n;
+
+ /*save mux info for control and usbnet devices*/
+ unet->data[1] = unet->data[4] = mux;
+
+ /*set rmnet operation mode to eth by default*/
+ set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+
+ /*update net device*/
+ rmnet_usb_setup(unet->net, mux);
+
+ /*create /sys/class/net/rmnet_usbx/dbg_mask*/
+ status = device_create_file(&unet->net->dev,
+ &dev_attr_dbg_mask);
+ if (status) {
+ usbnet_disconnect(iface);
+ goto out;
+ }
+
+ status = rmnet_usb_ctrl_probe(iface, unet->status, info->data,
+ &unet->data[1]);
+ if (status) {
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+ usbnet_disconnect(iface);
+ goto out;
+ }
+
+ status = rmnet_usb_data_debugfs_init(unet);
+ if (status)
+ dev_dbg(&iface->dev,
+ "mode debugfs file is not available\n");
}
- unet = usb_get_intfdata(iface);
-
- /*set rmnet operation mode to eth by default*/
- set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
-
- /*update net device*/
- rmnet_usb_setup(unet->net);
-
- /*create /sys/class/net/rmnet_usbx/dbg_mask*/
- status = device_create_file(&unet->net->dev, &dev_attr_dbg_mask);
- if (status)
- goto out;
-
- if (first_rmnet_iface_num == -EINVAL)
- first_rmnet_iface_num = iface_num;
-
- /*save control device intstance */
- unet->data[1] = (unsigned long)ctrl_dev \
- [iface_num - first_rmnet_iface_num];
-
- status = rmnet_usb_ctrl_probe(iface, unet->status,
- (struct rmnet_ctrl_dev *)unet->data[1]);
- if (status)
- goto out;
-
- status = rmnet_usb_data_debugfs_init(unet);
- if (status)
- dev_dbg(&iface->dev, "mode debugfs file is not available\n");
-
- udev = unet->udev;
usb_enable_autosuspend(udev);
@@ -528,83 +756,123 @@
device_set_wakeup_enable(&udev->parent->dev, 1);
}
+ return 0;
+
out:
+ for (i = 0; i < n; i++) {
+ /* This cleanup happens only for MUX case */
+ unet_id = i + info->data * no_rmnet_insts_per_dev;
+ unet = unet_list[unet_id];
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+
+ rmnet_usb_data_debugfs_cleanup(unet);
+ rmnet_usb_ctrl_disconnect(dev);
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+ usb_set_intfdata(iface, unet_list[unet_id]);
+ usbnet_disconnect(iface);
+ unet_list[unet_id] = NULL;
+ }
+
return status;
}
static void rmnet_usb_disconnect(struct usb_interface *intf)
{
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(intf);
struct rmnet_ctrl_dev *dev;
+ unsigned int n, rdev_cnt, unet_id;
- unet = usb_get_intfdata(intf);
- if (!unet) {
- dev_err(&intf->dev, "%s:data device not found\n", __func__);
- return;
- }
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
device_set_wakeup_enable(&unet->udev->dev, 0);
- rmnet_usb_data_debugfs_cleanup(unet);
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
- if (!dev) {
- dev_err(&intf->dev, "%s:ctrl device not found\n", __func__);
- return;
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(intf);
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_disconnect(dev);
+ unet->data[0] = 0;
+ unet->data[1] = 0;
+ rmnet_usb_data_debugfs_cleanup(unet);
+ usb_set_intfdata(intf, unet);
+ usbnet_disconnect(intf);
+ unet_list[unet_id] = NULL;
}
- unet->data[0] = 0;
- unet->data[1] = 0;
- rmnet_usb_ctrl_disconnect(dev);
- device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
- usbnet_disconnect(intf);
}
-/*bit position represents interface number*/
-#define PID9034_IFACE_MASK 0xF0
-#define PID9048_IFACE_MASK 0x1E0
-#define PID904C_IFACE_MASK 0x1C0
-
-static const struct driver_info rmnet_info_pid9034 = {
+static struct driver_info rmnet_info = {
.description = "RmNET net device",
.flags = FLAG_SEND_ZLP,
.bind = rmnet_usb_bind,
.tx_fixup = rmnet_usb_tx_fixup,
.rx_fixup = rmnet_usb_rx_fixup,
+ .rx_complete = rmnet_usb_rx_complete,
.manage_power = rmnet_usb_manage_power,
- .data = PID9034_IFACE_MASK,
+ .data = 0,
};
-static const struct driver_info rmnet_info_pid9048 = {
+static struct driver_info rmnet_usb_info = {
.description = "RmNET net device",
.flags = FLAG_SEND_ZLP,
.bind = rmnet_usb_bind,
.tx_fixup = rmnet_usb_tx_fixup,
.rx_fixup = rmnet_usb_rx_fixup,
+ .rx_complete = rmnet_usb_rx_complete,
.manage_power = rmnet_usb_manage_power,
- .data = PID9048_IFACE_MASK,
-};
-
-static const struct driver_info rmnet_info_pid904c = {
- .description = "RmNET net device",
- .flags = FLAG_SEND_ZLP,
- .bind = rmnet_usb_bind,
- .tx_fixup = rmnet_usb_tx_fixup,
- .rx_fixup = rmnet_usb_rx_fixup,
- .manage_power = rmnet_usb_manage_power,
- .data = PID904C_IFACE_MASK,
+ .data = 1,
};
static const struct usb_device_id vidpids[] = {
- {
- USB_DEVICE(0x05c6, 0x9034), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid9034,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 4),
+ .driver_info = (unsigned long)&rmnet_info,
},
- {
- USB_DEVICE(0x05c6, 0x9048), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid9048,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 5),
+ .driver_info = (unsigned long)&rmnet_info,
},
- {
- USB_DEVICE(0x05c6, 0x904c), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid904c,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 5),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 8),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 8),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9075, 6), /*mux over hsic mdm*/
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 5),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 6),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 7),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 8),
+ .driver_info = (unsigned long)&rmnet_usb_info,
},
{ }, /* Terminating entry */
@@ -622,31 +890,36 @@
.supports_autosuspend = true,
};
-static int __init rmnet_usb_init(void)
+static int rmnet_data_start(void)
{
int retval;
+ if (no_rmnet_devs > MAX_RMNET_DEVS) {
+ pr_err("ERROR:%s: param no_rmnet_devs(%d) > than maximum(%d)",
+ __func__, no_rmnet_devs, MAX_RMNET_DEVS);
+ return -EINVAL;
+ }
+
+ /* initialize ctrl devices */
+ retval = rmnet_usb_ctrl_init(no_rmnet_devs, no_rmnet_insts_per_dev);
+ if (retval) {
+ err("rmnet_usb_cmux_init failed: %d", retval);
+ return retval;
+ }
+
retval = usb_register(&rmnet_usb);
if (retval) {
err("usb_register failed: %d", retval);
return retval;
}
- /* initialize rmnet ctrl device here*/
- retval = rmnet_usb_ctrl_init();
- if (retval) {
- usb_deregister(&rmnet_usb);
- err("rmnet_usb_cmux_init failed: %d", retval);
- return retval;
- }
- return 0;
+ return retval;
}
-module_init(rmnet_usb_init);
static void __exit rmnet_usb_exit(void)
{
- rmnet_usb_ctrl_exit();
usb_deregister(&rmnet_usb);
+ rmnet_usb_ctrl_exit(no_rmnet_devs, no_rmnet_insts_per_dev);
}
module_exit(rmnet_usb_exit);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index fa0c568..b42050c 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -86,7 +86,7 @@
static const char driver_name [] = "usbnet";
-static struct workqueue_struct *usbnet_wq;
+struct workqueue_struct *usbnet_wq;
static DECLARE_WAIT_QUEUE_HEAD(unlink_wakeup);
@@ -343,12 +343,11 @@
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb);
-
static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
{
struct sk_buff *skb;
struct skb_data *entry;
+ usb_complete_t complete_fn;
int retval = 0;
unsigned long lockflags;
size_t size = dev->rx_urb_size;
@@ -366,8 +365,13 @@
entry->dev = dev;
entry->length = 0;
+ if (dev->driver_info->rx_complete)
+ complete_fn = dev->driver_info->rx_complete;
+ else
+ complete_fn = rx_complete;
+
usb_fill_bulk_urb (urb, dev->udev, dev->in,
- skb->data, size, rx_complete, skb);
+ skb->data, size, complete_fn, skb);
spin_lock_irqsave (&dev->rxq.lock, lockflags);
@@ -441,7 +445,7 @@
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb)
+void rx_complete(struct urb *urb)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
struct skb_data *entry = (struct skb_data *) skb->cb;
@@ -527,6 +531,7 @@
}
netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n");
}
+EXPORT_SYMBOL_GPL(rx_complete);
static void intr_complete (struct urb *urb)
{
@@ -662,7 +667,7 @@
/*-------------------------------------------------------------------------*/
// precondition: never called in_interrupt
-static void usbnet_terminate_urbs(struct usbnet *dev)
+void usbnet_terminate_urbs(struct usbnet *dev)
{
DECLARE_WAITQUEUE(wait, current);
int temp;
@@ -687,6 +692,7 @@
dev->wait = NULL;
remove_wait_queue(&unlink_wakeup, &wait);
}
+EXPORT_SYMBOL_GPL(usbnet_terminate_urbs);
int usbnet_stop (struct net_device *net)
{
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index a3d7f12..5e22c35 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -112,6 +112,9 @@
#define CCU_PRONTO_LAST_ADDR1_OFFSET 0x10
#define CCU_PRONTO_LAST_ADDR2_OFFSET 0x14
+#define MSM_PRONTO_SAW2_BASE 0xfb219000
+#define PRONTO_SAW2_SPM_STS_OFFSET 0x0c
+
#define WCNSS_DEF_WLAN_RX_BUFF_COUNT 1024
#define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
@@ -288,6 +291,7 @@
void __iomem *riva_ccu_base;
void __iomem *pronto_a2xb_base;
void __iomem *pronto_ccpu_base;
+ void __iomem *pronto_saw2_base;
void __iomem *fiq_reg;
int ssr_boot;
int nv_downloaded;
@@ -479,6 +483,10 @@
reg = readl_relaxed(reg_addr);
pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
+ reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_info_ratelimited("%s: PRONTO_SAW2_SPM_STS %08x\n", __func__, reg);
+
tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
@@ -1683,6 +1691,14 @@
ret = -ENOMEM;
goto fail_ioremap4;
}
+ penv->pronto_saw2_base = ioremap_nocache(MSM_PRONTO_SAW2_BASE,
+ SZ_32);
+ if (!penv->pronto_saw2_base) {
+ pr_err("%s: ioremap wcnss physical(saw2) failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto fail_ioremap5;
+ }
}
/* trigger initialization of the WCNSS */
@@ -1699,6 +1715,9 @@
fail_pil:
if (penv->riva_ccu_base)
iounmap(penv->riva_ccu_base);
+ if (penv->pronto_saw2_base)
+ iounmap(penv->pronto_saw2_base);
+fail_ioremap5:
if (penv->fiq_reg)
iounmap(penv->fiq_reg);
fail_ioremap4:
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 5af95927..3039b0d 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -39,3 +39,12 @@
into the kernel or say M to compile it as module.
endmenu
+
+config NFC_QNCI
+ bool "Qualcomm NCI based NFC Controller Driver for qca199x"
+ depends on I2C
+ select CRC_CCITT
+ help
+ This enables the NFC driver for QCA199x based devices.
+ This is for i2c connected version. NCI protocol logic
+ resides in the usermode and it has no other NFC dependencies.
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index ab99e85..35a71e8 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -5,5 +5,6 @@
obj-$(CONFIG_PN544_NFC) += pn544.o
obj-$(CONFIG_NFC_PN533) += pn533.o
obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
+obj-$(CONFIG_NFC_QNCI) += nfc-nci.o
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c
new file mode 100644
index 0000000..9e9a4ea
--- /dev/null
+++ b/drivers/nfc/nfc-nci.c
@@ -0,0 +1,764 @@
+/* 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/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/poll.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include "nfc-nci.h"
+
+
+struct qca199x_platform_data {
+ unsigned int irq_gpio;
+ unsigned int dis_gpio;
+ unsigned int ven_gpio;
+ unsigned int reg;
+};
+
+static struct of_device_id msm_match_table[] = {
+ {.compatible = "qcom,nfc-nci"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_match_table);
+
+#define MAX_BUFFER_SIZE (780)
+/* Read data */
+#define PACKET_HEADER_SIZE_NCI (4)
+#define PACKET_TYPE_NCI (16)
+#define MAX_PACKET_SIZE (PACKET_HEADER_SIZE_NCI + 255)
+#define MAX_QCA_REG (116)
+
+static int nfc_i2c_write(struct i2c_client *client, u8 *buf, int len);
+
+struct qca199x_dev {
+ wait_queue_head_t read_wq;
+ struct mutex read_mutex;
+ struct i2c_client *client;
+ struct miscdevice qca199x_device;
+ unsigned int irq_gpio;
+ unsigned int dis_gpio;
+ unsigned int ven_gpio;
+ bool irq_enabled;
+ spinlock_t irq_enabled_lock;
+ unsigned int count_irq;
+};
+
+/*
+ * To allow filtering of nfc logging from user. This is set via
+ * IOCTL NFC_KERNEL_LOGGING_MODE.
+ */
+static int logging_level;
+
+static void qca199x_init_stat(struct qca199x_dev *qca199x_dev)
+{
+ qca199x_dev->count_irq = 0;
+}
+
+static void qca199x_disable_irq(struct qca199x_dev *qca199x_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock, flags);
+ if (qca199x_dev->irq_enabled) {
+ disable_irq_nosync(qca199x_dev->client->irq);
+ qca199x_dev->irq_enabled = false;
+ }
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock, flags);
+}
+
+static void qca199x_enable_irq(struct qca199x_dev *qca199x_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock, flags);
+ if (!qca199x_dev->irq_enabled) {
+ qca199x_dev->irq_enabled = true;
+ enable_irq(qca199x_dev->client->irq);
+ }
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock, flags);
+}
+
+static irqreturn_t qca199x_dev_irq_handler(int irq, void *dev_id)
+{
+ struct qca199x_dev *qca199x_dev = dev_id;
+ unsigned long flags;
+
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock, flags);
+ qca199x_dev->count_irq++;
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock, flags);
+ wake_up(&qca199x_dev->read_wq);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int nfc_poll(struct file *filp, poll_table *wait)
+{
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+ unsigned int mask = 0;
+ unsigned long flags;
+
+
+ poll_wait(filp, &qca199x_dev->read_wq, wait);
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock, flags);
+ if (qca199x_dev->count_irq > 0) {
+ qca199x_dev->count_irq--;
+ mask |= POLLIN | POLLRDNORM;
+ }
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock, flags);
+
+
+ return mask;
+}
+
+static ssize_t nfc_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+ unsigned char tmp[MAX_BUFFER_SIZE];
+ unsigned char len[PAYLOAD_HEADER_LENGTH];
+ int total, length, ret;
+
+ total = 0;
+ length = 0;
+ if (count > MAX_BUFFER_SIZE)
+ count = MAX_BUFFER_SIZE;
+
+ mutex_lock(&qca199x_dev->read_mutex);
+ /* Read the header */
+ ret = i2c_master_recv(qca199x_dev->client, len, PAYLOAD_HEADER_LENGTH);
+ if (ret != PAYLOAD_HEADER_LENGTH)
+ goto err;
+ length = len[PAYLOAD_HEADER_LENGTH - 1];
+
+ /** make sure full packet fits in the buffer **/
+ if ((length > 0) && ((length + PAYLOAD_HEADER_LENGTH) <= count)) {
+ /* Read the packet */
+ ret = i2c_master_recv(qca199x_dev->client, tmp, (length +
+ PAYLOAD_HEADER_LENGTH));
+ if (ret < 0)
+ goto err;
+ total = (length + PAYLOAD_HEADER_LENGTH);
+ }
+ mutex_unlock(&qca199x_dev->read_mutex);
+ if (total > 0) {
+ if ((total > count) || copy_to_user(buf, tmp, total)) {
+ dev_err(&qca199x_dev->client->dev,
+ "failed to copy to user space, total = %d\n",
+ total);
+ total = -EFAULT;
+ }
+ }
+err:
+ if (ret < 0)
+ mutex_unlock(&qca199x_dev->read_mutex);
+
+ return total;
+}
+
+static ssize_t nfc_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+ char tmp[MAX_BUFFER_SIZE];
+ int ret;
+
+ if (count > MAX_BUFFER_SIZE) {
+ dev_err(&qca199x_dev->client->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ if (copy_from_user(tmp, buf, count)) {
+ dev_err(&qca199x_dev->client->dev,
+ "nfc-nci write: failed to copy from user space\n");
+ return -EFAULT;
+ }
+ mutex_lock(&qca199x_dev->read_mutex);
+ ret = i2c_master_send(qca199x_dev->client, tmp, count);
+ if (ret != count) {
+ dev_err(&qca199x_dev->client->dev,
+ "NFC: failed to write %d\n", ret);
+ ret = -EIO;
+ }
+ mutex_unlock(&qca199x_dev->read_mutex);
+
+ return ret;
+}
+
+static int nfc_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+
+ struct qca199x_dev *qca199x_dev = container_of(filp->private_data,
+ struct qca199x_dev,
+ qca199x_device);
+
+ filp->private_data = qca199x_dev;
+ qca199x_init_stat(qca199x_dev);
+ qca199x_enable_irq(qca199x_dev);
+ dev_dbg(&qca199x_dev->client->dev,
+ "%d,%d\n", imajor(inode), iminor(inode));
+ return ret;
+}
+
+/*
+ * Wake/Sleep Mode
+ */
+int nfcc_wake(int level, struct nfc_info *info)
+{
+ int r = 0;
+ unsigned char raw_nci_sleep[] = {0x2F, 0x03, 0x00};
+ /* Change slave address to 0xE */
+ unsigned char raw_nci_wake[] = {0x10, 0x0F};
+ unsigned short slave_addr = 0xE;
+ unsigned short curr_addr;
+
+ struct i2c_client *client = info->i2c_dev;
+
+ dev_dbg(&client->dev, "nfcc_wake: %s: info: %p\n", __func__, info);
+
+ if (level == NFCC_SLEEP) {
+ r = nfc_i2c_write(client, &raw_nci_sleep[0],
+ sizeof(raw_nci_sleep));
+
+ if (r != sizeof(raw_nci_sleep))
+ return -EMSGSIZE;
+ info->state = NFCC_STATE_NORMAL_SLEEP;
+ } else {
+ curr_addr = client->addr;
+ client->addr = slave_addr;
+ r = nfc_i2c_write(client, &raw_nci_wake[0],
+ sizeof(raw_nci_wake));
+ /* Restore original NFCC slave I2C address */
+ client->addr = curr_addr;
+
+ if (r != sizeof(raw_nci_sleep))
+ return -EMSGSIZE;
+
+ info->state = NFCC_STATE_NORMAL_WAKE;
+ }
+ msleep(20);
+ return r;
+}
+
+/*
+ * Inside nfc_ioctl_power_states
+ *
+ * @brief ioctl functions
+ *
+ *
+ * Device control
+ * remove control via ioctl
+ * (arg = 0): NFC_DISABLE GPIO = 0
+ * (arg = 1): NFC_DISABLE GPIO = 1
+ * NOT USED (arg = 2): FW_DL GPIO = 0
+ * NOT USED (arg = 3): FW_DL GPIO = 1
+ * (arg = 4): NFCC_WAKE = 1
+ * (arg = 5): NFCC_WAKE = 0
+ *
+ *
+ */
+int nfc_ioctl_power_states(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int r = 0;
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+ struct nfc_info *info = container_of(filp->private_data,
+ struct nfc_info, miscdev);
+
+ struct i2c_client *client = info->i2c_dev;
+
+ r = gpio_request(qca199x_dev->dis_gpio, "nfc_reset_gpio");
+ if (r) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ qca199x_dev->dis_gpio);
+ goto err_req;
+ }
+ gpio_set_value(qca199x_dev->dis_gpio, 0);
+ r = gpio_direction_output(qca199x_dev->dis_gpio, 1);
+ if (r) {
+ dev_err(&client->dev, "unable to set direction for gpio [%d]\n",
+ qca199x_dev->irq_gpio);
+ goto err_req;
+ }
+
+ if (arg == 0) {
+ gpio_set_value(qca199x_dev->dis_gpio, 0);
+ msleep(20);
+ } else if (arg == 1) {
+ gpio_set_value(qca199x_dev->dis_gpio, 1);
+ msleep(20);
+ } else if (arg == 2) {
+ msleep(20);
+ } else if (arg == 3) {
+ msleep(20);
+ } else if (arg == 4) {
+ nfcc_wake(NFCC_WAKE, info);
+ msleep(20);
+ } else if (arg == 5) {
+ nfcc_wake(NFCC_SLEEP, info);
+ msleep(20);
+ } else {
+ r = -ENOIOCTLCMD;
+ }
+
+err_req:
+ return r;
+}
+
+/*
+ * Inside nfc_ioctl_kernel_logging
+ *
+ * @brief nfc_ioctl_kernel_logging
+ *
+ * (arg = 0) ; NO_LOGGING
+ * (arg = 1) ; COMMS_LOGGING - BASIC LOGGING - Mainly just comms over I2C
+ * (arg = 2) ; FULL_LOGGING - ENABLE ALL - DBG messages for handlers etc.
+ * ; ! Be aware as amount of logging could impact behaviour !
+ *
+ *
+ */
+int nfc_ioctl_kernel_logging(unsigned long arg, struct file *filp)
+{
+ int retval = 0;
+ struct qca199x_dev *qca199x_dev = container_of(filp->private_data,
+ struct qca199x_dev,
+ qca199x_device);
+ if (arg == 0) {
+ dev_dbg(&qca199x_dev->client->dev,
+ "nfc_ioctl_kernel_logging : level = NO_LOGGING\n");
+ logging_level = 0;
+ } else if (arg == 1) {
+ dev_dbg(&qca199x_dev->client->dev,
+ "nfc_ioctl_kernel_logging: level = COMMS_LOGGING only\n");
+ logging_level = 1;
+ } else if (arg == 2) {
+ dev_dbg(&qca199x_dev->client->dev,
+ "nfc_ioctl_kernel_logging: level = FULL_LOGGING\n");
+ logging_level = 2;
+ }
+ return retval;
+}
+
+static long nfc_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
+{
+ int r = 0;
+
+ switch (cmd) {
+
+ case NFC_SET_PWR:
+ nfc_ioctl_power_states(pfile, cmd, arg);
+ break;
+ case NFCC_MODE:
+ break;
+ case NFC_KERNEL_LOGGING_MODE:
+ nfc_ioctl_kernel_logging(arg, pfile);
+ break;
+ case SET_RX_BLOCK:
+ break;
+ case SET_EMULATOR_TEST_POINT:
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ }
+ return r;
+}
+
+static const struct file_operations nfc_dev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .poll = nfc_poll,
+ .read = nfc_read,
+ .write = nfc_write,
+ .open = nfc_open,
+ .unlocked_ioctl = nfc_ioctl
+};
+
+void dumpqca1990(struct i2c_client *client)
+{
+ int r = 0;
+ int i = 0;
+ unsigned char raw_reg_rd = {0x0};
+ unsigned short temp_addr;
+
+ temp_addr = client->addr;
+ client->addr = 0x0E;
+
+ for (i = 0; i < MAX_QCA_REG; i++) {
+ raw_reg_rd = i;
+ if (((i >= 0x0) && (i < 0x4)) || ((i > 0x7) && (i < 0xA)) ||
+ ((i > 0xF) && (i < 0x12)) || ((i > 0x39) && (i < 0x4d)) ||
+ ((i > 0x69) && (i < 0x74)) || (i == 0x18) || (i == 0x30) ||
+ (i == 0x58)) {
+ r = nfc_i2c_write(client, &raw_reg_rd, 1);
+ msleep(20);
+ r = i2c_master_recv(client, &raw_reg_rd, 1);
+ }
+ }
+ client->addr = temp_addr;
+}
+
+static int nfc_i2c_write(struct i2c_client *client, u8 *buf, int len)
+{
+ int r;
+
+ r = i2c_master_send(client, buf, len);
+ dev_dbg(&client->dev, "send: %d\n", r);
+ if (r == -EREMOTEIO) { /* Retry, chip was in standby */
+ usleep_range(6000, 10000);
+ r = i2c_master_send(client, buf, len);
+ dev_dbg(&client->dev, "send2: %d\n", r);
+ }
+ if (r != len)
+ return -EREMOTEIO;
+
+ return r;
+}
+
+int nfcc_initialise(struct i2c_client *client, unsigned short curr_addr)
+{
+ int r = 0;
+ unsigned char raw_1p8_CONTROL_011[] = {0x11, XTAL_CLOCK};
+ unsigned char raw_1P8_CONTROL_010[] = {0x10, PWR_EN};
+ unsigned char raw_1P8_X0_0B0[] = {0xB0, (FREQ_SEL)};
+ unsigned char raw_slave1[] = {0x09, NCI_I2C_SLAVE};
+ unsigned char raw_slave2[] = {0x8, 0x10};
+ unsigned char raw_s73[] = {0x73, 0x02};
+ unsigned char raw_slave1_rd = {0x0};
+ unsigned char raw_1P8_PAD_CFG_CLK_REQ[] = {0xA5, 0x1};
+ unsigned char buf[4];
+
+ /* Set I2C address to enable configuration of QCA1990 */
+ client->addr = curr_addr;
+ RAW(s73, 0x02);
+
+ r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73));
+ usleep(1000);
+ RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01);
+
+ r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0],
+ sizeof(raw_1p8_CONTROL_011));
+ usleep(1000);
+ RAW(1P8_CONTROL_010, (0x8));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+
+ usleep(10000); /* 10ms wait */
+ RAW(1P8_CONTROL_010, (0xC));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ usleep(100); /* 100uS wait */
+ RAW(1P8_X0_0B0, (FREQ_SEL_19));
+ r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0], sizeof(raw_1P8_X0_0B0));
+ usleep(1000);
+
+ /* PWR_EN = 1 */
+ RAW(1P8_CONTROL_010, (0xd));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ usleep(20000); /* 20ms wait */
+ /* LS_EN = 1 */
+ RAW(1P8_CONTROL_010, 0xF);
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ usleep(20000); /* 20ms wait */
+
+ /* Enable the PMIC clock */
+ RAW(1P8_PAD_CFG_CLK_REQ, (0x1));
+ r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0],
+ sizeof(raw_1P8_PAD_CFG_CLK_REQ));
+ usleep(1000);
+
+ RAW(slave2, 0x10);
+ r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2));
+ usleep(1000);
+ {
+ r = i2c_master_send(client, buf, 1);
+ memset(buf, 0xAA, sizeof(buf));
+ r = i2c_master_recv(client, buf, 1);
+ }
+ RAW(slave1, NCI_I2C_SLAVE);
+ r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1));
+ usleep(1000);
+
+ /* QCA199x NFCC CPU should now boot... */
+ r = i2c_master_recv(client, &raw_slave1_rd, 1);
+ /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/
+ client->addr = NCI_I2C_SLAVE;
+
+ return r;
+}
+
+static int nfc_parse_dt(struct device *dev, struct qca199x_platform_data *pdata)
+{
+ int r = 0;
+ struct device_node *np = dev->of_node;
+
+ r = of_property_read_u32(np, "reg", &pdata->reg);
+ if (r)
+ return -EINVAL;
+
+ r = of_property_read_u32(np, "qcom,clk-gpio", &pdata->ven_gpio);
+ if (r)
+ return -EINVAL;
+
+ pdata->dis_gpio = of_get_named_gpio(np, "qcom,dis-gpio", 0);
+ if ((!gpio_is_valid(pdata->dis_gpio)))
+ return -EINVAL;
+
+ pdata->irq_gpio = of_get_named_gpio(np, "qcom,irq-gpio", 0);
+ if ((!gpio_is_valid(pdata->irq_gpio)))
+ return -EINVAL;
+
+ return r;
+}
+
+static int qca199x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int r = 0;
+ int irqn = 0;
+ struct clk *nfc_clk;
+ struct device_node *node = client->dev.of_node;
+ struct qca199x_platform_data *platform_data;
+ struct qca199x_dev *qca199x_dev;
+
+ if (client->dev.of_node) {
+ platform_data = devm_kzalloc(&client->dev,
+ sizeof(struct qca199x_platform_data), GFP_KERNEL);
+ if (!platform_data) {
+ dev_err(&client->dev,
+ "nfc-nci probe: Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ r = nfc_parse_dt(&client->dev, platform_data);
+ if (r)
+ return r;
+ } else {
+ platform_data = client->dev.platform_data;
+ }
+ if (!platform_data)
+ return -EINVAL;
+ dev_dbg(&client->dev,
+ "nfc-nci probe: %s, inside nfc-nci flags = %x\n",
+ __func__, client->flags);
+ if (platform_data == NULL) {
+ dev_err(&client->dev, "nfc-nci probe: failed\n");
+ return -ENODEV;
+ }
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "nfc-nci probe: need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+ qca199x_dev = kzalloc(sizeof(*qca199x_dev), GFP_KERNEL);
+ if (qca199x_dev == NULL) {
+ dev_err(&client->dev,
+ "nfc-nci probe: failed to allocate memory for module data\n");
+ return -ENOMEM;
+ }
+ if (gpio_is_valid(platform_data->irq_gpio)) {
+ r = gpio_request(platform_data->irq_gpio, "nfc_irq_gpio");
+ if (r) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_irq;
+ }
+ r = gpio_direction_input(platform_data->irq_gpio);
+ if (r) {
+
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_irq;
+ }
+ gpio_to_irq(0);
+ irqn = gpio_to_irq(platform_data->irq_gpio);
+ if (irqn < 0) {
+ r = irqn;
+ goto err_irq;
+ }
+ client->irq = irqn;
+
+ } else {
+ dev_err(&client->dev, "irq gpio not provided\n");
+ goto err_free_dev;
+ }
+ if (gpio_is_valid(platform_data->dis_gpio)) {
+ r = gpio_request(platform_data->dis_gpio, "nfc_reset_gpio");
+ if (r) {
+ dev_err(&client->dev,
+ "NFC: unable to request gpio [%d]\n",
+ platform_data->dis_gpio);
+ goto err_dis_gpio;
+ }
+ r = gpio_direction_output(platform_data->dis_gpio, 1);
+ if (r) {
+ dev_err(&client->dev,
+ "NFC: unable to set direction for gpio [%d]\n",
+ platform_data->dis_gpio);
+ goto err_dis_gpio;
+ }
+ } else {
+ dev_err(&client->dev, "dis gpio not provided\n");
+ goto err_irq;
+ }
+
+ nfc_clk = clk_get(&client->dev, "ref_clk");
+
+ if (nfc_clk == NULL)
+ goto err_dis_gpio;
+
+ r = clk_prepare_enable(nfc_clk);
+ if (r)
+ goto err_dis_gpio;
+
+ platform_data->ven_gpio = of_get_named_gpio(node,
+ "qcom,clk-gpio", 0);
+
+ if (gpio_is_valid(platform_data->ven_gpio)) {
+ r = gpio_request(platform_data->ven_gpio, "nfc_ven_gpio");
+ if (r) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_ven_gpio;
+ }
+ r = gpio_direction_input(platform_data->ven_gpio);
+ if (r) {
+
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_ven_gpio;
+ }
+
+ } else {
+
+ dev_err(&client->dev, "ven gpio not provided\n");
+ goto err_dis_gpio;
+ }
+ qca199x_dev->dis_gpio = platform_data->dis_gpio;
+ qca199x_dev->irq_gpio = platform_data->irq_gpio;
+ qca199x_dev->ven_gpio = platform_data->ven_gpio;
+ qca199x_dev->client = client;
+
+ /* init mutex and queues */
+ init_waitqueue_head(&qca199x_dev->read_wq);
+ mutex_init(&qca199x_dev->read_mutex);
+ spin_lock_init(&qca199x_dev->irq_enabled_lock);
+
+ qca199x_dev->qca199x_device.minor = MISC_DYNAMIC_MINOR;
+ qca199x_dev->qca199x_device.name = "nfc-nci";
+ qca199x_dev->qca199x_device.fops = &nfc_dev_fops;
+
+ r = misc_register(&qca199x_dev->qca199x_device);
+ if (r) {
+ dev_err(&client->dev, "misc_register failed\n");
+ goto err_misc_register;
+ }
+
+ logging_level = 0;
+ /* request irq. The irq is set whenever the chip has data available
+ * for reading. It is cleared when all data has been read.
+ */
+ nfcc_initialise(client, platform_data->reg);
+
+ qca199x_dev->irq_enabled = true;
+ r = request_irq(client->irq, qca199x_dev_irq_handler,
+ IRQF_TRIGGER_RISING, client->name, qca199x_dev);
+ if (r) {
+ dev_err(&client->dev, "nfc-nci probe: request_irq failed\n");
+ goto err_request_irq_failed;
+ }
+ qca199x_disable_irq(qca199x_dev);
+ i2c_set_clientdata(client, qca199x_dev);
+ dev_dbg(&client->dev,
+ "nfc-nci probe: %s, probing qca1990 exited successfully\n",
+ __func__);
+ return 0;
+
+err_request_irq_failed:
+ misc_deregister(&qca199x_dev->qca199x_device);
+err_misc_register:
+ mutex_destroy(&qca199x_dev->read_mutex);
+err_ven_gpio:
+ gpio_free(platform_data->ven_gpio);
+err_dis_gpio:
+ gpio_free(platform_data->dis_gpio);
+err_irq:
+ gpio_free(platform_data->irq_gpio);
+err_free_dev:
+ kfree(qca199x_dev);
+ return r;
+}
+
+static int qca199x_remove(struct i2c_client *client)
+{
+ struct qca199x_dev *qca199x_dev;
+
+ qca199x_dev = i2c_get_clientdata(client);
+ free_irq(client->irq, qca199x_dev);
+ misc_deregister(&qca199x_dev->qca199x_device);
+ mutex_destroy(&qca199x_dev->read_mutex);
+ gpio_free(qca199x_dev->irq_gpio);
+ gpio_free(qca199x_dev->dis_gpio);
+ gpio_free(qca199x_dev->ven_gpio);
+ kfree(qca199x_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id qca199x_id[] = {
+ {"qca199x-i2c", 0},
+ {}
+};
+
+static struct i2c_driver qca199x = {
+ .id_table = qca199x_id,
+ .probe = qca199x_probe,
+ .remove = qca199x_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "nfc-nci",
+ .of_match_table = msm_match_table,
+ },
+};
+
+/*
+ * module load/unload record keeping
+ */
+static int __init qca199x_dev_init(void)
+{
+ return i2c_add_driver(&qca199x);
+}
+module_init(qca199x_dev_init);
+
+static void __exit qca199x_dev_exit(void)
+{
+ i2c_del_driver(&qca199x);
+}
+module_exit(qca199x_dev_exit);
+
+MODULE_DESCRIPTION("NFC QCA199x");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/nfc/nfc-nci.h b/drivers/nfc/nfc-nci.h
new file mode 100644
index 0000000..4398df7
--- /dev/null
+++ b/drivers/nfc/nfc-nci.h
@@ -0,0 +1,222 @@
+/* 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 __NFC_NCI_H
+#define __NFC_NCI_H
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include <linux/semaphore.h>
+#include <linux/completion.h>
+
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/miscdevice.h>
+
+struct nfc_device {
+ struct cdev cdev;
+ struct class *char_class;
+};
+
+enum ehandler_mode {
+ UNSOLICITED_READ_MODE = 0,
+ SOLICITED_READ_MODE
+};
+
+enum ekernel_logging_mode {
+ LEVEL_0 = 0, /* For Basic Comms, such asNCI TX/TX to NFCC */
+ LEVEL_1, /* Other Debug e.g. Notifications, ISR hit, etc ..*/
+ LEVEL_2,
+ LEVEL_3,
+ LEVEL_4,
+ LEVEL_5
+};
+
+struct DeviceMode {
+ enum ehandler_mode handle_flavour;
+} tDeviceMode;
+
+#define NFC_DRIVER_NAME "nfc-nci"
+#define NFC_I2C_DRIVER_NAME "NCI NFC I2C Interface",
+
+#define NCI_I2C_SLAVE (0x2C)
+#define NFC_I2C_BUS 3 /* 6, 10, 4, 5 */
+#define NFC_SET_PWR _IOW(0xE9, 0x01, unsigned int)
+#define NFCC_MODE _IOW(0xE9, 0x02, unsigned int)
+#define NFC_KERNEL_LOGGING_MODE _IOW(0xE9, 0x03, unsigned int)
+#define SET_RX_BLOCK _IOW(0xE9, 0x04, unsigned int)
+#define SET_EMULATOR_TEST_POINT _IOW(0xE9, 0x05, unsigned int)
+
+#define NFC_MAX_I2C_TRANSFER (0x0400)
+#define NFC_MSG_MAX_SIZE (0x21)
+
+#define NFC_RX_BUFFER_CNT_START (0x0)
+
+#define NFC_RX_BUFFER_BLOCK_SIZE (0x120) /* Bytes per Block */
+#define NFC_RX_BUFFER_PAGE_SIZE (0x1000) /* Page size Bytes */
+#define NFC_RX_BUFFER_PAGES (0x8)
+#define NFC_RX_ORDER_FREE_PAGES (0x3) /* Free 8 Pages */
+
+/* The total no. of Blocks */
+#define NFC_RX_BUFFER_CNT_LIMIT (unsigned short)( \
+ ( \
+ ((NFC_RX_BUFFER_PAGE_SIZE) *\
+ (NFC_RX_BUFFER_PAGES))/\
+ (NFC_RX_BUFFER_BLOCK_SIZE)\
+ ) \
+ ) \
+
+#define PAYLOAD_HEADER_LENGTH (0x3)
+#define PAYLOAD_LENGTH_MAX (256)
+#define BYTE (0x8)
+#define NCI_IDENTIFIER (0x10)
+
+/** Power Management Related **/
+
+#define NFCC_WAKE (0x01)
+#define NFCC_SLEEP (0x00)
+
+#define XTAL_CLOCK (0X00)
+#define REFERENCE_CLOCK (0X01)
+
+/* LDO Trim Settings */
+#define IPTAT_TRIM (0x1F)
+#define V1P1_TRIM (0x0F)
+#define V1P8_TRIM (0x0F)
+#define VBATT_OK_THRESHOLD (0x07)
+
+#define PWR_EN (0x08) /* Enable 1.1V LDO Regulator */
+#define LS_EN (0x04) /* Enable 1.1V->1.8V Level Shifters */
+
+/* Write '1' to cause wake event to NFCC. If set NFCC will not go to SLEEP */
+#define NCI_WAKE (0x02)
+
+#define NCI_ENA (0x01) /* Write '1' to enable PLL */
+#define FREQ_SEL (0x00) /* XO Frequency Select */
+#define FREQ_SEL_13 (0x00) /* XO Frequency Select = 13.56MHz */
+#define FREQ_SEL_19 (0x01) /* XO Frequency Select = 19.20 MHz */
+#define FREQ_SEL_26 (0x02) /* XO Frequency Select = 26.00 MHz */
+#define FREQ_SEL_27 (0x03) /* XO Frequency Select = 27.12 MHz */
+#define FREQ_SEL_37 (0x04) /* XO Frequency Select = 37.40 MHz */
+#define FREQ_SEL_38 (0x05) /* XO Frequency Select = 38.40 MHz */
+#define FREQ_SEL_40 (0x06) /* XO Frequency Select = 40.00 MHz */
+#define FREQ_SEL_48 (0x07) /* XO Frequency Select = 48.00 MHz */
+#define FREQ_SEL_27 (0x03) /* XO Frequency Select */
+
+
+#define QUALIFY_REFCLK (0x80)
+#define QUALIFY_OSC (0x40)
+#define LOCALBIASXTAL (0x20)
+#define BIAS2X_FORCE (0x10)
+#define BIAS2X (0x08)
+#define LBIAS2X (0x04)
+#define SMALLRF (0x02)
+#define SMALLRBIAS (0x01)
+
+/* Select as appropriate */
+#define CRYSTAL_OSC ((QUALIFY_REFCLK) | (QUALIFY_OSC) | \
+ (LOCALBIASXTAL) | (BIAS2X_FORCE) | \
+ (BIAS2X) | (LBIAS2X) | (SMALLRF) | (SMALLRBIAS))
+
+#define CDACIN (0x3F) /* Tuning range for load capacitor at X1*/
+#define CDACOUT (0x3F) /* Tuning range for load capacitor at X2*/
+
+#define RAW(reg, value) (raw_##reg[1] = value)
+
+/* Logging macro with threshold control */
+#define PRINTK(LEVEL, THRESHOLD, pString, ...) ( \
+ if (LEVEL > THRESHOLD) { \
+ pr_info(pString, ##__VA_ARGS__); \
+ } \
+ )
+
+/* board config */
+struct nfc_platform_data {
+ int (*request_resources) (struct i2c_client *client);
+ void (*free_resources) (void);
+ void (*enable) (int fw);
+ int (*test) (void);
+ void (*disable) (void);
+};
+/*
+ * Internal NFCC Hardware states. At present these may not be possible to
+ * detect in software as possibly no power when
+ * in monitor state! Also, need to detect DISABLE control GPIO from PMIC.
+ */
+enum nfcc_hardware_state {
+ NFCC_STATE_MONITOR, /* VBAT < h/w Critcal Voltage */
+ /* VBAT > H/W Critical Voltage;
+ Lowest Power Mode - DISABLE = 1; only
+ possible when phone is ON */
+ NFCC_STATE_HPD,
+ /* VBAT > H/W Critical Voltage; DISABLE = 0;
+ Only possible when phone is ON */
+ NFCC_STSTE_ULPM,
+ /* VBAT > H/W Critical Voltage; DISABLE = 0;
+ Powered by PMIC & VBAT; 1.8V I/O supply on; VDDPX available, boot is
+ initiated by host over I2C */
+ NFCC_STATE_NORMAL_REGION1,
+ /* VBAT > H/W Critical Voltage; DISABLE = 0;
+ Powered by VBAT; 1.8V I/O supply on; VDDPX available, boot is initiated
+ by host over I2C */
+ NFCC_STATE_NORMAL_REGION2,
+};
+
+/* We assume here that VBATT > h/w Critical Voltage */
+enum nfcc_state {
+ /* Assume In ULPM state, ready for initialisation, cannot detect for
+ Monitor or HPD states */
+ NFCC_STATE_COLD,
+ /* (VDDPX==1) && (Following I2C initialisation). In Region 1 or Region2
+ state WAKE */
+ NFCC_STATE_NORMAL_WAKE,
+ /* (VDDPX==1) && (Following I2C initialisation). In Region 1 or Region2
+ state SLEEP */
+ NFCC_STATE_NORMAL_SLEEP,
+};
+
+
+enum nfcc_irq {
+ NFCC_NO_INT,
+ NFCC_INT,
+};
+
+
+struct nfc_info {
+ struct miscdevice miscdev;
+ struct i2c_client *i2c_dev;
+ struct regulator_bulk_data regs[3];
+ enum nfcc_state state;
+ wait_queue_head_t read_wait;
+ loff_t read_offset;
+ struct mutex read_mutex;
+ struct mutex mutex;
+ u8 *buf;
+ size_t buflen;
+ spinlock_t irq_enabled_lock;
+ unsigned int count_irq;
+ enum nfcc_irq read_irq;
+};
+
+
+struct nfc_i2c_platform_data {
+ unsigned int nfc_irq_gpio;
+ unsigned int nfc_clk_en_gpio;
+ unsigned int dis_gpio;
+ unsigned int irq_gpio;
+ unsigned int ven_gpio;
+ unsigned int firm_gpio;
+ unsigned int reg;
+};
+#endif
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
index 94e76d8..7b41fd8 100644
--- a/drivers/of/gpio.c
+++ b/drivers/of/gpio.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>
/**
@@ -206,6 +207,53 @@
}
EXPORT_SYMBOL(of_mm_gpiochip_add);
+#ifdef CONFIG_PINCTRL
+void of_gpiochip_add_pin_range(struct gpio_chip *chip)
+{
+ struct device_node *np = chip->of_node;
+ struct of_phandle_args pinspec;
+ struct pinctrl_dev *pctldev;
+ int index = 0, ret;
+
+ if (!np)
+ return;
+
+ do {
+ ret = of_parse_phandle_with_args(np, "gpio-ranges",
+ "#gpio-range-cells", index, &pinspec);
+ if (ret)
+ break;
+
+ pctldev = of_pinctrl_get(pinspec.np);
+ if (!pctldev)
+ break;
+
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_name(pctldev),
+ pinspec.args[0],
+ pinspec.args[1]);
+
+ if (ret)
+ break;
+
+ } while (index++);
+}
+
+void of_gpiochip_remove_pin_range(struct gpio_chip *chip)
+{
+ struct gpio_pin_range *pin_range, *tmp;
+
+ list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
+ list_del(&pin_range->node);
+ pinctrl_remove_gpio_range(pin_range->pctldev,
+ &pin_range->range);
+ }
+}
+#else
+void of_gpiochip_add_pin_range(struct gpio_chip *chip) {}
+void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {}
+#endif
+
void of_gpiochip_add(struct gpio_chip *chip)
{
if ((!chip->of_node) && (chip->dev))
@@ -219,11 +267,14 @@
chip->of_xlate = of_gpio_simple_xlate;
}
+ of_gpiochip_add_pin_range(chip);
of_node_get(chip->of_node);
}
void of_gpiochip_remove(struct gpio_chip *chip)
{
+ of_gpiochip_remove_pin_range(chip);
+
if (chip->of_node)
of_node_put(chip->of_node);
}
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index abfb964..3671ac2 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -4,7 +4,6 @@
config PINCTRL
bool
- depends on EXPERIMENTAL
if PINCTRL
@@ -35,19 +34,16 @@
bool "MMP2 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_PXA168
bool "PXA168 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_PXA910
bool "PXA910 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_SIRF
bool "CSR SiRFprimaII pin controller driver"
@@ -56,17 +52,15 @@
config PINCTRL_TEGRA
bool
+ select PINMUX
+ select PINCONF
config PINCTRL_TEGRA20
bool
- select PINMUX
- select PINCONF
select PINCTRL_TEGRA
config PINCTRL_TEGRA30
bool
- select PINMUX
- select PINCONF
select PINCTRL_TEGRA
config PINCTRL_U300
@@ -77,7 +71,7 @@
config PINCTRL_COH901
bool "ST-Ericsson U300 COH 901 335/571 GPIO"
- depends on GPIOLIB && ARCH_U300 && PINMUX_U300
+ depends on GPIOLIB && ARCH_U300 && PINCTRL_U300
help
Say yes here to support GPIO interface on ST-Ericsson U300.
The names of the two IP block variants supported are
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 6d4150b..8e3c95a 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -5,6 +5,9 @@
obj-$(CONFIG_PINCTRL) += core.o
obj-$(CONFIG_PINMUX) += pinmux.o
obj-$(CONFIG_PINCONF) += pinconf.o
+ifeq ($(CONFIG_OF),y)
+obj-$(CONFIG_PINCTRL) += devicetree.o
+endif
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
obj-$(CONFIG_PINCTRL_PXA3xx) += pinctrl-pxa3xx.o
obj-$(CONFIG_PINCTRL_MMP2) += pinctrl-mmp2.o
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index df6296c..b0de6e7 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "pinctrl core: " fmt
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -23,41 +24,42 @@
#include <linux/sysfs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/machine.h>
#include "core.h"
+#include "devicetree.h"
#include "pinmux.h"
#include "pinconf.h"
-/**
- * struct pinctrl_maps - a list item containing part of the mapping table
- * @node: mapping table list node
- * @maps: array of mapping table entries
- * @num_maps: the number of entries in @maps
- */
-struct pinctrl_maps {
- struct list_head node;
- struct pinctrl_map const *maps;
- unsigned num_maps;
-};
+
+static bool pinctrl_dummy_state;
/* Mutex taken by all entry points */
DEFINE_MUTEX(pinctrl_mutex);
/* Global list of pin control devices (struct pinctrl_dev) */
-static LIST_HEAD(pinctrldev_list);
+LIST_HEAD(pinctrldev_list);
/* List of pin controller handles (struct pinctrl) */
static LIST_HEAD(pinctrl_list);
/* List of pinctrl maps (struct pinctrl_maps) */
-static LIST_HEAD(pinctrl_maps);
+LIST_HEAD(pinctrl_maps);
-#define for_each_maps(_maps_node_, _i_, _map_) \
- list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
- for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
- _i_ < _maps_node_->num_maps; \
- i++, _map_ = &_maps_node_->maps[_i_])
+
+/**
+ * pinctrl_provide_dummies() - indicate if pinctrl provides dummy state support
+ *
+ * Usually this function is called by platforms without pinctrl driver support
+ * but run with some shared drivers using pinctrl APIs.
+ * After calling this function, the pinctrl core will return successfully
+ * with creating a dummy state for the driver to keep going smoothly.
+ */
+void pinctrl_provide_dummies(void)
+{
+ pinctrl_dummy_state = true;
+}
const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev)
{
@@ -66,6 +68,12 @@
}
EXPORT_SYMBOL_GPL(pinctrl_dev_get_name);
+const char *pinctrl_dev_get_devname(struct pinctrl_dev *pctldev)
+{
+ return dev_name(pctldev->dev);
+}
+EXPORT_SYMBOL_GPL(pinctrl_dev_get_devname);
+
void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev)
{
return pctldev->driver_data;
@@ -124,6 +132,25 @@
}
/**
+ * pin_get_name_from_id() - look up a pin name from a pin id
+ * @pctldev: the pin control device to lookup the pin on
+ * @name: the name of the pin to look up
+ */
+const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin)
+{
+ const struct pin_desc *desc;
+
+ desc = pin_desc_get(pctldev, pin);
+ if (desc == NULL) {
+ dev_err(pctldev->dev, "failed to get pin(%d) name\n",
+ pin);
+ return NULL;
+ }
+
+ return desc->name;
+}
+
+/**
* pin_is_valid() - check if pin exists on controller
* @pctldev: the pin control device to check the pin on
* @pin: pin to check, use the local pin controller index number
@@ -194,8 +221,10 @@
pindesc->name = name;
} else {
pindesc->name = kasprintf(GFP_KERNEL, "PIN%u", number);
- if (pindesc->name == NULL)
+ if (pindesc->name == NULL) {
+ kfree(pindesc);
return -ENOMEM;
+ }
pindesc->dynamic_name = true;
}
@@ -255,7 +284,8 @@
*
* Find the pin controller handling a certain GPIO pin from the pinspace of
* the GPIO subsystem, return the device and the matching GPIO range. Returns
- * negative if the GPIO range could not be found in any device.
+ * -EPROBE_DEFER if the GPIO range could not be found in any device since it
+ * may still have not been registered.
*/
static int pinctrl_get_device_gpio_range(unsigned gpio,
struct pinctrl_dev **outdev,
@@ -275,7 +305,7 @@
}
}
- return -EINVAL;
+ return -EPROBE_DEFER;
}
/**
@@ -295,6 +325,59 @@
}
EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range);
+void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *ranges,
+ unsigned nranges)
+{
+ int i;
+
+ for (i = 0; i < nranges; i++)
+ pinctrl_add_gpio_range(pctldev, &ranges[i]);
+}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges);
+
+struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
+ struct pinctrl_gpio_range *range)
+{
+ struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname);
+
+ /*
+ * If we can't find this device, let's assume that is because
+ * it has not probed yet, so the driver trying to register this
+ * range need to defer probing.
+ */
+ if (!pctldev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ pinctrl_add_gpio_range(pctldev, range);
+ return pctldev;
+}
+EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);
+
+/**
+ * pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
+ * @pctldev: the pin controller device to look in
+ * @pin: a controller-local number to find the range for
+ */
+struct pinctrl_gpio_range *
+pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ struct pinctrl_gpio_range *range = NULL;
+
+ /* Loop over the ranges */
+ list_for_each_entry(range, &pctldev->gpio_ranges, node) {
+ /* Check if we're in the valid range */
+ if (pin >= range->pin_base &&
+ pin < range->pin_base + range->npins) {
+ return range;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin);
+
/**
* pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller
* @pctldev: pin controller device to remove the range from
@@ -318,9 +401,10 @@
const char *pin_group)
{
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ unsigned ngroups = pctlops->get_groups_count(pctldev);
unsigned group_selector = 0;
- while (pctlops->list_groups(pctldev, group_selector) >= 0) {
+ while (group_selector < ngroups) {
const char *gname = pctlops->get_group_name(pctldev,
group_selector);
if (!strcmp(gname, pin_group)) {
@@ -360,7 +444,7 @@
ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
if (ret) {
mutex_unlock(&pinctrl_mutex);
- return -EINVAL;
+ return ret;
}
/* Convert to the pin controllers number space */
@@ -516,13 +600,21 @@
setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
if (setting->pctldev == NULL) {
- dev_err(p->dev, "unknown pinctrl device %s in map entry",
- map->ctrl_dev_name);
kfree(setting);
- /* Eventually, this should trigger deferred probe */
- return -ENODEV;
+ /* Do not defer probing of hogs (circular loop) */
+ if (!strcmp(map->ctrl_dev_name, map->dev_name))
+ return -ENODEV;
+ /*
+ * OK let us guess that the driver is not there yet, and
+ * let's defer obtaining this pinctrl handle to later...
+ */
+ dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",
+ map->ctrl_dev_name);
+ return -EPROBE_DEFER;
}
+ setting->dev_name = map->dev_name;
+
switch (map->type) {
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_map_to_setting(map, setting);
@@ -579,6 +671,13 @@
}
p->dev = dev;
INIT_LIST_HEAD(&p->states);
+ INIT_LIST_HEAD(&p->dt_maps);
+
+ ret = pinctrl_dt_to_map(p);
+ if (ret < 0) {
+ kfree(p);
+ return ERR_PTR(ret);
+ }
devname = dev_name(dev);
@@ -589,13 +688,33 @@
continue;
ret = add_setting(p, map);
- if (ret < 0) {
+ /*
+ * At this point the adding of a setting may:
+ *
+ * - Defer, if the pinctrl device is not yet available
+ * - Fail, if the pinctrl device is not yet available,
+ * AND the setting is a hog. We cannot defer that, since
+ * the hog will kick in immediately after the device
+ * is registered.
+ *
+ * If the error returned was not -EPROBE_DEFER then we
+ * accumulate the errors to see if we end up with
+ * an -EPROBE_DEFER later, as that is the worst case.
+ */
+ if (ret == -EPROBE_DEFER) {
pinctrl_put_locked(p, false);
return ERR_PTR(ret);
}
}
+ if (ret < 0) {
+ /* If some other error than deferral occured, return here */
+ pinctrl_put_locked(p, false);
+ return ERR_PTR(ret);
+ }
- /* Add the pinmux to the global list */
+ kref_init(&p->users);
+
+ /* Add the pinctrl handle to the global list */
list_add_tail(&p->node, &pinctrl_list);
return p;
@@ -608,15 +727,19 @@
if (WARN_ON(!dev))
return ERR_PTR(-EINVAL);
+ /*
+ * See if somebody else (such as the device core) has already
+ * obtained a handle to the pinctrl for this device. In that case,
+ * return another pointer to it.
+ */
p = find_pinctrl(dev);
- if (p != NULL)
- return ERR_PTR(-EBUSY);
-
- p = create_pinctrl(dev);
- if (IS_ERR(p))
+ if (p != NULL) {
+ dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
+ kref_get(&p->users);
return p;
+ }
- return p;
+ return create_pinctrl(dev);
}
/**
@@ -662,19 +785,32 @@
kfree(state);
}
+ pinctrl_dt_free_maps(p);
+
if (inlist)
list_del(&p->node);
kfree(p);
}
/**
- * pinctrl_put() - release a previously claimed pinctrl handle
+ * pinctrl_release() - release the pinctrl handle
+ * @kref: the kref in the pinctrl being released
+ */
+static void pinctrl_release(struct kref *kref)
+{
+ struct pinctrl *p = container_of(kref, struct pinctrl, users);
+
+ pinctrl_put_locked(p, true);
+}
+
+/**
+ * pinctrl_put() - decrease use count on a previously claimed pinctrl handle
* @p: the pinctrl handle to release
*/
void pinctrl_put(struct pinctrl *p)
{
mutex_lock(&pinctrl_mutex);
- pinctrl_put_locked(p, true);
+ kref_put(&p->users, pinctrl_release);
mutex_unlock(&pinctrl_mutex);
}
EXPORT_SYMBOL_GPL(pinctrl_put);
@@ -685,8 +821,15 @@
struct pinctrl_state *state;
state = find_state(p, name);
- if (!state)
- return ERR_PTR(-ENODEV);
+ if (!state) {
+ if (pinctrl_dummy_state) {
+ /* create dummy state */
+ dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
+ name);
+ state = create_state(p, name);
+ } else
+ state = ERR_PTR(-ENODEV);
+ }
return state;
}
@@ -787,15 +930,63 @@
}
EXPORT_SYMBOL_GPL(pinctrl_select_state);
+static void devm_pinctrl_release(struct device *dev, void *res)
+{
+ pinctrl_put(*(struct pinctrl **)res);
+}
+
/**
- * pinctrl_register_mappings() - register a set of pin controller mappings
- * @maps: the pincontrol mappings table to register. This should probably be
- * marked with __initdata so it can be discarded after boot. This
- * function will perform a shallow copy for the mapping entries.
- * @num_maps: the number of maps in the mapping table
+ * struct devm_pinctrl_get() - Resource managed pinctrl_get()
+ * @dev: the device to obtain the handle for
+ *
+ * If there is a need to explicitly destroy the returned struct pinctrl,
+ * devm_pinctrl_put() should be used, rather than plain pinctrl_put().
*/
-int pinctrl_register_mappings(struct pinctrl_map const *maps,
- unsigned num_maps)
+struct pinctrl *devm_pinctrl_get(struct device *dev)
+{
+ struct pinctrl **ptr, *p;
+
+ ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ p = pinctrl_get(dev);
+ if (!IS_ERR(p)) {
+ *ptr = p;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return p;
+}
+EXPORT_SYMBOL_GPL(devm_pinctrl_get);
+
+static int devm_pinctrl_match(struct device *dev, void *res, void *data)
+{
+ struct pinctrl **p = res;
+
+ return *p == data;
+}
+
+/**
+ * devm_pinctrl_put() - Resource managed pinctrl_put()
+ * @p: the pinctrl handle to release
+ *
+ * Deallocate a struct pinctrl obtained via devm_pinctrl_get(). Normally
+ * this function will not need to be called and the resource management
+ * code will ensure that the resource is freed.
+ */
+void devm_pinctrl_put(struct pinctrl *p)
+{
+ WARN_ON(devres_destroy(p->dev, devm_pinctrl_release,
+ devm_pinctrl_match, p));
+ pinctrl_put(p);
+}
+EXPORT_SYMBOL_GPL(devm_pinctrl_put);
+
+int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
+ bool dup, bool locked)
{
int i, ret;
struct pinctrl_maps *maps_node;
@@ -829,13 +1020,13 @@
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_validate_map(&maps[i], i);
if (ret < 0)
- return 0;
+ return ret;
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_validate_map(&maps[i], i);
if (ret < 0)
- return 0;
+ return ret;
break;
default:
pr_err("failed to register map %s (%d): invalid type given\n",
@@ -851,20 +1042,76 @@
}
maps_node->num_maps = num_maps;
- maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps, GFP_KERNEL);
- if (!maps_node->maps) {
- pr_err("failed to duplicate mapping table\n");
- kfree(maps_node);
- return -ENOMEM;
+ if (dup) {
+ maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps,
+ GFP_KERNEL);
+ if (!maps_node->maps) {
+ pr_err("failed to duplicate mapping table\n");
+ kfree(maps_node);
+ return -ENOMEM;
+ }
+ } else {
+ maps_node->maps = maps;
}
- mutex_lock(&pinctrl_mutex);
+ if (!locked)
+ mutex_lock(&pinctrl_mutex);
list_add_tail(&maps_node->node, &pinctrl_maps);
- mutex_unlock(&pinctrl_mutex);
+ if (!locked)
+ mutex_unlock(&pinctrl_mutex);
return 0;
}
+/**
+ * pinctrl_register_mappings() - register a set of pin controller mappings
+ * @maps: the pincontrol mappings table to register. This should probably be
+ * marked with __initdata so it can be discarded after boot. This
+ * function will perform a shallow copy for the mapping entries.
+ * @num_maps: the number of maps in the mapping table
+ */
+int pinctrl_register_mappings(struct pinctrl_map const *maps,
+ unsigned num_maps)
+{
+ return pinctrl_register_map(maps, num_maps, true, false);
+}
+
+void pinctrl_unregister_map(struct pinctrl_map const *map)
+{
+ struct pinctrl_maps *maps_node;
+
+ list_for_each_entry(maps_node, &pinctrl_maps, node) {
+ if (maps_node->maps == map) {
+ list_del(&maps_node->node);
+ return;
+ }
+ }
+}
+
+/**
+ * pinctrl_force_sleep() - turn a given controller device into sleep state
+ * @pctldev: pin controller device
+ */
+int pinctrl_force_sleep(struct pinctrl_dev *pctldev)
+{
+ if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep))
+ return pinctrl_select_state(pctldev->p, pctldev->hog_sleep);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pinctrl_force_sleep);
+
+/**
+ * pinctrl_force_default() - turn a given controller device into default state
+ * @pctldev: pin controller device
+ */
+int pinctrl_force_default(struct pinctrl_dev *pctldev)
+{
+ if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default))
+ return pinctrl_select_state(pctldev->p, pctldev->hog_default);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pinctrl_force_default);
+
#ifdef CONFIG_DEBUG_FS
static int pinctrl_pins_show(struct seq_file *s, void *what)
@@ -906,15 +1153,17 @@
{
struct pinctrl_dev *pctldev = s->private;
const struct pinctrl_ops *ops = pctldev->desc->pctlops;
- unsigned selector = 0;
+ unsigned ngroups, selector = 0;
+ ngroups = ops->get_groups_count(pctldev);
mutex_lock(&pinctrl_mutex);
seq_puts(s, "registered pin groups:\n");
- while (ops->list_groups(pctldev, selector) >= 0) {
+ while (selector < ngroups) {
const unsigned *pins;
unsigned num_pins;
const char *gname = ops->get_group_name(pctldev, selector);
+ const char *pname;
int ret;
int i;
@@ -924,10 +1173,16 @@
seq_printf(s, "%s [ERROR GETTING PINS]\n",
gname);
else {
- seq_printf(s, "group: %s, pins = [ ", gname);
- for (i = 0; i < num_pins; i++)
- seq_printf(s, "%d ", pins[i]);
- seq_puts(s, "]\n");
+ seq_printf(s, "group: %s\n", gname);
+ for (i = 0; i < num_pins; i++) {
+ pname = pin_get_name(pctldev, pins[i]);
+ if (WARN_ON(!pname)) {
+ mutex_unlock(&pinctrl_mutex);
+ return -EINVAL;
+ }
+ seq_printf(s, "pin %d (%s)\n", pins[i], pname);
+ }
+ seq_puts(s, "\n");
}
selector++;
}
@@ -1226,11 +1481,14 @@
const struct pinctrl_ops *ops = pctldev->desc->pctlops;
if (!ops ||
- !ops->list_groups ||
+ !ops->get_groups_count ||
!ops->get_group_name ||
!ops->get_group_pins)
return -EINVAL;
+ if (ops->dt_node_to_map && !ops->dt_free_map)
+ return -EINVAL;
+
return 0;
}
@@ -1246,9 +1504,9 @@
struct pinctrl_dev *pctldev;
int ret;
- if (pctldesc == NULL)
+ if (!pctldesc)
return NULL;
- if (pctldesc->name == NULL)
+ if (!pctldesc->name)
return NULL;
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
@@ -1266,39 +1524,28 @@
pctldev->dev = dev;
/* check core ops for sanity */
- ret = pinctrl_check_ops(pctldev);
- if (ret) {
- pr_err("%s pinctrl ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinctrl_check_ops(pctldev)) {
+ dev_err(dev, "pinctrl ops lacks necessary functions\n");
goto out_err;
}
/* If we're implementing pinmuxing, check the ops for sanity */
if (pctldesc->pmxops) {
- ret = pinmux_check_ops(pctldev);
- if (ret) {
- pr_err("%s pinmux ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinmux_check_ops(pctldev))
goto out_err;
- }
}
/* If we're implementing pinconfig, check the ops for sanity */
if (pctldesc->confops) {
- ret = pinconf_check_ops(pctldev);
- if (ret) {
- pr_err("%s pin config ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinconf_check_ops(pctldev))
goto out_err;
- }
}
/* Register all the pins */
- pr_debug("try to register %d pins on %s...\n",
- pctldesc->npins, pctldesc->name);
+ dev_dbg(dev, "try to register %d pins ...\n", pctldesc->npins);
ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
if (ret) {
- pr_err("error during pin registration\n");
+ dev_err(dev, "error during pin registration\n");
pinctrl_free_pindescs(pctldev, pctldesc->pins,
pctldesc->npins);
goto out_err;
@@ -1310,11 +1557,23 @@
pctldev->p = pinctrl_get_locked(pctldev->dev);
if (!IS_ERR(pctldev->p)) {
- struct pinctrl_state *s =
+ pctldev->hog_default =
pinctrl_lookup_state_locked(pctldev->p,
PINCTRL_STATE_DEFAULT);
- if (!IS_ERR(s))
- pinctrl_select_state_locked(pctldev->p, s);
+ if (IS_ERR(pctldev->hog_default)) {
+ dev_dbg(dev, "failed to lookup the default state\n");
+ } else {
+ if (pinctrl_select_state_locked(pctldev->p,
+ pctldev->hog_default))
+ dev_err(dev,
+ "failed to select default state\n");
+ }
+
+ pctldev->hog_sleep =
+ pinctrl_lookup_state_locked(pctldev->p,
+ PINCTRL_STATE_SLEEP);
+ if (IS_ERR(pctldev->hog_sleep))
+ dev_dbg(dev, "failed to lookup the sleep state\n");
}
mutex_unlock(&pinctrl_mutex);
@@ -1337,6 +1596,7 @@
*/
void pinctrl_unregister(struct pinctrl_dev *pctldev)
{
+ struct pinctrl_gpio_range *range, *n;
if (pctldev == NULL)
return;
@@ -1352,6 +1612,10 @@
/* Destroy descriptor tree */
pinctrl_free_pindescs(pctldev, pctldev->desc->pins,
pctldev->desc->npins);
+ /* remove gpio ranges map */
+ list_for_each_entry_safe(range, n, &pctldev->gpio_ranges, node)
+ list_del(&range->node);
+
kfree(pctldev);
mutex_unlock(&pinctrl_mutex);
diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h
index 17ecf65..ee72f1f 100644
--- a/drivers/pinctrl/core.h
+++ b/drivers/pinctrl/core.h
@@ -9,6 +9,7 @@
* License terms: GNU General Public License (GPL) version 2
*/
+#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/radix-tree.h>
#include <linux/pinctrl/pinconf.h>
@@ -30,6 +31,8 @@
* @driver_data: driver data for drivers registering to the pin controller
* subsystem
* @p: result of pinctrl_get() for this device
+ * @hog_default: default state for pins hogged by this device
+ * @hog_sleep: sleep state for pins hogged by this device
* @device_root: debugfs root for this device
*/
struct pinctrl_dev {
@@ -41,6 +44,8 @@
struct module *owner;
void *driver_data;
struct pinctrl *p;
+ struct pinctrl_state *hog_default;
+ struct pinctrl_state *hog_sleep;
#ifdef CONFIG_DEBUG_FS
struct dentry *device_root;
#endif
@@ -52,12 +57,17 @@
* @dev: the device using this pin control handle
* @states: a list of states for this device
* @state: the current state
+ * @dt_maps: the mapping table chunks dynamically parsed from device tree for
+ * this device, if any
+ * @users: reference count
*/
struct pinctrl {
struct list_head node;
struct device *dev;
struct list_head states;
struct pinctrl_state *state;
+ struct list_head dt_maps;
+ struct kref users;
};
/**
@@ -100,13 +110,16 @@
* struct pinctrl_setting - an individual mux or config setting
* @node: list node for struct pinctrl_settings's @settings field
* @type: the type of setting
- * @pctldev: pin control device handling to be programmed
+ * @pctldev: pin control device handling to be programmed. Not used for
+ * PIN_MAP_TYPE_DUMMY_STATE.
+ * @dev_name: the name of the device using this state
* @data: Data specific to the setting type
*/
struct pinctrl_setting {
struct list_head node;
enum pinctrl_map_type type;
struct pinctrl_dev *pctldev;
+ const char *dev_name;
union {
struct pinctrl_setting_mux mux;
struct pinctrl_setting_configs configs;
@@ -142,8 +155,21 @@
#endif
};
+/**
+ * struct pinctrl_maps - a list item containing part of the mapping table
+ * @node: mapping table list node
+ * @maps: array of mapping table entries
+ * @num_maps: the number of entries in @maps
+ */
+struct pinctrl_maps {
+ struct list_head node;
+ struct pinctrl_map const *maps;
+ unsigned num_maps;
+};
+
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name);
+const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin);
int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
const char *pin_group);
@@ -153,4 +179,19 @@
return radix_tree_lookup(&pctldev->pin_desc_tree, pin);
}
+int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
+ bool dup, bool locked);
+void pinctrl_unregister_map(struct pinctrl_map const *map);
+
+extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
+extern int pinctrl_force_default(struct pinctrl_dev *pctldev);
+
extern struct mutex pinctrl_mutex;
+extern struct list_head pinctrldev_list;
+extern struct list_head pinctrl_maps;
+
+#define for_each_maps(_maps_node_, _i_, _map_) \
+ list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
+ for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
+ _i_ < _maps_node_->num_maps; \
+ _i_++, _map_ = &_maps_node_->maps[_i_])
diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
new file mode 100644
index 0000000..fd40a11
--- /dev/null
+++ b/drivers/pinctrl/devicetree.c
@@ -0,0 +1,265 @@
+/*
+ * Device tree integration for the pin control subsystem
+ *
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "devicetree.h"
+
+/**
+ * struct pinctrl_dt_map - mapping table chunk parsed from device tree
+ * @node: list node for struct pinctrl's @dt_maps field
+ * @pctldev: the pin controller that allocated this struct, and will free it
+ * @maps: the mapping table entries
+ */
+struct pinctrl_dt_map {
+ struct list_head node;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_map *map;
+ unsigned num_maps;
+};
+
+static void dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ if (pctldev) {
+ struct pinctrl_ops *ops = pctldev->desc->pctlops;
+ ops->dt_free_map(pctldev, map, num_maps);
+ } else {
+ /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */
+ kfree(map);
+ }
+}
+
+void pinctrl_dt_free_maps(struct pinctrl *p)
+{
+ struct pinctrl_dt_map *dt_map, *n1;
+
+ list_for_each_entry_safe(dt_map, n1, &p->dt_maps, node) {
+ pinctrl_unregister_map(dt_map->map);
+ list_del(&dt_map->node);
+ dt_free_map(dt_map->pctldev, dt_map->map,
+ dt_map->num_maps);
+ kfree(dt_map);
+ }
+
+ of_node_put(p->dev->of_node);
+}
+
+static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
+ struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ int i;
+ struct pinctrl_dt_map *dt_map;
+
+ /* Initialize common mapping table entry fields */
+ for (i = 0; i < num_maps; i++) {
+ map[i].dev_name = dev_name(p->dev);
+ map[i].name = statename;
+ if (pctldev)
+ map[i].ctrl_dev_name = dev_name(pctldev->dev);
+ }
+
+ /* Remember the converted mapping table entries */
+ dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);
+ if (!dt_map) {
+ dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n");
+ dt_free_map(pctldev, map, num_maps);
+ return -ENOMEM;
+ }
+
+ dt_map->pctldev = pctldev;
+ dt_map->map = map;
+ dt_map->num_maps = num_maps;
+ list_add_tail(&dt_map->node, &p->dt_maps);
+
+ return pinctrl_register_map(map, num_maps, false, true);
+}
+
+static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np)
+{
+ struct pinctrl_dev *pctldev;
+
+ list_for_each_entry(pctldev, &pinctrldev_list, node)
+ if (pctldev->dev->of_node == np)
+ return pctldev;
+
+ return NULL;
+}
+
+struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
+{
+ struct pinctrl_dev *pctldev;
+
+ pctldev = find_pinctrl_by_of_node(np);
+ if (!pctldev)
+ return NULL;
+
+ return pctldev;
+}
+
+static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
+ struct device_node *np_config)
+{
+ struct device_node *np_pctldev;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_ops *ops;
+ int ret;
+ struct pinctrl_map *map;
+ unsigned num_maps;
+
+ /* Find the pin controller containing np_config */
+ np_pctldev = of_node_get(np_config);
+ for (;;) {
+ np_pctldev = of_get_next_parent(np_pctldev);
+ if (!np_pctldev || of_node_is_root(np_pctldev)) {
+ dev_info(p->dev, "could not find pctldev for node %s, deferring probe\n",
+ np_config->full_name);
+ of_node_put(np_pctldev);
+ /* OK let's just assume this will appear later then */
+ return -EPROBE_DEFER;
+ }
+ pctldev = find_pinctrl_by_of_node(np_pctldev);
+ if (pctldev)
+ break;
+ /* Do not defer probing of hogs (circular loop) */
+ if (np_pctldev == p->dev->of_node) {
+ of_node_put(np_pctldev);
+ return -ENODEV;
+ }
+ }
+ of_node_put(np_pctldev);
+
+ /*
+ * Call pinctrl driver to parse device tree node, and
+ * generate mapping table entries
+ */
+ ops = pctldev->desc->pctlops;
+ if (!ops->dt_node_to_map) {
+ dev_err(p->dev, "pctldev %s doesn't support DT\n",
+ dev_name(pctldev->dev));
+ return -ENODEV;
+ }
+ ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
+ if (ret < 0)
+ return ret;
+
+ /* Stash the mapping table chunk away for later use */
+ return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);
+}
+
+static int dt_remember_dummy_state(struct pinctrl *p, const char *statename)
+{
+ struct pinctrl_map *map;
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map) {
+ dev_err(p->dev, "failed to alloc struct pinctrl_map\n");
+ return -ENOMEM;
+ }
+
+ /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */
+ map->type = PIN_MAP_TYPE_DUMMY_STATE;
+
+ return dt_remember_or_free_map(p, statename, NULL, map, 1);
+}
+
+int pinctrl_dt_to_map(struct pinctrl *p)
+{
+ struct device_node *np = p->dev->of_node;
+ int state, ret;
+ char *propname;
+ struct property *prop;
+ const char *statename;
+ const __be32 *list;
+ int size, config;
+ phandle phandle;
+ struct device_node *np_config;
+
+ /* CONFIG_OF enabled, p->dev not instantiated from DT */
+ if (!np) {
+ dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n");
+ return 0;
+ }
+
+ /* We may store pointers to property names within the node */
+ of_node_get(np);
+
+ /* For each defined state ID */
+ for (state = 0; ; state++) {
+ /* Retrieve the pinctrl-* property */
+ propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
+ prop = of_find_property(np, propname, &size);
+ kfree(propname);
+ if (!prop)
+ break;
+ list = prop->value;
+ size /= sizeof(*list);
+
+ /* Determine whether pinctrl-names property names the state */
+ ret = of_property_read_string_index(np, "pinctrl-names",
+ state, &statename);
+ /*
+ * If not, statename is just the integer state ID. But rather
+ * than dynamically allocate it and have to free it later,
+ * just point part way into the property name for the string.
+ */
+ if (ret < 0) {
+ /* strlen("pinctrl-") == 8 */
+ statename = prop->name + 8;
+ }
+
+ /* For every referenced pin configuration node in it */
+ for (config = 0; config < size; config++) {
+ phandle = be32_to_cpup(list++);
+
+ /* Look up the pin configuration node */
+ np_config = of_find_node_by_phandle(phandle);
+ if (!np_config) {
+ dev_err(p->dev,
+ "prop %s index %i invalid phandle\n",
+ prop->name, config);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Parse the node */
+ ret = dt_to_map_one_config(p, statename, np_config);
+ of_node_put(np_config);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* No entries in DT? Generate a dummy state table entry */
+ if (!size) {
+ ret = dt_remember_dummy_state(p, statename);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ pinctrl_dt_free_maps(p);
+ return ret;
+}
diff --git a/drivers/pinctrl/devicetree.h b/drivers/pinctrl/devicetree.h
new file mode 100644
index 0000000..760bc49
--- /dev/null
+++ b/drivers/pinctrl/devicetree.h
@@ -0,0 +1,35 @@
+/*
+ * Internal interface to pinctrl device tree integration
+ *
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef CONFIG_OF
+
+void pinctrl_dt_free_maps(struct pinctrl *p);
+int pinctrl_dt_to_map(struct pinctrl *p);
+
+#else
+
+static inline int pinctrl_dt_to_map(struct pinctrl *p)
+{
+ return 0;
+}
+
+static inline void pinctrl_dt_free_maps(struct pinctrl *p)
+{
+}
+
+#endif
diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c
index 33fbaea..180b856 100644
--- a/drivers/pinctrl/pinconf-generic.c
+++ b/drivers/pinctrl/pinconf-generic.c
@@ -20,6 +20,7 @@
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/of.h>
#include "core.h"
#include "pinconf.h"
@@ -36,15 +37,22 @@
struct pin_config_item conf_items[] = {
PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL),
PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL),
+ PCONFDUMP(PIN_CONFIG_BIAS_BUS_HOLD, "input bias bus hold", NULL),
PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL),
PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL),
+ PCONFDUMP(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT,
+ "input bias pull to pin specific state", NULL),
PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL),
PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL),
PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL),
+ PCONFDUMP(PIN_CONFIG_DRIVE_STRENGTH, "output drive strength", "mA"),
+ PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL),
PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL),
PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "time units"),
PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector"),
+ PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL),
PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode"),
+ PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level"),
};
void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
@@ -118,3 +126,103 @@
}
#endif
+
+#ifdef CONFIG_OF
+struct pinconf_generic_dt_params {
+ const char * const property;
+ enum pin_config_param param;
+ u32 default_value;
+};
+
+static struct pinconf_generic_dt_params dt_params[] = {
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 },
+ { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+ { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
+ { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
+ { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
+ { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 },
+ { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
+ { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
+ { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
+ { "input-schmitt", PIN_CONFIG_INPUT_SCHMITT, 0 },
+ { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
+ { "power-source", PIN_CONFIG_POWER_SOURCE, 0 },
+ { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
+ { "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 },
+ { "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 },
+ { "output-low", PIN_CONFIG_OUTPUT, 0, },
+ { "output-high", PIN_CONFIG_OUTPUT, 1, },
+};
+
+/**
+ * pinconf_generic_parse_dt_config()
+ * parse the config properties into generic pinconfig values.
+ * @np: node containing the pinconfig properties
+ * @configs: array with nconfigs entries containing the generic pinconf values
+ * @nconfigs: umber of configurations
+ */
+int pinconf_generic_parse_dt_config(struct device_node *np,
+ unsigned long **configs,
+ unsigned int *nconfigs)
+{
+ unsigned long *cfg;
+ unsigned int ncfg = 0;
+ int ret;
+ int i;
+ u32 val;
+
+ if (!np)
+ return -EINVAL;
+
+ /* allocate a temporary array big enough to hold one of each option */
+ cfg = kzalloc(sizeof(*cfg) * ARRAY_SIZE(dt_params), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(dt_params); i++) {
+ struct pinconf_generic_dt_params *par = &dt_params[i];
+ ret = of_property_read_u32(np, par->property, &val);
+
+ /* property not found */
+ if (ret == -EINVAL)
+ continue;
+
+ /* use default value, when no value is specified */
+ if (ret)
+ val = par->default_value;
+
+ pr_debug("found %s with value %u\n", par->property, val);
+ cfg[ncfg] = pinconf_to_config_packed(par->param, val);
+ ncfg++;
+ }
+
+ ret = 0;
+
+ /* no configs found at all */
+ if (ncfg == 0) {
+ *configs = NULL;
+ *nconfigs = 0;
+ goto out;
+ }
+
+ /*
+ * Now limit the number of configs to the real number of
+ * found properties.
+ */
+ *configs = kzalloc(ncfg * sizeof(unsigned long), GFP_KERNEL);
+ if (!*configs) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(*configs, cfg, ncfg * sizeof(unsigned long));
+ *nconfigs = ncfg;
+
+out:
+ kfree(cfg);
+ return ret;
+}
+#endif
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c
index 7321e86..d611ecf 100644
--- a/drivers/pinctrl/pinconf.c
+++ b/drivers/pinctrl/pinconf.c
@@ -28,11 +28,17 @@
const struct pinconf_ops *ops = pctldev->desc->confops;
/* We must be able to read out pin status */
- if (!ops->pin_config_get && !ops->pin_config_group_get)
+ if (!ops->pin_config_get && !ops->pin_config_group_get) {
+ dev_err(pctldev->dev,
+ "pinconf must be able to read out pin status\n");
return -EINVAL;
+ }
/* We have to be able to config the pins in SOME way */
- if (!ops->pin_config_set && !ops->pin_config_group_set)
+ if (!ops->pin_config_set && !ops->pin_config_group_set) {
+ dev_err(pctldev->dev,
+ "pinconf has to be able to set a pins config\n");
return -EINVAL;
+ }
return 0;
}
@@ -44,9 +50,9 @@
return -EINVAL;
}
- if (map->data.configs.num_configs &&
+ if (!map->data.configs.num_configs ||
!map->data.configs.configs) {
- pr_err("failed to register map %s (%d): no configs ptr given\n",
+ pr_err("failed to register map %s (%d): no configs given\n",
map->name, i);
return -EINVAL;
}
@@ -379,8 +385,16 @@
void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map)
{
+ struct pinctrl_dev *pctldev;
+ const struct pinconf_ops *confops;
int i;
+ pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
+ if (pctldev)
+ confops = pctldev->desc->confops;
+ else
+ confops = NULL;
+
switch (map->type) {
case PIN_MAP_TYPE_CONFIGS_PIN:
seq_printf(s, "pin ");
@@ -394,8 +408,15 @@
seq_printf(s, "%s\n", map->data.configs.group_or_pin);
- for (i = 0; i < map->data.configs.num_configs; i++)
- seq_printf(s, "config %08lx\n", map->data.configs.configs[i]);
+ for (i = 0; i < map->data.configs.num_configs; i++) {
+ seq_printf(s, "config ");
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev, s,
+ map->data.configs.configs[i]);
+ else
+ seq_printf(s, "%08lx", map->data.configs.configs[i]);
+ seq_printf(s, "\n");
+ }
}
void pinconf_show_setting(struct seq_file *s,
@@ -403,6 +424,7 @@
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ const struct pinconf_ops *confops = pctldev->desc->confops;
struct pin_desc *desc;
int i;
@@ -428,8 +450,15 @@
* FIXME: We should really get the pin controler to dump the config
* values, so they can be decoded to something meaningful.
*/
- for (i = 0; i < setting->data.configs.num_configs; i++)
- seq_printf(s, " %08lx", setting->data.configs.configs[i]);
+ for (i = 0; i < setting->data.configs.num_configs; i++) {
+ seq_printf(s, " ");
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev, s,
+ setting->data.configs.configs[i]);
+ else
+ seq_printf(s, "%08lx",
+ setting->data.configs.configs[i]);
+ }
seq_printf(s, "\n");
}
@@ -448,10 +477,14 @@
static int pinconf_pins_show(struct seq_file *s, void *what)
{
struct pinctrl_dev *pctldev = s->private;
+ const struct pinconf_ops *ops = pctldev->desc->confops;
unsigned i, pin;
+ if (!ops || !ops->pin_config_get)
+ return 0;
+
seq_puts(s, "Pin config settings per pin\n");
- seq_puts(s, "Format: pin (name): pinmux setting array\n");
+ seq_puts(s, "Format: pin (name): configs\n");
mutex_lock(&pinctrl_mutex);
@@ -495,17 +528,16 @@
struct pinctrl_dev *pctldev = s->private;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
const struct pinconf_ops *ops = pctldev->desc->confops;
+ unsigned ngroups = pctlops->get_groups_count(pctldev);
unsigned selector = 0;
if (!ops || !ops->pin_config_group_get)
return 0;
seq_puts(s, "Pin config settings per pin group\n");
- seq_puts(s, "Format: group (name): pinmux setting array\n");
+ seq_puts(s, "Format: group (name): configs\n");
- mutex_lock(&pinctrl_mutex);
-
- while (pctlops->list_groups(pctldev, selector) >= 0) {
+ while (selector < ngroups) {
const char *gname = pctlops->get_group_name(pctldev, selector);
seq_printf(s, "%u (%s):", selector, gname);
@@ -515,8 +547,6 @@
selector++;
}
- mutex_unlock(&pinctrl_mutex);
-
return 0;
}
@@ -544,6 +574,207 @@
.release = single_release,
};
+/* 32bit read/write ressources */
+#define MAX_NAME_LEN 16
+char dbg_pinname[MAX_NAME_LEN]; /* shared: name of the state of the pin*/
+char dbg_state_name[MAX_NAME_LEN]; /* shared: state of the pin*/
+static u32 dbg_config; /* shared: config to be read/set for the pin & state*/
+
+static int pinconf_dbg_pinname_print(struct seq_file *s, void *d)
+{
+ if (strlen(dbg_pinname))
+ seq_printf(s, "%s\n", dbg_pinname);
+ else
+ seq_printf(s, "No pin name set\n");
+ return 0;
+}
+
+static int pinconf_dbg_pinname_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_pinname_print, inode->i_private);
+}
+
+static int pinconf_dbg_pinname_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+
+ if (count > MAX_NAME_LEN)
+ return -EINVAL;
+
+ err = sscanf(user_buf, "%15s", dbg_pinname);
+
+ if (err != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinname_fops = {
+ .open = pinconf_dbg_pinname_open,
+ .write = pinconf_dbg_pinname_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int pinconf_dbg_state_print(struct seq_file *s, void *d)
+{
+ if (strlen(dbg_state_name))
+ seq_printf(s, "%s\n", dbg_state_name);
+ else
+ seq_printf(s, "No pin state set\n");
+ return 0;
+}
+
+static int pinconf_dbg_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_state_print, inode->i_private);
+}
+
+static int pinconf_dbg_state_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+
+ if (count > MAX_NAME_LEN)
+ return -EINVAL;
+
+ err = sscanf(user_buf, "%15s", dbg_state_name);
+
+ if (err != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinstate_fops = {
+ .open = pinconf_dbg_state_open,
+ .write = pinconf_dbg_state_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/**
+ * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl
+ * map, of a pin/state pair based on pinname and state that have been
+ * selected with the debugfs entries pinconf-name and pinconf-state
+ * @s: contains the 32bits config to be written
+ * @d: not used
+ */
+static int pinconf_dbg_config_print(struct seq_file *s, void *d)
+{
+ struct pinctrl_maps *maps_node;
+ struct pinctrl_map const *map;
+ struct pinctrl_dev *pctldev = NULL;
+ struct pinconf_ops *confops = NULL;
+ int i, j;
+ bool found = false;
+
+ mutex_lock(&pinctrl_mutex);
+
+ /* Parse the pinctrl map and look for the elected pin/state */
+ for_each_maps(maps_node, i, map) {
+ if (map->type != PIN_MAP_TYPE_CONFIGS_PIN)
+ continue;
+
+ if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0)
+ continue;
+
+ for (j = 0; j < map->data.configs.num_configs; j++) {
+ if (0 == strncmp(map->data.configs.group_or_pin,
+ dbg_pinname, MAX_NAME_LEN)) {
+ /* We found the right pin / state, read the
+ * config and store the pctldev */
+ dbg_config = map->data.configs.configs[j];
+ pctldev = get_pinctrl_dev_from_devname
+ (map->ctrl_dev_name);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&pinctrl_mutex);
+
+ if (found) {
+ seq_printf(s, "Config of %s in state %s: 0x%08X\n", dbg_pinname,
+ dbg_state_name, dbg_config);
+
+ if (pctldev)
+ confops = pctldev->desc->confops;
+
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev,
+ s, dbg_config);
+ } else {
+ seq_printf(s, "No pin found for defined name/state\n");
+ }
+
+ return 0;
+}
+
+static int pinconf_dbg_config_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_config_print, inode->i_private);
+}
+
+/**
+ * pinconf_dbg_config_write() - overwrite the pinctrl config in thepinctrl
+ * map, of a pin/state pair based on pinname and state that have been
+ * selected with the debugfs entries pinconf-name and pinconf-state
+ */
+static int pinconf_dbg_config_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+ unsigned long config;
+ struct pinctrl_maps *maps_node;
+ struct pinctrl_map const *map;
+ int i, j;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &config);
+
+ if (err)
+ return err;
+
+ dbg_config = config;
+
+ mutex_lock(&pinctrl_mutex);
+
+ /* Parse the pinctrl map and look for the selected pin/state */
+ for_each_maps(maps_node, i, map) {
+ if (map->type != PIN_MAP_TYPE_CONFIGS_PIN)
+ continue;
+
+ if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0)
+ continue;
+
+ /* we found the right pin / state, so overwrite config */
+ for (j = 0; j < map->data.configs.num_configs; j++) {
+ if (strncmp(map->data.configs.group_or_pin, dbg_pinname,
+ MAX_NAME_LEN) == 0)
+ map->data.configs.configs[j] = dbg_config;
+ }
+ }
+
+ mutex_unlock(&pinctrl_mutex);
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinconfig_fops = {
+ .open = pinconf_dbg_config_open,
+ .write = pinconf_dbg_config_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
void pinconf_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev)
{
@@ -551,6 +782,12 @@
devroot, pctldev, &pinconf_pins_ops);
debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO,
devroot, pctldev, &pinconf_groups_ops);
+ debugfs_create_file("pinconf-name", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinname_fops);
+ debugfs_create_file("pinconf-state", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinstate_fops);
+ debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinconfig_fops);
}
#endif
diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h
index 54510de..bf21525 100644
--- a/drivers/pinctrl/pinconf.h
+++ b/drivers/pinctrl/pinconf.h
@@ -19,11 +19,6 @@
struct pinctrl_setting *setting);
void pinconf_free_setting(struct pinctrl_setting const *setting);
int pinconf_apply_setting(struct pinctrl_setting const *setting);
-void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map);
-void pinconf_show_setting(struct seq_file *s,
- struct pinctrl_setting const *setting);
-void pinconf_init_device_debugfs(struct dentry *devroot,
- struct pinctrl_dev *pctldev);
/*
* You will only be interested in these if you're using PINCONF
@@ -61,6 +56,18 @@
return 0;
}
+#endif
+
+#if defined(CONFIG_PINCONF) && defined(CONFIG_DEBUG_FS)
+
+void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map);
+void pinconf_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting);
+void pinconf_init_device_debugfs(struct dentry *devroot,
+ struct pinctrl_dev *pctldev);
+
+#else
+
static inline void pinconf_show_map(struct seq_file *s,
struct pinctrl_map const *map)
{
@@ -83,7 +90,7 @@
* pin config.
*/
-#ifdef CONFIG_GENERIC_PINCONF
+#if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_DEBUG_FS)
void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin);
@@ -108,3 +115,9 @@
}
#endif
+
+#if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_OF)
+int pinconf_generic_parse_dt_config(struct device_node *np,
+ unsigned long **configs,
+ unsigned int *nconfigs);
+#endif
diff --git a/drivers/pinctrl/pinctrl-pxa3xx.c b/drivers/pinctrl/pinctrl-pxa3xx.c
index 079dce0..7644e42 100644
--- a/drivers/pinctrl/pinctrl-pxa3xx.c
+++ b/drivers/pinctrl/pinctrl-pxa3xx.c
@@ -25,20 +25,18 @@
.pin_base = 0,
};
-static int pxa3xx_list_groups(struct pinctrl_dev *pctrldev, unsigned selector)
+static int pxa3xx_get_groups_count(struct pinctrl_dev *pctrldev)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return -EINVAL;
- return 0;
+
+ return info->num_grps;
}
static const char *pxa3xx_get_group_name(struct pinctrl_dev *pctrldev,
unsigned selector)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return NULL;
+
return info->grps[selector].name;
}
@@ -48,25 +46,23 @@
unsigned *num_pins)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return -EINVAL;
+
*pins = info->grps[selector].pins;
*num_pins = info->grps[selector].npins;
return 0;
}
static struct pinctrl_ops pxa3xx_pctrl_ops = {
- .list_groups = pxa3xx_list_groups,
+ .get_groups_count = pxa3xx_get_groups_count,
.get_group_name = pxa3xx_get_group_name,
.get_group_pins = pxa3xx_get_group_pins,
};
-static int pxa3xx_pmx_list_func(struct pinctrl_dev *pctrldev, unsigned func)
+static int pxa3xx_pmx_get_funcs_count(struct pinctrl_dev *pctrldev)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (func >= info->num_funcs)
- return -EINVAL;
- return 0;
+
+ return info->num_funcs;
}
static const char *pxa3xx_pmx_get_func_name(struct pinctrl_dev *pctrldev,
@@ -170,7 +166,7 @@
}
static struct pinmux_ops pxa3xx_pmx_ops = {
- .list_functions = pxa3xx_pmx_list_func,
+ .get_functions_count = pxa3xx_pmx_get_funcs_count,
.get_function_name = pxa3xx_pmx_get_func_name,
.get_function_groups = pxa3xx_pmx_get_groups,
.enable = pxa3xx_pmx_enable,
diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c
index 6b3534c..ba15b1a 100644
--- a/drivers/pinctrl/pinctrl-sirf.c
+++ b/drivers/pinctrl/pinctrl-sirf.c
@@ -853,18 +853,14 @@
SIRFSOC_PIN_GROUP("gpsgrp", gps_pins),
};
-static int sirfsoc_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int sirfsoc_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(sirfsoc_pin_groups);
}
static const char *sirfsoc_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return NULL;
return sirfsoc_pin_groups[selector].name;
}
@@ -872,8 +868,6 @@
const unsigned **pins,
unsigned *num_pins)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return -EINVAL;
*pins = sirfsoc_pin_groups[selector].pins;
*num_pins = sirfsoc_pin_groups[selector].num_pins;
return 0;
@@ -886,7 +880,7 @@
}
static struct pinctrl_ops sirfsoc_pctrl_ops = {
- .list_groups = sirfsoc_list_groups,
+ .get_groups_count = sirfsoc_get_groups_count,
.get_group_name = sirfsoc_get_group_name,
.get_group_pins = sirfsoc_get_group_pins,
.pin_dbg_show = sirfsoc_pin_dbg_show,
@@ -1033,11 +1027,9 @@
sirfsoc_pinmux_endisable(spmx, selector, false);
}
-static int sirfsoc_pinmux_list_funcs(struct pinctrl_dev *pmxdev, unsigned selector)
+static int sirfsoc_pinmux_get_funcs_count(struct pinctrl_dev *pmxdev)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pmx_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(sirfsoc_pmx_functions);
}
static const char *sirfsoc_pinmux_get_func_name(struct pinctrl_dev *pctldev,
@@ -1074,9 +1066,9 @@
}
static struct pinmux_ops sirfsoc_pinmux_ops = {
- .list_functions = sirfsoc_pinmux_list_funcs,
.enable = sirfsoc_pinmux_enable,
.disable = sirfsoc_pinmux_disable,
+ .get_functions_count = sirfsoc_pinmux_get_funcs_count,
.get_function_name = sirfsoc_pinmux_get_func_name,
.get_function_groups = sirfsoc_pinmux_get_groups,
.gpio_request_enable = sirfsoc_pinmux_request_gpio,
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c
index 9b32968..c4c47c5 100644
--- a/drivers/pinctrl/pinctrl-tegra.c
+++ b/drivers/pinctrl/pinctrl-tegra.c
@@ -53,15 +53,11 @@
writel(val, pmx->regs[bank] + reg);
}
-static int tegra_pinctrl_list_groups(struct pinctrl_dev *pctldev,
- unsigned group)
+static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
-
- return 0;
+ return pmx->soc->ngroups;
}
static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
@@ -69,9 +65,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return NULL;
-
return pmx->soc->groups[group].name;
}
@@ -82,9 +75,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
-
*pins = pmx->soc->groups[group].pins;
*num_pins = pmx->soc->groups[group].npins;
@@ -99,21 +89,17 @@
}
static struct pinctrl_ops tegra_pinctrl_ops = {
- .list_groups = tegra_pinctrl_list_groups,
+ .get_groups_count = tegra_pinctrl_get_groups_count,
.get_group_name = tegra_pinctrl_get_group_name,
.get_group_pins = tegra_pinctrl_get_group_pins,
.pin_dbg_show = tegra_pinctrl_pin_dbg_show,
};
-static int tegra_pinctrl_list_funcs(struct pinctrl_dev *pctldev,
- unsigned function)
+static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return -EINVAL;
-
- return 0;
+ return pmx->soc->nfunctions;
}
static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
@@ -121,9 +107,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return NULL;
-
return pmx->soc->functions[function].name;
}
@@ -134,9 +117,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return -EINVAL;
-
*groups = pmx->soc->functions[function].groups;
*num_groups = pmx->soc->functions[function].ngroups;
@@ -151,8 +131,6 @@
int i;
u32 val;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
if (g->mux_reg < 0)
@@ -180,8 +158,6 @@
const struct tegra_pingroup *g;
u32 val;
- if (group >= pmx->soc->ngroups)
- return;
g = &pmx->soc->groups[group];
if (g->mux_reg < 0)
@@ -194,7 +170,7 @@
}
static struct pinmux_ops tegra_pinmux_ops = {
- .list_functions = tegra_pinctrl_list_funcs,
+ .get_functions_count = tegra_pinctrl_get_funcs_count,
.get_function_name = tegra_pinctrl_get_func_name,
.get_function_groups = tegra_pinctrl_get_func_groups,
.enable = tegra_pinctrl_enable,
@@ -324,8 +300,6 @@
s16 reg;
u32 val, mask;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width);
@@ -353,8 +327,6 @@
s16 reg;
u32 val, mask;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width);
@@ -525,7 +497,6 @@
{
struct tegra_pmx *pmx = platform_get_drvdata(pdev);
- pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range);
pinctrl_unregister(pmx->pctl);
return 0;
diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c
index 26eb8cc..10de43c 100644
--- a/drivers/pinctrl/pinctrl-u300.c
+++ b/drivers/pinctrl/pinctrl-u300.c
@@ -836,18 +836,14 @@
},
};
-static int u300_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int u300_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(u300_pin_groups);
}
static const char *u300_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return NULL;
return u300_pin_groups[selector].name;
}
@@ -855,8 +851,6 @@
const unsigned **pins,
unsigned *num_pins)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return -EINVAL;
*pins = u300_pin_groups[selector].pins;
*num_pins = u300_pin_groups[selector].num_pins;
return 0;
@@ -869,7 +863,7 @@
}
static struct pinctrl_ops u300_pctrl_ops = {
- .list_groups = u300_list_groups,
+ .get_groups_count = u300_get_groups_count,
.get_group_name = u300_get_group_name,
.get_group_pins = u300_get_group_pins,
.pin_dbg_show = u300_pin_dbg_show,
@@ -991,11 +985,9 @@
u300_pmx_endisable(upmx, selector, false);
}
-static int u300_pmx_list_funcs(struct pinctrl_dev *pctldev, unsigned selector)
+static int u300_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(u300_pmx_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(u300_pmx_functions);
}
static const char *u300_pmx_get_func_name(struct pinctrl_dev *pctldev,
@@ -1014,7 +1006,7 @@
}
static struct pinmux_ops u300_pmx_ops = {
- .list_functions = u300_pmx_list_funcs,
+ .get_functions_count = u300_pmx_get_funcs_count,
.get_function_name = u300_pmx_get_func_name,
.get_function_groups = u300_pmx_get_groups,
.enable = u300_pmx_enable,
@@ -1185,8 +1177,6 @@
struct u300_pmx *upmx = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++)
- pinctrl_remove_gpio_range(upmx->pctl, &u300_gpio_ranges[i]);
pinctrl_unregister(upmx->pctl);
iounmap(upmx->virtbase);
release_mem_region(upmx->phybase, upmx->physize);
diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index 4e62783..bd83c8b 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -33,22 +33,25 @@
int pinmux_check_ops(struct pinctrl_dev *pctldev)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ unsigned nfuncs;
unsigned selector = 0;
/* Check that we implement required operations */
- if (!ops->list_functions ||
+ if (!ops ||
+ !ops->get_functions_count ||
!ops->get_function_name ||
!ops->get_function_groups ||
- !ops->enable ||
- !ops->disable)
+ !ops->enable) {
+ dev_err(pctldev->dev, "pinmux ops lacks necessary functions\n");
return -EINVAL;
-
+ }
/* Check that all functions registered have names */
- while (ops->list_functions(pctldev, selector) >= 0) {
+ nfuncs = ops->get_functions_count(pctldev);
+ while (selector < nfuncs) {
const char *fname = ops->get_function_name(pctldev,
selector);
if (!fname) {
- pr_err("pinmux ops has no name for function%u\n",
+ dev_err(pctldev->dev, "pinmux ops has no name for function%u\n",
selector);
return -EINVAL;
}
@@ -85,20 +88,23 @@
const struct pinmux_ops *ops = pctldev->desc->pmxops;
int status = -EINVAL;
- dev_dbg(pctldev->dev, "request pin %d for %s\n", pin, owner);
-
desc = pin_desc_get(pctldev, pin);
if (desc == NULL) {
dev_err(pctldev->dev,
- "pin is not registered so it cannot be requested\n");
+ "pin %d is not registered so it cannot be requested\n",
+ pin);
goto out;
}
+ dev_dbg(pctldev->dev, "request pin %d (%s) for %s\n",
+ pin, desc->name, owner);
+
if (gpio_range) {
/* There's no need to support multiple GPIO requests */
if (desc->gpio_owner) {
dev_err(pctldev->dev,
- "pin already requested\n");
+ "pin %s already requested by %s; cannot claim for %s\n",
+ desc->name, desc->gpio_owner, owner);
goto out;
}
@@ -106,7 +112,8 @@
} else {
if (desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
dev_err(pctldev->dev,
- "pin already requested\n");
+ "pin %s already requested by %s; cannot claim for %s\n",
+ desc->name, desc->mux_owner, owner);
goto out;
}
@@ -139,8 +146,7 @@
status = 0;
if (status) {
- dev_err(pctldev->dev, "->request on device %s failed for pin %d\n",
- pctldev->desc->name, pin);
+ dev_err(pctldev->dev, "request() failed for pin %d\n", pin);
module_put(pctldev->owner);
}
@@ -157,7 +163,7 @@
out:
if (status)
dev_err(pctldev->dev, "pin-%d (%s) status %d\n",
- pin, owner, status);
+ pin, owner, status);
return status;
}
@@ -188,6 +194,11 @@
}
if (!gpio_range) {
+ /*
+ * A pin should not be freed more times than allocated.
+ */
+ if (WARN_ON(!desc->mux_usecount))
+ return NULL;
desc->mux_usecount--;
if (desc->mux_usecount)
return NULL;
@@ -226,14 +237,11 @@
struct pinctrl_gpio_range *range,
unsigned pin, unsigned gpio)
{
- char gpiostr[16];
const char *owner;
int ret;
/* Conjure some name stating what chip and pin this is taken by */
- snprintf(gpiostr, 15, "%s:%d", range->name, gpio);
-
- owner = kstrdup(gpiostr, GFP_KERNEL);
+ owner = kasprintf(GFP_KERNEL, "%s:%d", range->name, gpio);
if (!owner)
return -EINVAL;
@@ -287,10 +295,11 @@
const char *function)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ unsigned nfuncs = ops->get_functions_count(pctldev);
unsigned selector = 0;
/* See if this pctldev has this function */
- while (ops->list_functions(pctldev, selector) >= 0) {
+ while (selector < nfuncs) {
const char *fname = ops->get_function_name(pctldev,
selector);
@@ -310,27 +319,38 @@
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
- const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
char const * const *groups;
unsigned num_groups;
int ret;
const char *group;
int i;
- const unsigned *pins;
- unsigned num_pins;
- setting->data.mux.func =
- pinmux_func_name_to_selector(pctldev, map->data.mux.function);
- if (setting->data.mux.func < 0)
- return setting->data.mux.func;
+ if (!pmxops) {
+ dev_err(pctldev->dev, "does not support mux function\n");
+ return -EINVAL;
+ }
+
+ ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "invalid function %s in map table\n",
+ map->data.mux.function);
+ return ret;
+ }
+ setting->data.mux.func = ret;
ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,
&groups, &num_groups);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(pctldev->dev, "can't query groups for function %s\n",
+ map->data.mux.function);
return ret;
- if (!num_groups)
+ }
+ if (!num_groups) {
+ dev_err(pctldev->dev,
+ "function %s can't be selected on any group\n",
+ map->data.mux.function);
return -EINVAL;
-
+ }
if (map->data.mux.group) {
bool found = false;
group = map->data.mux.group;
@@ -340,63 +360,30 @@
break;
}
}
- if (!found)
+ if (!found) {
+ dev_err(pctldev->dev,
+ "invalid group \"%s\" for function \"%s\"\n",
+ group, map->data.mux.function);
return -EINVAL;
+ }
} else {
group = groups[0];
}
- setting->data.mux.group = pinctrl_get_group_selector(pctldev, group);
- if (setting->data.mux.group < 0)
- return setting->data.mux.group;
-
- ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins,
- &num_pins);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get pins for device %s group selector %d\n",
- pinctrl_dev_get_name(pctldev), setting->data.mux.group);
- return -ENODEV;
+ ret = pinctrl_get_group_selector(pctldev, group);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "invalid group %s in map table\n",
+ map->data.mux.group);
+ return ret;
}
-
- /* Try to allocate all pins in this group, one by one */
- for (i = 0; i < num_pins; i++) {
- ret = pin_request(pctldev, pins[i], map->dev_name, NULL);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get request pin %d on device %s\n",
- pins[i], pinctrl_dev_get_name(pctldev));
- /* On error release all taken pins */
- i--; /* this pin just failed */
- for (; i >= 0; i--)
- pin_free(pctldev, pins[i], NULL);
- return -ENODEV;
- }
- }
+ setting->data.mux.group = ret;
return 0;
}
void pinmux_free_setting(struct pinctrl_setting const *setting)
{
- struct pinctrl_dev *pctldev = setting->pctldev;
- const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
- const unsigned *pins;
- unsigned num_pins;
- int ret;
- int i;
-
- ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
- &pins, &num_pins);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get pins for device %s group selector %d\n",
- pinctrl_dev_get_name(pctldev), setting->data.mux.group);
- return;
- }
-
- for (i = 0; i < num_pins; i++)
- pin_free(pctldev, pins[i], NULL);
+ /* This function is currently unused */
}
int pinmux_enable_setting(struct pinctrl_setting const *setting)
@@ -420,6 +407,18 @@
num_pins = 0;
}
+ /* Try to allocate all pins in this group, one by one */
+ for (i = 0; i < num_pins; i++) {
+ ret = pin_request(pctldev, pins[i], setting->dev_name, NULL);
+ if (ret) {
+ dev_err(pctldev->dev,
+ "could not request pin %d on device %s\n",
+ pins[i], pinctrl_dev_get_name(pctldev));
+ goto err_pin_request;
+ }
+ }
+
+ /* Now that we have acquired the pins, encode the mux setting */
for (i = 0; i < num_pins; i++) {
desc = pin_desc_get(pctldev, pins[i]);
if (desc == NULL) {
@@ -431,8 +430,26 @@
desc->mux_setting = &(setting->data.mux);
}
- return ops->enable(pctldev, setting->data.mux.func,
- setting->data.mux.group);
+ ret = ops->enable(pctldev, setting->data.mux.func,
+ setting->data.mux.group);
+
+ if (ret)
+ goto err_enable;
+
+ return 0;
+
+err_enable:
+ for (i = 0; i < num_pins; i++) {
+ desc = pin_desc_get(pctldev, pins[i]);
+ if (desc)
+ desc->mux_setting = NULL;
+ }
+err_pin_request:
+ /* On error release all taken pins */
+ while (--i >= 0)
+ pin_free(pctldev, pins[i], NULL);
+
+ return ret;
}
void pinmux_disable_setting(struct pinctrl_setting const *setting)
@@ -456,6 +473,7 @@
num_pins = 0;
}
+ /* Flag the descs that no setting is active */
for (i = 0; i < num_pins; i++) {
desc = pin_desc_get(pctldev, pins[i]);
if (desc == NULL) {
@@ -467,7 +485,12 @@
desc->mux_setting = NULL;
}
- ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group);
+ /* And release the pins */
+ for (i = 0; i < num_pins; i++)
+ pin_free(pctldev, pins[i], NULL);
+
+ if (ops->disable)
+ ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group);
}
#ifdef CONFIG_DEBUG_FS
@@ -477,11 +500,15 @@
{
struct pinctrl_dev *pctldev = s->private;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
+ unsigned nfuncs;
unsigned func_selector = 0;
- mutex_lock(&pinctrl_mutex);
+ if (!pmxops)
+ return 0;
- while (pmxops->list_functions(pctldev, func_selector) >= 0) {
+ mutex_lock(&pinctrl_mutex);
+ nfuncs = pmxops->get_functions_count(pctldev);
+ while (func_selector < nfuncs) {
const char *func = pmxops->get_function_name(pctldev,
func_selector);
const char * const *groups;
@@ -515,6 +542,9 @@
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
unsigned i, pin;
+ if (!pmxops)
+ return 0;
+
seq_puts(s, "Pinmux settings per pin\n");
seq_puts(s, "Format: pin (name): mux_owner gpio_owner hog?\n");
diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h
index 6fc4700..d1a98b1 100644
--- a/drivers/pinctrl/pinmux.h
+++ b/drivers/pinctrl/pinmux.h
@@ -31,12 +31,6 @@
int pinmux_enable_setting(struct pinctrl_setting const *setting);
void pinmux_disable_setting(struct pinctrl_setting const *setting);
-void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map);
-void pinmux_show_setting(struct seq_file *s,
- struct pinctrl_setting const *setting);
-void pinmux_init_device_debugfs(struct dentry *devroot,
- struct pinctrl_dev *pctldev);
-
#else
static inline int pinmux_check_ops(struct pinctrl_dev *pctldev)
@@ -89,6 +83,18 @@
{
}
+#endif
+
+#if defined(CONFIG_PINMUX) && defined(CONFIG_DEBUG_FS)
+
+void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map);
+void pinmux_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting);
+void pinmux_init_device_debugfs(struct dentry *devroot,
+ struct pinctrl_dev *pctldev);
+
+#else
+
static inline void pinmux_show_map(struct seq_file *s,
struct pinctrl_map const *map)
{
diff --git a/drivers/platform/msm/ipa/a2_service.c b/drivers/platform/msm/ipa/a2_service.c
index 4921ec2..ae9277e 100644
--- a/drivers/platform/msm/ipa/a2_service.c
+++ b/drivers/platform/msm/ipa/a2_service.c
@@ -1469,6 +1469,35 @@
return ret;
}
+/**
+ * a2_mux_is_ch_empty() - checks if channel is empty.
+ * @lcid: logical channel ID
+ *
+ * Returns: true if the channel is empty,
+ * false otherwise
+ */
+int a2_mux_is_ch_empty(enum a2_mux_logical_channel_id lcid)
+{
+ unsigned long flags;
+ int ret;
+
+ if (lcid >= A2_MUX_NUM_CHANNELS ||
+ lcid < 0)
+ return -EINVAL;
+ if (!a2_mux_ctx->a2_mux_initialized)
+ return -ENODEV;
+ spin_lock_irqsave(&a2_mux_ctx->bam_ch[lcid].lock, flags);
+ a2_mux_ctx->bam_ch[lcid].use_wm = 1;
+ ret = a2_mux_ctx->bam_ch[lcid].num_tx_pkts == 0;
+ if (!bam_ch_is_local_open(lcid)) {
+ ret = -ENODEV;
+ IPAERR("%s: port not open: %d\n", __func__,
+ a2_mux_ctx->bam_ch[lcid].status);
+ }
+ spin_unlock_irqrestore(&a2_mux_ctx->bam_ch[lcid].lock, flags);
+ return ret;
+}
+
static int a2_mux_initialize_context(int handle)
{
int i;
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index 0724aa3..6a16cf3 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -140,7 +140,7 @@
static bool hdr_tbl_lcl = 1;
module_param(hdr_tbl_lcl, bool, 0644);
MODULE_PARM_DESC(hdr_tbl_lcl, "where hdr tbl resides 1-local; 0-system");
-static bool ip4_rt_tbl_lcl = 1;
+static bool ip4_rt_tbl_lcl;
module_param(ip4_rt_tbl_lcl, bool, 0644);
MODULE_PARM_DESC(ip4_rt_tbl_lcl,
"where ip4 rt tables reside 1-local; 0-system");
@@ -1667,13 +1667,6 @@
}
/* register IPA with SPS driver */
bam_props.phys_addr = resource_p->bam_mem_base;
- bam_props.virt_addr = ioremap(resource_p->bam_mem_base,
- resource_p->bam_mem_size);
- if (!bam_props.virt_addr) {
- IPAERR(":bam-base ioremap err.\n");
- result = -EFAULT;
- goto fail_bam_remap;
- }
bam_props.virt_size = resource_p->bam_mem_size;
bam_props.irq = resource_p->bam_irq;
bam_props.num_pipes = IPA_NUM_PIPES;
@@ -1685,7 +1678,7 @@
if (result) {
IPAERR(":bam register err.\n");
result = -ENODEV;
- goto fail_bam_register;
+ goto fail_init_hw;
}
if (ipa_setup_bam_cfg(resource_p)) {
@@ -2006,18 +1999,12 @@
kmem_cache_destroy(ipa_ctx->flt_rule_cache);
fail_flt_rule_cache:
sps_deregister_bam_device(ipa_ctx->bam_handle);
-fail_bam_register:
- iounmap(bam_props.virt_addr);
-fail_bam_remap:
fail_init_hw:
iounmap(ipa_ctx->mmio);
fail_remap:
kfree(ipa_ctx);
ipa_ctx = NULL;
fail_mem:
- /* gate IPA clocks */
- if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_NORMAL)
- ipa_disable_clks();
return result;
}
@@ -2178,8 +2165,12 @@
/* Proceed to real initialization */
result = ipa_init(&ipa_res);
- if (result)
+ if (result) {
IPAERR("ipa_init failed\n");
+ /* gate IPA clocks */
+ if (ipa_res.ipa_hw_mode == IPA_HW_MODE_NORMAL)
+ ipa_disable_clks();
+ }
result = device_create_file(&pdev_p->dev,
&dev_attr_aggregation_type);
diff --git a/drivers/platform/msm/ipa/ipa_bridge.c b/drivers/platform/msm/ipa/ipa_bridge.c
index 83c4db0..f9401f0 100644
--- a/drivers/platform/msm/ipa/ipa_bridge.c
+++ b/drivers/platform/msm/ipa/ipa_bridge.c
@@ -468,7 +468,7 @@
{
int i;
- ipa_ctx->smem_pipe_mem = smem_alloc(SMEM_BAM_PIPE_MEMORY,
+ ipa_ctx->smem_pipe_mem = smem_alloc2(SMEM_BAM_PIPE_MEMORY,
IPA_SMEM_PIPE_MEM_SZ);
if (!ipa_ctx->smem_pipe_mem) {
IPAERR("smem alloc failed\n");
diff --git a/drivers/platform/msm/ipa/ipa_client.c b/drivers/platform/msm/ipa/ipa_client.c
index e98c9b7..3aaec07 100644
--- a/drivers/platform/msm/ipa/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_client.c
@@ -45,7 +45,7 @@
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_1) {
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_CTRL_n_OFST(clnt_hdl), 1);
- usleep(IPA_PKT_FLUSH_TO_US);
+ udelay(IPA_PKT_FLUSH_TO_US);
if (IPA_CLIENT_IS_CONS(ep->client) &&
ep->cfg.aggr.aggr_en == IPA_ENABLE_AGGR &&
ep->cfg.aggr.aggr_time_limit)
@@ -203,6 +203,7 @@
}
memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
+ ipa_enable_data_path(ipa_ep_idx);
ep->valid = 1;
ep->client = in->client;
@@ -380,7 +381,6 @@
return -EPERM;
}
- ipa_enable_data_path(clnt_hdl);
memset(&ipa_ctx->ep[clnt_hdl], 0, sizeof(struct ipa_ep_context));
ipa_dec_client_disable_clks();
diff --git a/drivers/platform/msm/ipa/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_debugfs.c
index aaf5cc0..c429414 100644
--- a/drivers/platform/msm/ipa/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_debugfs.c
@@ -113,6 +113,7 @@
{
int nbytes;
+ ipa_inc_client_enable_clks();
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0)
nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
"IPA_VERSION=0x%x\n"
@@ -143,6 +144,7 @@
ipa_read_reg(ipa_ctx->mmio, IPA_FILTER_OFST_v2),
ipa_read_reg(ipa_ctx->mmio, IPA_SHARED_MEM_SIZE_OFST_v2)
);
+ ipa_dec_client_disable_clks();
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
}
@@ -241,6 +243,7 @@
end_idx = start_idx + 1;
}
pos = *ppos;
+ ipa_inc_client_enable_clks();
for (i = start_idx; i < end_idx; i++) {
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
@@ -292,13 +295,16 @@
*ppos = pos;
ret = simple_read_from_buffer(ubuf, count, ppos, dbg_buff,
nbytes);
- if (ret < 0)
+ if (ret < 0) {
+ ipa_dec_client_disable_clks();
return ret;
+ }
size += ret;
ubuf += nbytes;
count -= nbytes;
}
+ ipa_dec_client_disable_clks();
*ppos = pos + size;
return size;
@@ -346,11 +352,18 @@
uint32_t mask[4];
int i;
- if (attrib->attrib_mask & IPA_FLT_TOS) {
- nbytes = scnprintf(buff + cnt, sz - cnt, "tos:%d ",
- attrib->u.v4.tos);
+
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ nbytes = scnprintf(buff + cnt, sz - cnt, "tos_value:%d ",
+ attrib->tos_value);
cnt += nbytes;
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ nbytes = scnprintf(buff + cnt, sz - cnt, "tos_mask:%d ",
+ attrib->tos_mask);
+ cnt += nbytes;
+ }
+
if (attrib->attrib_mask & IPA_FLT_PROTOCOL) {
nbytes = scnprintf(buff + cnt, sz - cnt, "protocol:%d ",
attrib->u.v4.protocol);
@@ -493,6 +506,32 @@
set = &ipa_ctx->rt_tbl_set[ip];
mutex_lock(&ipa_ctx->lock);
+ if (ip == IPA_IP_v6) {
+ if (ipa_ctx->ip6_rt_tbl_lcl) {
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN - cnt,
+ "Table Resides on local memory\n");
+ cnt += nbytes;
+ } else {
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN - cnt,
+ "Table Resides on system(ddr) memory\n");
+ cnt += nbytes;
+ }
+ } else if (ip == IPA_IP_v4) {
+ if (ipa_ctx->ip4_rt_tbl_lcl) {
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN - cnt,
+ "Table Resides on local memory\n");
+ cnt += nbytes;
+ } else {
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN - cnt,
+ "Table Resides on system(ddr) memory\n");
+ cnt += nbytes;
+ }
+ }
+
list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
i = 0;
list_for_each_entry(entry, &tbl->head_rt_rule_list, link) {
@@ -676,12 +715,14 @@
if (kstrtou32(dbg_buff, 0, &option))
return -EFAULT;
+ ipa_inc_client_enable_clks();
if (option == 1)
ipa_write_reg(ipa_ctx->mmio, IPA_DEBUG_CNT_CTRL_n_OFST(0),
IPA_DBG_CNTR_ON);
else
ipa_write_reg(ipa_ctx->mmio, IPA_DEBUG_CNT_CTRL_n_OFST(0),
IPA_DBG_CNTR_OFF);
+ ipa_dec_client_disable_clks();
return count;
}
@@ -691,10 +732,12 @@
{
int nbytes;
+ ipa_inc_client_enable_clks();
nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
"IPA_DEBUG_CNT_REG_0=0x%x\n",
ipa_read_reg(ipa_ctx->mmio,
IPA_DEBUG_CNT_REG_n_OFST(0)));
+ ipa_dec_client_disable_clks();
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
}
diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c
index 1f232d4..e2856cf 100644
--- a/drivers/platform/msm/ipa/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_dp.c
@@ -641,7 +641,7 @@
switch (notify->event_id) {
case SPS_EVENT_EOT:
tx_pkt = notify->data.transfer.user;
- queue_work(ipa_ctx->tx_wq, &tx_pkt->work);
+ schedule_work(&tx_pkt->work);
break;
default:
IPAERR("recieved unexpected event id %d\n", notify->event_id);
diff --git a/drivers/platform/msm/ipa/ipa_flt.c b/drivers/platform/msm/ipa/ipa_flt.c
index edb9fb1..2d75141 100644
--- a/drivers/platform/msm/ipa/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_flt.c
@@ -634,12 +634,13 @@
return -EINVAL;
}
ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode, ep);
- if (ipa_ep_idx == IPA_FLT_TABLE_INDEX_NOT_FOUND ||
- ipa_ctx->ep[ipa_ep_idx].valid == 0) {
- IPAERR("ep not valid and/or connected ep_idx=%d\n", ipa_ep_idx);
-
+ if (ipa_ep_idx == IPA_FLT_TABLE_INDEX_NOT_FOUND) {
+ IPAERR("ep not valid ep=%d\n", ep);
return -EINVAL;
}
+ if (ipa_ctx->ep[ipa_ep_idx].valid == 0)
+ IPADBG("ep not connected ep_idx=%d\n", ipa_ep_idx);
+
tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][ip];
IPADBG("add ep flt rule ip=%d ep=%d\n", ip, ep);
diff --git a/drivers/platform/msm/ipa/ipa_ram_mmap.h b/drivers/platform/msm/ipa/ipa_ram_mmap.h
index 78093b8..20eb52a 100644
--- a/drivers/platform/msm/ipa/ipa_ram_mmap.h
+++ b/drivers/platform/msm/ipa/ipa_ram_mmap.h
@@ -21,16 +21,19 @@
#define IPA_RAM_NAT_OFST 0
#define IPA_RAM_NAT_SIZE 0
#define IPA_RAM_HDR_OFST (IPA_RAM_NAT_OFST + IPA_RAM_NAT_SIZE)
-#define IPA_RAM_HDR_SIZE 1280
+#define IPA_RAM_HDR_SIZE 1664
#define IPA_RAM_V4_FLT_OFST (IPA_RAM_HDR_OFST + IPA_RAM_HDR_SIZE)
-#define IPA_RAM_V4_FLT_SIZE 1408
+#define IPA_RAM_V4_FLT_SIZE 2176
#define IPA_RAM_V4_RT_OFST (IPA_RAM_V4_FLT_OFST + IPA_RAM_V4_FLT_SIZE)
-#define IPA_RAM_V4_RT_SIZE 2176
+#define IPA_RAM_V4_RT_SIZE 512
#define IPA_RAM_V6_FLT_OFST (IPA_RAM_V4_RT_OFST + IPA_RAM_V4_RT_SIZE)
-#define IPA_RAM_V6_FLT_SIZE 1280
+#define IPA_RAM_V6_FLT_SIZE 1792
#define IPA_RAM_V6_RT_OFST (IPA_RAM_V6_FLT_OFST + IPA_RAM_V6_FLT_SIZE)
#define IPA_RAM_V6_RT_SIZE 512
#define IPA_RAM_END_OFST (IPA_RAM_V6_RT_OFST + IPA_RAM_V6_RT_SIZE)
+
#define IPA_RAM_V6_RT_SIZE_DDR 16384
+#define IPA_RAM_V4_RT_SIZE_DDR 16384
#endif /* _IPA_RAM_MMAP_H_ */
+
diff --git a/drivers/platform/msm/ipa/ipa_rm.c b/drivers/platform/msm/ipa/ipa_rm.c
index e057c5a..64c77a0 100644
--- a/drivers/platform/msm/ipa/ipa_rm.c
+++ b/drivers/platform/msm/ipa/ipa_rm.c
@@ -119,12 +119,12 @@
{
int result;
- read_lock(&ipa_rm_ctx->lock);
+ write_lock(&ipa_rm_ctx->lock);
result = ipa_rm_dep_graph_add_dependency(
ipa_rm_ctx->dep_graph,
resource_name,
depends_on_name);
- read_unlock(&ipa_rm_ctx->lock);
+ write_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_add_dependency);
@@ -145,12 +145,12 @@
enum ipa_rm_resource_name depends_on_name)
{
int result;
- read_lock(&ipa_rm_ctx->lock);
+ write_lock(&ipa_rm_ctx->lock);
result = ipa_rm_dep_graph_delete_dependency(
ipa_rm_ctx->dep_graph,
resource_name,
depends_on_name);
- read_unlock(&ipa_rm_ctx->lock);
+ write_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_delete_dependency);
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c
index 7142dc7..986892b 100644
--- a/drivers/platform/msm/ipa/ipa_rm_resource.c
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.c
@@ -720,6 +720,7 @@
producer->resource.name,
IPA_RM_RESOURCE_GRANTED,
true);
+ result = 0;
}
unlock_and_bail:
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
@@ -786,15 +787,11 @@
&producer->resource.state_lock, flags);
consumer_result = ipa_rm_resource_consumer_release(
(struct ipa_rm_resource_cons *)consumer);
- if (consumer_result == -EINPROGRESS) {
- result = -EINPROGRESS;
- } else {
- spin_lock_irqsave(
- &producer->resource.state_lock, flags);
- producer->pending_release--;
- spin_unlock_irqrestore(
- &producer->resource.state_lock, flags);
- }
+ spin_lock_irqsave(
+ &producer->resource.state_lock, flags);
+ producer->pending_release--;
+ spin_unlock_irqrestore(
+ &producer->resource.state_lock, flags);
}
}
spin_lock_irqsave(&producer->resource.state_lock, flags);
diff --git a/drivers/platform/msm/ipa/ipa_rt.c b/drivers/platform/msm/ipa/ipa_rt.c
index 6430c07..8c0adbd 100644
--- a/drivers/platform/msm/ipa/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_rt.c
@@ -67,13 +67,31 @@
}
rule_hdr->u.hdr.pipe_dest_idx = pipe_idx;
rule_hdr->u.hdr.system = !ipa_ctx->hdr_tbl_lcl;
- if (entry->hdr)
+ if (entry->hdr) {
rule_hdr->u.hdr.hdr_offset =
entry->hdr->offset_entry->offset >> 2;
- else
+ } else {
rule_hdr->u.hdr.hdr_offset = 0;
-
+ }
buf += sizeof(struct ipa_rt_rule_hw_hdr);
+ if ((ip == IPA_IP_v4) &&
+ (entry->rule.attrib.attrib_mask & IPA_FLT_TOS)) {
+ entry->rule.attrib.tos_value =
+ (entry->rule.attrib.u.v4.tos << 5);
+ entry->rule.attrib.tos_mask = 0xe0;
+ entry->rule.attrib.attrib_mask &= ~IPA_FLT_TOS;
+ entry->rule.attrib.attrib_mask |= IPA_FLT_TOS_MASKED;
+ }
+
+ if ((ip == IPA_IP_v6) &&
+ (entry->rule.attrib.attrib_mask & IPA_FLT_TC)) {
+ entry->rule.attrib.tos_value =
+ (entry->rule.attrib.u.v6.tc << 5);
+ entry->rule.attrib.tos_mask = 0xe0;
+ entry->rule.attrib.attrib_mask &= ~IPA_FLT_TC;
+ entry->rule.attrib.attrib_mask |= IPA_FLT_TOS_MASKED;
+ }
+
if (ipa_generate_hw_rule(ip, &rule->attrib, &buf, &en_rule)) {
IPAERR("fail to generate hw rule\n");
return -EPERM;
@@ -359,7 +377,8 @@
}
if (ip == IPA_IP_v4) {
- avail = IPA_RAM_V4_RT_SIZE;
+ avail = ipa_ctx->ip4_rt_tbl_lcl ? IPA_RAM_V4_RT_SIZE :
+ IPA_RAM_V4_RT_SIZE_DDR;
size = sizeof(struct ipa_ip_v4_routing_init);
} else {
avail = ipa_ctx->ip6_rt_tbl_lcl ? IPA_RAM_V6_RT_SIZE :
diff --git a/drivers/platform/msm/ipa/ipa_utils.c b/drivers/platform/msm/ipa/ipa_utils.c
index 23de300..4a5fa4d 100644
--- a/drivers/platform/msm/ipa/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_utils.c
@@ -13,7 +13,9 @@
#include <net/ip.h>
#include <linux/genalloc.h> /* gen_pool_alloc() */
#include <linux/io.h>
+#include <linux/ratelimit.h>
#include "ipa_i.h"
+
static const int ipa_ofst_meq32[] = { IPA_OFFSET_MEQ32_0,
IPA_OFFSET_MEQ32_1, -1 };
static const int ipa_ofst_meq128[] = { IPA_OFFSET_MEQ128_0,
@@ -46,6 +48,7 @@
if (ipa_ctx->ipa_hw_type != IPA_HW_v1_0)
ipa_route_offset = IPA_ROUTE_OFST_v2;
+ ipa_inc_client_enable_clks();
ipa_write_reg(ipa_ctx->mmio, ipa_route_offset,
IPA_SETFIELD(route->route_dis,
IPA_ROUTE_ROUTE_DIS_SHFT,
@@ -59,6 +62,7 @@
IPA_SETFIELD(route->route_def_hdr_ofst,
IPA_ROUTE_ROUTE_DEF_HDR_OFST_SHFT,
IPA_ROUTE_ROUTE_DEF_HDR_OFST_BMSK));
+ ipa_dec_client_disable_clks();
return 0;
}
@@ -75,10 +79,12 @@
if (ipa_ctx->ipa_hw_type != IPA_HW_v1_0)
ipa_filter_ofst = IPA_FILTER_OFST_v2;
+ ipa_inc_client_enable_clks();
ipa_write_reg(ipa_ctx->mmio, ipa_filter_ofst,
IPA_SETFIELD(!disable,
IPA_FILTER_FILTER_EN_SHFT,
IPA_FILTER_FILTER_EN_BMSK));
+ ipa_dec_client_disable_clks();
return 0;
}
@@ -237,6 +243,20 @@
*buf = ipa_pad_to_32(*buf);
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ if (ipa_ofst_meq32[ofst_meq32] == -1) {
+ IPAERR("ran out of meq32 eq\n");
+ return -EPERM;
+ }
+ *en_rule |= ipa_ofst_meq32[ofst_meq32];
+ /* 0 => offset of TOS in v4 header */
+ *buf = ipa_write_8(0, *buf);
+ *buf = ipa_write_32((attrib->tos_mask << 16), *buf);
+ *buf = ipa_write_32(attrib->tos_value, *buf);
+ *buf = ipa_pad_to_32(*buf);
+ ofst_meq32++;
+ }
+
if (attrib->attrib_mask & IPA_FLT_PROTOCOL) {
*en_rule |= IPA_PROTOCOL_EQ;
*buf = ipa_write_8(attrib->u.v4.protocol, *buf);
@@ -568,6 +588,20 @@
*buf = ipa_pad_to_32(*buf);
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ if (ipa_ofst_meq32[ofst_meq32] == -1) {
+ IPAERR("ran out of meq32 eq\n");
+ return -EPERM;
+ }
+ *en_rule |= ipa_ofst_meq32[ofst_meq32];
+ /* 0 => offset of TOS in v4 header */
+ *buf = ipa_write_8(0, *buf);
+ *buf = ipa_write_32((attrib->tos_mask << 20), *buf);
+ *buf = ipa_write_32(attrib->tos_value, *buf);
+ *buf = ipa_pad_to_32(*buf);
+ ofst_meq32++;
+ }
+
if (attrib->attrib_mask & IPA_FLT_FLOW_LABEL) {
*en_rule |= IPA_FLT_FLOW_LABEL;
/* FIXME FL is only 20 bits */
@@ -679,6 +713,7 @@
}
/* copy over EP cfg */
ipa_ctx->ep[clnt_hdl].cfg.nat = *ipa_ep_cfg;
+ ipa_inc_client_enable_clks();
/* clnt_hdl is used as pipe_index */
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0)
ipa_write_reg(ipa_ctx->mmio,
@@ -692,6 +727,7 @@
IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].cfg.nat.nat_en,
IPA_ENDP_INIT_NAT_n_NAT_EN_SHFT,
IPA_ENDP_INIT_NAT_n_NAT_EN_BMSK));
+ ipa_dec_client_disable_clks();
return 0;
}
@@ -744,12 +780,14 @@
IPA_ENDP_INIT_HDR_n_HDR_A5_MUX_SHFT,
IPA_ENDP_INIT_HDR_n_HDR_A5_MUX_BMSK);
+ ipa_inc_client_enable_clks();
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0)
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_HDR_n_OFST_v1(clnt_hdl), val);
else
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_HDR_n_OFST_v2(clnt_hdl), val);
+ ipa_dec_client_disable_clks();
return 0;
}
@@ -798,12 +836,14 @@
IPA_ENDP_INIT_MODE_n_DEST_PIPE_INDEX_SHFT,
IPA_ENDP_INIT_MODE_n_DEST_PIPE_INDEX_BMSK);
+ ipa_inc_client_enable_clks();
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0)
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_MODE_n_OFST_v1(clnt_hdl), val);
else
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_MODE_n_OFST_v2(clnt_hdl), val);
+ ipa_dec_client_disable_clks();
return 0;
}
@@ -843,12 +883,14 @@
IPA_ENDP_INIT_AGGR_n_AGGR_TIME_LIMIT_SHFT,
IPA_ENDP_INIT_AGGR_n_AGGR_TIME_LIMIT_BMSK);
+ ipa_inc_client_enable_clks();
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0)
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_AGGR_n_OFST_v1(clnt_hdl), val);
else
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_AGGR_n_OFST_v2(clnt_hdl), val);
+ ipa_dec_client_disable_clks();
return 0;
}
@@ -891,6 +933,7 @@
/* always use the "default" routing tables whose indices are 0 */
ipa_ctx->ep[clnt_hdl].rt_tbl_idx = 0;
+ ipa_inc_client_enable_clks();
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_ROUTE_n_OFST_v1(clnt_hdl),
@@ -904,6 +947,7 @@
IPA_ENDP_INIT_ROUTE_n_ROUTE_TABLE_INDEX_SHFT,
IPA_ENDP_INIT_ROUTE_n_ROUTE_TABLE_INDEX_BMSK));
}
+ ipa_dec_client_disable_clks();
return 0;
}
@@ -942,12 +986,14 @@
return -EPERM;
} else {
ipa_ctx->ep[clnt_hdl].holb = *ipa_ep_cfg;
+ ipa_inc_client_enable_clks();
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_HOL_BLOCK_EN_n_OFST(clnt_hdl),
ipa_ep_cfg->en);
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_HOL_BLOCK_TIMER_n_OFST(clnt_hdl),
ipa_ep_cfg->tmr_val);
+ ipa_dec_client_disable_clks();
IPAERR("cfg holb %u ep=%d tmr=%d\n", ipa_ep_cfg->en, clnt_hdl,
ipa_ep_cfg->tmr_val);
}
@@ -1201,6 +1247,7 @@
{
u32 reg_val;
+ ipa_inc_client_enable_clks();
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
reg_val = ipa_read_reg(ipa_ctx->mmio,
IPA_AGGREGATION_SPARE_REG_2_OFST);
@@ -1215,6 +1262,7 @@
(reg_val & 0xfffffffe));
}
+ ipa_dec_client_disable_clks();
return 0;
}
EXPORT_SYMBOL(ipa_set_aggr_mode);
@@ -1234,11 +1282,12 @@
{
u32 reg_val;
+ if (sig == NULL) {
+ IPAERR("bad argument for ipa_set_qcncm_ndp_sig/n");
+ return -EINVAL;
+ }
+ ipa_inc_client_enable_clks();
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
- if (sig == NULL) {
- IPAERR("bad argument for ipa_set_qcncm_ndp_sig/n");
- return -EINVAL;
- }
reg_val = ipa_read_reg(ipa_ctx->mmio,
IPA_AGGREGATION_SPARE_REG_2_OFST);
ipa_write_reg(ipa_ctx->mmio,
@@ -1253,6 +1302,7 @@
(sig[1] << 12) | (sig[2] << 4) |
(reg_val & 0xf000000f));
}
+ ipa_dec_client_disable_clks();
return 0;
}
EXPORT_SYMBOL(ipa_set_qcncm_ndp_sig);
@@ -1268,6 +1318,7 @@
{
u32 reg_val;
+ ipa_inc_client_enable_clks();
if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
reg_val = ipa_read_reg(ipa_ctx->mmio,
IPA_AGGREGATION_SPARE_REG_1_OFST);
@@ -1280,6 +1331,7 @@
ipa_write_reg(ipa_ctx->mmio, IPA_SINGLE_NDP_MODE_OFST,
(enable & 0x1) | (reg_val & 0xfffffffe));
}
+ ipa_dec_client_disable_clks();
return 0;
}
EXPORT_SYMBOL(ipa_set_single_ndp_per_mbim);
@@ -1294,10 +1346,12 @@
int ipa_set_hw_timer_fix_for_mbim_aggr(bool enable)
{
u32 reg_val;
+ ipa_inc_client_enable_clks();
reg_val = ipa_read_reg(ipa_ctx->mmio, IPA_AGGREGATION_SPARE_REG_1_OFST);
ipa_write_reg(ipa_ctx->mmio, IPA_AGGREGATION_SPARE_REG_1_OFST,
(enable << IPA_AGGREGATION_HW_TIMER_FIX_MBIM_AGGR_SHFT) |
(reg_val & ~IPA_AGGREGATION_HW_TIMER_FIX_MBIM_AGGR_BMSK));
+ ipa_dec_client_disable_clks();
return 0;
}
EXPORT_SYMBOL(ipa_set_hw_timer_fix_for_mbim_aggr);
@@ -1330,3 +1384,22 @@
else
return 0;
}
+
+/**
+ * ipa_bam_reg_dump() - Dump selected BAM registers for IPA and DMA-BAM
+ *
+ * Function is rate limited to avoid flooding kernel log buffer
+ */
+void ipa_bam_reg_dump(void)
+{
+ static DEFINE_RATELIMIT_STATE(_rs, 500*HZ, 1);
+ if (__ratelimit(&_rs)) {
+ ipa_inc_client_enable_clks();
+ pr_err("IPA BAM START\n");
+ sps_get_bam_debug_info(ipa_ctx->bam_handle, 5, 479182, 0, 0);
+ sps_get_bam_debug_info(ipa_ctx->bam_handle, 93, 0, 0, 0);
+ ipa_dec_client_disable_clks();
+ }
+}
+EXPORT_SYMBOL(ipa_bam_reg_dump);
+
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index b390280..4889a79 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -153,8 +153,6 @@
bool prod_stopped;
struct completion prod_avail[MAX_BAMS];
- struct completion cons_avail[MAX_BAMS];
- struct completion cons_released[MAX_BAMS];
struct completion prod_released[MAX_BAMS];
struct mutex suspend_resume_mutex;
@@ -176,7 +174,6 @@
static int __usb_bam_register_wake_cb(u8 idx, int (*callback)(void *user),
void *param, bool trigger_cb_per_pipe);
static void wait_for_prod_release(enum usb_bam cur_bam);
-static void wait_for_cons_release(enum usb_bam cur_bam);
void msm_bam_set_hsic_host_dev(struct device *dev)
{
@@ -878,7 +875,6 @@
spin_lock(&usb_bam_ipa_handshake_info_lock);
info.cur_cons_state[cur_bam] = IPA_RM_RESOURCE_GRANTED;
- complete_all(&info.cons_avail[cur_bam]);
spin_lock(&usb_bam_lock);
@@ -938,7 +934,6 @@
__func__, bam_enable_strings[cur_bam]);
info.cur_cons_state[cur_bam] = IPA_RM_RESOURCE_RELEASED;
- complete_all(&info.cons_released[cur_bam]);
spin_lock(&usb_bam_lock);
if (!ctx.pipes_enabled_per_bam[cur_bam]) {
@@ -1024,7 +1019,7 @@
}
}
-static void wait_for_prod_granted(enum usb_bam cur_bam, bool start_cons)
+static void wait_for_prod_granted(enum usb_bam cur_bam)
{
int ret;
@@ -1038,8 +1033,6 @@
__func__);
init_completion(&info.prod_avail[cur_bam]);
- if (start_cons)
- init_completion(&info.cons_avail[cur_bam]);
ret = ipa_rm_request_resource(ipa_rm_resource_prod[cur_bam]);
if (!ret) {
@@ -1056,22 +1049,14 @@
pr_err("%s: ipa_rm_request_resource ret =%d\n", __func__, ret);
}
-void wait_for_cons_granted(enum usb_bam cur_bam)
+void notify_usb_connected(enum usb_bam cur_bam)
{
- pr_debug("%s: Waiting for CONS\n", __func__);
- if (info.cur_cons_state[cur_bam] != IPA_RM_RESOURCE_GRANTED) {
- if (!wait_for_completion_timeout(&info.cons_avail[cur_bam],
- USB_BAM_TIMEOUT))
- pr_err("%s: Timeout wainting for CONS_REQUEST\n",
- __func__);
- pr_err("%s: Finished waiting for CONS\n", __func__);
- }
+ pr_debug("%s: enter\n", __func__);
spin_lock(&usb_bam_ipa_handshake_info_lock);
if (cur_bam == HSUSB_BAM)
info.connect_complete = 1;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- pr_debug("%s: CONS is granted\n", __func__);
if (info.cur_cons_state[HSUSB_BAM] == IPA_RM_RESOURCE_GRANTED) {
pr_debug("%s: Notify CONS_GRANTED\n", __func__);
@@ -1080,20 +1065,6 @@
}
}
-void usb_bam_wait_for_cons_granted(
- struct usb_bam_connect_ipa_params *ipa_params)
-{
- struct usb_bam_pipe_connect *pipe_connect;
- enum usb_bam cur_bam;
- u8 src_idx;
-
- src_idx = ipa_params->src_idx;
- pipe_connect = &usb_bam_connections[src_idx];
- cur_bam = pipe_connect->bam_type;
-
- wait_for_cons_granted(cur_bam);
-}
-
static void wait_for_prod_release(enum usb_bam cur_bam)
{
int ret;
@@ -1104,7 +1075,6 @@
pr_debug("%s producer already released\n", __func__);
init_completion(&info.prod_released[cur_bam]);
- init_completion(&info.cons_released[cur_bam]);
pr_debug("%s: Releasing %s_PROD\n", __func__,
bam_enable_strings[cur_bam]);
ret = ipa_rm_release_resource(ipa_rm_resource_prod[cur_bam]);
@@ -1270,8 +1240,8 @@
info.lpm_wait_handshake[HSUSB_BAM] = true;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- wait_for_prod_granted(HSUSB_BAM, true);
- wait_for_cons_granted(HSUSB_BAM);
+ wait_for_prod_granted(HSUSB_BAM);
+ notify_usb_connected(HSUSB_BAM);
if (info.cons_stopped) {
ipa_resume_pipes();
if (info.start) {
@@ -1339,7 +1309,7 @@
usb_bam_resume_hsic_host();
/* Ensure getting the producer resource */
- wait_for_prod_granted(HSIC_BAM, false);
+ wait_for_prod_granted(HSIC_BAM);
}
void msm_bam_hsic_notify_on_resume(void)
@@ -1500,7 +1470,7 @@
if (ipa_params->dir == USB_TO_PEER_PERIPHERAL) {
pr_debug("%s: Starting connect sequence\n", __func__);
- wait_for_prod_granted(cur_bam, true);
+ wait_for_prod_granted(cur_bam);
}
ret = connect_pipe_ipa(idx, ipa_params);
@@ -1524,7 +1494,7 @@
ctx.pipes_enabled_per_bam[cur_bam] += 1;
spin_unlock(&usb_bam_lock);
if (ipa_params->dir == PEER_PERIPHERAL_TO_USB && cur_bam == HSUSB_BAM)
- wait_for_cons_granted(cur_bam);
+ notify_usb_connected(cur_bam);
if (cur_bam == HSUSB_BAM)
mutex_unlock(&info.suspend_resume_mutex);
@@ -1586,7 +1556,7 @@
if (pipe_connect->peer_bam == IPA_P_BAM &&
pipe_connect->bam_type == HSIC_BAM &&
info.cur_prod_state[HSIC_BAM] != IPA_RM_RESOURCE_GRANTED) {
- wait_for_prod_granted(HSIC_BAM, false);
+ wait_for_prod_granted(HSIC_BAM);
}
/*
@@ -1618,7 +1588,7 @@
if (pipe_connect->bam_type == HSUSB_BAM) {
/* A2 wakeup not from LPM (CONS was up) */
- wait_for_prod_granted(pipe_connect->bam_type, true);
+ wait_for_prod_granted(pipe_connect->bam_type);
if (info.start) {
pr_debug("%s: Enqueue PROD transfer", __func__);
info.start(info.start_stop_param,
@@ -1934,19 +1904,6 @@
return 0;
}
-static void wait_for_cons_release(enum usb_bam cur_bam)
-{
- pr_debug("%s: Waiting for CONS release\n", __func__);
- if (info.cur_cons_state[cur_bam] != IPA_RM_RESOURCE_RELEASED) {
- if (!wait_for_completion_timeout(&info.cons_released[cur_bam],
- USB_BAM_TIMEOUT))
- pr_err("%s: Timeout wainting for CONS_RELEASE\n",
- __func__);
- } else
- pr_debug("%s Didn't need to wait for CONS release\n",
- __func__);
-}
-
int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
{
int ret;
@@ -2013,7 +1970,6 @@
pipe_connect->priv = NULL;
cur_bam = pipe_connect->bam_type;
- wait_for_cons_release(cur_bam);
/* close IPA -> USB pipe */
ret = ipa_disconnect(ipa_params->cons_clnt_hdl);
if (ret) {
@@ -2568,10 +2524,6 @@
ctx.is_bam_inactivity[i] = false;
init_completion(&info.prod_avail[i]);
complete(&info.prod_avail[i]);
- init_completion(&info.cons_avail[i]);
- complete(&info.cons_avail[i]);
- init_completion(&info.cons_released[i]);
- complete(&info.cons_released[i]);
init_completion(&info.prod_released[i]);
complete(&info.prod_released[i]);
info.cur_prod_state[i] = IPA_RM_RESOURCE_RELEASED;
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index 1fd4434..a9af974 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -2118,6 +2118,10 @@
if (the_chip->start_percent == -EINVAL)
return prev_soc;
+ /* do not scale at 100 */
+ if (new_soc == 100)
+ return new_soc;
+
chg_time_sec = DIV_ROUND_UP(the_chip->charge_time_us, USEC_PER_SEC);
catch_up_sec = DIV_ROUND_UP(the_chip->catch_up_time_us, USEC_PER_SEC);
if (catch_up_sec == 0)
@@ -2501,7 +2505,7 @@
}
/* last_soc < soc ... scale and catch up */
- if (last_soc != -EINVAL && last_soc < soc && soc != 100)
+ if (last_soc != -EINVAL && last_soc < soc)
soc = scale_soc_while_chg(chip, delta_time_us, soc, last_soc);
if (last_soc != -EINVAL) {
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 14cba58..08995cc 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -45,8 +45,10 @@
#define BMS1_OCV_USE_LIMIT_CTL 0x4C
/* Delay control */
#define BMS1_S1_DELAY_CTL 0x5A
-/* CC interrupt threshold */
-#define BMS1_CC_THR0 0x7A
+/* OCV interrupt threshold */
+#define BMS1_OCV_THR0 0x50
+/* SW CC interrupt threshold */
+#define BMS1_SW_CC_THR0 0xA0
/* OCV for r registers */
#define BMS1_OCV_FOR_R_DATA0 0x80
#define BMS1_VSENSE_FOR_R_DATA0 0x82
@@ -62,11 +64,10 @@
/* Extra bms registers */
#define SOC_STORAGE_REG 0xB0
#define IAVG_STORAGE_REG 0xB1
-#define BMS_BATT_REMOVED_REG 0xB2
-#define BMS1_BMS_DATA_REG_3 0xB3
-#define CHARGE_INCREASE_STORAGE 0xB4
-#define FCC_BATT_TEMP_STORAGE 0xB5
-#define FCC_STORAGE_LSB 0xBC /* LSB=0xBC, MSB=0xBD */
+#define BMS_FCC_COUNT 0xB2
+#define BMS_FCC_BASE_REG 0xB3 /* FCC updates - 0xB3 to 0xB7 */
+#define BMS_CHGCYL_BASE_REG 0xB8 /* FCC chgcyl - 0xB8 to 0xBC */
+#define CHARGE_INCREASE_STORAGE 0xBD
#define CHARGE_CYCLE_STORAGE_LSB 0xBE /* LSB=0xBE, MSB=0xBF */
/* IADC Channel Select */
@@ -82,8 +83,11 @@
#define IAVG_SAMPLES 16
/* FCC learning constants */
+#define MAX_FCC_CYCLES 5
#define DELTA_FCC_PERCENT 5
#define VALID_FCC_CHGCYL_RANGE 50
+#define CHGCYL_RESOLUTION 20
+#define FCC_DEFAULT_TEMP 250
#define QPNP_BMS_DEV_NAME "qcom,qpnp-bms"
@@ -114,15 +118,25 @@
int last_good_ocv_uv;
};
-struct fcc_data {
+struct fcc_sample {
int fcc_new;
- int batt_temp;
int chargecycles;
};
+struct bms_irq {
+ unsigned int irq;
+ unsigned long disabled;
+};
+
+struct bms_wakeup_source {
+ struct wakeup_source source;
+ unsigned long disabled;
+};
+
struct qpnp_bms_chip {
struct device *dev;
struct power_supply bms_psy;
+ bool bms_psy_registered;
struct power_supply *batt_psy;
struct spmi_device *spmi;
u16 base;
@@ -176,7 +190,7 @@
int low_soc_calc_threshold;
int low_soc_calculate_soc_ms;
int calculate_soc_ms;
- struct wake_lock soc_wake_lock;
+ struct bms_wakeup_source soc_wake_source;
struct wake_lock cv_wake_lock;
uint16_t ocv_reading_at_100;
@@ -231,7 +245,8 @@
int ocv_low_threshold_uv;
unsigned long last_recalc_time;
- struct fcc_data *fcc_table;
+ struct fcc_sample *fcc_learning_samples;
+ u8 fcc_sample_count;
int enable_fcc_learning;
int min_fcc_learning_soc;
int min_fcc_ocv_pc;
@@ -246,8 +261,10 @@
int fcc_new_batt_temp;
uint16_t charge_cycles;
u8 charge_increase;
- int fcc_new_sysfs;
- int fcc_update_complete;
+ int fcc_resolution;
+ bool battery_removed;
+ struct bms_irq sw_cc_thr_irq;
+ struct bms_irq ocv_thr_irq;
};
static struct of_device_id qpnp_bms_match_table[] = {
@@ -266,48 +283,14 @@
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CYCLE_COUNT,
};
+static int discard_backup_fcc_data(struct qpnp_bms_chip *chip);
+static void backup_charge_cycle(struct qpnp_bms_chip *chip);
+
static bool bms_reset;
-static int min_fcc_cycles = -EINVAL;
-static int last_fcc_update_count;
-static int battery_removed;
-
-static int
-bms_ro_ops_set(const char *val, const struct kernel_param *kp)
-{
- return -EINVAL;
-}
-
-static int
-bms_last_fcc_count_set(const char *val, const struct kernel_param *kp)
-{
- int rc;
-
- if (battery_removed) {
- last_fcc_update_count = 0;
- return 0;
- }
- rc = param_set_int(val, kp);
- if (rc)
- pr_err("Failed to set last_fcc_update_count rc=%d\n", rc);
-
- return rc;
-}
-
-static struct kernel_param_ops bms_ro_param_ops = {
- .set = bms_ro_ops_set,
- .get = param_get_int,
-};
-static struct kernel_param_ops bms_last_fcc_count_param_ops = {
- .set = bms_last_fcc_count_set,
- .get = param_get_int,
-};
-module_param_cb(min_fcc_cycles, &bms_ro_param_ops, &min_fcc_cycles, 0644);
-module_param_cb(battery_removed, &bms_ro_param_ops, &battery_removed, 0644);
-module_param_cb(last_fcc_update_count, &bms_last_fcc_count_param_ops,
- &last_fcc_update_count, 0644);
static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val,
u16 base, int count)
@@ -371,6 +354,38 @@
return qpnp_masked_write_base(chip, chip->base + addr, mask, val);
}
+static void bms_stay_awake(struct bms_wakeup_source *source)
+{
+ if (__test_and_clear_bit(0, &source->disabled)) {
+ __pm_stay_awake(&source->source);
+ pr_debug("enabled source %s\n", source->source.name);
+ }
+}
+
+static void bms_relax(struct bms_wakeup_source *source)
+{
+ if (!__test_and_set_bit(0, &source->disabled)) {
+ __pm_relax(&source->source);
+ pr_debug("disabled source %s\n", source->source.name);
+ }
+}
+
+static void enable_bms_irq(struct bms_irq *irq)
+{
+ if (__test_and_clear_bit(0, &irq->disabled)) {
+ enable_irq(irq->irq);
+ pr_debug("enabled irq %d\n", irq->irq);
+ }
+}
+
+static void disable_bms_irq(struct bms_irq *irq)
+{
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ disable_irq(irq->irq);
+ pr_debug("disabled irq %d\n", irq->irq);
+ }
+}
+
#define HOLD_OREG_DATA BIT(0)
static int lock_output_data(struct qpnp_bms_chip *chip)
{
@@ -412,7 +427,6 @@
#define VADC_CALIB_UV 625000
#define VBATT_MUL_FACTOR 3
-
static int adjust_vbatt_reading(struct qpnp_bms_chip *chip, int reading_uv)
{
s64 numerator, denominator;
@@ -490,6 +504,30 @@
return result_uv;
}
+static s64 cc_reverse_adjust_for_gain(s64 uv)
+{
+ struct qpnp_iadc_calib calibration;
+ int gain;
+ s64 result_uv;
+
+ qpnp_iadc_get_gain_and_offset(&calibration);
+ gain = (int)calibration.gain_raw - (int)calibration.offset_raw;
+
+ pr_debug("reverse adjusting_uv = %lld\n", uv);
+ if (gain == 0) {
+ pr_debug("gain is %d, not adjusting\n", gain);
+ return uv;
+ }
+ pr_debug("adjusting by factor: %hu/%lld = %lld%%\n",
+ gain, QPNP_ADC_GAIN_IDEAL,
+ div64_s64((s64)gain * 100LL,
+ (s64)QPNP_ADC_GAIN_IDEAL));
+
+ result_uv = div64_s64(uv * (s64)gain, QPNP_ADC_GAIN_IDEAL);
+ pr_debug("result_uv = %lld\n", result_uv);
+ return result_uv;
+}
+
static int convert_vsense_to_uv(struct qpnp_bms_chip *chip,
int16_t reading)
{
@@ -565,6 +603,20 @@
}
#define CC_36_BIT_MASK 0xFFFFFFFFFLL
+static uint64_t convert_s64_to_s36(int64_t raw64)
+{
+ return (uint64_t) raw64 & CC_36_BIT_MASK;
+}
+
+#define SIGN_EXTEND_36_TO_64_MASK (-1LL ^ CC_36_BIT_MASK)
+static int64_t convert_s36_to_s64(uint64_t raw36)
+{
+ raw36 = raw36 & CC_36_BIT_MASK;
+ /* convert 36 bit signed value into 64 signed value */
+ return (raw36 >> 35) == 0LL ?
+ raw36 : (SIGN_EXTEND_36_TO_64_MASK | raw36);
+}
+
static int read_cc_raw(struct qpnp_bms_chip *chip, int64_t *reading,
int cc_type)
{
@@ -582,12 +634,7 @@
return -ENXIO;
}
- raw_reading = raw_reading & CC_36_BIT_MASK;
- /* convert 36 bit signed value into 64 signed value */
- *reading = (raw_reading >> 35) == 0LL ?
- raw_reading : ((-1LL ^ CC_36_BIT_MASK) | raw_reading);
- pr_debug("before conversion: %llx, after conversion: %llx\n",
- raw_reading, *reading);
+ *reading = convert_s36_to_s64(raw_reading);
return 0;
}
@@ -791,6 +838,19 @@
chip->last_cc_uah = INT_MIN;
chip->last_ocv_temp = batt_temp;
chip->prev_batt_terminal_uv = 0;
+ if (chip->enable_fcc_learning) {
+ chip->adjusted_fcc_temp_lut = NULL;
+ chip->fcc_new_mah = -EINVAL;
+ /* reset the charge-cycle and charge-increase registers */
+ chip->charge_increase = 0;
+ chip->charge_cycles = 0;
+ backup_charge_cycle(chip);
+ /* discard all the FCC learnt data and reset the local table */
+ discard_backup_fcc_data(chip);
+ memset(chip->fcc_learning_samples, 0,
+ chip->min_fcc_learning_samples *
+ sizeof(struct fcc_sample));
+ }
}
#define OCV_RAW_UNINITIALIZED 0xFFFF
@@ -1282,6 +1342,74 @@
return rc;
}
+/* Returns estimated battery resistance */
+static int get_prop_bms_batt_resistance(struct qpnp_bms_chip *chip)
+{
+ return chip->rbatt_mohm * 1000;
+}
+
+/* Returns instantaneous current in uA */
+static int get_prop_bms_current_now(struct qpnp_bms_chip *chip)
+{
+ int rc, result_ua;
+
+ rc = get_battery_current(chip, &result_ua);
+ if (rc) {
+ pr_err("failed to get current: %d\n", rc);
+ return rc;
+ }
+ return result_ua;
+}
+
+/* Returns coulomb counter in uAh */
+static int get_prop_bms_charge_counter(struct qpnp_bms_chip *chip)
+{
+ int64_t cc_raw;
+
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+ read_cc_raw(chip, &cc_raw, false);
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+
+ return calculate_cc(chip, cc_raw, CC, NORESET);
+}
+
+/* Returns shadow coulomb counter in uAh */
+static int get_prop_bms_charge_counter_shadow(struct qpnp_bms_chip *chip)
+{
+ int64_t cc_raw;
+
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+ read_cc_raw(chip, &cc_raw, true);
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+
+ return calculate_cc(chip, cc_raw, SHDW_CC, NORESET);
+}
+
+/* Returns full charge design in uAh */
+static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip)
+{
+ return chip->fcc_mah * 1000;
+}
+
+/* Returns the current full charge in uAh */
+static int get_prop_bms_charge_full(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+ if (rc) {
+ pr_err("Unable to read battery temperature\n");
+ return rc;
+ }
+
+ return calculate_fcc(chip, (int)result.physical);
+}
+
static int calculate_delta_time(unsigned long *time_stamp, int *delta_time_s)
{
unsigned long now_tm_sec = 0;
@@ -1336,7 +1464,7 @@
if (params->rbatt_mohm != chip->rbatt_mohm) {
chip->rbatt_mohm = params->rbatt_mohm;
- if (chip->bms_psy.name != NULL)
+ if (chip->bms_psy_registered)
power_supply_changed(&chip->bms_psy);
}
@@ -1920,6 +2048,92 @@
}
}
+static int64_t convert_cc_uah_to_raw(struct qpnp_bms_chip *chip, int64_t cc_uah)
+{
+ int64_t cc_uv, cc_pvh, cc_raw;
+
+ cc_pvh = cc_uah * chip->r_sense_uohm;
+ cc_uv = div_s64(cc_pvh * SLEEP_CLK_HZ * SECONDS_PER_HOUR,
+ CC_READING_TICKS * 1000000LL);
+ cc_raw = div_s64(cc_uv * CC_READING_RESOLUTION_D,
+ CC_READING_RESOLUTION_N);
+ return cc_raw;
+}
+
+#define CC_STEP_INCREMENT_UAH 1500
+#define OCV_STEP_INCREMENT 0x10
+static void configure_soc_wakeup(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int batt_temp, int target_soc)
+{
+ int target_ocv_uv;
+ int64_t target_cc_uah, cc_raw_64, current_shdw_cc_raw_64;
+ int64_t current_shdw_cc_uah, iadc_comp_factor;
+ uint64_t cc_raw, current_shdw_cc_raw;
+ int16_t ocv_raw, current_ocv_raw;
+
+ current_shdw_cc_raw = 0;
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+ qpnp_read_wrapper(chip, (u8 *)¤t_ocv_raw,
+ chip->base + BMS1_OCV_FOR_SOC_DATA0, 2);
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+ current_shdw_cc_uah = get_prop_bms_charge_counter_shadow(chip);
+ current_shdw_cc_raw_64 = convert_cc_uah_to_raw(chip,
+ current_shdw_cc_uah);
+
+ /*
+ * Calculate the target shadow coulomb counter threshold for when
+ * the SoC changes.
+ *
+ * Since the BMS driver resets the shadow coulomb counter every
+ * 20 seconds when the device is awake, calculate the threshold as
+ * a delta from the current shadow coulomb count.
+ */
+ target_cc_uah = (100 - target_soc)
+ * (params->fcc_uah - params->uuc_uah)
+ / 100 - current_shdw_cc_uah;
+ if (target_cc_uah < 0) {
+ /*
+ * If the target cc is below 0, that means we have already
+ * passed the point where SoC should have fallen.
+ * Set a wakeup in a few more mAh and check back again
+ */
+ target_cc_uah = CC_STEP_INCREMENT_UAH;
+ }
+ iadc_comp_factor = 100000;
+ qpnp_iadc_comp_result(&iadc_comp_factor);
+ target_cc_uah = div64_s64(target_cc_uah * 100000, iadc_comp_factor);
+ target_cc_uah = cc_reverse_adjust_for_gain(target_cc_uah);
+ cc_raw_64 = convert_cc_uah_to_raw(chip, target_cc_uah);
+ cc_raw = convert_s64_to_s36(cc_raw_64);
+
+ find_ocv_for_soc(chip, params, batt_temp, target_soc, &target_ocv_uv);
+ ocv_raw = convert_vbatt_uv_to_raw(chip, target_ocv_uv);
+
+ /*
+ * If the current_ocv_raw was updated since reaching 100% and is lower
+ * than the calculated target ocv threshold, set the new target
+ * threshold 1.5mAh lower in order to check if the SoC changed yet.
+ */
+ if (current_ocv_raw != chip->ocv_reading_at_100
+ && current_ocv_raw < ocv_raw)
+ ocv_raw = current_ocv_raw - OCV_STEP_INCREMENT;
+
+ qpnp_write_wrapper(chip, (u8 *)&cc_raw,
+ chip->base + BMS1_SW_CC_THR0, 5);
+ qpnp_write_wrapper(chip, (u8 *)&ocv_raw,
+ chip->base + BMS1_OCV_THR0, 2);
+
+ pr_debug("current sw_cc_raw = 0x%llx, current ocv = 0x%hx\n",
+ current_shdw_cc_raw, (uint16_t)current_ocv_raw);
+ pr_debug("target_cc_uah = %lld, raw64 = 0x%llx, raw 36 = 0x%llx, ocv_raw = 0x%hx\n",
+ target_cc_uah,
+ (uint64_t)cc_raw_64, cc_raw,
+ (uint16_t)ocv_raw);
+}
+
#define SLEEP_RECALC_INTERVAL 3
static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
@@ -2022,7 +2236,13 @@
/* always clamp soc due to BMS hw/sw immaturities */
new_calculated_soc = clamp_soc_based_on_voltage(chip,
new_calculated_soc);
-
+ /*
+ * If the battery is full, configure the cc threshold so the system
+ * wakes up after SoC changes
+ */
+ if (is_battery_full(chip))
+ configure_soc_wakeup(chip, ¶ms,
+ batt_temp, bound_soc(new_calculated_soc - 1));
done_calculating:
mutex_lock(&chip->last_soc_mutex);
previous_soc = chip->calculated_soc;
@@ -2049,8 +2269,7 @@
}
mutex_unlock(&chip->last_soc_mutex);
- if (new_calculated_soc != previous_soc
- && chip->bms_psy.name != NULL) {
+ if (new_calculated_soc != previous_soc && chip->bms_psy_registered) {
power_supply_changed(&chip->bms_psy);
pr_debug("power supply changed\n");
} else {
@@ -2084,7 +2303,7 @@
voltage_based_soc = clamp(voltage_based_soc, 0, 100);
if (chip->prev_voltage_based_soc != voltage_based_soc
- && chip->bms_psy.name != NULL) {
+ && chip->bms_psy_registered) {
power_supply_changed(&chip->bms_psy);
pr_debug("power supply changed\n");
}
@@ -2101,8 +2320,7 @@
struct qpnp_vadc_result result;
struct raw_soc_params raw;
- if (!wake_lock_active(&chip->soc_wake_lock))
- wake_lock(&chip->soc_wake_lock);
+ bms_stay_awake(&chip->soc_wake_source);
mutex_lock(&chip->vbat_monitor_mutex);
qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
mutex_unlock(&chip->vbat_monitor_mutex);
@@ -2127,7 +2345,7 @@
mutex_unlock(&chip->last_ocv_uv_mutex);
}
}
- wake_unlock(&chip->soc_wake_lock);
+ bms_relax(&chip->soc_wake_source);
return soc;
}
@@ -2292,7 +2510,7 @@
} else {
pr_debug("unknown voltage notification state: %d\n", state);
}
- if (chip->bms_psy.name != NULL)
+ if (chip->bms_psy_registered)
power_supply_changed(&chip->bms_psy);
}
@@ -2386,26 +2604,85 @@
kfree(old);
}
-
-static void backup_fcc_new(struct qpnp_bms_chip *chip)
+static int read_fcc_data_from_backup(struct qpnp_bms_chip *chip)
{
- int rc = 0;
- u8 temp = 0;
+ int rc, i;
+ u8 fcc = 0, chgcyl = 0;
- if (chip->fcc_new_mah > 0) {
- rc = qpnp_write_wrapper(chip, (u8 *)&chip->fcc_new_mah,
- chip->base + FCC_STORAGE_LSB, 2);
- if (rc)
- pr_err("Unable to backup new_fcc\n");
-
- temp = chip->fcc_new_batt_temp / 10;
- rc = qpnp_write_wrapper(chip, &temp,
- chip->base + FCC_BATT_TEMP_STORAGE, 1);
- if (rc)
- pr_err("Unable to backup fcc temp.\n");
+ for (i = 0; i < chip->min_fcc_learning_samples; i++) {
+ rc = qpnp_read_wrapper(chip, &fcc,
+ chip->base + BMS_FCC_BASE_REG + i, 1);
+ rc |= qpnp_read_wrapper(chip, &chgcyl,
+ chip->base + BMS_CHGCYL_BASE_REG + i, 1);
+ if (rc) {
+ pr_err("Unable to read FCC data\n");
+ return rc;
+ }
+ if (fcc == 0 || (fcc == 0xFF && chgcyl == 0xFF)) {
+ /* FCC invalid/not present */
+ chip->fcc_learning_samples[i].fcc_new = 0;
+ chip->fcc_learning_samples[i].chargecycles = 0;
+ } else {
+ /* valid FCC data */
+ chip->fcc_sample_count++;
+ chip->fcc_learning_samples[i].fcc_new =
+ fcc * chip->fcc_resolution;
+ chip->fcc_learning_samples[i].chargecycles =
+ chgcyl * CHGCYL_RESOLUTION;
+ }
}
+
+ return 0;
}
+static int discard_backup_fcc_data(struct qpnp_bms_chip *chip)
+{
+ int rc = 0, i;
+ u8 temp_u8 = 0;
+
+ chip->fcc_sample_count = 0;
+ for (i = 0; i < chip->min_fcc_learning_samples; i++) {
+ rc = qpnp_write_wrapper(chip, &temp_u8,
+ chip->base + BMS_FCC_BASE_REG + i, 1);
+ rc |= qpnp_write_wrapper(chip, &temp_u8,
+ chip->base + BMS_CHGCYL_BASE_REG + i, 1);
+ if (rc) {
+ pr_err("Unable to clear FCC data\n");
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static void
+average_fcc_samples_and_readjust_fcc_table(struct qpnp_bms_chip *chip)
+{
+ int i, temp_fcc_avg = 0, temp_fcc_delta = 0, new_fcc_avg = 0;
+ struct fcc_sample *ft;
+
+ for (i = 0; i < chip->min_fcc_learning_samples; i++)
+ temp_fcc_avg += chip->fcc_learning_samples[i].fcc_new;
+
+ temp_fcc_avg /= chip->min_fcc_learning_samples;
+ temp_fcc_delta = div_u64(temp_fcc_avg * DELTA_FCC_PERCENT, 100);
+
+ /* fix the fcc if its an outlier i.e. > 5% of the average */
+ for (i = 0; i < chip->min_fcc_learning_samples; i++) {
+ ft = &chip->fcc_learning_samples[i];
+ if (abs(ft->fcc_new - temp_fcc_avg) > temp_fcc_delta)
+ new_fcc_avg += temp_fcc_avg;
+ else
+ new_fcc_avg += ft->fcc_new;
+ }
+ new_fcc_avg /= chip->min_fcc_learning_samples;
+
+ chip->fcc_new_mah = new_fcc_avg;
+ chip->fcc_new_batt_temp = FCC_DEFAULT_TEMP;
+ pr_info("FCC update: New fcc_mah=%d, fcc_batt_temp=%d\n",
+ new_fcc_avg, FCC_DEFAULT_TEMP);
+ readjust_fcc_table(chip);
+}
static void backup_charge_cycle(struct qpnp_bms_chip *chip)
{
@@ -2413,7 +2690,7 @@
if (chip->charge_increase >= 0) {
rc = qpnp_write_wrapper(chip, &chip->charge_increase,
- chip->base + CHARGE_INCREASE_STORAGE, 1);
+ chip->base + CHARGE_INCREASE_STORAGE, 1);
if (rc)
pr_err("Unable to backup charge_increase\n");
}
@@ -2426,9 +2703,31 @@
}
}
-static void restore_fcc_data(struct qpnp_bms_chip *chip)
+static bool chargecycles_in_range(struct qpnp_bms_chip *chip)
{
- int rc = 0;
+ int i, min_cycle, max_cycle, valid_range;
+
+ /* find the smallest and largest charge cycle */
+ max_cycle = min_cycle = chip->fcc_learning_samples[0].chargecycles;
+ for (i = 1; i < chip->min_fcc_learning_samples; i++) {
+ if (min_cycle > chip->fcc_learning_samples[i].chargecycles)
+ min_cycle = chip->fcc_learning_samples[i].chargecycles;
+ if (max_cycle < chip->fcc_learning_samples[i].chargecycles)
+ max_cycle = chip->fcc_learning_samples[i].chargecycles;
+ }
+
+ /* check if chargecyles are in range to continue with FCC update */
+ valid_range = DIV_ROUND_UP(VALID_FCC_CHGCYL_RANGE,
+ CHGCYL_RESOLUTION) * CHGCYL_RESOLUTION;
+ if (abs(max_cycle - min_cycle) > valid_range)
+ return false;
+
+ return true;
+}
+
+static int read_chgcycle_data_from_backup(struct qpnp_bms_chip *chip)
+{
+ int rc;
uint16_t temp_u16 = 0;
u8 temp_u8 = 0;
@@ -2442,25 +2741,18 @@
if (!rc && temp_u16 != 0xFFFF)
chip->charge_cycles = temp_u16;
- rc = qpnp_read_wrapper(chip, (u8 *)&temp_u16,
- chip->base + FCC_STORAGE_LSB, 2);
- if (!rc && temp_u16 != 0xFFFF) {
- chip->fcc_new_mah = temp_u16;
- } else {
- pr_debug("Backed-up FCC not initialized, FCC not updated\n");
- return;
- }
+ return rc;
+}
- rc = qpnp_read_wrapper(chip, &temp_u8,
- chip->base + FCC_BATT_TEMP_STORAGE, 1);
- if (!rc && temp_u8 != 0xFF) {
- chip->fcc_new_batt_temp = (s8)temp_u8 * 10;
- } else {
- pr_debug("Backed-up temp. not initialized, FCC not updated\n");
- return;
- }
- /* readjust the FCC table if fcc and temp are valid */
- readjust_fcc_table(chip);
+static void
+attempt_learning_new_fcc(struct qpnp_bms_chip *chip)
+{
+ pr_debug("Total FCC sample count=%d\n", chip->fcc_sample_count);
+
+ /* update FCC if we have the required samples */
+ if ((chip->fcc_sample_count == chip->min_fcc_learning_samples) &&
+ chargecycles_in_range(chip))
+ average_fcc_samples_and_readjust_fcc_table(chip);
}
static int calculate_real_soc(struct qpnp_bms_chip *chip,
@@ -2474,82 +2766,80 @@
return ((rc_uah - cc_uah) * 100) / fcc_uah;
}
-static void update_fcc_table_for_temp(struct qpnp_bms_chip *chip,
- int batt_temp_final)
-{
- int i, fcc_t1, fcc_t2, fcc_final;
- struct fcc_data *ft;
+#define MAX_U8_VALUE ((u8)(~0U))
- /* Interpolate all the FCC entries to the same temperature */
- for (i = 0; i < chip->min_fcc_learning_samples; i++) {
- ft = &chip->fcc_table[i];
- if (ft->batt_temp == batt_temp_final)
- continue;
- fcc_t1 = interpolate_fcc(chip->fcc_temp_lut, ft->batt_temp);
- fcc_t2 = interpolate_fcc(chip->fcc_temp_lut, batt_temp_final);
- fcc_final = (ft->fcc_new / fcc_t1) * fcc_t2;
- ft->fcc_new = fcc_final;
- ft->batt_temp = batt_temp_final;
+static int backup_new_fcc(struct qpnp_bms_chip *chip, int fcc_mah,
+ int chargecycles)
+{
+ int rc, min_cycle, i;
+ u8 fcc_new, chgcyl, pos = 0;
+ struct fcc_sample *ft;
+
+ if ((fcc_mah > (chip->fcc_resolution * MAX_U8_VALUE)) ||
+ (chargecycles > (CHGCYL_RESOLUTION * MAX_U8_VALUE))) {
+ pr_warn("FCC/Chgcyl beyond storage limit. FCC=%d, chgcyl=%d\n",
+ fcc_mah, chargecycles);
+ return -EINVAL;
}
+
+ if (chip->fcc_sample_count == chip->min_fcc_learning_samples) {
+ /* search best location - oldest entry */
+ min_cycle = chip->fcc_learning_samples[0].chargecycles;
+ for (i = 1; i < chip->min_fcc_learning_samples; i++) {
+ if (min_cycle >
+ chip->fcc_learning_samples[i].chargecycles)
+ pos = i;
+ }
+ } else {
+ /* find an empty location */
+ for (i = 0; i < chip->min_fcc_learning_samples; i++) {
+ ft = &chip->fcc_learning_samples[i];
+ if (ft->fcc_new == 0 || (ft->fcc_new == 0xFF &&
+ ft->chargecycles == 0xFF)) {
+ pos = i;
+ break;
+ }
+ }
+ chip->fcc_sample_count++;
+ }
+ chip->fcc_learning_samples[pos].fcc_new = fcc_mah;
+ chip->fcc_learning_samples[pos].chargecycles = chargecycles;
+
+ fcc_new = DIV_ROUND_UP(fcc_mah, chip->fcc_resolution);
+ rc = qpnp_write_wrapper(chip, (u8 *)&fcc_new,
+ chip->base + BMS_FCC_BASE_REG + pos, 1);
+ if (rc)
+ return rc;
+
+ chgcyl = DIV_ROUND_UP(chargecycles, CHGCYL_RESOLUTION);
+ rc = qpnp_write_wrapper(chip, (u8 *)&chgcyl,
+ chip->base + BMS_CHGCYL_BASE_REG + pos, 1);
+ if (rc)
+ return rc;
+
+ pr_debug("Backup new FCC: fcc_new=%d, chargecycle=%d, pos=%d\n",
+ fcc_new, chgcyl, pos);
+
+ return rc;
}
static void update_fcc_learning_table(struct qpnp_bms_chip *chip,
- int fcc_uah, int new_fcc_uah, int chargecycles, int batt_temp)
+ int new_fcc_uah, int chargecycles, int batt_temp)
{
- int i, count, new_fcc_avg = 0, temp_fcc_avg = 0, temp_fcc_delta = 0;
- struct fcc_data *ft;
+ int rc, fcc_default, fcc_temp;
- count = last_fcc_update_count % chip->min_fcc_learning_samples;
- ft = &chip->fcc_table[count];
- ft->fcc_new = chip->fcc_new_sysfs = new_fcc_uah;
- ft->batt_temp = batt_temp;
- ft->chargecycles = chargecycles;
- last_fcc_update_count++;
- /* update userspace */
- sysfs_notify(&chip->dev->kobj, NULL, "fcc_data");
+ /* convert the fcc at batt_temp to new fcc at FCC_DEFAULT_TEMP */
+ fcc_default = calculate_fcc(chip, FCC_DEFAULT_TEMP) / 1000;
+ fcc_temp = calculate_fcc(chip, batt_temp) / 1000;
+ new_fcc_uah = (new_fcc_uah / fcc_temp) * fcc_default;
- pr_debug("Updated fcc table. new_fcc=%d, chargecycle=%d, temp=%d fcc_update_count=%d\n",
- new_fcc_uah, chargecycles, batt_temp, last_fcc_update_count);
-
- if (last_fcc_update_count < chip->min_fcc_learning_samples) {
- pr_debug("Not enough FCC samples. Current count = %d\n",
- last_fcc_update_count);
- return; /* Not enough samples to update fcc */
+ rc = backup_new_fcc(chip, new_fcc_uah / 1000, chargecycles);
+ if (rc) {
+ pr_err("Unable to backup new FCC\n");
+ return;
}
-
- /* reject entries if they are > 50 chargecycles apart */
- for (i = 0; i < chip->min_fcc_learning_samples; i++) {
- if ((chip->fcc_table[i].chargecycles + VALID_FCC_CHGCYL_RANGE)
- < chargecycles) {
- pr_debug("Charge cycle too old (> %d cycles apart)\n",
- VALID_FCC_CHGCYL_RANGE);
- return; /* Samples old, > 50 cycles apart*/
- }
- }
- /* update the fcc table for temperature difference*/
- update_fcc_table_for_temp(chip, batt_temp);
-
- for (i = 0; i < chip->min_fcc_learning_samples; i++)
- temp_fcc_avg += chip->fcc_table[i].fcc_new;
-
- temp_fcc_avg /= chip->min_fcc_learning_samples;
- temp_fcc_delta = div_u64(temp_fcc_avg * DELTA_FCC_PERCENT, 100);
-
- /* fix the fcc if its an outlier i.e. > 5% of the average */
- for (i = 0; i < chip->min_fcc_learning_samples; i++) {
- ft = &chip->fcc_table[i];
- if (abs(ft->fcc_new - temp_fcc_avg) > temp_fcc_delta)
- ft->fcc_new = temp_fcc_avg;
- new_fcc_avg += ft->fcc_new;
- }
- new_fcc_avg /= chip->min_fcc_learning_samples;
-
- chip->fcc_new_mah = new_fcc_avg / 1000;
- chip->fcc_new_batt_temp = batt_temp;
- pr_info("FCC update: New fcc_mah=%d, fcc_batt_temp=%d\n",
- new_fcc_avg, batt_temp);
- readjust_fcc_table(chip);
- backup_fcc_new(chip);
+ /* check if FCC can be updated */
+ attempt_learning_new_fcc(chip);
}
static bool is_new_fcc_valid(int new_fcc_uah, int fcc_uah)
@@ -2603,8 +2893,8 @@
chip->start_cc_uah, chip->end_cc_uah, new_fcc_uah);
if (is_new_fcc_valid(new_fcc_uah, fcc_uah))
- update_fcc_learning_table(chip, fcc_uah,
- new_fcc_uah, chip->charge_cycles, batt_temp);
+ update_fcc_learning_table(chip, new_fcc_uah,
+ chip->charge_cycles, batt_temp);
}
}
@@ -2681,8 +2971,20 @@
pr_debug("charging ended\n");
charging_ended(chip);
}
+
+ if (status == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("battery full\n");
+ enable_bms_irq(&chip->ocv_thr_irq);
+ enable_bms_irq(&chip->sw_cc_thr_irq);
+ } else if (chip->battery_status
+ == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("battery not full any more\n");
+ disable_bms_irq(&chip->ocv_thr_irq);
+ disable_bms_irq(&chip->sw_cc_thr_irq);
+ }
+
chip->battery_status = status;
- /* a new battery was inserted or removed, so force a soc
+ /* battery charge status has changed, so force a soc
* recalculation to update the SoC */
schedule_work(&chip->recalc_work);
}
@@ -2716,59 +3018,6 @@
return report_state_of_charge(chip);
}
-/* Returns estimated battery resistance */
-static int get_prop_bms_batt_resistance(struct qpnp_bms_chip *chip)
-{
- return chip->rbatt_mohm * 1000;
-}
-
-/* Returns instantaneous current in uA */
-static int get_prop_bms_current_now(struct qpnp_bms_chip *chip)
-{
- int rc, result_ua;
-
- rc = get_battery_current(chip, &result_ua);
- if (rc) {
- pr_err("failed to get current: %d\n", rc);
- return rc;
- }
- return result_ua;
-}
-
-/* Returns coulomb counter in uAh */
-static int get_prop_bms_charge_counter(struct qpnp_bms_chip *chip)
-{
- int64_t cc_raw;
-
- mutex_lock(&chip->bms_output_lock);
- lock_output_data(chip);
- read_cc_raw(chip, &cc_raw, CC);
- unlock_output_data(chip);
- mutex_unlock(&chip->bms_output_lock);
-
- return calculate_cc(chip, cc_raw, CC, NORESET);
-}
-
-/* Returns shadow coulomb counter in uAh */
-static int get_prop_bms_charge_counter_shadow(struct qpnp_bms_chip *chip)
-{
- int64_t cc_raw;
-
- mutex_lock(&chip->bms_output_lock);
- lock_output_data(chip);
- read_cc_raw(chip, &cc_raw, SHDW_CC);
- unlock_output_data(chip);
- mutex_unlock(&chip->bms_output_lock);
-
- return calculate_cc(chip, cc_raw, SHDW_CC, NORESET);
-}
-
-/* Returns full charge design in uAh */
-static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip)
-{
- return chip->fcc_mah * 1000;
-}
-
static void qpnp_bms_external_power_changed(struct power_supply *psy)
{
struct qpnp_bms_chip *chip = container_of(psy, struct qpnp_bms_chip,
@@ -2804,6 +3053,9 @@
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = get_prop_bms_charge_full_design(chip);
break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = get_prop_bms_charge_full(chip);
+ break;
case POWER_SUPPLY_PROP_CYCLE_COUNT:
val->intval = chip->charge_cycles;
break;
@@ -2813,123 +3065,6 @@
return 0;
}
-static ssize_t fcc_data_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
- static int i;
- int fcc_new = 0, rc;
-
- if (chip->fcc_update_complete) {
- pr_debug("Invalid FCC update\n");
- return count;
- }
-
- i %= chip->min_fcc_learning_samples;
- rc = sscanf(buf, "%d", &fcc_new);
- if (rc != 1)
- return -EINVAL;
- chip->fcc_table[i].fcc_new = fcc_new;
- pr_debug("Rcvd: [%d] fcc_new=%d\n", i, fcc_new);
- i++;
-
- return count;
-}
-
-static ssize_t fcc_data_get(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- int count = 0;
- struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
-
- count = snprintf(buf, PAGE_SIZE, "%d", chip->fcc_new_sysfs);
- pr_debug("Sent: fcc_new=%d\n", chip->fcc_new_sysfs);
- chip->fcc_update_complete = 1;
-
- return count;
-}
-
-static ssize_t fcc_temp_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- static int i;
- int batt_temp = 0, rc;
- struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
-
- if (chip->fcc_update_complete)
- return count;
-
- i %= chip->min_fcc_learning_samples;
- rc = sscanf(buf, "%d", &batt_temp);
- if (rc != 1)
- return -EINVAL;
- chip->fcc_table[i].batt_temp = batt_temp;
- pr_debug("Rcvd: [%d] batt_temp=%d\n", i, batt_temp);
- i++;
-
- return count;
-}
-
-static ssize_t fcc_chgcyl_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- static int i;
- int chargecycle = 0, rc;
- struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
-
- if (chip->fcc_update_complete)
- return count;
-
- i %= chip->min_fcc_learning_samples;
- rc = sscanf(buf, "%d", &chargecycle);
- if (rc != 1)
- return -EINVAL;
- chip->fcc_table[i].chargecycles = chargecycle;
- pr_debug("Rcvd: [%d] chargecycle=%d\n", i, chargecycle);
- i++;
-
- return count;
-}
-
-static ssize_t fcc_list_get(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
- struct fcc_data *ft;
- int i = 0, j, count = 0;
-
- if (last_fcc_update_count < chip->min_fcc_learning_samples)
- i = last_fcc_update_count;
- else
- i = chip->min_fcc_learning_samples;
-
- for (j = 0; j < i; j++) {
- ft = &chip->fcc_table[j];
- count += snprintf(buf + count, PAGE_SIZE - count,
- "%d %d %d\n", ft->fcc_new, ft->chargecycles,
- ft->batt_temp);
- }
-
- return count;
-}
-
-static DEVICE_ATTR(fcc_data, 0664, fcc_data_get, fcc_data_set);
-static DEVICE_ATTR(fcc_temp, 0664, NULL, fcc_temp_set);
-static DEVICE_ATTR(fcc_chgcyl, 0664, NULL, fcc_chgcyl_set);
-static DEVICE_ATTR(fcc_list, 0664, fcc_list_get, NULL);
-
-static struct attribute *fcc_attrs[] = {
- &dev_attr_fcc_data.attr,
- &dev_attr_fcc_temp.attr,
- &dev_attr_fcc_chgcyl.attr,
- &dev_attr_fcc_list.attr,
- NULL
-};
-
-static const struct attribute_group fcc_attr_group = {
- .attrs = fcc_attrs,
-};
-
#define OCV_USE_LIMIT_EN BIT(7)
static int set_ocv_voltage_thresholds(struct qpnp_bms_chip *chip,
int low_voltage_threshold,
@@ -3013,10 +3148,39 @@
}
}
- pr_debug("shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d\n",
+ /* read the SOC storage to determine if there was a battery removal */
+ rc = qpnp_read_wrapper(chip, &temp, chip->base + SOC_STORAGE_REG, 1);
+ if (!rc) {
+ if (temp == SOC_INVALID)
+ chip->battery_removed = true;
+ }
+
+
+ pr_debug("shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d, battery_removed = %d\n",
chip->shutdown_soc,
chip->shutdown_iavg_ma,
- chip->shutdown_soc_invalid);
+ chip->shutdown_soc_invalid,
+ chip->battery_removed);
+}
+
+static irqreturn_t bms_ocv_thr_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_bms_chip *chip = _chip;
+
+ pr_debug("ocv_thr irq triggered\n");
+ bms_stay_awake(&chip->soc_wake_source);
+ schedule_work(&chip->recalc_work);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bms_sw_cc_thr_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_bms_chip *chip = _chip;
+
+ pr_debug("sw_cc_thr irq triggered\n");
+ bms_stay_awake(&chip->soc_wake_source);
+ schedule_work(&chip->recalc_work);
+ return IRQ_HANDLED;
}
#define PALLADIUM_ID_MIN 0x7F40
@@ -3105,6 +3269,12 @@
} \
} while (0)
+#define SPMI_PROP_READ_BOOL(chip_prop, qpnp_spmi_property) \
+do { \
+ chip->chip_prop = of_property_read_bool(chip->spmi->dev.of_node,\
+ "qcom," qpnp_spmi_property); \
+} while (0)
+
static inline int bms_read_properties(struct qpnp_bms_chip *chip)
{
int rc;
@@ -3151,8 +3321,7 @@
if (chip->adjust_soc_low_threshold >= 45)
chip->adjust_soc_low_threshold = 45;
- chip->enable_fcc_learning = of_property_read_bool(
- chip->spmi->dev.of_node, "qcom,enable-fcc-learning");
+ SPMI_PROP_READ_BOOL(enable_fcc_learning, "enable-fcc-learning");
if (chip->enable_fcc_learning) {
SPMI_PROP_READ(min_fcc_learning_soc,
"min-fcc-learning-soc", rc);
@@ -3160,12 +3329,16 @@
"min-fcc-ocv-pc", rc);
SPMI_PROP_READ(min_fcc_learning_samples,
"min-fcc-learning-samples", rc);
- chip->fcc_table = kzalloc((sizeof(struct fcc_data) *
- chip->min_fcc_learning_samples), GFP_KERNEL);
+ SPMI_PROP_READ(fcc_resolution,
+ "fcc-resolution", rc);
+ if (chip->min_fcc_learning_samples > MAX_FCC_CYCLES)
+ chip->min_fcc_learning_samples = MAX_FCC_CYCLES;
+ chip->fcc_learning_samples = devm_kzalloc(&chip->spmi->dev,
+ (sizeof(struct fcc_sample) *
+ chip->min_fcc_learning_samples), GFP_KERNEL);
pr_debug("min-fcc-soc=%d, min-fcc-pc=%d, min-fcc-cycles=%d\n",
chip->min_fcc_learning_soc, chip->min_fcc_ocv_pc,
chip->min_fcc_learning_samples);
- min_fcc_cycles = chip->min_fcc_learning_samples;
}
pr_debug("dts data: r_sense_uohm:%d, v_cutoff_uv:%d, max_v:%d\n",
@@ -3199,6 +3372,46 @@
chip->first_time_calc_uuc = 1;
}
+#define SPMI_FIND_IRQ(chip, irq_name) \
+do { \
+ chip->irq_name##_irq.irq = spmi_get_irq_byname(chip->spmi, \
+ resource, #irq_name); \
+ if (chip->irq_name##_irq.irq < 0) { \
+ pr_err("Unable to get " #irq_name " irq\n"); \
+ return -ENXIO; \
+ } \
+} while (0)
+
+static int bms_find_irqs(struct qpnp_bms_chip *chip,
+ struct spmi_resource *resource)
+{
+ SPMI_FIND_IRQ(chip, sw_cc_thr);
+ SPMI_FIND_IRQ(chip, ocv_thr);
+ return 0;
+}
+
+#define SPMI_REQUEST_IRQ(chip, rc, irq_name) \
+do { \
+ rc = devm_request_irq(chip->dev, chip->irq_name##_irq.irq, \
+ bms_##irq_name##_irq_handler, \
+ IRQF_TRIGGER_RISING, #irq_name, chip); \
+ if (rc < 0) { \
+ pr_err("Unable to request " #irq_name " irq: %d\n", rc);\
+ return -ENXIO; \
+ } \
+} while (0)
+
+static int bms_request_irqs(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ SPMI_REQUEST_IRQ(chip, rc, sw_cc_thr);
+ enable_irq_wake(chip->sw_cc_thr_irq.irq);
+ SPMI_REQUEST_IRQ(chip, rc, ocv_thr);
+ enable_irq_wake(chip->ocv_thr_irq.irq);
+ return 0;
+}
+
#define REG_OFFSET_PERP_TYPE 0x04
#define REG_OFFSET_PERP_SUBTYPE 0x05
#define BMS_BMS_TYPE 0xD
@@ -3246,6 +3459,11 @@
if (type == BMS_BMS_TYPE && subtype == BMS_BMS1_SUBTYPE) {
chip->base = resource->start;
+ rc = bms_find_irqs(chip, spmi_resource);
+ if (rc) {
+ pr_err("Could not find irqs\n");
+ return rc;
+ }
} else if (type == BMS_IADC_TYPE
&& (subtype == BMS_IADC1_SUBTYPE
|| subtype == BMS_IADC2_SUBTYPE)) {
@@ -3397,28 +3615,6 @@
return 0;
}
-static void check_battery_removal(struct qpnp_bms_chip *chip)
-{
- int rc;
- u8 temp = 0;
-
- /* check if battery was removed at PON */
- rc = qpnp_read_wrapper(chip, &temp,
- chip->base + BMS_BATT_REMOVED_REG, 1);
- if (temp == 0xFF) {
- pr_debug("New battery inserted at PON\n");
- temp = battery_removed = 1;
- rc = qpnp_write_wrapper(chip, &temp,
- chip->base + BMS_BATT_REMOVED_REG, 1);
- if (rc)
- pr_err("Unable to set BMS_BATT_REMOVED_REG\n");
- } else {
- if (rc)
- pr_err("Unable to read BMS_BATT_REMOVED_REG\n");
- battery_removed = 0;
- }
-}
-
static int __devinit qpnp_bms_probe(struct spmi_device *spmi)
{
struct qpnp_bms_chip *chip;
@@ -3446,6 +3642,12 @@
goto error_read;
}
+ mutex_init(&chip->bms_output_lock);
+ mutex_init(&chip->last_ocv_uv_mutex);
+ mutex_init(&chip->vbat_monitor_mutex);
+ mutex_init(&chip->soc_invalidation_mutex);
+ mutex_init(&chip->last_soc_mutex);
+
warm_reset = qpnp_pon_is_warm_reset();
rc = warm_reset;
if (rc < 0)
@@ -3503,14 +3705,7 @@
bms_initialize_constants(chip);
- mutex_init(&chip->bms_output_lock);
- mutex_init(&chip->last_ocv_uv_mutex);
- mutex_init(&chip->vbat_monitor_mutex);
- mutex_init(&chip->soc_invalidation_mutex);
- mutex_init(&chip->last_soc_mutex);
-
- wake_lock_init(&chip->soc_wake_lock, WAKE_LOCK_SUSPEND,
- "qpnp_soc_lock");
+ wakeup_source_init(&chip->soc_wake_source.source, "qpnp_soc_wake");
wake_lock_init(&chip->low_voltage_wake_lock, WAKE_LOCK_SUSPEND,
"qpnp_low_voltage_lock");
wake_lock_init(&chip->cv_wake_lock, WAKE_LOCK_SUSPEND,
@@ -3522,20 +3717,26 @@
read_shutdown_soc_and_iavg(chip);
if (chip->enable_fcc_learning) {
- restore_fcc_data(chip);
- rc = sysfs_create_group(&spmi->dev.kobj, &fcc_attr_group);
- if (rc) {
- pr_err("Unable to create sysfs entries\n");
- goto error_setup;
+ if (chip->battery_removed) {
+ rc = discard_backup_fcc_data(chip);
+ if (rc)
+ pr_err("Could not discard backed-up FCC data\n");
+ } else {
+ rc = read_chgcycle_data_from_backup(chip);
+ if (rc)
+ pr_err("Unable to restore charge-cycle data\n");
+
+ rc = read_fcc_data_from_backup(chip);
+ if (rc)
+ pr_err("Unable to restore FCC-learning data\n");
+ else
+ attempt_learning_new_fcc(chip);
}
}
dev_set_drvdata(&spmi->dev, chip);
device_init_wakeup(&spmi->dev, 1);
- check_battery_removal(chip);
-
-
rc = setup_vbat_monitoring(chip);
if (rc < 0) {
pr_err("failed to set up voltage notifications: %d\n", rc);
@@ -3571,6 +3772,7 @@
goto unregister_dc;
}
+ chip->bms_psy_registered = true;
vbatt = 0;
rc = get_battery_voltage(&vbatt);
if (rc) {
@@ -3579,16 +3781,23 @@
goto unregister_dc;
}
+ rc = bms_request_irqs(chip);
+ if (rc) {
+ pr_err("error requesting bms irqs, rc = %d\n", rc);
+ goto unregister_dc;
+ }
+
pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_uohm = %u warm_reset = %d\n",
get_prop_bms_capacity(chip), vbatt, chip->last_ocv_uv,
chip->r_sense_uohm, warm_reset);
return 0;
unregister_dc:
+ chip->bms_psy_registered = false;
power_supply_unregister(&chip->bms_psy);
error_setup:
dev_set_drvdata(&spmi->dev, NULL);
- wake_lock_destroy(&chip->soc_wake_lock);
+ wakeup_source_trash(&chip->soc_wake_source.source);
wake_lock_destroy(&chip->low_voltage_wake_lock);
wake_lock_destroy(&chip->cv_wake_lock);
error_resource:
@@ -3640,9 +3849,8 @@
- (int)(time_since_last_recalc * 1000));
}
- if (!wake_lock_active(&chip->soc_wake_lock)
- && time_until_next_recalc == 0)
- wake_lock(&chip->soc_wake_lock);
+ if (time_until_next_recalc == 0)
+ bms_stay_awake(&chip->soc_wake_source);
schedule_delayed_work(&chip->calculate_soc_delayed_work,
round_jiffies_relative(msecs_to_jiffies
(time_until_next_recalc)));
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 85bdd67..3679aa9 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -98,6 +98,7 @@
#define BOOST_VSET 0x41
#define BOOST_ENABLE_CONTROL 0x46
#define COMP_OVR1 0xEA
+#define BAT_IF_BTC_CTRL 0x49
#define REG_OFFSET_PERP_SUBTYPE 0x05
@@ -222,6 +223,7 @@
* @dc_present: present status of dc
* @batt_present: present status of battery
* @use_default_batt_values: flag to report default battery properties
+ * @btc_disabled Flag to disable btc (disables hot and cold irqs)
* @max_voltage_mv: the max volts the batt should be charged up to
* @min_voltage_mv: min battery voltage before turning the FET on
* @max_bat_chg_current: maximum battery charge current in mA
@@ -234,6 +236,8 @@
* @safe_current: battery safety current setting
* @maxinput_usb_ma: Maximum Input current USB
* @maxinput_dc_ma: Maximum Input current DC
+ * @hot_batt_p Hot battery threshold setting
+ * @cold_batt_p Cold battery threshold setting
* @warm_bat_decidegc Warm battery temperature in degree Celsius
* @cool_bat_decidegc Cool battery temperature in degree Celsius
* @revision: PMIC revision
@@ -276,6 +280,7 @@
bool dc_present;
bool batt_present;
bool charging_disabled;
+ bool btc_disabled;
bool use_default_batt_values;
bool duty_cycle_100p;
unsigned int bpd_detection;
@@ -291,8 +296,12 @@
unsigned int cool_bat_mv;
unsigned int resume_delta_mv;
int term_current;
+ int soc_resume_limit;
+ bool resuming_charging;
unsigned int maxinput_usb_ma;
unsigned int maxinput_dc_ma;
+ unsigned int hot_batt_p;
+ unsigned int cold_batt_p;
unsigned int warm_bat_decidegc;
unsigned int cool_bat_decidegc;
unsigned int safe_current;
@@ -334,7 +343,21 @@
[BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id",
};
-static inline int
+enum btc_type {
+ HOT_THD_25_PCT = 25,
+ HOT_THD_35_PCT = 35,
+ COLD_THD_70_PCT = 70,
+ COLD_THD_80_PCT = 80,
+};
+
+static u8 btc_value[] = {
+ [HOT_THD_25_PCT] = 0x0,
+ [HOT_THD_35_PCT] = BIT(0),
+ [COLD_THD_70_PCT] = 0x0,
+ [COLD_THD_80_PCT] = BIT(1),
+};
+
+ static inline int
get_bpd(const char *name)
{
int i = 0;
@@ -736,6 +759,23 @@
}
static void
+qpnp_chg_set_appropriate_vbatdet(struct qpnp_chg_chip *chip)
+{
+ if (chip->bat_is_cool)
+ qpnp_chg_vbatdet_set(chip, chip->cool_bat_mv
+ - chip->resume_delta_mv);
+ else if (chip->bat_is_warm)
+ qpnp_chg_vbatdet_set(chip, chip->warm_bat_mv
+ - chip->resume_delta_mv);
+ else if (chip->resuming_charging)
+ qpnp_chg_vbatdet_set(chip, chip->max_voltage_mv
+ + chip->resume_delta_mv);
+ else
+ qpnp_chg_vbatdet_set(chip, chip->max_voltage_mv
+ - chip->resume_delta_mv);
+}
+
+static void
qpnp_arb_stop_work(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
@@ -934,6 +974,10 @@
power_supply_changed(chip->usb_psy);
if (chip->dc_chgpth_base)
power_supply_changed(&chip->dc_psy);
+ if (chip->resuming_charging) {
+ chip->resuming_charging = false;
+ qpnp_chg_set_appropriate_vbatdet(chip);
+ }
qpnp_chg_enable_irq(&chip->chg_vbatdet_lo);
return IRQ_HANDLED;
@@ -1066,6 +1110,7 @@
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
POWER_SUPPLY_PROP_CYCLE_COUNT,
@@ -1098,7 +1143,7 @@
val->intval = qpnp_chg_is_dc_chg_plugged_in(chip);
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = chip->maxinput_dc_ma;
+ val->intval = chip->maxinput_dc_ma * 1000;
break;
default:
return -EINVAL;
@@ -1245,6 +1290,22 @@
return 0;
}
+static int
+get_prop_charge_full(struct qpnp_chg_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->bms_psy) {
+ chip->bms_psy->get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CHARGE_FULL, &ret);
+ return ret.intval;
+ } else {
+ pr_debug("No BMS supply registered return 0\n");
+ }
+
+ return 0;
+}
+
#define DEFAULT_CAPACITY 50
static int
get_prop_capacity(struct qpnp_chg_chip *chip)
@@ -1257,6 +1318,16 @@
if (chip->bms_psy) {
chip->bms_psy->get_property(chip->bms_psy,
POWER_SUPPLY_PROP_CAPACITY, &ret);
+ if (get_prop_batt_status(chip) == POWER_SUPPLY_STATUS_FULL
+ && !chip->resuming_charging
+ && !chip->charging_disabled
+ && chip->soc_resume_limit
+ && ret.intval <= chip->soc_resume_limit) {
+ pr_debug("resuming charging at %d%% soc\n", ret.intval);
+ chip->resuming_charging = true;
+ qpnp_chg_set_appropriate_vbatdet(chip);
+ qpnp_chg_charge_en(chip, !chip->charging_disabled);
+ }
if (ret.intval == 0) {
if (!qpnp_chg_is_usb_chg_plugged_in(chip)
&& !qpnp_chg_is_usb_chg_plugged_in(chip))
@@ -1380,6 +1451,9 @@
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = get_prop_full_design(chip);
break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = get_prop_charge_full(chip);
+ break;
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
val->intval = !(chip->charging_disabled);
break;
@@ -1396,7 +1470,39 @@
return 0;
}
-#define QPNP_CHG_VINMIN_MIN_MV 3400
+#define BTC_CONFIG_ENABLED BIT(7)
+#define BTC_COLD BIT(1)
+#define BTC_HOT BIT(0)
+static int
+qpnp_chg_bat_if_configure_btc(struct qpnp_chg_chip *chip)
+{
+ u8 btc_cfg = 0, mask = 0;
+
+ /* Do nothing if battery peripheral not present */
+ if (!chip->bat_if_base)
+ return 0;
+
+ if ((chip->hot_batt_p == HOT_THD_25_PCT)
+ || (chip->hot_batt_p == HOT_THD_35_PCT)) {
+ btc_cfg |= btc_value[chip->hot_batt_p];
+ mask |= BTC_HOT;
+ }
+
+ if ((chip->cold_batt_p == COLD_THD_70_PCT) ||
+ (chip->cold_batt_p == COLD_THD_80_PCT)) {
+ btc_cfg |= btc_value[chip->cold_batt_p];
+ mask |= BTC_COLD;
+ }
+
+ if (chip->btc_disabled)
+ mask |= BTC_CONFIG_ENABLED;
+
+ return qpnp_chg_masked_write(chip,
+ chip->bat_if_base + BAT_IF_BTC_CTRL,
+ mask, btc_cfg, 1);
+}
+
+#define QPNP_CHG_VINMIN_MIN_MV 4200
#define QPNP_CHG_VINMIN_HIGH_MIN_MV 5600
#define QPNP_CHG_VINMIN_HIGH_MIN_VAL 0x2B
#define QPNP_CHG_VINMIN_MAX_MV 9600
@@ -1601,20 +1707,6 @@
}
static void
-qpnp_chg_set_appropriate_vbatdet(struct qpnp_chg_chip *chip)
-{
- if (chip->bat_is_cool)
- qpnp_chg_vbatdet_set(chip, chip->cool_bat_mv
- - chip->resume_delta_mv);
- else if (chip->bat_is_warm)
- qpnp_chg_vbatdet_set(chip, chip->warm_bat_mv
- - chip->resume_delta_mv);
- else
- qpnp_chg_vbatdet_set(chip, chip->max_voltage_mv
- - chip->resume_delta_mv);
-}
-
-static void
qpnp_chg_set_appropriate_battery_current(struct qpnp_chg_chip *chip)
{
unsigned int chg_current = chip->max_bat_chg_current;
@@ -2056,7 +2148,16 @@
chip->bat_is_cool = bat_cool;
chip->bat_is_warm = bat_warm;
- /* set appropriate voltages and currents */
+ if (bat_cool || bat_warm)
+ chip->resuming_charging = false;
+
+ /**
+ * set appropriate voltages and currents.
+ *
+ * Note that when the battery is hot or cold, the charger
+ * driver will not resume with SoC. Only vbatdet is used to
+ * determine resume of charging.
+ */
qpnp_chg_set_appropriate_vddmax(chip);
qpnp_chg_set_appropriate_battery_current(chip);
qpnp_chg_set_appropriate_vbatdet(chip);
@@ -2251,7 +2352,8 @@
}
rc = devm_request_irq(chip->dev, chip->batt_pres.irq,
qpnp_chg_bat_if_batt_pres_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_SHARED | IRQF_ONESHOT,
"batt-pres", chip);
if (rc < 0) {
pr_err("Can't request %d batt-pres irq: %d\n",
@@ -2617,6 +2719,10 @@
OF_PROP_READ(chip, warm_bat_decidegc, "warm-bat-decidegc", rc, 1);
OF_PROP_READ(chip, cool_bat_decidegc, "cool-bat-decidegc", rc, 1);
OF_PROP_READ(chip, tchg_mins, "tchg-mins", rc, 1);
+ OF_PROP_READ(chip, hot_batt_p, "batt-hot-percentage", rc, 1);
+ OF_PROP_READ(chip, cold_batt_p, "batt-cold-percentage", rc, 1);
+ OF_PROP_READ(chip, soc_resume_limit, "resume-soc", rc, 1);
+
if (rc)
return rc;
@@ -2649,6 +2755,10 @@
return rc;
}
+ /* Get the btc-disabled property */
+ chip->btc_disabled = of_property_read_bool(chip->spmi->dev.of_node,
+ "qcom,btc-disabled");
+
/* Get the charging-disabled property */
chip->charging_disabled = of_property_read_bool(chip->spmi->dev.of_node,
"qcom,charging-disabled");
@@ -2657,13 +2767,6 @@
chip->duty_cycle_100p = of_property_read_bool(
chip->spmi->dev.of_node,
"qcom,duty-cycle-100p");
- if (chip->duty_cycle_100p) {
- rc = qpnp_buck_set_100_duty_cycle_enable(chip, 1);
- if (rc) {
- pr_err("failed to enable duty cycle %d\n", rc);
- return rc;
- }
- }
/* Get the fake-batt-values property */
chip->use_default_batt_values =
@@ -2829,6 +2932,16 @@
0xFF,
0x80, 1);
+ if (chip->duty_cycle_100p) {
+ rc = qpnp_buck_set_100_duty_cycle_enable(chip,
+ 1);
+ if (rc) {
+ pr_err("failed to set duty cycle %d\n",
+ rc);
+ goto fail_chg_enable;
+ }
+ }
+
break;
case SMBB_BAT_IF_SUBTYPE:
case SMBBP_BAT_IF_SUBTYPE:
@@ -2972,6 +3085,11 @@
}
}
}
+ rc = qpnp_chg_bat_if_configure_btc(chip);
+ if (rc) {
+ pr_err("failed to configure btc %d\n", rc);
+ goto unregister_batt;
+ }
qpnp_chg_charge_en(chip, !chip->charging_disabled);
qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c
index c9d0500..d859651 100644
--- a/drivers/regulator/qpnp-regulator.c
+++ b/drivers/regulator/qpnp-regulator.c
@@ -1535,7 +1535,9 @@
&(pdata->init_data), vreg, spmi->dev.of_node);
if (IS_ERR(vreg->rdev)) {
rc = PTR_ERR(vreg->rdev);
- vreg_err(vreg, "regulator_register failed, rc=%d\n", rc);
+ if (rc != -EPROBE_DEFER)
+ vreg_err(vreg, "regulator_register failed, rc=%d\n",
+ rc);
goto cancel_ocp_work;
}
@@ -1547,7 +1549,7 @@
if (vreg->ocp_irq)
cancel_delayed_work_sync(&vreg->ocp_work);
bail:
- if (rc)
+ if (rc && rc != -EPROBE_DEFER)
vreg_err(vreg, "probe failed, rc=%d\n", rc);
kfree(vreg->rdesc.name);
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
index e0bffb9..bfbae78 100644
--- a/drivers/rtc/qpnp-rtc.c
+++ b/drivers/rtc/qpnp-rtc.c
@@ -45,6 +45,11 @@
#define TO_SECS(arr) (arr[0] | (arr[1] << 8) | (arr[2] << 16) | \
(arr[3] << 24))
+/* Module parameter to control power-on-alarm */
+static bool poweron_alarm;
+module_param(poweron_alarm, bool, 0644);
+MODULE_PARM_DESC(poweron_alarm, "Enable/Disable power-on alarm");
+
/* rtc driver internal structure */
struct qpnp_rtc {
u8 rtc_ctrl_reg;
@@ -586,7 +591,7 @@
struct qpnp_rtc *rtc_dd = dev_get_drvdata(&spmi->dev);
bool rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
- if (!rtc_alarm_powerup) {
+ if (!rtc_alarm_powerup && !poweron_alarm) {
spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
dev_dbg(&spmi->dev, "Disabling alarm interrupts\n");
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 5dfd749..1def2dd 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -255,11 +255,10 @@
}
EXPORT_SYMBOL(scsi_execute);
-
-int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
+int scsi_execute_req_flags(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
struct scsi_sense_hdr *sshdr, int timeout, int retries,
- int *resid)
+ int *resid, int flags)
{
char *sense = NULL;
int result;
@@ -270,14 +269,14 @@
return DRIVER_ERROR << 24;
}
result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
- sense, timeout, retries, 0, resid);
+ sense, timeout, retries, flags, resid);
if (sshdr)
scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
kfree(sense);
return result;
}
-EXPORT_SYMBOL(scsi_execute_req);
+EXPORT_SYMBOL(scsi_execute_req_flags);
/*
* Function: scsi_init_cmd_errh()
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c
index c467064..ff78095 100644
--- a/drivers/scsi/scsi_pm.c
+++ b/drivers/scsi/scsi_pm.c
@@ -16,29 +16,28 @@
#include "scsi_priv.h"
-static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
+static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *))
{
- struct device_driver *drv;
int err;
err = scsi_device_quiesce(to_scsi_device(dev));
if (err == 0) {
- drv = dev->driver;
- if (drv && drv->suspend)
- err = drv->suspend(dev, msg);
+ if (cb) {
+ err = cb(dev);
+ if (err)
+ scsi_device_resume(to_scsi_device(dev));
+ }
}
dev_dbg(dev, "scsi suspend: %d\n", err);
return err;
}
-static int scsi_dev_type_resume(struct device *dev)
+static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *))
{
- struct device_driver *drv;
int err = 0;
- drv = dev->driver;
- if (drv && drv->resume)
- err = drv->resume(dev);
+ if (cb)
+ err = cb(dev);
scsi_device_resume(to_scsi_device(dev));
dev_dbg(dev, "scsi resume: %d\n", err);
return err;
@@ -46,30 +45,28 @@
#ifdef CONFIG_PM_SLEEP
-static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg)
+static int
+scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
{
int err = 0;
if (scsi_is_sdev_device(dev)) {
/*
- * sd is the only high-level SCSI driver to implement runtime
- * PM, and sd treats runtime suspend, system suspend, and
- * system hibernate identically (but not system freeze).
+ * All the high-level SCSI drivers that implement runtime
+ * PM treat runtime suspend, system suspend, and system
+ * hibernate identically.
*/
- if (pm_runtime_suspended(dev)) {
- if (msg.event == PM_EVENT_SUSPEND ||
- msg.event == PM_EVENT_HIBERNATE)
- return 0; /* already suspended */
+ if (pm_runtime_suspended(dev))
+ return 0;
- /* wake up device so that FREEZE will succeed */
- pm_runtime_resume(dev);
- }
- err = scsi_dev_type_suspend(dev, msg);
+ err = scsi_dev_type_suspend(dev, cb);
}
+
return err;
}
-static int scsi_bus_resume_common(struct device *dev)
+static int
+scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *))
{
int err = 0;
@@ -81,7 +78,7 @@
* Resume it on behalf of child.
*/
pm_runtime_get_sync(dev->parent);
- err = scsi_dev_type_resume(dev);
+ err = scsi_dev_type_resume(dev, cb);
pm_runtime_put_sync(dev->parent);
}
@@ -108,55 +105,131 @@
static int scsi_bus_suspend(struct device *dev)
{
- return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL);
+}
+
+static int scsi_bus_resume(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_resume_common(dev, pm ? pm->resume : NULL);
}
static int scsi_bus_freeze(struct device *dev)
{
- return scsi_bus_suspend_common(dev, PMSG_FREEZE);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL);
+}
+
+static int scsi_bus_thaw(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL);
}
static int scsi_bus_poweroff(struct device *dev)
{
- return scsi_bus_suspend_common(dev, PMSG_HIBERNATE);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL);
+}
+
+static int scsi_bus_restore(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_resume_common(dev, pm ? pm->restore : NULL);
}
#else /* CONFIG_PM_SLEEP */
-#define scsi_bus_resume_common NULL
#define scsi_bus_prepare NULL
#define scsi_bus_suspend NULL
+#define scsi_bus_resume NULL
#define scsi_bus_freeze NULL
+#define scsi_bus_thaw NULL
#define scsi_bus_poweroff NULL
+#define scsi_bus_restore NULL
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM_RUNTIME
+static int sdev_blk_runtime_suspend(struct scsi_device *sdev,
+ int (*cb)(struct device *))
+{
+ int err;
+
+ err = blk_pre_runtime_suspend(sdev->request_queue);
+ if (err)
+ return err;
+ if (cb)
+ err = cb(&sdev->sdev_gendev);
+ blk_post_runtime_suspend(sdev->request_queue, err);
+
+ return err;
+}
+
+static int sdev_runtime_suspend(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int (*cb)(struct device *) = pm ? pm->runtime_suspend : NULL;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ int err;
+
+ if (sdev->request_queue->dev)
+ return sdev_blk_runtime_suspend(sdev, cb);
+
+ err = scsi_dev_type_suspend(dev, cb);
+ if (err == -EAGAIN)
+ pm_schedule_suspend(dev, jiffies_to_msecs(
+ round_jiffies_up_relative(HZ/10)));
+ return err;
+}
+
static int scsi_runtime_suspend(struct device *dev)
{
int err = 0;
dev_dbg(dev, "scsi_runtime_suspend\n");
- if (scsi_is_sdev_device(dev)) {
- err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
- if (err == -EAGAIN)
- pm_schedule_suspend(dev, jiffies_to_msecs(
- round_jiffies_up_relative(HZ/10)));
- }
+ if (scsi_is_sdev_device(dev))
+ err = sdev_runtime_suspend(dev);
/* Insert hooks here for targets, hosts, and transport classes */
return err;
}
+static int sdev_blk_runtime_resume(struct scsi_device *sdev,
+ int (*cb)(struct device *))
+{
+ int err = 0;
+
+ blk_pre_runtime_resume(sdev->request_queue);
+ if (cb)
+ err = cb(&sdev->sdev_gendev);
+ blk_post_runtime_resume(sdev->request_queue, err);
+
+ return err;
+}
+
+static int sdev_runtime_resume(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int (*cb)(struct device *) = pm ? pm->runtime_resume : NULL;
+
+ if (sdev->request_queue->dev)
+ return sdev_blk_runtime_resume(sdev, cb);
+ else
+ return scsi_dev_type_resume(dev, cb);
+}
+
static int scsi_runtime_resume(struct device *dev)
{
int err = 0;
dev_dbg(dev, "scsi_runtime_resume\n");
if (scsi_is_sdev_device(dev))
- err = scsi_dev_type_resume(dev);
+ err = sdev_runtime_resume(dev);
/* Insert hooks here for targets, hosts, and transport classes */
@@ -171,10 +244,18 @@
/* Insert hooks here for targets, hosts, and transport classes */
- if (scsi_is_sdev_device(dev))
- err = pm_schedule_suspend(dev, 100);
- else
+ if (scsi_is_sdev_device(dev)) {
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ if (sdev->request_queue->dev) {
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_autosuspend(dev);
+ } else {
+ err = pm_runtime_suspend(dev);
+ }
+ } else {
err = pm_runtime_suspend(dev);
+ }
return err;
}
@@ -235,11 +316,11 @@
const struct dev_pm_ops scsi_bus_pm_ops = {
.prepare = scsi_bus_prepare,
.suspend = scsi_bus_suspend,
- .resume = scsi_bus_resume_common,
+ .resume = scsi_bus_resume,
.freeze = scsi_bus_freeze,
- .thaw = scsi_bus_resume_common,
+ .thaw = scsi_bus_thaw,
.poweroff = scsi_bus_poweroff,
- .restore = scsi_bus_resume_common,
+ .restore = scsi_bus_restore,
.runtime_suspend = scsi_runtime_suspend,
.runtime_resume = scsi_runtime_resume,
.runtime_idle = scsi_runtime_idle,
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 508c2bb..f454610 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -966,10 +966,6 @@
sdev = sdkp->device;
- retval = scsi_autopm_get_device(sdev);
- if (retval)
- goto error_autopm;
-
/*
* If the device is in error recovery, wait until it is done.
* If the device is offline, then disallow any access to it.
@@ -1014,8 +1010,6 @@
return 0;
error_out:
- scsi_autopm_put_device(sdev);
-error_autopm:
scsi_disk_put(sdkp);
return retval;
}
@@ -1050,7 +1044,6 @@
* XXX is followed by a "rmmod sd_mod"?
*/
- scsi_autopm_put_device(sdev);
scsi_disk_put(sdkp);
return 0;
}
@@ -1212,14 +1205,9 @@
retval = -ENODEV;
if (scsi_block_when_processing_errors(sdp)) {
- retval = scsi_autopm_get_device(sdp);
- if (retval)
- goto out;
-
sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
sshdr);
- scsi_autopm_put_device(sdp);
}
/* failed to execute TUR, assume media not present */
@@ -1269,8 +1257,9 @@
* Leave the rest of the command zero to indicate
* flush everything.
*/
- res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL);
+ res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0,
+ &sshdr, SD_FLUSH_TIMEOUT,
+ SD_MAX_RETRIES, NULL, REQ_PM);
if (res == 0)
break;
}
@@ -2630,6 +2619,7 @@
sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
sdp->removable ? "removable " : "");
+ blk_pm_runtime_init(sdp->request_queue, dev);
scsi_autopm_put_device(sdp);
put_device(&sdkp->dev);
}
@@ -2812,8 +2802,8 @@
if (!scsi_device_online(sdp))
return -ENODEV;
- res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+ res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL, REQ_PM);
if (res) {
sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
sd_print_result(sdkp, res);
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 086ff03..139bc06 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -36,20 +36,10 @@
#ifndef _UFS_H
#define _UFS_H
-#include <linux/mutex.h>
-#include <linux/types.h>
-
#define MAX_CDB_SIZE 16
-#define GENERAL_UPIU_REQUEST_SIZE 32
-#define UPIU_HEADER_DATA_SEGMENT_MAX_SIZE ((ALIGNED_UPIU_SIZE) - \
- (GENERAL_UPIU_REQUEST_SIZE))
-#define QUERY_OSF_SIZE ((GENERAL_UPIU_REQUEST_SIZE) - \
- (sizeof(struct utp_upiu_header)))
-#define UFS_QUERY_RESERVED_SCSI_CMD 0xCC
-#define UFS_QUERY_CMD_SIZE 10
#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
- cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
+ ((byte3 << 24) | (byte2 << 16) |\
(byte1 << 8) | (byte0))
/*
@@ -72,7 +62,7 @@
UPIU_TRANSACTION_COMMAND = 0x01,
UPIU_TRANSACTION_DATA_OUT = 0x02,
UPIU_TRANSACTION_TASK_REQ = 0x04,
- UPIU_TRANSACTION_QUERY_REQ = 0x16,
+ UPIU_TRANSACTION_QUERY_REQ = 0x26,
};
/* UTP UPIU Transaction Codes Target to Initiator */
@@ -83,7 +73,6 @@
UPIU_TRANSACTION_TASK_RSP = 0x24,
UPIU_TRANSACTION_READY_XFER = 0x31,
UPIU_TRANSACTION_QUERY_RSP = 0x36,
- UPIU_TRANSACTION_REJECT_UPIU = 0x3F,
};
/* UPIU Read/Write flags */
@@ -101,12 +90,6 @@
UPIU_TASK_ATTR_ACA = 0x03,
};
-/* UPIU Query request function */
-enum {
- UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
- UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
-};
-
/* UTP QUERY Transaction Specific Fields OpCode */
enum {
UPIU_QUERY_OPCODE_NOP = 0x0,
@@ -120,21 +103,6 @@
UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
};
-/* Query response result code */
-enum {
- QUERY_RESULT_SUCCESS = 0x00,
- QUERY_RESULT_NOT_READABLE = 0xF6,
- QUERY_RESULT_NOT_WRITEABLE = 0xF7,
- QUERY_RESULT_ALREADY_WRITTEN = 0xF8,
- QUERY_RESULT_INVALID_LENGTH = 0xF9,
- QUERY_RESULT_INVALID_VALUE = 0xFA,
- QUERY_RESULT_INVALID_SELECTOR = 0xFB,
- QUERY_RESULT_INVALID_INDEX = 0xFC,
- QUERY_RESULT_INVALID_IDN = 0xFD,
- QUERY_RESULT_INVALID_OPCODE = 0xFE,
- QUERY_RESULT_GENERAL_FAILURE = 0xFF,
-};
-
/* UTP Transfer Request Command Type (CT) */
enum {
UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
@@ -142,17 +110,10 @@
UPIU_COMMAND_SET_TYPE_QUERY = 0x2,
};
-/* UTP Transfer Request Command Offset */
-#define UPIU_COMMAND_TYPE_OFFSET 28
-
-/* Offset of the response code in the UPIU header */
-#define UPIU_RSP_CODE_OFFSET 8
-
enum {
MASK_SCSI_STATUS = 0xFF,
MASK_TASK_RESPONSE = 0xFF00,
MASK_RSP_UPIU_RESULT = 0xFFFF,
- MASK_QUERY_DATA_SEG_LEN = 0xFFFF,
};
/* Task management service response */
@@ -177,59 +138,26 @@
/**
* struct utp_upiu_cmd - Command UPIU structure
+ * @header: UPIU header structure DW-0 to DW-2
* @data_transfer_len: Data Transfer Length DW-3
* @cdb: Command Descriptor Block CDB DW-4 to DW-7
*/
struct utp_upiu_cmd {
+ struct utp_upiu_header header;
u32 exp_data_transfer_len;
u8 cdb[MAX_CDB_SIZE];
};
/**
- * struct utp_upiu_query - upiu request buffer structure for
- * query request.
- * @opcode: command to perform B-0
- * @idn: a value that indicates the particular type of data B-1
- * @index: Index to further identify data B-2
- * @selector: Index to further identify data B-3
- * @reserved_osf: spec reserved field B-4,5
- * @length: number of descriptor bytes to read/write B-6,7
- * @value: Attribute value to be written DW-6
- * @reserved: spec reserved DW-7,8
- */
-struct utp_upiu_query {
- u8 opcode;
- u8 idn;
- u8 index;
- u8 selector;
- u16 reserved_osf;
- u16 length;
- u32 value;
- u32 reserved[2];
-};
-
-/**
- * struct utp_upiu_req - general upiu request structure
- * @header:UPIU header structure DW-0 to DW-2
- * @sc: fields structure for scsi command
- * @qr: fields structure for query request
- */
-struct utp_upiu_req {
- struct utp_upiu_header header;
- union {
- struct utp_upiu_cmd sc;
- struct utp_upiu_query qr;
- };
-};
-
-/**
- * struct utp_cmd_rsp - Response UPIU structure
+ * struct utp_upiu_rsp - Response UPIU structure
+ * @header: UPIU header DW-0 to DW-2
* @residual_transfer_count: Residual transfer count DW-3
* @reserved: Reserved double words DW-4 to DW-7
* @sense_data_len: Sense data length DW-8 U16
* @sense_data: Sense data field DW-8 to DW-12
*/
-struct utp_cmd_rsp {
+struct utp_upiu_rsp {
+ struct utp_upiu_header header;
u32 residual_transfer_count;
u32 reserved[4];
u16 sense_data_len;
@@ -237,20 +165,6 @@
};
/**
- * struct utp_upiu_rsp - general upiu response structure
- * @header: UPIU header structure DW-0 to DW-2
- * @sc: fields structure for scsi command
- * @qr: fields structure for query request
- */
-struct utp_upiu_rsp {
- struct utp_upiu_header header;
- union {
- struct utp_cmd_rsp sc;
- struct utp_upiu_query qr;
- };
-};
-
-/**
* struct utp_upiu_task_req - Task request UPIU structure
* @header - UPIU header structure DW0 to DW-2
* @input_param1: Input parameter 1 DW-3
@@ -280,24 +194,4 @@
u32 reserved[3];
};
-/**
- * struct ufs_query_req - parameters for building a query request
- * @query_func: UPIU header query function
- * @upiu_req: the query request data
- */
-struct ufs_query_req {
- u8 query_func;
- struct utp_upiu_query upiu_req;
-};
-
-/**
- * struct ufs_query_resp - UPIU QUERY
- * @response: device response code
- * @upiu_res: query response data
- */
-struct ufs_query_res {
- u8 response;
- struct utp_upiu_query upiu_res;
-};
-
#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufs_test.c b/drivers/scsi/ufs/ufs_test.c
index 03c58a4..dbab808 100644
--- a/drivers/scsi/ufs/ufs_test.c
+++ b/drivers/scsi/ufs/ufs_test.c
@@ -18,13 +18,27 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <../sd.h>
+#include <linux/delay.h>
#define MODULE_NAME "ufs_test"
-#define TEST_MAX_BIOS_PER_REQ 120
+#define TEST_MAX_BIOS_PER_REQ 16
#define LARGE_PRIME_1 1103515367
#define LARGE_PRIME_2 35757
-#define DEFAULT_NUM_OF_BIOS 2
+#define DEFAULT_NUM_OF_BIOS 2
+
+/* the amount of requests that will be inserted */
+#define LONG_SEQ_TEST_NUM_REQS 256
+/* request queue limitation is 128 requests, and we leave 10 spare requests */
+#define QUEUE_MAX_REQUESTS 118
+#define MB_MSEC_RATIO_APPROXIMATION ((1024 * 1024) / 1000)
+/* actual number of MiB in test multiplied by 10, for single digit precision*/
+#define BYTE_TO_MB_x_10(x) ((x * 10) / (1024 * 1024))
+/* extract integer value */
+#define LONG_TEST_SIZE_INTEGER(x) (BYTE_TO_MB_x_10(x) / 10)
+/* and calculate the MiB value fraction */
+#define LONG_TEST_SIZE_FRACTION(x) (BYTE_TO_MB_x_10(x) - \
+ (LONG_TEST_SIZE_INTEGER(x) * 10))
#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
@@ -32,11 +46,16 @@
enum ufs_test_testcases {
UFS_TEST_WRITE_READ_TEST,
+
+ TEST_LONG_SEQUENTIAL_READ,
+ TEST_LONG_SEQUENTIAL_WRITE,
};
struct ufs_test_debug {
struct dentry *write_read_test; /* basic test */
struct dentry *random_test_seed; /* parameters in utils */
+ struct dentry *long_sequential_read_test;
+ struct dentry *long_sequential_write_test;
};
struct ufs_test_data {
@@ -60,6 +79,8 @@
* disabled and 2 BIOs are written.
*/
unsigned int random_test_seed;
+ /* A counter for the number of test requests completed */
+ unsigned int completed_req_count;
};
static struct ufs_test_data *utd;
@@ -77,6 +98,12 @@
case UFS_TEST_WRITE_READ_TEST:
return "UFS write read test";
break;
+ case TEST_LONG_SEQUENTIAL_READ:
+ return "UFS long sequential read test";
+ break;
+ case TEST_LONG_SEQUENTIAL_WRITE:
+ return "UFS long sequential write test";
+ break;
default:
return "Unknown test";
}
@@ -166,8 +193,9 @@
num_bios = DEFAULT_NUM_OF_BIOS;
/* Adding a write request */
- test_pr_info("%s: Adding a write requests to Q, first req_id=%d",
- __func__, td->wr_rd_next_req_id);
+ test_pr_info(
+ "%s: Adding a write request with %d bios to Q, req_id=%d"
+ , __func__, num_bios, td->wr_rd_next_req_id);
utd->write_completed = false;
ret = test_iosched_add_wr_rd_test_req(0, WRITE, start_sec,
@@ -265,21 +293,290 @@
.read = ufs_test_write_read_test_read_cb,
};
+static void long_seq_test_free_end_io_fn(struct request *rq, int err)
+{
+ struct test_request *test_rq;
+ struct test_data *ptd = test_get_test_data();
+
+ if (rq)
+ test_rq = (struct test_request *)rq->elv.priv[0];
+ else {
+ test_pr_err("%s: error: NULL request", __func__);
+ return;
+ }
+
+ BUG_ON(!test_rq);
+
+ spin_lock_irq(&ptd->lock);
+ ptd->dispatched_count--;
+ list_del_init(&test_rq->queuelist);
+ __blk_put_request(ptd->req_q, test_rq->rq);
+ spin_unlock_irq(&ptd->lock);
+
+ kfree(test_rq->bios_buffer);
+ kfree(test_rq);
+ utd->completed_req_count++;
+
+ test_pr_err("%s: request %d completed, err=%d",
+ __func__, test_rq->req_id, err);
+
+ check_test_completion();
+
+}
+
+static int run_long_seq_test(struct test_data *td)
+{
+ int ret = 0;
+ int direction;
+ static unsigned int inserted_requests;
+
+ BUG_ON(!td);
+ td->test_count = 0;
+ utd->completed_req_count = 0;
+ inserted_requests = 0;
+
+ if (td->test_info.testcase == TEST_LONG_SEQUENTIAL_READ)
+ direction = READ;
+ else
+ direction = WRITE;
+
+ test_pr_info("%s: Adding %d requests, first req_id=%d",
+ __func__, LONG_SEQ_TEST_NUM_REQS,
+ td->wr_rd_next_req_id);
+
+ do {
+ /*
+ * since our requests come from a pool containing 128
+ * requests, we don't want to exhaust this quantity,
+ * therefore we add up to QUEUE_MAX_REQUESTS (which
+ * includes a safety margin) and then call the mmc layer
+ * to fetch them
+ */
+ if (td->test_count >= QUEUE_MAX_REQUESTS) {
+ blk_run_queue(td->req_q);
+ continue;
+ }
+
+ ret = test_iosched_add_wr_rd_test_req(0, direction,
+ td->start_sector, TEST_MAX_BIOS_PER_REQ,
+ TEST_PATTERN_5A,
+ long_seq_test_free_end_io_fn);
+ if (ret) {
+ test_pr_err("%s: failed to create request" , __func__);
+ break;
+ }
+ inserted_requests++;
+ td->test_info.test_byte_count +=
+ (TEST_MAX_BIOS_PER_REQ * sizeof(unsigned int) *
+ BIO_U32_SIZE);
+
+ } while (inserted_requests < LONG_SEQ_TEST_NUM_REQS);
+
+ /* in this case the queue will not run in the above loop */
+ if (LONG_SEQ_TEST_NUM_REQS < QUEUE_MAX_REQUESTS)
+ blk_run_queue(td->req_q);
+
+ return ret;
+}
+
+
+void long_seq_test_calc_throughput(unsigned long mtime,
+ unsigned long byte_count)
+{
+ unsigned long fraction, integer;
+
+ test_pr_info("%s: time is %lu msec, size is %lu.%lu MiB",
+ __func__, mtime, LONG_TEST_SIZE_INTEGER(byte_count),
+ LONG_TEST_SIZE_FRACTION(byte_count));
+
+ /* we first multiply in order not to lose precision */
+ mtime *= MB_MSEC_RATIO_APPROXIMATION;
+ /* divide values to get a MiB/sec integer value with one
+ digit of precision
+ */
+ fraction = integer = (byte_count * 10) / mtime;
+ integer /= 10;
+ /* and calculate the MiB value fraction */
+ fraction -= integer * 10;
+
+ test_pr_info("%s: Throughput: %lu.%lu MiB/sec\n",
+ __func__, integer, fraction);
+}
+
+static ssize_t long_sequential_read_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ unsigned long mtime, byte_count;
+
+ test_pr_info("%s: -- UFS Long Sequential Read TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ memset(&utd->test_info, 0, sizeof(struct test_info));
+
+ utd->test_info.data = utd;
+ utd->test_info.get_rq_disk_fn = ufs_test_get_rq_disk;
+ utd->test_info.run_test_fn = run_long_seq_test;
+ utd->test_info.get_test_case_str_fn = ufs_test_get_test_case_str;
+ utd->test_info.testcase = TEST_LONG_SEQUENTIAL_READ;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ ret = test_iosched_start_test(&utd->test_info);
+ if (ret)
+ break;
+
+ mtime = ktime_to_ms(utd->test_info.test_duration);
+ byte_count = utd->test_info.test_byte_count;
+
+ long_seq_test_calc_throughput(mtime, byte_count);
+
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ return count;
+}
+
+static ssize_t long_sequential_read_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nufs_long_sequential_read_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test runs the following scenarios\n"
+ "- Long Sequential Read Test: this test measures read "
+ "throughput at the driver level by sequentially reading many "
+ "large requests.\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else
+ return 0;
+}
+
+static bool message_repeat;
+static int test_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ message_repeat = 1;
+ return 0;
+}
+
+const struct file_operations long_sequential_read_test_ops = {
+ .open = test_open,
+ .write = long_sequential_read_test_write,
+ .read = long_sequential_read_test_read,
+};
+
+static ssize_t long_sequential_write_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ unsigned long mtime, byte_count;
+
+ test_pr_info("%s: -- UFS Long Sequential Write TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ memset(&utd->test_info, 0, sizeof(struct test_info));
+
+ utd->test_info.data = utd;
+ utd->test_info.get_rq_disk_fn = ufs_test_get_rq_disk;
+ utd->test_info.get_test_case_str_fn = ufs_test_get_test_case_str;
+ utd->test_info.run_test_fn = run_long_seq_test;
+ utd->test_info.testcase = TEST_LONG_SEQUENTIAL_WRITE;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ utd->test_info.test_byte_count = 0;
+ ret = test_iosched_start_test(&utd->test_info);
+ if (ret)
+ break;
+
+ mtime = ktime_to_ms(utd->test_info.test_duration);
+ byte_count = utd->test_info.test_byte_count;
+
+ long_seq_test_calc_throughput(mtime, byte_count);
+
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ return count;
+}
+
+static ssize_t long_sequential_write_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nufs_long_sequential_write_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test runs the following scenarios\n"
+ "- Long Sequential Write Test: this test measures write "
+ "throughput at the driver level by sequentially writing many "
+ "large requests\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else
+ return 0;
+}
+
+const struct file_operations long_sequential_write_test_ops = {
+ .open = test_open,
+ .write = long_sequential_write_test_write,
+ .read = long_sequential_write_test_read,
+};
+
static void ufs_test_debugfs_cleanup(void)
{
- debugfs_remove(utd->debug.write_read_test);
+ debugfs_remove_recursive(test_iosched_get_debugfs_tests_root());
}
static int ufs_test_debugfs_init(void)
{
struct dentry *utils_root, *tests_root;
+ int ret = 0;
utils_root = test_iosched_get_debugfs_utils_root();
tests_root = test_iosched_get_debugfs_tests_root();
if (!utils_root || !tests_root) {
test_pr_err("%s: Failed to create debugfs root.", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto exit;
}
utd->debug.random_test_seed = debugfs_create_u32("random_test_seed",
@@ -288,21 +585,49 @@
if (!utd->debug.random_test_seed) {
test_pr_err("%s: Could not create debugfs random_test_seed.",
__func__);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto exit;
}
- utd->debug.write_read_test = debugfs_create_file("write_read_test",
+ utd->debug.write_read_test = debugfs_create_file("ufs_write_read_test",
S_IRUGO | S_IWUGO, tests_root,
NULL, &write_read_test_ops);
if (!utd->debug.write_read_test) {
- debugfs_remove(utd->debug.random_test_seed);
- test_pr_err("%s: Could not create debugfs write_read_test.",
- __func__);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto exit_err;
}
- return 0;
+ utd->debug.long_sequential_read_test = debugfs_create_file(
+ "ufs_long_sequential_read_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &long_sequential_read_test_ops);
+
+ if (!utd->debug.long_sequential_read_test) {
+ ret = -ENOMEM;
+ goto exit_err;
+ }
+
+ utd->debug.long_sequential_write_test = debugfs_create_file(
+ "ufs_long_sequential_write_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &long_sequential_write_test_ops);
+
+ if (!utd->debug.long_sequential_write_test) {
+ ret = -ENOMEM;
+ goto exit_err;
+ }
+
+ goto exit;
+
+exit_err:
+ debugfs_remove_recursive(tests_root);
+exit:
+ return ret;
}
static void ufs_test_probe(void)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 5f21c7a..2230f14 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -33,8 +33,16 @@
* this program.
*/
+#include <linux/async.h>
+
#include "ufshcd.h"
+#define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\
+ UTP_TASK_REQ_COMPL |\
+ UFSHCD_ERROR_MASK)
+/* UIC command timeout, unit: ms */
+#define UIC_CMD_TIMEOUT 500
+
enum {
UFSHCD_MAX_CHANNEL = 0,
UFSHCD_MAX_ID = 1,
@@ -64,6 +72,20 @@
};
/**
+ * ufshcd_get_intr_mask - Get the interrupt bit mask
+ * @hba - Pointer to adapter instance
+ *
+ * Returns interrupt bit mask per version
+ */
+static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba)
+{
+ if (hba->ufs_version == UFSHCI_VERSION_10)
+ return INTERRUPT_MASK_ALL_VER_10;
+ else
+ return INTERRUPT_MASK_ALL_VER_11;
+}
+
+/**
* ufshcd_get_ufs_version - Get the UFS version supported by the HBA
* @hba - Pointer to adapter instance
*
@@ -71,7 +93,7 @@
*/
static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
{
- return readl(hba->mmio_base + REG_UFS_VERSION);
+ return ufshcd_readl(hba, REG_UFS_VERSION);
}
/**
@@ -130,8 +152,7 @@
*/
static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos)
{
- writel(~(1 << pos),
- (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_CLEAR));
+ ufshcd_writel(hba, ~(1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR);
}
/**
@@ -165,7 +186,7 @@
*/
static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
{
- return readl(hba->mmio_base + REG_UIC_COMMAND_ARG_2) &
+ return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2) &
MASK_UIC_COMMAND_RESULT;
}
@@ -202,13 +223,18 @@
}
/**
- * ufshcd_get_req_rsp - returns the TR response
+ * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
* @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * This function checks the response UPIU for valid transaction type in
+ * response field
+ * Returns 0 on success, non-zero on failure
*/
static inline int
-ufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
+ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
{
- return be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24;
+ return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
+ UPIU_TRANSACTION_RESPONSE) ? 0 : DID_ERROR << 16;
}
/**
@@ -238,18 +264,15 @@
{
switch (option) {
case INT_AGGR_RESET:
- writel((INT_AGGR_ENABLE |
- INT_AGGR_COUNTER_AND_TIMER_RESET),
- (hba->mmio_base +
- REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+ ufshcd_writel(hba, INT_AGGR_ENABLE |
+ INT_AGGR_COUNTER_AND_TIMER_RESET,
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
break;
case INT_AGGR_CONFIG:
- writel((INT_AGGR_ENABLE |
- INT_AGGR_PARAM_WRITE |
- INT_AGGR_COUNTER_THRESHOLD_VALUE |
- INT_AGGR_TIMEOUT_VALUE),
- (hba->mmio_base +
- REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+ ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
+ INT_AGGR_COUNTER_THRESHOLD_VALUE |
+ INT_AGGR_TIMEOUT_VALUE,
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
break;
}
}
@@ -262,12 +285,10 @@
*/
static void ufshcd_enable_run_stop_reg(struct ufs_hba *hba)
{
- writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
- (hba->mmio_base +
- REG_UTP_TASK_REQ_LIST_RUN_STOP));
- writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
- (hba->mmio_base +
- REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
+ ufshcd_writel(hba, UTP_TASK_REQ_LIST_RUN_STOP_BIT,
+ REG_UTP_TASK_REQ_LIST_RUN_STOP);
+ ufshcd_writel(hba, UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
+ REG_UTP_TRANSFER_REQ_LIST_RUN_STOP);
}
/**
@@ -276,7 +297,7 @@
*/
static inline void ufshcd_hba_start(struct ufs_hba *hba)
{
- writel(CONTROLLER_ENABLE , (hba->mmio_base + REG_CONTROLLER_ENABLE));
+ ufshcd_writel(hba, CONTROLLER_ENABLE, REG_CONTROLLER_ENABLE);
}
/**
@@ -287,7 +308,7 @@
*/
static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
{
- return (readl(hba->mmio_base + REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
+ return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
}
/**
@@ -299,8 +320,7 @@
void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
{
__set_bit(task_tag, &hba->outstanding_reqs);
- writel((1 << task_tag),
- (hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL));
+ ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
}
/**
@@ -311,78 +331,20 @@
{
int len;
if (lrbp->sense_buffer) {
- len = be16_to_cpu(lrbp->ucd_rsp_ptr->sc.sense_data_len);
+ len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
memcpy(lrbp->sense_buffer,
- lrbp->ucd_rsp_ptr->sc.sense_data,
+ lrbp->ucd_rsp_ptr->sense_data,
min_t(int, len, SCSI_SENSE_BUFFERSIZE));
}
}
/**
- * ufshcd_query_to_cpu() - formats the received buffer in to the native cpu
- * endian
- * @response: upiu query response to convert
- */
-static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response)
-{
- response->length = be16_to_cpu(response->length);
- response->value = be32_to_cpu(response->value);
-}
-
-/**
- * ufshcd_query_to_be() - formats the buffer before sending in to big endian
- * @response: upiu query request to convert
- */
-static inline void ufshcd_query_to_be(struct utp_upiu_query *request)
-{
- request->length = cpu_to_be16(request->length);
- request->value = cpu_to_be32(request->value);
-}
-
-/**
- * ufshcd_copy_query_response() - Copy Query Response and descriptor
- * @lrb - pointer to local reference block
- * @query_res - pointer to the query result
- */
-static
-void ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
-{
- struct ufs_query_res *query_res = hba->query.response;
-
- /* Get the UPIU response */
- if (query_res) {
- query_res->response = ufshcd_get_rsp_upiu_result(
- lrbp->ucd_rsp_ptr) >> UPIU_RSP_CODE_OFFSET;
-
- memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr,
- QUERY_OSF_SIZE);
- ufshcd_query_to_cpu(&query_res->upiu_res);
- }
-
- /* Get the descriptor */
- if (hba->query.descriptor && lrbp->ucd_rsp_ptr->qr.opcode ==
- UPIU_QUERY_OPCODE_READ_DESC) {
- u8 *descp = (u8 *)&lrbp->ucd_rsp_ptr +
- GENERAL_UPIU_REQUEST_SIZE;
- u16 len;
-
- /* data segment length */
- len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) &
- MASK_QUERY_DATA_SEG_LEN;
-
- memcpy(hba->query.descriptor, descp,
- min_t(u16, len, UPIU_HEADER_DATA_SEGMENT_MAX_SIZE));
- }
-}
-
-/**
* ufshcd_hba_capabilities - Read controller capabilities
* @hba: per adapter instance
*/
static inline void ufshcd_hba_capabilities(struct ufs_hba *hba)
{
- hba->capabilities =
- readl(hba->mmio_base + REG_CONTROLLER_CAPABILITIES);
+ hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES);
/* nutrs and nutmrs are 0 based values */
hba->nutrs = (hba->capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1;
@@ -391,24 +353,119 @@
}
/**
- * ufshcd_send_uic_command - Send UIC commands to unipro layers
+ * ufshcd_ready_for_uic_cmd - Check if controller is ready
+ * to accept UIC commands
* @hba: per adapter instance
- * @uic_command: UIC command
+ * Return true on success, else false
+ */
+static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
+{
+ if (ufshcd_readl(hba, REG_CONTROLLER_STATUS) & UIC_COMMAND_READY)
+ return true;
+ else
+ return false;
+}
+
+/**
+ * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
+ * @hba: per adapter instance
+ * @uic_cmd: UIC command
+ *
+ * Mutex must be held.
*/
static inline void
-ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
+ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
{
+ WARN_ON(hba->active_uic_cmd);
+
+ hba->active_uic_cmd = uic_cmd;
+
/* Write Args */
- writel(uic_cmnd->argument1,
- (hba->mmio_base + REG_UIC_COMMAND_ARG_1));
- writel(uic_cmnd->argument2,
- (hba->mmio_base + REG_UIC_COMMAND_ARG_2));
- writel(uic_cmnd->argument3,
- (hba->mmio_base + REG_UIC_COMMAND_ARG_3));
+ ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1);
+ ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2);
+ ufshcd_writel(hba, uic_cmd->argument3, REG_UIC_COMMAND_ARG_3);
/* Write UIC Cmd */
- writel((uic_cmnd->command & COMMAND_OPCODE_MASK),
- (hba->mmio_base + REG_UIC_COMMAND));
+ ufshcd_writel(hba, uic_cmd->command & COMMAND_OPCODE_MASK,
+ REG_UIC_COMMAND);
+}
+
+/**
+ * ufshcd_wait_for_uic_cmd - Wait complectioin of UIC command
+ * @hba: per adapter instance
+ * @uic_command: UIC command
+ *
+ * Must be called with mutex held.
+ * Returns 0 only if success.
+ */
+static int
+ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
+{
+ int ret;
+ unsigned long flags;
+
+ if (wait_for_completion_timeout(&uic_cmd->done,
+ msecs_to_jiffies(UIC_CMD_TIMEOUT)))
+ ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT;
+ else
+ ret = -ETIMEDOUT;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->active_uic_cmd = NULL;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ return ret;
+}
+
+/**
+ * __ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
+ * @hba: per adapter instance
+ * @uic_cmd: UIC command
+ *
+ * Identical to ufshcd_send_uic_cmd() expect mutex. Must be called
+ * with mutex held.
+ * Returns 0 only if success.
+ */
+static int
+__ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
+{
+ int ret;
+ unsigned long flags;
+
+ if (!ufshcd_ready_for_uic_cmd(hba)) {
+ dev_err(hba->dev,
+ "Controller not ready to accept UIC commands\n");
+ return -EIO;
+ }
+
+ init_completion(&uic_cmd->done);
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ ufshcd_dispatch_uic_cmd(hba, uic_cmd);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd);
+
+ return ret;
+}
+
+/**
+ * ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
+ * @hba: per adapter instance
+ * @uic_cmd: UIC command
+ *
+ * Returns 0 only if success.
+ */
+static int
+ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
+{
+ int ret;
+
+ mutex_lock(&hba->uic_cmd_mutex);
+ ret = __ufshcd_send_uic_cmd(hba, uic_cmd);
+ mutex_unlock(&hba->uic_cmd_mutex);
+
+ return ret;
}
/**
@@ -452,177 +509,118 @@
}
/**
- * ufshcd_int_config - enable/disable interrupts
+ * ufshcd_enable_intr - enable interrupts
* @hba: per adapter instance
- * @option: interrupt option
+ * @intrs: interrupt bits
*/
-static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
+static void ufshcd_enable_intr(struct ufs_hba *hba, u32 intrs)
{
- switch (option) {
- case UFSHCD_INT_ENABLE:
- writel(hba->int_enable_mask,
- (hba->mmio_base + REG_INTERRUPT_ENABLE));
- break;
- case UFSHCD_INT_DISABLE:
- if (hba->ufs_version == UFSHCI_VERSION_10)
- writel(INTERRUPT_DISABLE_MASK_10,
- (hba->mmio_base + REG_INTERRUPT_ENABLE));
- else
- writel(INTERRUPT_DISABLE_MASK_11,
- (hba->mmio_base + REG_INTERRUPT_ENABLE));
- break;
- }
-}
+ u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
-/**
- * ufshcd_prepare_req_desc - Fills the requests header
- * descriptor according to request
- * lrbp: pointer to local reference block
- * upiu_flags: flags required in the header
- */
-static void ufshcd_prepare_req_desc(struct ufshcd_lrb *lrbp, u32 *upiu_flags)
-{
- struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr;
- enum dma_data_direction cmd_dir =
- lrbp->cmd->sc_data_direction;
- u32 data_direction;
- u32 dword_0;
-
- if (cmd_dir == DMA_FROM_DEVICE) {
- data_direction = UTP_DEVICE_TO_HOST;
- *upiu_flags = UPIU_CMD_FLAGS_READ;
- } else if (cmd_dir == DMA_TO_DEVICE) {
- data_direction = UTP_HOST_TO_DEVICE;
- *upiu_flags = UPIU_CMD_FLAGS_WRITE;
+ if (hba->ufs_version == UFSHCI_VERSION_10) {
+ u32 rw;
+ rw = set & INTERRUPT_MASK_RW_VER_10;
+ set = rw | ((set ^ intrs) & intrs);
} else {
- data_direction = UTP_NO_DATA_TRANSFER;
- *upiu_flags = UPIU_CMD_FLAGS_NONE;
+ set |= intrs;
}
- dword_0 = data_direction | (lrbp->command_type
- << UPIU_COMMAND_TYPE_OFFSET);
-
- /* Transfer request descriptor header fields */
- req_desc->header.dword_0 = cpu_to_le32(dword_0);
-
- /*
- * assigning invalid value for command status. Controller
- * updates OCS on command completion, with the command
- * status
- */
- req_desc->header.dword_2 =
- cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
-}
-
-static inline bool ufshcd_is_query_req(struct ufshcd_lrb *lrbp)
-{
- return lrbp->cmd ? lrbp->cmd->cmnd[0] == UFS_QUERY_RESERVED_SCSI_CMD :
- false;
+ ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE);
}
/**
- * ufshcd_prepare_utp_scsi_cmd_upiu() - fills the utp_transfer_req_desc,
- * for scsi commands
- * @lrbp - local reference block pointer
- * @upiu_flags - flags
+ * ufshcd_disable_intr - disable interrupts
+ * @hba: per adapter instance
+ * @intrs: interrupt bits
*/
-static
-void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u32 upiu_flags)
+static void ufshcd_disable_intr(struct ufs_hba *hba, u32 intrs)
{
- struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+ u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
- /* command descriptor fields */
- ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
- UPIU_TRANSACTION_COMMAND, upiu_flags,
- lrbp->lun, lrbp->task_tag);
- ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
- UPIU_COMMAND_SET_TYPE_SCSI, 0, 0, 0);
+ if (hba->ufs_version == UFSHCI_VERSION_10) {
+ u32 rw;
+ rw = (set & INTERRUPT_MASK_RW_VER_10) &
+ ~(intrs & INTERRUPT_MASK_RW_VER_10);
+ set = rw | ((set & intrs) & ~INTERRUPT_MASK_RW_VER_10);
- /* Total EHS length and Data segment length will be zero */
- ucd_req_ptr->header.dword_2 = 0;
-
- ucd_req_ptr->sc.exp_data_transfer_len =
- cpu_to_be32(lrbp->cmd->sdb.length);
-
- memcpy(ucd_req_ptr->sc.cdb, lrbp->cmd->cmnd,
- (min_t(unsigned short, lrbp->cmd->cmd_len, MAX_CDB_SIZE)));
-}
-
-/**
- * ufshcd_prepare_utp_query_req_upiu() - fills the utp_transfer_req_desc,
- * for query requsts
- * @hba: UFS hba
- * @lrbp: local reference block pointer
- * @upiu_flags: flags
- */
-static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba,
- struct ufshcd_lrb *lrbp,
- u32 upiu_flags)
-{
- struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
- u16 len = hba->query.request->upiu_req.length;
- u8 *descp = (u8 *)lrbp->ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE;
-
- /* Query request header */
- ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
- UPIU_TRANSACTION_QUERY_REQ, upiu_flags,
- lrbp->lun, lrbp->task_tag);
- ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
- 0, hba->query.request->query_func, 0, 0);
-
- /* Data segment length */
- ucd_req_ptr->header.dword_2 = UPIU_HEADER_DWORD(
- 0, 0, len >> 8, (u8)len);
-
- /* Copy the Query Request buffer as is */
- memcpy(&lrbp->ucd_req_ptr->qr, &hba->query.request->upiu_req,
- QUERY_OSF_SIZE);
- ufshcd_query_to_be(&lrbp->ucd_req_ptr->qr);
-
- /* Copy the Descriptor */
- if (hba->query.descriptor != NULL && len > 0 &&
- (hba->query.request->upiu_req.opcode ==
- UPIU_QUERY_OPCODE_WRITE_DESC)) {
- memcpy(descp, hba->query.descriptor,
- min_t(u16, len, UPIU_HEADER_DATA_SEGMENT_MAX_SIZE));
+ } else {
+ set &= ~intrs;
}
+ ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE);
}
/**
* ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
- * @hba - UFS hba
* @lrb - pointer to local reference block
*/
-static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+static void ufshcd_compose_upiu(struct ufshcd_lrb *lrbp)
{
+ struct utp_transfer_req_desc *req_desc;
+ struct utp_upiu_cmd *ucd_cmd_ptr;
+ u32 data_direction;
u32 upiu_flags;
- int ret = 0;
+
+ ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
+ req_desc = lrbp->utr_descriptor_ptr;
switch (lrbp->command_type) {
case UTP_CMD_TYPE_SCSI:
+ if (lrbp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ data_direction = UTP_DEVICE_TO_HOST;
+ upiu_flags = UPIU_CMD_FLAGS_READ;
+ } else if (lrbp->cmd->sc_data_direction == DMA_TO_DEVICE) {
+ data_direction = UTP_HOST_TO_DEVICE;
+ upiu_flags = UPIU_CMD_FLAGS_WRITE;
+ } else {
+ data_direction = UTP_NO_DATA_TRANSFER;
+ upiu_flags = UPIU_CMD_FLAGS_NONE;
+ }
+
+ /* Transfer request descriptor header fields */
+ req_desc->header.dword_0 =
+ cpu_to_le32(data_direction | UTP_SCSI_COMMAND);
+
+ /*
+ * assigning invalid value for command status. Controller
+ * updates OCS on command completion, with the command
+ * status
+ */
+ req_desc->header.dword_2 =
+ cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+
+ /* command descriptor fields */
+ ucd_cmd_ptr->header.dword_0 =
+ cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
+ upiu_flags,
+ lrbp->lun,
+ lrbp->task_tag));
+ ucd_cmd_ptr->header.dword_1 =
+ cpu_to_be32(
+ UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
+ 0,
+ 0,
+ 0));
+
+ /* Total EHS length and Data segment length will be zero */
+ ucd_cmd_ptr->header.dword_2 = 0;
+
+ ucd_cmd_ptr->exp_data_transfer_len =
+ cpu_to_be32(lrbp->cmd->sdb.length);
+
+ memcpy(ucd_cmd_ptr->cdb,
+ lrbp->cmd->cmnd,
+ (min_t(unsigned short,
+ lrbp->cmd->cmd_len,
+ MAX_CDB_SIZE)));
+ break;
case UTP_CMD_TYPE_DEV_MANAGE:
- ufshcd_prepare_req_desc(lrbp, &upiu_flags);
- if (lrbp->command_type == UTP_CMD_TYPE_SCSI)
- ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags);
- else
- ufshcd_prepare_utp_query_req_upiu(hba, lrbp,
- upiu_flags);
+ /* For query function implementation */
break;
case UTP_CMD_TYPE_UFS:
/* For UFS native command implementation */
- dev_err(hba->dev, "%s: UFS native command are not supported\n",
- __func__);
- ret = -ENOTSUPP;
- break;
- default:
- ret = -ENOTSUPP;
- dev_err(hba->dev, "%s: unknown command type: 0x%x\n",
- __func__, lrbp->command_type);
break;
} /* end of switch */
-
- return ret;
}
/**
@@ -657,13 +655,10 @@
lrbp->task_tag = tag;
lrbp->lun = cmd->device->lun;
- if (ufshcd_is_query_req(lrbp))
- lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
- else
- lrbp->command_type = UTP_CMD_TYPE_SCSI;
+ lrbp->command_type = UTP_CMD_TYPE_SCSI;
/* form UPIU before issuing the command */
- ufshcd_compose_upiu(hba, lrbp);
+ ufshcd_compose_upiu(lrbp);
err = ufshcd_map_sg(lrbp);
if (err)
goto out;
@@ -677,109 +672,6 @@
}
/**
- * ufshcd_query_request() - Entry point for issuing query request to a
- * ufs device.
- * @hba: ufs driver context
- * @query: params for query request
- * @descriptor: buffer for sending/receiving descriptor
- * @response: pointer to a buffer that will contain the response code and
- * response upiu
- * @timeout: time limit for the command in seconds
- * @retries: number of times to try executing the command
- *
- * The query request is submitted to the same request queue as the rest of
- * the scsi commands passed to the UFS controller. In order to use this
- * queue, we need to receive a tag, same as all other commands. The tags
- * are issued from the block layer. To simulate a request from the block
- * layer, we use the same interface as the SCSI layer does when it issues
- * commands not generated by users. To distinguish a query request from
- * the SCSI commands, we use a vendor specific unused SCSI command
- * op-code. This op-code is not part of the SCSI command subset used in
- * UFS. In such way it is easy to check the command in the driver and
- * handle it appropriately.
- *
- * All necessary fields for issuing a query and receiving its response are
- * stored in the UFS hba struct. We can use this method since we know
- * there is only one active query request at all times.
- *
- * The request that will pass to the device is stored in "query" argument
- * passed to this function, while the "response" argument (which is output
- * field) will hold the query response from the device along with the
- * response code.
- */
-int ufshcd_query_request(struct ufs_hba *hba,
- struct ufs_query_req *query,
- u8 *descriptor,
- struct ufs_query_res *response,
- int timeout,
- int retries)
-{
- struct scsi_device *sdev;
- u8 cmd[UFS_QUERY_CMD_SIZE] = {0};
- int result;
- bool sdev_lookup = true;
-
- if (!hba || !query || !response) {
- pr_err("%s: NULL pointer hba = %p, query = %p response = %p\n",
- __func__, hba, query, response);
- return -EINVAL;
- }
-
- /*
- * A SCSI command structure is composed from opcode at the
- * begining and 0 at the end.
- */
- cmd[0] = UFS_QUERY_RESERVED_SCSI_CMD;
-
- /* extracting the SCSI Device */
- sdev = scsi_device_lookup(hba->host, 0, 0, 0);
- if (!sdev) {
- /**
- * There are some Query Requests that are sent during device
- * initialization, this happens before the scsi device was
- * initialized. If there is no scsi device, we generate a
- * temporary device to allow the Query Request flow.
- */
- sdev_lookup = false;
- sdev = scsi_get_host_dev(hba->host);
- }
-
- if (!sdev) {
- dev_err(hba->dev, "%s: Could not fetch scsi device\n",
- __func__);
- return -ENODEV;
- }
-
- mutex_lock(&hba->query.lock_ufs_query);
- hba->query.request = query;
- hba->query.descriptor = descriptor;
- hba->query.response = response;
-
- /* wait until request is completed */
- result = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL,
- timeout, retries, 0, NULL);
- if (result) {
- dev_err(hba->dev,
- "%s: Query with opcode 0x%x, failed with result %d\n",
- __func__, query->upiu_req.opcode, result);
- result = -EIO;
- }
-
- hba->query.request = NULL;
- hba->query.descriptor = NULL;
- hba->query.response = NULL;
- mutex_unlock(&hba->query.lock_ufs_query);
-
- /* Releasing scsi device resource */
- if (sdev_lookup)
- scsi_device_put(sdev);
- else
- scsi_free_host_dev(sdev);
-
- return result;
-}
-
-/**
* ufshcd_memory_alloc - allocate memory for host memory space data structures
* @hba: per adapter instance
*
@@ -910,11 +802,11 @@
utrdlp[i].prd_table_offset =
cpu_to_le16((prdt_offset >> 2));
utrdlp[i].response_upiu_length =
- cpu_to_le16(ALIGNED_UPIU_SIZE);
+ cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
- hba->lrb[i].ucd_req_ptr =
- (struct utp_upiu_req *)(cmd_descp + i);
+ hba->lrb[i].ucd_cmd_ptr =
+ (struct utp_upiu_cmd *)(cmd_descp + i);
hba->lrb[i].ucd_rsp_ptr =
(struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
hba->lrb[i].ucd_prdt_ptr =
@@ -935,35 +827,16 @@
*/
static int ufshcd_dme_link_startup(struct ufs_hba *hba)
{
- struct uic_command *uic_cmd;
- unsigned long flags;
+ struct uic_command uic_cmd = {0};
+ int ret;
- /* check if controller is ready to accept UIC commands */
- if (((readl(hba->mmio_base + REG_CONTROLLER_STATUS)) &
- UIC_COMMAND_READY) == 0x0) {
+ uic_cmd.command = UIC_CMD_DME_LINK_STARTUP;
+
+ ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+ if (ret)
dev_err(hba->dev,
- "Controller not ready"
- " to accept UIC commands\n");
- return -EIO;
- }
-
- spin_lock_irqsave(hba->host->host_lock, flags);
-
- /* form UIC command */
- uic_cmd = &hba->active_uic_cmd;
- uic_cmd->command = UIC_CMD_DME_LINK_STARTUP;
- uic_cmd->argument1 = 0;
- uic_cmd->argument2 = 0;
- uic_cmd->argument3 = 0;
-
- /* enable UIC related interrupts */
- hba->int_enable_mask |= UIC_COMMAND_COMPL;
- ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
-
- /* sending UIC commands to controller */
- ufshcd_send_uic_command(hba, uic_cmd);
- spin_unlock_irqrestore(hba->host->host_lock, flags);
- return 0;
+ "dme-link-startup: error code %d\n", ret);
+ return ret;
}
/**
@@ -972,9 +845,10 @@
*
* To bring UFS host controller to operational state,
* 1. Check if device is present
- * 2. Configure run-stop-registers
- * 3. Enable required interrupts
- * 4. Configure interrupt aggregation
+ * 2. Enable required interrupts
+ * 3. Configure interrupt aggregation
+ * 4. Program UTRL and UTMRL base addres
+ * 5. Configure run-stop-registers
*
* Returns 0 on success, non-zero value on failure
*/
@@ -984,13 +858,29 @@
u32 reg;
/* check if device present */
- reg = readl((hba->mmio_base + REG_CONTROLLER_STATUS));
+ reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS);
if (!ufshcd_is_device_present(reg)) {
dev_err(hba->dev, "cc: Device not present\n");
err = -ENXIO;
goto out;
}
+ /* Enable required interrupts */
+ ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
+
+ /* Configure interrupt aggregation */
+ ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+
+ /* Configure UTRL and UTMRL base address registers */
+ ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
+ REG_UTP_TRANSFER_REQ_LIST_BASE_L);
+ ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr),
+ REG_UTP_TRANSFER_REQ_LIST_BASE_H);
+ ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr),
+ REG_UTP_TASK_REQ_LIST_BASE_L);
+ ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr),
+ REG_UTP_TASK_REQ_LIST_BASE_H);
+
/*
* UCRDY, UTMRLDY and UTRLRDY bits must be 1
* DEI, HEI bits must be 0
@@ -1004,23 +894,11 @@
goto out;
}
- /* Enable required interrupts */
- hba->int_enable_mask |= (UTP_TRANSFER_REQ_COMPL |
- UIC_ERROR |
- UTP_TASK_REQ_COMPL |
- DEVICE_FATAL_ERROR |
- CONTROLLER_FATAL_ERROR |
- SYSTEM_BUS_FATAL_ERROR);
- ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
-
- /* Configure interrupt aggregation */
- ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
-
if (hba->ufshcd_state == UFSHCD_STATE_RESET)
scsi_unblock_requests(hba->host);
hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
- scsi_scan_host(hba->host);
+
out:
return err;
}
@@ -1089,34 +967,28 @@
}
/**
- * ufshcd_initialize_hba - start the initialization process
+ * ufshcd_link_startup - Initialize unipro link startup
* @hba: per adapter instance
*
- * 1. Enable the controller via ufshcd_hba_enable.
- * 2. Program the Transfer Request List Address with the starting address of
- * UTRDL.
- * 3. Program the Task Management Request List Address with starting address
- * of UTMRDL.
- *
- * Returns 0 on success, non-zero value on failure.
+ * Returns 0 for success, non-zero in case of failure
*/
-static int ufshcd_initialize_hba(struct ufs_hba *hba)
+static int ufshcd_link_startup(struct ufs_hba *hba)
{
- if (ufshcd_hba_enable(hba))
- return -EIO;
+ int ret;
- /* Configure UTRL and UTMRL base address registers */
- writel(lower_32_bits(hba->utrdl_dma_addr),
- (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_L));
- writel(upper_32_bits(hba->utrdl_dma_addr),
- (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_H));
- writel(lower_32_bits(hba->utmrdl_dma_addr),
- (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_L));
- writel(upper_32_bits(hba->utmrdl_dma_addr),
- (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_H));
+ /* enable UIC related interrupts */
+ ufshcd_enable_intr(hba, UIC_COMMAND_COMPL);
- /* Initialize unipro link startup procedure */
- return ufshcd_dme_link_startup(hba);
+ ret = ufshcd_dme_link_startup(hba);
+ if (ret)
+ goto out;
+
+ ret = ufshcd_make_hba_operational(hba);
+
+out:
+ if (ret)
+ dev_err(hba->dev, "link startup failed %d\n", ret);
+ return ret;
}
/**
@@ -1156,12 +1028,19 @@
hba->outstanding_reqs = 0;
hba->outstanding_tasks = 0;
- /* start the initialization process */
- if (ufshcd_initialize_hba(hba)) {
+ /* Host controller enable */
+ if (ufshcd_hba_enable(hba)) {
dev_err(hba->dev,
"Reset: Controller initialization failed\n");
return FAILED;
}
+
+ if (ufshcd_link_startup(hba)) {
+ dev_err(hba->dev,
+ "Reset: Link start-up failed\n");
+ return FAILED;
+ }
+
return SUCCESS;
}
@@ -1337,9 +1216,7 @@
* @hba: per adapter instance
* @lrb: pointer to local reference block of completed command
*
- * Returns result of the command to notify SCSI midlayer. In
- * case of query request specific result, returns DID_OK, and
- * the error will be handled by the dispatcher.
+ * Returns result of the command to notify SCSI midlayer
*/
static inline int
ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
@@ -1353,46 +1230,27 @@
switch (ocs) {
case OCS_SUCCESS:
+
/* check if the returned transfer response is valid */
- result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
-
- switch (result) {
- case UPIU_TRANSACTION_RESPONSE:
- /*
- * get the response UPIU result to extract
- * the SCSI command status
- */
- result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
-
- /*
- * get the result based on SCSI status response
- * to notify the SCSI midlayer of the command status
- */
- scsi_status = result & MASK_SCSI_STATUS;
- result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
- break;
- case UPIU_TRANSACTION_QUERY_RSP:
- /*
- * Return result = ok, since SCSI layer wouldn't
- * know how to handle errors from query requests.
- * The result is saved with the response so that
- * the ufs_core layer will handle it.
- */
- result = DID_OK << 16;
- ufshcd_copy_query_response(hba, lrbp);
- break;
- case UPIU_TRANSACTION_REJECT_UPIU:
- /* TODO: handle Reject UPIU Response */
- result = DID_ERROR << 16;
+ result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
+ if (result) {
dev_err(hba->dev,
- "Reject UPIU not fully implemented\n");
+ "Invalid response = %x\n", result);
break;
- default:
- result = DID_ERROR << 16;
- dev_err(hba->dev,
- "Unexpected request response code = %x\n",
- result);
}
+
+ /*
+ * get the response UPIU result to extract
+ * the SCSI command status
+ */
+ result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
+
+ /*
+ * get the result based on SCSI status response
+ * to notify the SCSI midlayer of the command status
+ */
+ scsi_status = result & MASK_SCSI_STATUS;
+ result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
break;
case OCS_ABORTED:
result |= DID_ABORT << 16;
@@ -1414,6 +1272,19 @@
}
/**
+ * ufshcd_uic_cmd_compl - handle completion of uic command
+ * @hba: per adapter instance
+ */
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+{
+ if (hba->active_uic_cmd) {
+ hba->active_uic_cmd->argument2 |=
+ ufshcd_get_uic_cmd_result(hba);
+ complete(&hba->active_uic_cmd->done);
+ }
+}
+
+/**
* ufshcd_transfer_req_compl - handle SCSI and query command completion
* @hba: per adapter instance
*/
@@ -1426,8 +1297,7 @@
int index;
lrb = hba->lrb;
- tr_doorbell =
- readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL);
+ tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
for (index = 0; index < hba->nutrs; index++) {
@@ -1454,28 +1324,6 @@
}
/**
- * ufshcd_uic_cc_handler - handle UIC command completion
- * @work: pointer to a work queue structure
- *
- * Returns 0 on success, non-zero value on failure
- */
-static void ufshcd_uic_cc_handler (struct work_struct *work)
-{
- struct ufs_hba *hba;
-
- hba = container_of(work, struct ufs_hba, uic_workq);
-
- if ((hba->active_uic_cmd.command == UIC_CMD_DME_LINK_STARTUP) &&
- !(ufshcd_get_uic_cmd_result(hba))) {
-
- if (ufshcd_make_hba_operational(hba))
- dev_err(hba->dev,
- "cc: hba not operational state\n");
- return;
- }
-}
-
-/**
* ufshcd_fatal_err_handler - handle fatal errors
* @hba: per adapter instance
*/
@@ -1501,9 +1349,7 @@
goto fatal_eh;
if (hba->errors & UIC_ERROR) {
-
- reg = readl(hba->mmio_base +
- REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
+ reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
goto fatal_eh;
}
@@ -1521,7 +1367,7 @@
{
u32 tm_doorbell;
- tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL);
+ tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks;
wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
}
@@ -1538,7 +1384,7 @@
ufshcd_err_handler(hba);
if (intr_status & UIC_COMMAND_COMPL)
- schedule_work(&hba->uic_workq);
+ ufshcd_uic_cmd_compl(hba);
if (intr_status & UTP_TASK_REQ_COMPL)
ufshcd_tmc_handler(hba);
@@ -1562,15 +1408,11 @@
struct ufs_hba *hba = __hba;
spin_lock(hba->host->host_lock);
- intr_status = readl(hba->mmio_base + REG_INTERRUPT_STATUS);
+ intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
if (intr_status) {
+ ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
ufshcd_sl_intr(hba, intr_status);
-
- /* If UFSHCI 1.0 then clear interrupt status register */
- if (hba->ufs_version == UFSHCI_VERSION_10)
- writel(intr_status,
- (hba->mmio_base + REG_INTERRUPT_STATUS));
retval = IRQ_HANDLED;
}
spin_unlock(hba->host->host_lock);
@@ -1621,10 +1463,10 @@
task_req_upiup =
(struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
task_req_upiup->header.dword_0 =
- UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
- lrbp->lun, lrbp->task_tag);
+ cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
+ lrbp->lun, lrbp->task_tag));
task_req_upiup->header.dword_1 =
- UPIU_HEADER_DWORD(0, tm_function, 0, 0);
+ cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
task_req_upiup->input_param1 = lrbp->lun;
task_req_upiup->input_param1 =
@@ -1635,8 +1477,7 @@
/* send command to the controller */
__set_bit(free_slot, &hba->outstanding_tasks);
- writel((1 << free_slot),
- (hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL));
+ ufshcd_writel(hba, 1 << free_slot, REG_UTP_TASK_REQ_DOOR_BELL);
spin_unlock_irqrestore(host->host_lock, flags);
@@ -1766,6 +1607,21 @@
return err;
}
+/**
+ * ufshcd_async_scan - asynchronous execution for link startup
+ * @data: data pointer to pass to this function
+ * @cookie: cookie data
+ */
+static void ufshcd_async_scan(void *data, async_cookie_t cookie)
+{
+ struct ufs_hba *hba = (struct ufs_hba *)data;
+ int ret;
+
+ ret = ufshcd_link_startup(hba);
+ if (!ret)
+ scsi_scan_host(hba->host);
+}
+
static struct scsi_host_template ufshcd_driver_template = {
.module = THIS_MODULE,
.name = UFSHCD,
@@ -1844,7 +1700,7 @@
void ufshcd_remove(struct ufs_hba *hba)
{
/* disable interrupts */
- ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
+ ufshcd_disable_intr(hba, hba->intr_mask);
ufshcd_hba_stop(hba);
ufshcd_hba_free(hba);
@@ -1902,6 +1758,9 @@
/* Get UFS version supported by the controller */
hba->ufs_version = ufshcd_get_ufs_version(hba);
+ /* Get Interrupt bit mask per version */
+ hba->intr_mask = ufshcd_get_intr_mask(hba);
+
/* Allocate memory for host memory space */
err = ufshcd_memory_alloc(hba);
if (err) {
@@ -1924,11 +1783,10 @@
init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
/* Initialize work queues */
- INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
- /* Initialize mutex for query requests */
- mutex_init(&hba->query.lock_ufs_query);
+ /* Initialize UIC command mutex */
+ mutex_init(&hba->uic_cmd_mutex);
/* IRQ registration */
err = request_irq(irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
@@ -1950,14 +1808,17 @@
goto out_free_irq;
}
- /* Initialization routine */
- err = ufshcd_initialize_hba(hba);
+ /* Host controller enable */
+ err = ufshcd_hba_enable(hba);
if (err) {
- dev_err(hba->dev, "Initialization failed\n");
+ dev_err(hba->dev, "Host controller enable failed\n");
goto out_remove_scsi_host;
}
+
*hba_handle = hba;
+ async_schedule(ufshcd_async_scan, hba);
+
return 0;
out_remove_scsi_host:
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 336980b..49590ee 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -51,6 +51,7 @@
#include <linux/bitops.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
+#include <linux/completion.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
@@ -60,7 +61,6 @@
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>
-#include <scsi/scsi_device.h>
#include "ufs.h"
#include "ufshci.h"
@@ -76,6 +76,7 @@
* @argument3: UIC command argument 3
* @cmd_active: Indicate if UIC command is outstanding
* @result: UIC command result
+ * @done: UIC command completion
*/
struct uic_command {
u32 command;
@@ -84,12 +85,13 @@
u32 argument3;
int cmd_active;
int result;
+ struct completion done;
};
/**
* struct ufshcd_lrb - local reference block
* @utr_descriptor_ptr: UTRD address of the command
- * @ucd_req_ptr: UCD address of the command
+ * @ucd_cmd_ptr: UCD address of the command
* @ucd_rsp_ptr: Response UPIU address for this command
* @ucd_prdt_ptr: PRDT address of the command
* @cmd: pointer to SCSI command
@@ -102,7 +104,7 @@
*/
struct ufshcd_lrb {
struct utp_transfer_req_desc *utr_descriptor_ptr;
- struct utp_upiu_req *ucd_req_ptr;
+ struct utp_upiu_cmd *ucd_cmd_ptr;
struct utp_upiu_rsp *ucd_rsp_ptr;
struct ufshcd_sg_entry *ucd_prdt_ptr;
@@ -116,19 +118,6 @@
unsigned int lun;
};
-/**
- * struct ufs_query - keeps the query request information
- * @request: request upiu and function
- * @descriptor: buffer for sending/receiving descriptor
- * @response: response upiu and response
- * @mutex: lock to allow one query at a time
- */
-struct ufs_query {
- struct ufs_query_req *request;
- u8 *descriptor;
- struct ufs_query_res *response;
- struct mutex lock_ufs_query;
-};
/**
* struct ufs_hba - per adapter private structure
@@ -150,14 +139,13 @@
* @ufs_version: UFS Version to which controller complies
* @irq: Irq number of the controller
* @active_uic_cmd: handle of active UIC command
+ * @uic_cmd_mutex: mutex for uic command
* @ufshcd_tm_wait_queue: wait queue for task management
* @tm_condition: condition variable for task management
* @ufshcd_state: UFSHCD states
- * @int_enable_mask: Interrupt Mask Bits
- * @uic_workq: Work queue for UIC completion handling
+ * @intr_mask: Interrupt Mask Bits
* @feh_workq: Work queue for fatal controller error handling
* @errors: HBA errors
- * @query: query request information
*/
struct ufs_hba {
void __iomem *mmio_base;
@@ -186,24 +174,27 @@
u32 ufs_version;
unsigned int irq;
- struct uic_command active_uic_cmd;
+ struct uic_command *active_uic_cmd;
+ struct mutex uic_cmd_mutex;
+
wait_queue_head_t ufshcd_tm_wait_queue;
unsigned long tm_condition;
u32 ufshcd_state;
- u32 int_enable_mask;
+ u32 intr_mask;
/* Work Queues */
- struct work_struct uic_workq;
struct work_struct feh_workq;
/* HBA Errors */
u32 errors;
-
- /* Query Request */
- struct ufs_query query;
};
+#define ufshcd_writel(hba, val, reg) \
+ writel((val), (hba)->mmio_base + (reg))
+#define ufshcd_readl(hba, reg) \
+ readl((hba)->mmio_base + (reg))
+
int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * ,
unsigned int);
void ufshcd_remove(struct ufs_hba *);
@@ -214,7 +205,7 @@
*/
static inline void ufshcd_hba_stop(struct ufs_hba *hba)
{
- writel(CONTROLLER_DISABLE, (hba->mmio_base + REG_CONTROLLER_ENABLE));
+ ufshcd_writel(hba, CONTROLLER_DISABLE, REG_CONTROLLER_ENABLE);
}
#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index 4a86247..d5c5f14 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -39,7 +39,7 @@
enum {
TASK_REQ_UPIU_SIZE_DWORDS = 8,
TASK_RSP_UPIU_SIZE_DWORDS = 8,
- ALIGNED_UPIU_SIZE = 512,
+ ALIGNED_UPIU_SIZE = 128,
};
/* UFSHCI Registers */
@@ -232,10 +232,11 @@
/* Interrupt disable masks */
enum {
/* Interrupt disable mask for UFSHCI v1.0 */
- INTERRUPT_DISABLE_MASK_10 = 0xFFFF,
+ INTERRUPT_MASK_ALL_VER_10 = 0x30FFF,
+ INTERRUPT_MASK_RW_VER_10 = 0x30000,
/* Interrupt disable mask for UFSHCI v1.1 */
- INTERRUPT_DISABLE_MASK_11 = 0x0,
+ INTERRUPT_MASK_ALL_VER_11 = 0x31FFF,
};
/*
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index 4a3ea76..6e7a815 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -30,12 +30,6 @@
#define MSM_SLIM_NAME "msm_slim_ctrl"
#define SLIM_ROOT_FREQ 24576000
-#define QC_MFGID_LSB 0x2
-#define QC_MFGID_MSB 0x17
-#define QC_CHIPID_SL 0x10
-#define QC_DEVID_SAT1 0x3
-#define QC_DEVID_SAT2 0x4
-#define QC_DEVID_PGD 0x5
#define QC_MSM_DEVS 5
/* Manager registers */
@@ -325,35 +319,7 @@
}
pstat = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_ST_EEn, dev->ver));
if (pstat != 0) {
- int i = 0;
- for (i = dev->pipe_b; i < MSM_SLIM_NPORTS; i++) {
- if (pstat & 1 << i) {
- u32 val = readl_relaxed(PGD_PORT(PGD_PORT_STATn,
- i, dev->ver));
- if (val & (1 << 19)) {
- dev->ctrl.ports[i].err =
- SLIM_P_DISCONNECT;
- dev->pipes[i-dev->pipe_b].connected =
- false;
- /*
- * SPS will call completion since
- * ERROR flags are registered
- */
- } else if (val & (1 << 2))
- dev->ctrl.ports[i].err =
- SLIM_P_OVERFLOW;
- else if (val & (1 << 3))
- dev->ctrl.ports[i].err =
- SLIM_P_UNDERFLOW;
- }
- writel_relaxed(1, PGD_THIS_EE(PGD_PORT_INT_CL_EEn,
- dev->ver));
- }
- /*
- * Guarantee that port interrupt bit(s) clearing writes go
- * through before exiting ISR
- */
- mb();
+ return msm_slim_port_irq_handler(dev, pstat);
}
return IRQ_HANDLED;
@@ -446,16 +412,13 @@
if (mc != SLIM_MSG_MC_DISCONNECT_PORT)
dev->err = msm_slim_connect_pipe_port(dev, *puc);
else {
- struct msm_slim_endp *endpoint = &dev->pipes[*puc];
- struct sps_register_event sps_event;
- memset(&sps_event, 0, sizeof(sps_event));
- sps_register_event(endpoint->sps, &sps_event);
- sps_disconnect(endpoint->sps);
/*
* Remove channel disconnects master-side ports from
* channel. No need to send that again on the bus
+ * Only disable port
*/
- dev->pipes[*puc].connected = false;
+ writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn,
+ (*puc + dev->port_b), dev->ver));
mutex_unlock(&dev->tx_lock);
if (msgv >= 0)
msm_slim_put_ctrl(dev);
@@ -468,7 +431,7 @@
msm_slim_put_ctrl(dev);
return dev->err;
}
- *(puc) = *(puc) + dev->pipe_b;
+ *(puc) = *(puc) + dev->port_b;
}
if (txn->mt == SLIM_MSG_MT_CORE &&
mc == SLIM_MSG_MC_BEGIN_RECONFIGURATION)
@@ -1258,7 +1221,8 @@
dev->ctrl.set_laddr = msm_set_laddr;
dev->ctrl.xfer_msg = msm_xfer_msg;
dev->ctrl.wakeup = msm_clk_pause_wakeup;
- dev->ctrl.config_port = msm_config_port;
+ dev->ctrl.alloc_port = msm_alloc_port;
+ dev->ctrl.dealloc_port = msm_dealloc_port;
dev->ctrl.port_xfer = msm_slim_port_xfer;
dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
/* Reserve some messaging BW for satellite-apps driver communication */
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 6962d53..2f19863 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -89,6 +89,7 @@
struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
u32 stat = readl_relaxed(ngd + NGD_INT_STAT);
+ u32 pstat;
if (stat & NGD_INT_TX_MSG_SENT) {
writel_relaxed(NGD_INT_TX_MSG_SENT, ngd + NGD_INT_CLR);
@@ -147,6 +148,10 @@
mb();
dev_err(dev->dev, "NGD IE VE change");
}
+
+ pstat = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_ST_EEn, dev->ver));
+ if (pstat != 0)
+ return msm_slim_port_irq_handler(dev, pstat);
return IRQ_HANDLED;
}
@@ -301,8 +306,26 @@
txn->mc = SLIM_USR_MC_CONNECT_SINK;
else if (txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)
txn->mc = SLIM_USR_MC_DISCONNECT_PORT;
- if (txn->la == SLIM_LA_MGR)
+ if (txn->la == SLIM_LA_MGR) {
+ if (dev->pgdla == SLIM_LA_MGR) {
+ u8 ea[] = {0, QC_DEVID_PGD, 0, 0, QC_MFGID_MSB,
+ QC_MFGID_LSB};
+ ea[2] = (u8)(dev->pdata.eapc & 0xFF);
+ ea[3] = (u8)((dev->pdata.eapc & 0xFF00) >> 8);
+ mutex_unlock(&dev->tx_lock);
+ ret = dev->ctrl.get_laddr(&dev->ctrl, ea, 6,
+ &dev->pgdla);
+ pr_debug("SLIM PGD LA:0x%x, ret:%d", dev->pgdla,
+ ret);
+ if (ret) {
+ pr_err("Incorrect SLIM-PGD EAPC:0x%x",
+ dev->pdata.eapc);
+ return ret;
+ }
+ mutex_lock(&dev->tx_lock);
+ }
txn->la = dev->pgdla;
+ }
wbuf[i++] = txn->la;
la = SLIM_LA_MGR;
wbuf[i++] = txn->wbuf[0];
@@ -357,19 +380,16 @@
txn->mc == SLIM_USR_MC_CONNECT_SINK ||
txn->mc == SLIM_USR_MC_DISCONNECT_PORT) && txn->wbuf &&
wbuf[0] == dev->pgdla) {
- if (txn->mc != SLIM_MSG_MC_DISCONNECT_PORT)
+ if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT)
dev->err = msm_slim_connect_pipe_port(dev, wbuf[1]);
else {
- struct msm_slim_endp *endpoint = &dev->pipes[wbuf[1]];
- struct sps_register_event sps_event;
- memset(&sps_event, 0, sizeof(sps_event));
- sps_register_event(endpoint->sps, &sps_event);
- sps_disconnect(endpoint->sps);
/*
* Remove channel disconnects master-side ports from
* channel. No need to send that again on the bus
+ * Only disable port
*/
- dev->pipes[wbuf[1]].connected = false;
+ writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn,
+ (wbuf[1] + dev->port_b), dev->ver));
mutex_unlock(&dev->tx_lock);
msm_slim_put_ctrl(dev);
return 0;
@@ -378,6 +398,8 @@
dev_err(dev->dev, "pipe-port connect err:%d", dev->err);
goto ngd_xfer_err;
}
+ /* Add port-base to port number if this is manager side port */
+ puc[1] += dev->port_b;
}
dev->err = 0;
dev->wr_comp = &tx_sent;
@@ -1068,9 +1090,18 @@
}
rxreg_access = of_property_read_bool(pdev->dev.of_node,
"qcom,rxreg-access");
+ of_property_read_u32(pdev->dev.of_node, "qcom,apps-ch-pipes",
+ &dev->pdata.apps_pipes);
+ of_property_read_u32(pdev->dev.of_node, "qcom,ea-pc",
+ &dev->pdata.eapc);
} else {
dev->ctrl.nr = pdev->id;
}
+ /*
+ * Keep PGD's logical address as manager's. Query it when first data
+ * channel request comes in
+ */
+ dev->pgdla = SLIM_LA_MGR;
dev->ctrl.nchans = MSM_SLIM_NCHANS;
dev->ctrl.nports = MSM_SLIM_NPORTS;
dev->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
@@ -1083,7 +1114,8 @@
dev->ctrl.allocbw = ngd_allocbw;
dev->ctrl.xfer_msg = ngd_xfer_msg;
dev->ctrl.wakeup = ngd_clk_pause_wakeup;
- dev->ctrl.config_port = msm_config_port;
+ dev->ctrl.alloc_port = msm_alloc_port;
+ dev->ctrl.dealloc_port = msm_dealloc_port;
dev->ctrl.port_xfer = msm_slim_port_xfer;
dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
dev->bam_mem = bam_mem;
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 0166196..37bc883 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -73,6 +73,52 @@
#endif
}
+irqreturn_t msm_slim_port_irq_handler(struct msm_slim_ctrl *dev, u32 pstat)
+{
+ int i;
+ u32 int_en = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+ dev->ver));
+ /*
+ * different port-interrupt than what we enabled, ignore.
+ * This may happen if overflow/underflow is reported, but
+ * was disabled due to unavailability of buffers provided by
+ * client.
+ */
+ if ((pstat & int_en) == 0)
+ return IRQ_HANDLED;
+ for (i = dev->port_b; i < MSM_SLIM_NPORTS; i++) {
+ if (pstat & (1 << i)) {
+ u32 val = readl_relaxed(PGD_PORT(PGD_PORT_STATn,
+ i, dev->ver));
+ if (val & MSM_PORT_OVERFLOW) {
+ dev->ctrl.ports[i-dev->port_b].err =
+ SLIM_P_OVERFLOW;
+ } else if (val & MSM_PORT_UNDERFLOW) {
+ dev->ctrl.ports[i-dev->port_b].err =
+ SLIM_P_UNDERFLOW;
+ }
+ }
+ }
+ /*
+ * Disable port interrupt here. Re-enable when more
+ * buffers are provided for this port.
+ */
+ writel_relaxed((int_en & (~pstat)),
+ PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+ dev->ver));
+ /* clear port interrupts */
+ writel_relaxed(pstat, PGD_THIS_EE(PGD_PORT_INT_CL_EEn,
+ dev->ver));
+ pr_info("disabled overflow/underflow for port 0x%x", pstat);
+
+ /*
+ * Guarantee that port interrupt bit(s) clearing writes go
+ * through before exiting ISR
+ */
+ mb();
+ return IRQ_HANDLED;
+}
+
int msm_slim_init_endpoint(struct msm_slim_ctrl *dev, struct msm_slim_endp *ep)
{
int ret;
@@ -138,17 +184,27 @@
void msm_hw_set_port(struct msm_slim_ctrl *dev, u8 pn)
{
u32 set_cfg = DEF_WATERMARK | DEF_ALIGN | DEF_PACK | ENABLE_PORT;
- u32 int_port = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
- dev->ver));
writel_relaxed(set_cfg, PGD_PORT(PGD_PORT_CFGn, pn, dev->ver));
writel_relaxed(DEF_BLKSZ, PGD_PORT(PGD_PORT_BLKn, pn, dev->ver));
writel_relaxed(DEF_TRANSZ, PGD_PORT(PGD_PORT_TRANn, pn, dev->ver));
- writel_relaxed((int_port | 1 << pn) , PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
- dev->ver));
/* Make sure that port registers are updated before returning */
mb();
}
+static void msm_slim_disconn_pipe_port(struct msm_slim_ctrl *dev, u8 pn)
+{
+ struct msm_slim_endp *endpoint = &dev->pipes[pn];
+ struct sps_register_event sps_event;
+ writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn, (pn + dev->port_b),
+ dev->ver));
+ /* Make sure port register is updated */
+ mb();
+ memset(&sps_event, 0, sizeof(sps_event));
+ sps_register_event(endpoint->sps, &sps_event);
+ sps_disconnect(endpoint->sps);
+ dev->pipes[pn].connected = false;
+}
+
int msm_slim_connect_pipe_port(struct msm_slim_ctrl *dev, u8 pn)
{
struct msm_slim_endp *endpoint = &dev->pipes[pn];
@@ -162,16 +218,26 @@
cfg->options = SPS_O_DESC_DONE | SPS_O_ERROR |
SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
- if (dev->pipes[pn].connected) {
- ret = sps_set_config(dev->pipes[pn].sps, cfg);
- if (ret) {
- dev_err(dev->dev, "sps pipe-port set config erro:%x\n",
- ret);
- return ret;
+ if (dev->pipes[pn].connected &&
+ dev->ctrl.ports[pn].state == SLIM_P_CFG) {
+ return -EISCONN;
+ } else if (dev->pipes[pn].connected) {
+ writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn, (pn + dev->port_b),
+ dev->ver));
+ /* Make sure port disabling goes through */
+ mb();
+ /* Is pipe already connected in desired direction */
+ if ((dev->ctrl.ports[pn].flow == SLIM_SRC &&
+ cfg->mode == SPS_MODE_DEST) ||
+ (dev->ctrl.ports[pn].flow == SLIM_SINK &&
+ cfg->mode == SPS_MODE_SRC)) {
+ msm_hw_set_port(dev, pn + dev->port_b);
+ return 0;
}
+ msm_slim_disconn_pipe_port(dev, pn);
}
- stat = readl_relaxed(PGD_PORT(PGD_PORT_STATn, (pn + dev->pipe_b),
+ stat = readl_relaxed(PGD_PORT(PGD_PORT_STATn, (pn + dev->port_b),
dev->ver));
if (dev->ctrl.ports[pn].flow == SLIM_SRC) {
cfg->destination = dev->bam.hdl;
@@ -191,17 +257,21 @@
cfg->mode = SPS_MODE_SRC;
}
/* Space for desciptor FIFOs */
- cfg->desc.size = MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec);
- cfg->config = SPS_CONFIG_DEFAULT;
- ret = sps_connect(dev->pipes[pn].sps, cfg);
+ ret = msm_slim_sps_mem_alloc(dev, &cfg->desc,
+ MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec));
+ if (ret)
+ pr_err("mem alloc for descr failed:%d", ret);
+ else
+ ret = sps_connect(dev->pipes[pn].sps, cfg);
+
if (!ret) {
dev->pipes[pn].connected = true;
- msm_hw_set_port(dev, pn + dev->pipe_b);
+ msm_hw_set_port(dev, pn + dev->port_b);
}
return ret;
}
-int msm_config_port(struct slim_controller *ctrl, u8 pn)
+int msm_alloc_port(struct slim_controller *ctrl, u8 pn)
{
struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
struct msm_slim_endp *endpoint;
@@ -209,7 +279,7 @@
if (ctrl->ports[pn].req == SLIM_REQ_HALF_DUP ||
ctrl->ports[pn].req == SLIM_REQ_MULTI_CH)
return -EPROTONOSUPPORT;
- if (pn >= (MSM_SLIM_NPORTS - dev->pipe_b))
+ if (pn >= (MSM_SLIM_NPORTS - dev->port_b))
return -ENODEV;
endpoint = &dev->pipes[pn];
@@ -218,6 +288,22 @@
return ret;
}
+void msm_dealloc_port(struct slim_controller *ctrl, u8 pn)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ struct msm_slim_endp *endpoint;
+ if (pn >= (MSM_SLIM_NPORTS - dev->port_b))
+ return;
+ endpoint = &dev->pipes[pn];
+ if (dev->pipes[pn].connected)
+ msm_slim_disconn_pipe_port(dev, pn);
+ if (endpoint->sps) {
+ struct sps_connect *config = &endpoint->config;
+ msm_slim_free_endpoint(endpoint);
+ msm_slim_sps_mem_free(dev, &config->desc);
+ }
+}
+
enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
u8 pn, u8 **done_buf, u32 *done_len)
{
@@ -241,6 +327,25 @@
return SLIM_P_INPROGRESS;
}
+static void msm_slim_port_cb(struct sps_event_notify *ev)
+{
+
+ struct completion *comp = ev->data.transfer.user;
+ struct sps_iovec *iovec = &ev->data.transfer.iovec;
+
+ if (ev->event_id == SPS_EVENT_DESC_DONE) {
+
+ pr_debug("desc done iovec = (0x%x 0x%x 0x%x)\n",
+ iovec->addr, iovec->size, iovec->flags);
+
+ } else {
+ pr_err("%s: ERR event %d\n",
+ __func__, ev->event_id);
+ }
+ if (comp)
+ complete(comp);
+}
+
int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, u8 *iobuf,
u32 len, struct completion *comp)
{
@@ -251,20 +356,29 @@
return -ENODEV;
- ctrl->ports[pn].xcomp = comp;
sreg.options = (SPS_EVENT_DESC_DONE|SPS_EVENT_ERROR);
sreg.mode = SPS_TRIGGER_WAIT;
- sreg.xfer_done = comp;
- sreg.callback = NULL;
- sreg.user = &ctrl->ports[pn];
+ sreg.xfer_done = NULL;
+ sreg.callback = msm_slim_port_cb;
+ sreg.user = NULL;
ret = sps_register_event(dev->pipes[pn].sps, &sreg);
if (ret) {
dev_dbg(dev->dev, "sps register event error:%x\n", ret);
return ret;
}
- ret = sps_transfer_one(dev->pipes[pn].sps, (u32)iobuf, len, NULL,
+ ret = sps_transfer_one(dev->pipes[pn].sps, (u32)iobuf, len, comp,
SPS_IOVEC_FLAG_INT);
dev_dbg(dev->dev, "sps submit xfer error code:%x\n", ret);
+ if (!ret) {
+ /* Enable port interrupts */
+ u32 int_port = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+ dev->ver));
+ if (!(int_port & (1 << (dev->port_b + pn))))
+ writel_relaxed((int_port | (1 << (dev->port_b + pn))),
+ PGD_THIS_EE(PGD_PORT_INT_EN_EEn, dev->ver));
+ /* Make sure that port registers are updated before returning */
+ mb();
+ }
return ret;
}
@@ -674,13 +788,17 @@
bam_props.options = SPS_O_DESC_DONE | SPS_O_ERROR |
SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+ /* override apps channel pipes if specified in platform-data or DT */
+ if (dev->pdata.apps_pipes)
+ sec_props.ees[dev->ee].pipe_mask = dev->pdata.apps_pipes;
+
/* First 7 bits are for message Qs */
for (i = 7; i < 32; i++) {
/* Check what pipes are owned by Apps. */
if ((sec_props.ees[dev->ee].pipe_mask >> i) & 0x1)
break;
}
- dev->pipe_b = i - 7;
+ dev->port_b = i - 7;
/* Register the BAM device with the SPS driver */
ret = sps_register_bam_device(&bam_props, &bam_handle);
@@ -750,6 +868,12 @@
if (dev->use_tx_msgqs >= MSM_MSGQ_ENABLED)
msm_slim_remove_ep(dev, &dev->tx_msgq, &dev->use_tx_msgqs);
if (dereg) {
+ int i;
+ for (i = dev->port_b; i < MSM_SLIM_NPORTS; i++) {
+ if (dev->pipes[i - dev->port_b].connected)
+ msm_dealloc_port(&dev->ctrl,
+ i - dev->port_b);
+ }
sps_deregister_bam_device(dev->bam.hdl);
dev->bam.hdl = 0L;
}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index f8f625e..b5c41ed 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -13,6 +13,7 @@
#ifndef _SLIM_MSM_H
#define _SLIM_MSM_H
+#include <linux/irq.h>
#include <linux/kthread.h>
#include <mach/msm_qmi_interface.h>
@@ -49,6 +50,13 @@
#define MSM_SLIM_NPORTS 24
#define MSM_SLIM_NCHANS 32
+#define QC_MFGID_LSB 0x2
+#define QC_MFGID_MSB 0x17
+#define QC_CHIPID_SL 0x10
+#define QC_DEVID_SAT1 0x3
+#define QC_DEVID_SAT2 0x4
+#define QC_DEVID_PGD 0x5
+
#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \
((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16))
@@ -152,6 +160,12 @@
PGD_VE_STAT_V1 = 0x1710,
};
+enum msm_slim_port_status {
+ MSM_PORT_OVERFLOW = 1 << 2,
+ MSM_PORT_UNDERFLOW = 1 << 3,
+ MSM_PORT_DISCONNECT = 1 << 19,
+};
+
enum msm_ctrl_state {
MSM_CTRL_AWAKE,
MSM_CTRL_SLEEPING,
@@ -177,7 +191,6 @@
struct sps_connect config;
struct sps_register_event event;
struct sps_mem_buffer buf;
- struct completion *xcomp;
bool connected;
};
@@ -192,6 +205,11 @@
struct work_struct ssr_up;
};
+struct msm_slim_pdata {
+ u32 apps_pipes;
+ u32 eapc;
+};
+
struct msm_slim_ctrl {
struct slim_controller ctrl;
struct slim_framer framer;
@@ -224,7 +242,7 @@
u8 pgdla;
enum msm_slim_msgq use_rx_msgqs;
enum msm_slim_msgq use_tx_msgqs;
- int pipe_b;
+ int port_b;
struct completion reconf;
bool reconf_busy;
bool chan_active;
@@ -234,6 +252,7 @@
u32 ver;
struct work_struct slave_notify;
struct msm_slim_qmi qmi;
+ struct msm_slim_pdata pdata;
};
struct msm_sat_chan {
@@ -271,11 +290,13 @@
int msm_slim_rx_dequeue(struct msm_slim_ctrl *dev, u8 *buf);
int msm_slim_get_ctrl(struct msm_slim_ctrl *dev);
void msm_slim_put_ctrl(struct msm_slim_ctrl *dev);
+irqreturn_t msm_slim_port_irq_handler(struct msm_slim_ctrl *dev, u32 pstat);
int msm_slim_init_endpoint(struct msm_slim_ctrl *dev, struct msm_slim_endp *ep);
void msm_slim_free_endpoint(struct msm_slim_endp *ep);
void msm_hw_set_port(struct msm_slim_ctrl *dev, u8 pn);
+int msm_alloc_port(struct slim_controller *ctrl, u8 pn);
+void msm_dealloc_port(struct slim_controller *ctrl, u8 pn);
int msm_slim_connect_pipe_port(struct msm_slim_ctrl *dev, u8 pn);
-int msm_config_port(struct slim_controller *ctrl, u8 pn);
enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
u8 pn, u8 **done_buf, u32 *done_len);
int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, u8 *iobuf,
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index e9f056e..201470f 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1089,8 +1089,11 @@
}
break;
}
- if (i >= ctrl->nports)
+ if (i >= ctrl->nports) {
ret = -EDQUOT;
+ goto alloc_err;
+ }
+ ret = 0;
for (j = i; j < i + nphysp; j++) {
ctrl->ports[j].state = SLIM_P_UNCFG;
ctrl->ports[j].req = req;
@@ -1098,7 +1101,8 @@
ctrl->ports[j].flow = SLIM_SINK;
else
ctrl->ports[j].flow = SLIM_SRC;
- ret = ctrl->config_port(ctrl, j);
+ if (ctrl->alloc_port)
+ ret = ctrl->alloc_port(ctrl, j);
if (ret) {
for (; j >= i; j--)
ctrl->ports[j].state = SLIM_P_FREE;
@@ -1126,17 +1130,26 @@
for (i = 0; i < nports; i++) {
u8 pn;
pn = SLIM_HDL_TO_PORT(hdl[i]);
- if (ctrl->ports[pn].state == SLIM_P_CFG) {
- int j;
- dev_err(&ctrl->dev, "Can't dealloc connected port:%d",
- i);
+
+ if (pn >= ctrl->nports || ctrl->ports[pn].state == SLIM_P_CFG) {
+ int j, ret;
+ if (pn >= ctrl->nports) {
+ dev_err(&ctrl->dev, "invalid port number");
+ ret = -EINVAL;
+ } else {
+ dev_err(&ctrl->dev,
+ "Can't dealloc connected port:%d", i);
+ ret = -EISCONN;
+ }
for (j = i - 1; j >= 0; j--) {
pn = SLIM_HDL_TO_PORT(hdl[j]);
ctrl->ports[pn].state = SLIM_P_UNCFG;
}
mutex_unlock(&ctrl->m_ctrl);
- return -EISCONN;
+ return ret;
}
+ if (ctrl->dealloc_port)
+ ctrl->dealloc_port(ctrl, pn);
ctrl->ports[pn].state = SLIM_P_FREE;
}
mutex_unlock(&ctrl->m_ctrl);
@@ -1215,6 +1228,8 @@
* Channel specified in chanh needs to be allocated first.
* Returns -EALREADY if source is already configured for this channel.
* Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid direction is specified for non-manager port,
+ * or if the manager side port number is out of bounds, or in incorrect state
*/
int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh)
{
@@ -1223,12 +1238,23 @@
u8 chan = SLIM_HDL_TO_CHIDX(chanh);
struct slim_ich *slc = &ctrl->chans[chan];
enum slim_port_flow flow = SLIM_HDL_TO_FLOW(srch);
+ u8 la = SLIM_HDL_TO_LA(srch);
- if (flow != SLIM_SRC)
+ /* manager ports don't have direction when they are allocated */
+ if (la != SLIM_LA_MANAGER && flow != SLIM_SRC)
return -EINVAL;
mutex_lock(&ctrl->sched.m_reconf);
+ if (la == SLIM_LA_MANAGER) {
+ u8 pn = SLIM_HDL_TO_PORT(srch);
+ if (pn >= ctrl->nports ||
+ ctrl->ports[pn].state != SLIM_P_UNCFG) {
+ ret = -EINVAL;
+ goto connect_src_err;
+ }
+ }
+
if (slc->state == SLIM_CH_FREE) {
ret = -ENOTCONN;
goto connect_src_err;
@@ -1264,6 +1290,9 @@
* Channel specified in chanh needs to be allocated first.
* Returns -EALREADY if sink is already configured for this channel.
* Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid parameters are passed, or invalid direction is
+ * specified for non-manager port, or if the manager side port number is out of
+ * bounds, or in incorrect state
*/
int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink, u16 chanh)
{
@@ -1290,8 +1319,14 @@
for (j = 0; j < nsink; j++) {
enum slim_port_flow flow = SLIM_HDL_TO_FLOW(sinkh[j]);
- if (flow != SLIM_SINK)
+ u8 la = SLIM_HDL_TO_LA(sinkh[j]);
+ u8 pn = SLIM_HDL_TO_PORT(sinkh[j]);
+ if (la != SLIM_LA_MANAGER && flow != SLIM_SINK)
ret = -EINVAL;
+ else if (la == SLIM_LA_MANAGER &&
+ (pn >= ctrl->nports ||
+ ctrl->ports[pn].state != SLIM_P_UNCFG))
+ ret = -EINVAL;
else
ret = connect_port_ch(ctrl, chan, sinkh[j], SLIM_SINK);
if (ret) {
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 53aa475..c7db832 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -720,6 +720,73 @@
}
}
+static int msm_spi_bam_pipe_disconnect(struct msm_spi *dd,
+ struct msm_spi_bam_pipe *pipe)
+{
+ int ret = sps_disconnect(pipe->handle);
+ if (ret) {
+ dev_dbg(dd->dev, "%s disconnect bam %s pipe failed\n",
+ __func__, pipe->name);
+ return ret;
+ }
+ return 0;
+}
+
+static int msm_spi_bam_pipe_connect(struct msm_spi *dd,
+ struct msm_spi_bam_pipe *pipe, struct sps_connect *config)
+{
+ int ret;
+ struct sps_register_event event = {
+ .mode = SPS_TRIGGER_WAIT,
+ .options = SPS_O_EOT,
+ .xfer_done = &dd->transfer_complete,
+ };
+
+ ret = sps_connect(pipe->handle, config);
+ if (ret) {
+ dev_err(dd->dev, "%s: sps_connect(%s:0x%p):%d",
+ __func__, pipe->name, pipe->handle, ret);
+ return ret;
+ }
+
+ ret = sps_register_event(pipe->handle, &event);
+ if (ret) {
+ dev_err(dd->dev, "%s sps_register_event(hndl:0x%p %s):%d",
+ __func__, pipe->handle, pipe->name, ret);
+ msm_spi_bam_pipe_disconnect(dd, pipe);
+ return ret;
+ }
+
+ pipe->teardown_required = true;
+ return 0;
+}
+
+
+static void msm_spi_bam_pipe_flush(struct msm_spi *dd,
+ enum msm_spi_pipe_direction pipe_dir)
+{
+ struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
+ (&dd->bam.prod) : (&dd->bam.cons);
+ struct sps_connect config = pipe->config;
+ int ret;
+
+ ret = msm_spi_bam_pipe_disconnect(dd, pipe);
+ if (ret)
+ return;
+
+ ret = msm_spi_bam_pipe_connect(dd, pipe, &config);
+ if (ret)
+ return;
+}
+
+static void msm_spi_bam_flush(struct msm_spi *dd)
+{
+ dev_dbg(dd->dev, "%s flushing bam for recovery\n" , __func__);
+
+ msm_spi_bam_pipe_flush(dd, SPI_BAM_CONSUMER_PIPE);
+ msm_spi_bam_pipe_flush(dd, SPI_BAM_PRODUCER_PIPE);
+}
+
/**
* msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes
* using BAM.
@@ -1681,6 +1748,8 @@
msm_dmov_flush(dd->tx_dma_chan, 1);
msm_dmov_flush(dd->rx_dma_chan, 1);
}
+ if (dd->mode == SPI_BAM_MODE)
+ msm_spi_bam_flush(dd);
break;
}
} while (msm_spi_dm_send_next(dd));
@@ -2312,7 +2381,7 @@
if (!pipe->teardown_required)
return;
- sps_disconnect(pipe->handle);
+ msm_spi_bam_pipe_disconnect(dd, pipe);
dma_free_coherent(dd->dev, pipe->config.desc.size,
pipe->config.desc.base, pipe->config.desc.phys_base);
sps_free_endpoint(pipe->handle);
@@ -2325,13 +2394,13 @@
{
int rc = 0;
struct sps_pipe *pipe_handle;
- struct sps_register_event event = {0};
struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
(&dd->bam.prod) : (&dd->bam.cons);
struct sps_connect *pipe_conf = &pipe->config;
+ pipe->name = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ? "cons" : "prod";
pipe->handle = 0;
- pipe_handle = sps_alloc_endpoint();
+ pipe_handle = sps_alloc_endpoint();
if (!pipe_handle) {
dev_err(dd->dev, "%s: Failed to allocate BAM endpoint\n"
, __func__);
@@ -2373,32 +2442,16 @@
rc = -ENOMEM;
goto config_err;
}
-
+ /* zero descriptor FIFO for convenient debugging of first descs */
memset(pipe_conf->desc.base, 0x00, pipe_conf->desc.size);
- rc = sps_connect(pipe_handle, pipe_conf);
- if (rc) {
- dev_err(dd->dev, "%s: Failed to connect BAM pipe", __func__);
- goto connect_err;
- }
-
- event.mode = SPS_TRIGGER_WAIT;
- event.options = SPS_O_EOT;
- event.xfer_done = &dd->transfer_complete;
- event.user = (void *)dd;
- rc = sps_register_event(pipe_handle, &event);
- if (rc) {
- dev_err(dd->dev, "%s: Failed to register BAM EOT event",
- __func__);
- goto register_err;
- }
-
pipe->handle = pipe_handle;
- pipe->teardown_required = true;
+ rc = msm_spi_bam_pipe_connect(dd, pipe, pipe_conf);
+ if (rc)
+ goto connect_err;
+
return 0;
-register_err:
- sps_disconnect(pipe_handle);
connect_err:
dma_free_coherent(dd->dev, pipe_conf->desc.size,
pipe_conf->desc.base, pipe_conf->desc.phys_base);
diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h
index 90d7481..2a67a61 100644
--- a/drivers/spi/spi_qsd.h
+++ b/drivers/spi/spi_qsd.h
@@ -277,6 +277,7 @@
};
struct msm_spi_bam_pipe {
+ const char *name;
struct sps_pipe *handle;
struct sps_connect config;
bool teardown_required;
diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c
index 082c9ff..eedb1e5 100644
--- a/drivers/spmi/qpnp-int.c
+++ b/drivers/spmi/qpnp-int.c
@@ -578,11 +578,14 @@
unsigned long hwirq, busno;
int irq;
+ if (!spec || !spmi_ctrl)
+ return -EINVAL;
+
pr_debug("spec slave = %u per = %u irq = %u\n",
spec->slave, spec->per, spec->irq);
busno = spmi_ctrl->nr;
- if (!spec || !spmi_ctrl || busno >= QPNPINT_MAX_BUSSES)
+ if (busno >= QPNPINT_MAX_BUSSES)
return -EINVAL;
hwirq = qpnpint_encode_hwirq(spec);
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index bcec934..97d412d 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -84,8 +84,6 @@
source "drivers/staging/zcache/Kconfig"
-source "drivers/staging/qcache/Kconfig"
-
source "drivers/staging/zsmalloc/Kconfig"
source "drivers/staging/wlags49_h2/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index c31f2ec..ffe7d44 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -34,7 +34,6 @@
obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_ZRAM) += zram/
obj-$(CONFIG_ZCACHE) += zcache/
-obj-$(CONFIG_QCACHE) += qcache/
obj-$(CONFIG_ZSMALLOC) += zsmalloc/
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
diff --git a/drivers/staging/qcache/Kconfig b/drivers/staging/qcache/Kconfig
deleted file mode 100644
index 389341c..0000000
--- a/drivers/staging/qcache/Kconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-config QCACHE
- tristate "Dynamic compression of clean pagecache pages"
- depends on CLEANCACHE
- select LZO_COMPRESS
- select LZO_DECOMPRESS
- default n
- help
- Qcache is the backend for fmem
diff --git a/drivers/staging/qcache/Makefile b/drivers/staging/qcache/Makefile
deleted file mode 100644
index 4fdf05c..0000000
--- a/drivers/staging/qcache/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-qcache-y := qcache-main.o tmem.o fmem.o
-
-obj-$(CONFIG_QCACHE) += qcache.o
diff --git a/drivers/staging/qcache/fmem.c b/drivers/staging/qcache/fmem.c
deleted file mode 100644
index 0609f4a..0000000
--- a/drivers/staging/qcache/fmem.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- *
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * 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/export.h>
-#include <linux/fmem.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#ifdef CONFIG_MEMORY_HOTPLUG
-#include <linux/memory.h>
-#include <linux/memory_hotplug.h>
-#endif
-#include "tmem.h"
-#include <asm/mach/map.h>
-
-struct fmem_data fmem_data;
-enum fmem_state fmem_state;
-static spinlock_t fmem_state_lock;
-
-#ifdef CONFIG_MEMORY_HOTPLUG
-static unsigned int section_powered_off[NR_MEM_SECTIONS];
-static unsigned int fmem_section_start, fmem_section_end;
-#endif
-
-void *fmem_map_virtual_area(int cacheability)
-{
- unsigned long addr;
- const struct mem_type *type;
- int ret;
-
- addr = (unsigned long) fmem_data.area->addr;
- type = get_mem_type(cacheability);
- ret = ioremap_pages(addr, fmem_data.phys, fmem_data.size, type);
- if (ret)
- return ERR_PTR(ret);
-
- fmem_data.virt = fmem_data.area->addr;
-
- return fmem_data.virt;
-}
-
-void fmem_unmap_virtual_area(void)
-{
- unmap_kernel_range((unsigned long)fmem_data.virt, fmem_data.size);
- fmem_data.virt = NULL;
-}
-
-static int fmem_probe(struct platform_device *pdev)
-{
- struct fmem_platform_data *pdata = pdev->dev.platform_data;
-
- if (!pdata->phys)
- pdata->phys = allocate_contiguous_ebi_nomap(pdata->size,
- pdata->align);
-
-#ifdef CONFIG_MEMORY_HOTPLUG
- fmem_section_start = pdata->phys >> PA_SECTION_SHIFT;
- fmem_section_end = (pdata->phys - 1 + pdata->size) >> PA_SECTION_SHIFT;
-#endif
- fmem_data.phys = pdata->phys + pdata->reserved_size_low;
- fmem_data.size = pdata->size - pdata->reserved_size_low -
- pdata->reserved_size_high;
- fmem_data.reserved_size_low = pdata->reserved_size_low;
- fmem_data.reserved_size_high = pdata->reserved_size_high;
-
- if (!fmem_data.size)
- return -ENODEV;
-
- fmem_data.area = get_vm_area(fmem_data.size, VM_IOREMAP);
- if (!fmem_data.area)
- return -ENOMEM;
-
- if (!fmem_map_virtual_area(MT_DEVICE_CACHED)) {
- remove_vm_area(fmem_data.area->addr);
- return -ENOMEM;
- }
- pr_info("fmem phys %lx virt %p size %lx\n",
- fmem_data.phys, fmem_data.virt, fmem_data.size);
-
- spin_lock_init(&fmem_state_lock);
-
- return 0;
-}
-
-static int fmem_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct platform_driver fmem_driver = {
- .probe = fmem_probe,
- .remove = fmem_remove,
- .driver = { .name = "fmem" }
-};
-
-#ifdef CONFIG_SYSFS
-static ssize_t fmem_state_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
-{
- if (fmem_state == FMEM_T_STATE)
- return snprintf(buf, 3, "t\n");
- else if (fmem_state == FMEM_C_STATE)
- return snprintf(buf, 3, "c\n");
-#ifdef CONFIG_MEMORY_HOTPLUG
- else if (fmem_state == FMEM_O_STATE)
- return snprintf(buf, 3, "o\n");
-#endif
- else if (fmem_state == FMEM_UNINITIALIZED)
- return snprintf(buf, 15, "uninitialized\n");
- return snprintf(buf, 3, "?\n");
-}
-
-static ssize_t fmem_state_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t count)
-{
- int ret = -EINVAL;
-
- if (!strncmp(buf, "t", 1))
- ret = fmem_set_state(FMEM_T_STATE);
- else if (!strncmp(buf, "c", 1))
- ret = fmem_set_state(FMEM_C_STATE);
-#ifdef CONFIG_MEMORY_HOTPLUG
- else if (!strncmp(buf, "o", 1))
- ret = fmem_set_state(FMEM_O_STATE);
-#endif
- if (ret)
- return ret;
- return 1;
-}
-
-static struct kobj_attribute fmem_state_attr = {
- .attr = { .name = "state", .mode = 0644 },
- .show = fmem_state_show,
- .store = fmem_state_store,
-};
-
-static struct attribute *fmem_attrs[] = {
- &fmem_state_attr.attr,
- NULL,
-};
-
-static struct attribute_group fmem_attr_group = {
- .attrs = fmem_attrs,
- .name = "fmem",
-};
-
-static int fmem_create_sysfs(void)
-{
- int ret = 0;
-
- ret = sysfs_create_group(mm_kobj, &fmem_attr_group);
- if (ret)
- pr_err("fmem: can't create sysfs\n");
- return ret;
-}
-
-#endif
-
-#ifdef CONFIG_MEMORY_HOTPLUG
-bool fmem_is_disjoint(unsigned long start_pfn, unsigned long nr_pages)
-{
- unsigned long fmem_start_pfn, fmem_end_pfn;
- unsigned long unstable_end_pfn;
- unsigned long highest_start_pfn, lowest_end_pfn;
-
- fmem_start_pfn = (fmem_data.phys - fmem_data.reserved_size_low)
- >> PAGE_SHIFT;
- fmem_end_pfn = (fmem_data.phys + fmem_data.size +
- fmem_data.reserved_size_high - 1) >> PAGE_SHIFT;
- unstable_end_pfn = start_pfn + nr_pages - 1;
-
- highest_start_pfn = max(fmem_start_pfn, start_pfn);
- lowest_end_pfn = min(fmem_end_pfn, unstable_end_pfn);
-
- return lowest_end_pfn < highest_start_pfn;
-}
-
-static int fmem_mem_going_offline_callback(void *arg)
-{
- struct memory_notify *marg = arg;
-
- if (fmem_is_disjoint(marg->start_pfn, marg->nr_pages))
- return 0;
- return fmem_set_state(FMEM_O_STATE);
-}
-
-static void fmem_mem_online_callback(void *arg)
-{
- struct memory_notify *marg = arg;
- int i;
-
- section_powered_off[marg->start_pfn >> PFN_SECTION_SHIFT] = 0;
-
- if (fmem_state != FMEM_O_STATE)
- return;
-
- for (i = fmem_section_start; i <= fmem_section_end; i++) {
- if (section_powered_off[i])
- return;
- }
-
- fmem_set_state(FMEM_T_STATE);
-}
-
-static void fmem_mem_offline_callback(void *arg)
-{
- struct memory_notify *marg = arg;
-
- section_powered_off[marg->start_pfn >> PFN_SECTION_SHIFT] = 1;
-}
-
-static int fmem_memory_callback(struct notifier_block *self,
- unsigned long action, void *arg)
-{
- int ret = 0;
-
- if (fmem_state == FMEM_UNINITIALIZED)
- return NOTIFY_OK;
-
- switch (action) {
- case MEM_ONLINE:
- fmem_mem_online_callback(arg);
- break;
- case MEM_GOING_OFFLINE:
- ret = fmem_mem_going_offline_callback(arg);
- break;
- case MEM_OFFLINE:
- fmem_mem_offline_callback(arg);
- break;
- case MEM_GOING_ONLINE:
- case MEM_CANCEL_ONLINE:
- case MEM_CANCEL_OFFLINE:
- break;
- }
- if (ret)
- ret = notifier_from_errno(ret);
- else
- ret = NOTIFY_OK;
- return ret;
-}
-#endif
-
-static int __init fmem_init(void)
-{
-#ifdef CONFIG_MEMORY_HOTPLUG
- hotplug_memory_notifier(fmem_memory_callback, 0);
-#endif
- return platform_driver_register(&fmem_driver);
-}
-
-static void __exit fmem_exit(void)
-{
- platform_driver_unregister(&fmem_driver);
-}
-
-struct fmem_data *fmem_get_info(void)
-{
- return &fmem_data;
-}
-EXPORT_SYMBOL(fmem_get_info);
-
-void lock_fmem_state(void)
-{
- spin_lock(&fmem_state_lock);
-}
-
-void unlock_fmem_state(void)
-{
- spin_unlock(&fmem_state_lock);
-}
-
-int fmem_set_state(enum fmem_state new_state)
-{
- int ret = 0;
- int create_sysfs = 0;
-
- lock_fmem_state();
- if (fmem_state == new_state)
- goto out;
-
- if (fmem_state == FMEM_UNINITIALIZED) {
- if (new_state == FMEM_T_STATE) {
- tmem_enable();
- create_sysfs = 1;
- goto out_set;
- } else {
- ret = -EINVAL;
- goto out;
- }
- }
-
-#ifdef CONFIG_MEMORY_HOTPLUG
- if (fmem_state == FMEM_C_STATE && new_state == FMEM_O_STATE) {
- ret = -EAGAIN;
- goto out;
- }
-
- if (fmem_state == FMEM_O_STATE && new_state == FMEM_C_STATE) {
- pr_warn("attempting to use powered off memory as fmem\n");
- ret = -EAGAIN;
- goto out;
- }
-#endif
-
- if (new_state == FMEM_T_STATE) {
- void *v;
- v = fmem_map_virtual_area(MT_DEVICE_CACHED);
- if (IS_ERR_OR_NULL(v)) {
- ret = PTR_ERR(v);
- goto out;
- }
- tmem_enable();
- } else {
- tmem_disable();
- fmem_unmap_virtual_area();
- }
-
-out_set:
- fmem_state = new_state;
-out:
- unlock_fmem_state();
-#ifdef CONFIG_SYSFS
- if (create_sysfs)
- fmem_create_sysfs();
-#endif
- return ret;
-}
-EXPORT_SYMBOL(fmem_set_state);
-
-arch_initcall(fmem_init);
-module_exit(fmem_exit);
diff --git a/drivers/staging/qcache/qcache-main.c b/drivers/staging/qcache/qcache-main.c
deleted file mode 100644
index f416cfc..0000000
--- a/drivers/staging/qcache/qcache-main.c
+++ /dev/null
@@ -1,1358 +0,0 @@
-/*
- * Copyright (c) 2010,2011, Dan Magenheimer, Oracle Corp.
- * Copyright (c) 2010,2011, Nitin Gupta
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
- *
- * Qcache provides an in-kernel "host implementation" for transcendent memory
- * and, thus indirectly, for cleancache and frontswap. Qcache includes a
- * page-accessible memory [1] interface, utilizing lzo1x compression:
- * 1) "compression buddies" ("zbud") is used for ephemeral pages
- * Zbud allows pairs (and potentially,
- * in the future, more than a pair of) compressed pages to be closely linked
- * so that reclaiming can be done via the kernel's physical-page-oriented
- * "shrinker" interface.
- *
- * [1] For a definition of page-accessible memory (aka PAM), see:
- * http://marc.info/?l=linux-mm&m=127811271605009
- */
-
-#include <linux/module.h>
-#include <linux/cpu.h>
-#include <linux/highmem.h>
-#include <linux/list.h>
-#include <linux/lzo.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/atomic.h>
-#include <linux/math64.h>
-#include <linux/bitmap.h>
-#include <linux/fmem.h>
-#include "tmem.h"
-
-#if !defined(CONFIG_CLEANCACHE)
-#error "qcache is useless without CONFIG_CLEANCACHE"
-#endif
-#include <linux/cleancache.h>
-
-#define ZCACHE_GFP_MASK \
- (__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC)
-
-#define MAX_POOLS_PER_CLIENT 16
-
-#define MAX_CLIENTS 16
-#define LOCAL_CLIENT ((uint16_t)-1)
-
-MODULE_LICENSE("GPL");
-
-struct zcache_client {
- struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT];
- struct xv_pool *xvpool;
- bool allocated;
- atomic_t refcount;
-};
-
-struct qcache_info {
- void *addr;
- unsigned long *bitmap;
- spinlock_t lock;
- unsigned pages;
-};
-static struct qcache_info qcache_info;
-static unsigned long zcache_qc_allocated;
-static unsigned long zcache_qc_freed;
-static unsigned long zcache_qc_used;
-static unsigned long zcache_qc_max_used;
-
-static struct zcache_client zcache_host;
-static struct zcache_client zcache_clients[MAX_CLIENTS];
-
-static inline uint16_t get_client_id_from_client(struct zcache_client *cli)
-{
- BUG_ON(cli == NULL);
- if (cli == &zcache_host)
- return LOCAL_CLIENT;
- return cli - &zcache_clients[0];
-}
-
-static inline bool is_local_client(struct zcache_client *cli)
-{
- return cli == &zcache_host;
-}
-
-/**********
- * Compression buddies ("zbud") provides for packing two (or, possibly
- * in the future, more) compressed ephemeral pages into a single "raw"
- * (physical) page and tracking them with data structures so that
- * the raw pages can be easily reclaimed.
- *
- * A zbud page ("zbpg") is an aligned page containing a list_head,
- * a lock, and two "zbud headers". The remainder of the physical
- * page is divided up into aligned 64-byte "chunks" which contain
- * the compressed data for zero, one, or two zbuds. Each zbpg
- * resides on: (1) an "unused list" if it has no zbuds; (2) a
- * "buddied" list if it is fully populated with two zbuds; or
- * (3) one of PAGE_SIZE/64 "unbuddied" lists indexed by how many chunks
- * the one unbuddied zbud uses. The data inside a zbpg cannot be
- * read or written unless the zbpg's lock is held.
- */
-
-#define ZBH_SENTINEL 0x43214321
-#define ZBPG_SENTINEL 0xdeadbeef
-
-#define ZBUD_MAX_BUDS 2
-
-struct zbud_hdr {
- uint16_t client_id;
- uint16_t pool_id;
- struct tmem_oid oid;
- uint32_t index;
- uint16_t size; /* compressed size in bytes, zero means unused */
- DECL_SENTINEL
-};
-
-struct zbud_page {
- struct list_head bud_list;
- spinlock_t lock;
- struct zbud_hdr buddy[ZBUD_MAX_BUDS];
- DECL_SENTINEL
- /* followed by NUM_CHUNK aligned CHUNK_SIZE-byte chunks */
-};
-
-#define CHUNK_SHIFT 6
-#define CHUNK_SIZE (1 << CHUNK_SHIFT)
-#define CHUNK_MASK (~(CHUNK_SIZE-1))
-#define NCHUNKS (((PAGE_SIZE - sizeof(struct zbud_page)) & \
- CHUNK_MASK) >> CHUNK_SHIFT)
-#define MAX_CHUNK (NCHUNKS-1)
-
-static struct {
- struct list_head list;
- unsigned count;
-} zbud_unbuddied[NCHUNKS];
-/* list N contains pages with N chunks USED and NCHUNKS-N unused */
-/* element 0 is never used but optimizing that isn't worth it */
-static unsigned long zbud_cumul_chunk_counts[NCHUNKS];
-
-struct list_head zbud_buddied_list;
-static unsigned long zcache_zbud_buddied_count;
-
-/* protects the buddied list and all unbuddied lists */
-static DEFINE_SPINLOCK(zbud_budlists_spinlock);
-
-static atomic_t zcache_zbud_curr_raw_pages;
-static atomic_t zcache_zbud_curr_zpages;
-static unsigned long zcache_zbud_curr_zbytes;
-static unsigned long zcache_zbud_cumul_zpages;
-static unsigned long zcache_zbud_cumul_zbytes;
-static unsigned long zcache_compress_poor;
-static unsigned long zcache_mean_compress_poor;
-
-/* forward references */
-static void *zcache_get_free_page(void);
-
-static void *qcache_alloc(void)
-{
- void *addr;
- unsigned long flags;
- int offset;
- struct qcache_info *qc = &qcache_info;
-
- spin_lock_irqsave(&qc->lock, flags);
- offset = bitmap_find_free_region(qc->bitmap, qc->pages, 0);
-
- if (offset < 0) {
- spin_unlock_irqrestore(&qc->lock, flags);
- return NULL;
- }
-
- zcache_qc_allocated++;
- zcache_qc_used++;
- zcache_qc_max_used = max(zcache_qc_max_used, zcache_qc_used);
- spin_unlock_irqrestore(&qc->lock, flags);
-
- addr = qc->addr + offset * PAGE_SIZE;
-
- return addr;
-}
-
-static void qcache_free(void *addr)
-{
- unsigned long flags;
- int offset;
- struct qcache_info *qc = &qcache_info;
-
- offset = (addr - qc->addr) / PAGE_SIZE;
-
- spin_lock_irqsave(&qc->lock, flags);
- bitmap_release_region(qc->bitmap, offset, 0);
-
- zcache_qc_freed++;
- zcache_qc_used--;
- spin_unlock_irqrestore(&qc->lock, flags);
-}
-
-/*
- * zbud helper functions
- */
-
-static inline unsigned zbud_max_buddy_size(void)
-{
- return MAX_CHUNK << CHUNK_SHIFT;
-}
-
-static inline unsigned zbud_size_to_chunks(unsigned size)
-{
- BUG_ON(size == 0 || size > zbud_max_buddy_size());
- return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
-}
-
-static inline int zbud_budnum(struct zbud_hdr *zh)
-{
- unsigned offset = (unsigned long)zh & (PAGE_SIZE - 1);
- struct zbud_page *zbpg = NULL;
- unsigned budnum = -1U;
- int i;
-
- for (i = 0; i < ZBUD_MAX_BUDS; i++)
- if (offset == offsetof(typeof(*zbpg), buddy[i])) {
- budnum = i;
- break;
- }
- BUG_ON(budnum == -1U);
- return budnum;
-}
-
-static char *zbud_data(struct zbud_hdr *zh, unsigned size)
-{
- struct zbud_page *zbpg;
- char *p;
- unsigned budnum;
-
- ASSERT_SENTINEL(zh, ZBH);
- budnum = zbud_budnum(zh);
- BUG_ON(size == 0 || size > zbud_max_buddy_size());
- zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
- p = (char *)zbpg;
- if (budnum == 0)
- p += ((sizeof(struct zbud_page) + CHUNK_SIZE - 1) &
- CHUNK_MASK);
- else if (budnum == 1)
- p += PAGE_SIZE - ((size + CHUNK_SIZE - 1) & CHUNK_MASK);
- return p;
-}
-
-/*
- * zbud raw page management
- */
-
-static struct zbud_page *zbud_alloc_raw_page(void)
-{
- struct zbud_page *zbpg = NULL;
- struct zbud_hdr *zh0, *zh1;
-
- zbpg = zcache_get_free_page();
- if (likely(zbpg != NULL)) {
- INIT_LIST_HEAD(&zbpg->bud_list);
- zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1];
- spin_lock_init(&zbpg->lock);
- atomic_inc(&zcache_zbud_curr_raw_pages);
- INIT_LIST_HEAD(&zbpg->bud_list);
- SET_SENTINEL(zbpg, ZBPG);
- zh0->size = 0; zh1->size = 0;
- tmem_oid_set_invalid(&zh0->oid);
- tmem_oid_set_invalid(&zh1->oid);
- }
- return zbpg;
-}
-
-static void zbud_free_raw_page(struct zbud_page *zbpg)
-{
- struct zbud_hdr *zh0 = &zbpg->buddy[0], *zh1 = &zbpg->buddy[1];
-
- ASSERT_SENTINEL(zbpg, ZBPG);
- BUG_ON(!list_empty(&zbpg->bud_list));
- BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid));
- BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid));
- INVERT_SENTINEL(zbpg, ZBPG);
- spin_unlock(&zbpg->lock);
- qcache_free(zbpg);
-}
-
-/*
- * core zbud handling routines
- */
-
-static unsigned zbud_free(struct zbud_hdr *zh)
-{
- unsigned size;
-
- ASSERT_SENTINEL(zh, ZBH);
- BUG_ON(!tmem_oid_valid(&zh->oid));
- size = zh->size;
- BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size());
- zh->size = 0;
- tmem_oid_set_invalid(&zh->oid);
- INVERT_SENTINEL(zh, ZBH);
- zcache_zbud_curr_zbytes -= size;
- atomic_dec(&zcache_zbud_curr_zpages);
- return size;
-}
-
-static void zbud_free_and_delist(struct zbud_hdr *zh)
-{
- unsigned chunks;
- struct zbud_hdr *zh_other;
- unsigned budnum = zbud_budnum(zh), size;
- struct zbud_page *zbpg =
- container_of(zh, struct zbud_page, buddy[budnum]);
-
- spin_lock(&zbpg->lock);
- if (list_empty(&zbpg->bud_list)) {
- spin_unlock(&zbpg->lock);
- return;
- }
- size = zbud_free(zh);
- zh_other = &zbpg->buddy[(budnum == 0) ? 1 : 0];
- if (zh_other->size == 0) { /* was unbuddied: unlist and free */
- chunks = zbud_size_to_chunks(size) ;
- spin_lock(&zbud_budlists_spinlock);
- BUG_ON(list_empty(&zbud_unbuddied[chunks].list));
- list_del_init(&zbpg->bud_list);
- zbud_unbuddied[chunks].count--;
- spin_unlock(&zbud_budlists_spinlock);
- zbud_free_raw_page(zbpg);
- } else { /* was buddied: move remaining buddy to unbuddied list */
- chunks = zbud_size_to_chunks(zh_other->size) ;
- spin_lock(&zbud_budlists_spinlock);
- list_del_init(&zbpg->bud_list);
- zcache_zbud_buddied_count--;
- list_add_tail(&zbpg->bud_list, &zbud_unbuddied[chunks].list);
- zbud_unbuddied[chunks].count++;
- spin_unlock(&zbud_budlists_spinlock);
- spin_unlock(&zbpg->lock);
- }
-}
-
-static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id,
- struct tmem_oid *oid,
- uint32_t index, struct page *page,
- void *cdata, unsigned size)
-{
- struct zbud_hdr *zh0, *zh1, *zh = NULL;
- struct zbud_page *zbpg = NULL, *ztmp;
- unsigned nchunks;
- char *to;
- int i, found_good_buddy = 0;
-
- nchunks = zbud_size_to_chunks(size) ;
- for (i = MAX_CHUNK - nchunks + 1; i > 0; i--) {
- spin_lock(&zbud_budlists_spinlock);
- if (!list_empty(&zbud_unbuddied[i].list)) {
- list_for_each_entry_safe(zbpg, ztmp,
- &zbud_unbuddied[i].list, bud_list) {
- if (spin_trylock(&zbpg->lock)) {
- found_good_buddy = i;
- goto found_unbuddied;
- }
- }
- }
- spin_unlock(&zbud_budlists_spinlock);
- }
- /* didn't find a good buddy, try allocating a new page */
- zbpg = zbud_alloc_raw_page();
- if (unlikely(zbpg == NULL))
- goto out;
- /* ok, have a page, now compress the data before taking locks */
- spin_lock(&zbpg->lock);
- spin_lock(&zbud_budlists_spinlock);
- list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list);
- zbud_unbuddied[nchunks].count++;
- zh = &zbpg->buddy[0];
- goto init_zh;
-
-found_unbuddied:
- zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1];
- BUG_ON(!((zh0->size == 0) ^ (zh1->size == 0)));
- if (zh0->size != 0) { /* buddy0 in use, buddy1 is vacant */
- ASSERT_SENTINEL(zh0, ZBH);
- zh = zh1;
- } else if (zh1->size != 0) { /* buddy1 in use, buddy0 is vacant */
- ASSERT_SENTINEL(zh1, ZBH);
- zh = zh0;
- } else
- BUG();
- list_del_init(&zbpg->bud_list);
- zbud_unbuddied[found_good_buddy].count--;
- list_add_tail(&zbpg->bud_list, &zbud_buddied_list);
- zcache_zbud_buddied_count++;
-
-init_zh:
- SET_SENTINEL(zh, ZBH);
- zh->size = size;
- zh->index = index;
- zh->oid = *oid;
- zh->pool_id = pool_id;
- zh->client_id = client_id;
- /* can wait to copy the data until the list locks are dropped */
- spin_unlock(&zbud_budlists_spinlock);
-
- to = zbud_data(zh, size);
- memcpy(to, cdata, size);
- spin_unlock(&zbpg->lock);
- zbud_cumul_chunk_counts[nchunks]++;
- atomic_inc(&zcache_zbud_curr_zpages);
- zcache_zbud_cumul_zpages++;
- zcache_zbud_curr_zbytes += size;
- zcache_zbud_cumul_zbytes += size;
-out:
- return zh;
-}
-
-static int zbud_decompress(struct page *page, struct zbud_hdr *zh)
-{
- struct zbud_page *zbpg;
- unsigned budnum = zbud_budnum(zh);
- size_t out_len = PAGE_SIZE;
- char *to_va, *from_va;
- unsigned size;
- int ret = 0;
-
- zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
- spin_lock(&zbpg->lock);
- if (list_empty(&zbpg->bud_list)) {
- ret = -EINVAL;
- goto out;
- }
- ASSERT_SENTINEL(zh, ZBH);
- BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size());
- to_va = kmap_atomic(page);
- size = zh->size;
- from_va = zbud_data(zh, size);
- ret = lzo1x_decompress_safe(from_va, size, to_va, &out_len);
- BUG_ON(ret != LZO_E_OK);
- BUG_ON(out_len != PAGE_SIZE);
- kunmap_atomic(to_va);
-out:
- spin_unlock(&zbpg->lock);
- return ret;
-}
-
-static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id,
- uint16_t poolid);
-static void zcache_put_pool(struct tmem_pool *pool);
-
-static void zbud_init(void)
-{
- int i;
-
- INIT_LIST_HEAD(&zbud_buddied_list);
- zcache_zbud_buddied_count = 0;
- for (i = 0; i < NCHUNKS; i++) {
- INIT_LIST_HEAD(&zbud_unbuddied[i].list);
- zbud_unbuddied[i].count = 0;
- }
-}
-
-#ifdef CONFIG_SYSFS
-/*
- * These sysfs routines show a nice distribution of how many zbpg's are
- * currently (and have ever been placed) in each unbuddied list. It's fun
- * to watch but can probably go away before final merge.
- */
-static int zbud_show_unbuddied_list_counts(char *buf)
-{
- int i;
- char *p = buf;
-
- for (i = 0; i < NCHUNKS; i++)
- p += sprintf(p, "%u ", zbud_unbuddied[i].count);
- return p - buf;
-}
-
-static int zbud_show_cumul_chunk_counts(char *buf)
-{
- unsigned long i, chunks = 0, total_chunks = 0, sum_total_chunks = 0;
- unsigned long total_chunks_lte_21 = 0, total_chunks_lte_32 = 0;
- unsigned long total_chunks_lte_42 = 0;
- char *p = buf;
-
- for (i = 0; i < NCHUNKS; i++) {
- p += sprintf(p, "%lu ", zbud_cumul_chunk_counts[i]);
- chunks += zbud_cumul_chunk_counts[i];
- total_chunks += zbud_cumul_chunk_counts[i];
- sum_total_chunks += i * zbud_cumul_chunk_counts[i];
- if (i == 21)
- total_chunks_lte_21 = total_chunks;
- if (i == 32)
- total_chunks_lte_32 = total_chunks;
- if (i == 42)
- total_chunks_lte_42 = total_chunks;
- }
- p += sprintf(p, "<=21:%lu <=32:%lu <=42:%lu, mean:%lu\n",
- total_chunks_lte_21, total_chunks_lte_32, total_chunks_lte_42,
- chunks == 0 ? 0 : sum_total_chunks / chunks);
- return p - buf;
-}
-#endif
-
-/*
- * zcache core code starts here
- */
-
-/* useful stats not collected by cleancache or frontswap */
-static unsigned long zcache_flush_total;
-static unsigned long zcache_flush_found;
-static unsigned long zcache_flobj_total;
-static unsigned long zcache_flobj_found;
-static unsigned long zcache_failed_eph_puts;
-
-/*
- * Tmem operations assume the poolid implies the invoking client.
- * Zcache only has one client (the kernel itself): LOCAL_CLIENT.
- * RAMster has each client numbered by cluster node, and a KVM version
- * of zcache would have one client per guest and each client might
- * have a poolid==N.
- */
-static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, uint16_t poolid)
-{
- struct tmem_pool *pool = NULL;
- struct zcache_client *cli = NULL;
-
- if (cli_id == LOCAL_CLIENT)
- cli = &zcache_host;
- else {
- if (cli_id >= MAX_CLIENTS)
- goto out;
- cli = &zcache_clients[cli_id];
- if (cli == NULL)
- goto out;
- atomic_inc(&cli->refcount);
- }
- if (poolid < MAX_POOLS_PER_CLIENT) {
- pool = cli->tmem_pools[poolid];
- if (pool != NULL)
- atomic_inc(&pool->refcount);
- }
-out:
- return pool;
-}
-
-static void zcache_put_pool(struct tmem_pool *pool)
-{
- struct zcache_client *cli = NULL;
-
- if (pool == NULL)
- BUG();
- cli = pool->client;
- atomic_dec(&pool->refcount);
- atomic_dec(&cli->refcount);
-}
-
-int zcache_new_client(uint16_t cli_id)
-{
- struct zcache_client *cli = NULL;
- int ret = -1;
-
- if (cli_id == LOCAL_CLIENT)
- cli = &zcache_host;
- else if ((unsigned int)cli_id < MAX_CLIENTS)
- cli = &zcache_clients[cli_id];
- if (cli == NULL)
- goto out;
- if (cli->allocated)
- goto out;
- cli->allocated = 1;
- ret = 0;
-out:
- return ret;
-}
-
-/* counters for debugging */
-static unsigned long zcache_failed_get_free_pages;
-static unsigned long zcache_failed_alloc;
-static unsigned long zcache_put_to_flush;
-static unsigned long zcache_aborted_preload;
-static unsigned long zcache_aborted_shrink;
-
-/*
- * Ensure that memory allocation requests in zcache don't result
- * in direct reclaim requests via the shrinker, which would cause
- * an infinite loop. Maybe a GFP flag would be better?
- */
-static DEFINE_SPINLOCK(zcache_direct_reclaim_lock);
-
-/*
- * for now, used named slabs so can easily track usage; later can
- * either just use kmalloc, or perhaps add a slab-like allocator
- * to more carefully manage total memory utilization
- */
-static struct kmem_cache *zcache_objnode_cache;
-static struct kmem_cache *zcache_obj_cache;
-static atomic_t zcache_curr_obj_count = ATOMIC_INIT(0);
-static unsigned long zcache_curr_obj_count_max;
-static atomic_t zcache_curr_objnode_count = ATOMIC_INIT(0);
-static unsigned long zcache_curr_objnode_count_max;
-
-/*
- * to avoid memory allocation recursion (e.g. due to direct reclaim), we
- * preload all necessary data structures so the hostops callbacks never
- * actually do a malloc
- */
-struct zcache_preload {
- void *page;
- struct tmem_obj *obj;
- int nr;
- struct tmem_objnode *objnodes[OBJNODE_TREE_MAX_PATH];
-};
-static DEFINE_PER_CPU(struct zcache_preload, zcache_preloads) = { 0, };
-
-static int zcache_do_preload(struct tmem_pool *pool)
-{
- struct zcache_preload *kp;
- struct tmem_objnode *objnode;
- struct tmem_obj *obj;
- void *page;
- int ret = -ENOMEM;
-
- if (unlikely(zcache_objnode_cache == NULL))
- goto out;
- if (unlikely(zcache_obj_cache == NULL))
- goto out;
- if (!spin_trylock(&zcache_direct_reclaim_lock)) {
- zcache_aborted_preload++;
- goto out;
- }
- preempt_disable();
- kp = &__get_cpu_var(zcache_preloads);
- while (kp->nr < ARRAY_SIZE(kp->objnodes)) {
- preempt_enable_no_resched();
- objnode = kmem_cache_alloc(zcache_objnode_cache,
- ZCACHE_GFP_MASK);
- if (unlikely(objnode == NULL)) {
- zcache_failed_alloc++;
- goto unlock_out;
- }
- preempt_disable();
- kp = &__get_cpu_var(zcache_preloads);
- if (kp->nr < ARRAY_SIZE(kp->objnodes))
- kp->objnodes[kp->nr++] = objnode;
- else
- kmem_cache_free(zcache_objnode_cache, objnode);
- }
- preempt_enable_no_resched();
- obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK);
- if (unlikely(obj == NULL)) {
- zcache_failed_alloc++;
- goto unlock_out;
- }
- page = qcache_alloc();
- if (unlikely(page == NULL)) {
- zcache_failed_get_free_pages++;
- kmem_cache_free(zcache_obj_cache, obj);
- goto unlock_out;
- }
- preempt_disable();
- kp = &__get_cpu_var(zcache_preloads);
- if (kp->obj == NULL)
- kp->obj = obj;
- else
- kmem_cache_free(zcache_obj_cache, obj);
- if (kp->page == NULL)
- kp->page = page;
- else
- qcache_free(page);
- ret = 0;
-unlock_out:
- spin_unlock(&zcache_direct_reclaim_lock);
-out:
- return ret;
-}
-
-static void *zcache_get_free_page(void)
-{
- struct zcache_preload *kp;
- void *page;
-
- kp = &__get_cpu_var(zcache_preloads);
- page = kp->page;
- BUG_ON(page == NULL);
- kp->page = NULL;
- return page;
-}
-
-/*
- * zcache implementation for tmem host ops
- */
-
-static struct tmem_objnode *zcache_objnode_alloc(struct tmem_pool *pool)
-{
- struct tmem_objnode *objnode = NULL;
- unsigned long count;
- struct zcache_preload *kp;
-
- kp = &__get_cpu_var(zcache_preloads);
- if (kp->nr <= 0)
- goto out;
- objnode = kp->objnodes[kp->nr - 1];
- BUG_ON(objnode == NULL);
- kp->objnodes[kp->nr - 1] = NULL;
- kp->nr--;
- count = atomic_inc_return(&zcache_curr_objnode_count);
- if (count > zcache_curr_objnode_count_max)
- zcache_curr_objnode_count_max = count;
-out:
- return objnode;
-}
-
-static void zcache_objnode_free(struct tmem_objnode *objnode,
- struct tmem_pool *pool)
-{
- atomic_dec(&zcache_curr_objnode_count);
- BUG_ON(atomic_read(&zcache_curr_objnode_count) < 0);
- kmem_cache_free(zcache_objnode_cache, objnode);
-}
-
-static struct tmem_obj *zcache_obj_alloc(struct tmem_pool *pool)
-{
- struct tmem_obj *obj = NULL;
- unsigned long count;
- struct zcache_preload *kp;
-
- kp = &__get_cpu_var(zcache_preloads);
- obj = kp->obj;
- BUG_ON(obj == NULL);
- kp->obj = NULL;
- count = atomic_inc_return(&zcache_curr_obj_count);
- if (count > zcache_curr_obj_count_max)
- zcache_curr_obj_count_max = count;
- return obj;
-}
-
-static void zcache_obj_free(struct tmem_obj *obj, struct tmem_pool *pool)
-{
- atomic_dec(&zcache_curr_obj_count);
- BUG_ON(atomic_read(&zcache_curr_obj_count) < 0);
- kmem_cache_free(zcache_obj_cache, obj);
-}
-
-static void zcache_flush_all_obj(void)
-{
- struct tmem_pool *pool;
- int pool_id;
- struct zcache_preload *kp;
-
- kp = &__get_cpu_var(zcache_preloads);
-
- for (pool_id = 0; pool_id < MAX_POOLS_PER_CLIENT; pool_id++) {
- pool = zcache_get_pool_by_id(LOCAL_CLIENT, pool_id);
- tmem_flush_pool(pool);
- if (pool)
- zcache_put_pool(pool);
- }
- if (kp->page) {
- qcache_free(kp->page);
- kp->page = NULL;
- }
- if (zcache_qc_used)
- pr_warn("pages used not 0 after qcache flush all, is %ld\n",
- zcache_qc_used);
-}
-
-/*
- * When zcache is disabled ("frozen"), pools can be created and destroyed,
- * but all puts (and thus all other operations that require memory allocation)
- * must fail. If zcache is unfrozen, accepts puts, then frozen again,
- * data consistency requires all puts while frozen to be converted into
- * flushes.
- */
-static bool zcache_freeze;
-
-static void zcache_control(bool freeze)
-{
- zcache_freeze = freeze;
-}
-
-static struct tmem_hostops zcache_hostops = {
- .obj_alloc = zcache_obj_alloc,
- .obj_free = zcache_obj_free,
- .objnode_alloc = zcache_objnode_alloc,
- .objnode_free = zcache_objnode_free,
- .flush_all_obj = zcache_flush_all_obj,
- .control = zcache_control,
-};
-
-/*
- * zcache implementations for PAM page descriptor ops
- */
-
-static atomic_t zcache_curr_eph_pampd_count = ATOMIC_INIT(0);
-static unsigned long zcache_curr_eph_pampd_count_max;
-
-/* forward reference */
-static int zcache_compress(struct page *from, void **out_va, size_t *out_len);
-
-static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph,
- struct tmem_pool *pool, struct tmem_oid *oid,
- uint32_t index)
-{
- void *pampd = NULL, *cdata;
- size_t clen;
- int ret;
- unsigned long count;
- struct page *page = (struct page *)(data);
- struct zcache_client *cli = pool->client;
- uint16_t client_id = get_client_id_from_client(cli);
-
- ret = zcache_compress(page, &cdata, &clen);
- if (ret == 0)
- goto out;
- if (clen == 0 || clen > zbud_max_buddy_size()) {
- zcache_compress_poor++;
- goto out;
- }
- pampd = (void *)zbud_create(client_id, pool->pool_id, oid,
- index, page, cdata, clen);
- if (pampd != NULL) {
- count = atomic_inc_return(&zcache_curr_eph_pampd_count);
- if (count > zcache_curr_eph_pampd_count_max)
- zcache_curr_eph_pampd_count_max = count;
- }
-out:
- return pampd;
-}
-
-/*
- * fill the pageframe corresponding to the struct page with the data
- * from the passed pampd
- */
-static int zcache_pampd_get_data(char *data, size_t *bufsize, bool raw,
- void *pampd, struct tmem_pool *pool,
- struct tmem_oid *oid, uint32_t index)
-{
- BUG();
- return 0;
-}
-
-/*
- * fill the pageframe corresponding to the struct page with the data
- * from the passed pampd
- */
-static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw,
- void *pampd, struct tmem_pool *pool,
- struct tmem_oid *oid, uint32_t index)
-{
- int ret = 0;
-
- zbud_decompress((struct page *)(data), pampd);
- zbud_free_and_delist((struct zbud_hdr *)pampd);
- atomic_dec(&zcache_curr_eph_pampd_count);
- return ret;
-}
-
-/*
- * free the pampd and remove it from any zcache lists
- * pampd must no longer be pointed to from any tmem data structures!
- */
-static void zcache_pampd_free(void *pampd, struct tmem_pool *pool,
- struct tmem_oid *oid, uint32_t index)
-{
- zbud_free_and_delist((struct zbud_hdr *)pampd);
- atomic_dec(&zcache_curr_eph_pampd_count);
- BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0);
-}
-
-static void zcache_pampd_free_obj(struct tmem_pool *pool, struct tmem_obj *obj)
-{
-}
-
-static void zcache_pampd_new_obj(struct tmem_obj *obj)
-{
-}
-
-static int zcache_pampd_replace_in_obj(void *pampd, struct tmem_obj *obj)
-{
- return -1;
-}
-
-static bool zcache_pampd_is_remote(void *pampd)
-{
- return 0;
-}
-
-static struct tmem_pamops zcache_pamops = {
- .create = zcache_pampd_create,
- .get_data = zcache_pampd_get_data,
- .get_data_and_free = zcache_pampd_get_data_and_free,
- .free = zcache_pampd_free,
- .free_obj = zcache_pampd_free_obj,
- .new_obj = zcache_pampd_new_obj,
- .replace_in_obj = zcache_pampd_replace_in_obj,
- .is_remote = zcache_pampd_is_remote,
-};
-
-/*
- * zcache compression/decompression and related per-cpu stuff
- */
-
-#define LZO_WORKMEM_BYTES LZO1X_1_MEM_COMPRESS
-#define LZO_DSTMEM_PAGE_ORDER 1
-static DEFINE_PER_CPU(unsigned char *, zcache_workmem);
-static DEFINE_PER_CPU(unsigned char *, zcache_dstmem);
-
-static int zcache_compress(struct page *from, void **out_va, size_t *out_len)
-{
- int ret = 0;
- unsigned char *dmem = __get_cpu_var(zcache_dstmem);
- unsigned char *wmem = __get_cpu_var(zcache_workmem);
- char *from_va;
-
- BUG_ON(!irqs_disabled());
- if (unlikely(dmem == NULL || wmem == NULL))
- goto out; /* no buffer, so can't compress */
- from_va = kmap_atomic(from);
- mb();
- ret = lzo1x_1_compress(from_va, PAGE_SIZE, dmem, out_len, wmem);
- BUG_ON(ret != LZO_E_OK);
- *out_va = dmem;
- kunmap_atomic(from_va);
- ret = 1;
-out:
- return ret;
-}
-
-#ifdef CONFIG_SYSFS
-#define ZCACHE_SYSFS_RO(_name) \
- static ssize_t zcache_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return sprintf(buf, "%lu\n", zcache_##_name); \
- } \
- static struct kobj_attribute zcache_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = zcache_##_name##_show, \
- }
-
-#define ZCACHE_SYSFS_RO_ATOMIC(_name) \
- static ssize_t zcache_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return sprintf(buf, "%d\n", atomic_read(&zcache_##_name)); \
- } \
- static struct kobj_attribute zcache_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = zcache_##_name##_show, \
- }
-
-#define ZCACHE_SYSFS_RO_CUSTOM(_name, _func) \
- static ssize_t zcache_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return _func(buf); \
- } \
- static struct kobj_attribute zcache_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = zcache_##_name##_show, \
- }
-
-ZCACHE_SYSFS_RO(curr_obj_count_max);
-ZCACHE_SYSFS_RO(curr_objnode_count_max);
-ZCACHE_SYSFS_RO(flush_total);
-ZCACHE_SYSFS_RO(flush_found);
-ZCACHE_SYSFS_RO(flobj_total);
-ZCACHE_SYSFS_RO(flobj_found);
-ZCACHE_SYSFS_RO(failed_eph_puts);
-ZCACHE_SYSFS_RO(zbud_curr_zbytes);
-ZCACHE_SYSFS_RO(zbud_cumul_zpages);
-ZCACHE_SYSFS_RO(zbud_cumul_zbytes);
-ZCACHE_SYSFS_RO(zbud_buddied_count);
-ZCACHE_SYSFS_RO(failed_get_free_pages);
-ZCACHE_SYSFS_RO(failed_alloc);
-ZCACHE_SYSFS_RO(put_to_flush);
-ZCACHE_SYSFS_RO(aborted_preload);
-ZCACHE_SYSFS_RO(aborted_shrink);
-ZCACHE_SYSFS_RO(compress_poor);
-ZCACHE_SYSFS_RO(mean_compress_poor);
-ZCACHE_SYSFS_RO(qc_allocated);
-ZCACHE_SYSFS_RO(qc_freed);
-ZCACHE_SYSFS_RO(qc_used);
-ZCACHE_SYSFS_RO(qc_max_used);
-ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_raw_pages);
-ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_zpages);
-ZCACHE_SYSFS_RO_ATOMIC(curr_obj_count);
-ZCACHE_SYSFS_RO_ATOMIC(curr_objnode_count);
-ZCACHE_SYSFS_RO_CUSTOM(zbud_unbuddied_list_counts,
- zbud_show_unbuddied_list_counts);
-ZCACHE_SYSFS_RO_CUSTOM(zbud_cumul_chunk_counts,
- zbud_show_cumul_chunk_counts);
-
-static struct attribute *qcache_attrs[] = {
- &zcache_curr_obj_count_attr.attr,
- &zcache_curr_obj_count_max_attr.attr,
- &zcache_curr_objnode_count_attr.attr,
- &zcache_curr_objnode_count_max_attr.attr,
- &zcache_flush_total_attr.attr,
- &zcache_flobj_total_attr.attr,
- &zcache_flush_found_attr.attr,
- &zcache_flobj_found_attr.attr,
- &zcache_failed_eph_puts_attr.attr,
- &zcache_compress_poor_attr.attr,
- &zcache_mean_compress_poor_attr.attr,
- &zcache_zbud_curr_raw_pages_attr.attr,
- &zcache_zbud_curr_zpages_attr.attr,
- &zcache_zbud_curr_zbytes_attr.attr,
- &zcache_zbud_cumul_zpages_attr.attr,
- &zcache_zbud_cumul_zbytes_attr.attr,
- &zcache_zbud_buddied_count_attr.attr,
- &zcache_failed_get_free_pages_attr.attr,
- &zcache_failed_alloc_attr.attr,
- &zcache_put_to_flush_attr.attr,
- &zcache_aborted_preload_attr.attr,
- &zcache_aborted_shrink_attr.attr,
- &zcache_zbud_unbuddied_list_counts_attr.attr,
- &zcache_zbud_cumul_chunk_counts_attr.attr,
- &zcache_qc_allocated_attr.attr,
- &zcache_qc_freed_attr.attr,
- &zcache_qc_used_attr.attr,
- &zcache_qc_max_used_attr.attr,
- NULL,
-};
-
-static struct attribute_group qcache_attr_group = {
- .attrs = qcache_attrs,
- .name = "qcache",
-};
-
-#endif /* CONFIG_SYSFS */
-
-/*
- * zcache shims between cleancache ops and tmem
- */
-
-static int zcache_put_page(int cli_id, int pool_id, struct tmem_oid *oidp,
- uint32_t index, struct page *page)
-{
- struct tmem_pool *pool;
- int ret = -1;
-
- BUG_ON(!irqs_disabled());
- pool = zcache_get_pool_by_id(cli_id, pool_id);
- if (unlikely(pool == NULL))
- goto out;
- if (!zcache_freeze && zcache_do_preload(pool) == 0) {
- /* preload does preempt_disable on success */
- ret = tmem_put(pool, oidp, index, (char *)(page),
- PAGE_SIZE, 0, is_ephemeral(pool));
- if (ret < 0) {
- zcache_failed_eph_puts++;
- }
- zcache_put_pool(pool);
- preempt_enable_no_resched();
- } else {
- zcache_put_to_flush++;
- if (atomic_read(&pool->obj_count) > 0)
- /* the put fails whether the flush succeeds or not */
- (void)tmem_flush_page(pool, oidp, index);
- zcache_put_pool(pool);
- }
-out:
- return ret;
-}
-
-static int zcache_get_page(int cli_id, int pool_id, struct tmem_oid *oidp,
- uint32_t index, struct page *page)
-{
- struct tmem_pool *pool;
- int ret = -1;
- unsigned long flags;
- size_t size = PAGE_SIZE;
-
- local_irq_save(flags);
- pool = zcache_get_pool_by_id(cli_id, pool_id);
- if (likely(pool != NULL)) {
- if (atomic_read(&pool->obj_count) > 0)
- ret = tmem_get(pool, oidp, index, (char *)(page),
- &size, 0, is_ephemeral(pool));
- zcache_put_pool(pool);
- }
- local_irq_restore(flags);
- return ret;
-}
-
-static int zcache_flush_page(int cli_id, int pool_id,
- struct tmem_oid *oidp, uint32_t index)
-{
- struct tmem_pool *pool;
- int ret = -1;
- unsigned long flags;
-
- local_irq_save(flags);
- zcache_flush_total++;
- pool = zcache_get_pool_by_id(cli_id, pool_id);
- if (likely(pool != NULL)) {
- if (atomic_read(&pool->obj_count) > 0)
- ret = tmem_flush_page(pool, oidp, index);
- zcache_put_pool(pool);
- }
- if (ret >= 0)
- zcache_flush_found++;
- local_irq_restore(flags);
- return ret;
-}
-
-static int zcache_flush_object(int cli_id, int pool_id,
- struct tmem_oid *oidp)
-{
- struct tmem_pool *pool;
- int ret = -1;
- unsigned long flags;
-
- local_irq_save(flags);
- zcache_flobj_total++;
- pool = zcache_get_pool_by_id(cli_id, pool_id);
- if (likely(pool != NULL)) {
- if (atomic_read(&pool->obj_count) > 0)
- ret = tmem_flush_object(pool, oidp);
- zcache_put_pool(pool);
- }
- if (ret >= 0)
- zcache_flobj_found++;
- local_irq_restore(flags);
- return ret;
-}
-
-static int zcache_destroy_pool(int cli_id, int pool_id)
-{
- struct tmem_pool *pool = NULL;
- struct zcache_client *cli = NULL;
- int ret = -1;
-
- if (pool_id < 0)
- goto out;
- if (cli_id == LOCAL_CLIENT)
- cli = &zcache_host;
- else if ((unsigned int)cli_id < MAX_CLIENTS)
- cli = &zcache_clients[cli_id];
- if (cli == NULL)
- goto out;
- atomic_inc(&cli->refcount);
- pool = cli->tmem_pools[pool_id];
- if (pool == NULL)
- goto out;
- cli->tmem_pools[pool_id] = NULL;
- /* wait for pool activity on other cpus to quiesce */
- while (atomic_read(&pool->refcount) != 0)
- ;
- atomic_dec(&cli->refcount);
- local_bh_disable();
- ret = tmem_destroy_pool(pool);
- local_bh_enable();
- kfree(pool);
- pr_info("qcache: destroyed pool id=%d, cli_id=%d\n",
- pool_id, cli_id);
-out:
- return ret;
-}
-
-static int zcache_new_pool(uint16_t cli_id, uint32_t flags)
-{
- int poolid = -1;
- struct tmem_pool *pool;
- struct zcache_client *cli = NULL;
-
- if (cli_id == LOCAL_CLIENT)
- cli = &zcache_host;
- else if ((unsigned int)cli_id < MAX_CLIENTS)
- cli = &zcache_clients[cli_id];
- if (cli == NULL)
- goto out;
- atomic_inc(&cli->refcount);
- pool = kmalloc(sizeof(struct tmem_pool), GFP_KERNEL);
- if (pool == NULL) {
- pr_info("qcache: pool creation failed: out of memory\n");
- goto out;
- }
-
- for (poolid = 0; poolid < MAX_POOLS_PER_CLIENT; poolid++)
- if (cli->tmem_pools[poolid] == NULL)
- break;
- if (poolid >= MAX_POOLS_PER_CLIENT) {
- pr_info("qcache: pool creation failed: max exceeded\n");
- kfree(pool);
- poolid = -1;
- goto out;
- }
- atomic_set(&pool->refcount, 0);
- pool->client = cli;
- pool->pool_id = poolid;
- tmem_new_pool(pool, flags);
- cli->tmem_pools[poolid] = pool;
- pr_info("qcache: created %s tmem pool, id=%d, client=%d\n",
- flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral",
- poolid, cli_id);
-out:
- if (cli != NULL)
- atomic_dec(&cli->refcount);
- return poolid;
-}
-
-/**********
- * Two kernel functionalities currently can be layered on top of tmem.
- * These are "cleancache" which is used as a second-chance cache for clean
- * page cache pages; and "frontswap" which is used for swap pages
- * to avoid writes to disk. A generic "shim" is provided here for each
- * to translate in-kernel semantics to zcache semantics.
- */
-
-static void zcache_cleancache_put_page(int pool_id,
- struct cleancache_filekey key,
- pgoff_t index, struct page *page)
-{
- u32 ind = (u32) index;
- struct tmem_oid oid = *(struct tmem_oid *)&key;
-
- if (likely(ind == index))
- (void)zcache_put_page(LOCAL_CLIENT, pool_id, &oid, index, page);
-}
-
-static int zcache_cleancache_get_page(int pool_id,
- struct cleancache_filekey key,
- pgoff_t index, struct page *page)
-{
- u32 ind = (u32) index;
- struct tmem_oid oid = *(struct tmem_oid *)&key;
- int ret = -1;
-
- if (likely(ind == index))
- ret = zcache_get_page(LOCAL_CLIENT, pool_id, &oid, index, page);
- return ret;
-}
-
-static void zcache_cleancache_flush_page(int pool_id,
- struct cleancache_filekey key,
- pgoff_t index)
-{
- u32 ind = (u32) index;
- struct tmem_oid oid = *(struct tmem_oid *)&key;
-
- if (likely(ind == index))
- (void)zcache_flush_page(LOCAL_CLIENT, pool_id, &oid, ind);
-}
-
-static void zcache_cleancache_flush_inode(int pool_id,
- struct cleancache_filekey key)
-{
- struct tmem_oid oid = *(struct tmem_oid *)&key;
-
- (void)zcache_flush_object(LOCAL_CLIENT, pool_id, &oid);
-}
-
-static void zcache_cleancache_flush_fs(int pool_id)
-{
- if (pool_id >= 0)
- (void)zcache_destroy_pool(LOCAL_CLIENT, pool_id);
-}
-
-static int zcache_cleancache_init_fs(size_t pagesize)
-{
- BUG_ON(sizeof(struct cleancache_filekey) !=
- sizeof(struct tmem_oid));
- BUG_ON(pagesize != PAGE_SIZE);
- return zcache_new_pool(LOCAL_CLIENT, 0);
-}
-
-static int zcache_cleancache_init_shared_fs(char *uuid, size_t pagesize)
-{
- /* shared pools are unsupported and map to private */
- BUG_ON(sizeof(struct cleancache_filekey) !=
- sizeof(struct tmem_oid));
- BUG_ON(pagesize != PAGE_SIZE);
- return zcache_new_pool(LOCAL_CLIENT, 0);
-}
-
-static struct cleancache_ops zcache_cleancache_ops = {
- .put_page = zcache_cleancache_put_page,
- .get_page = zcache_cleancache_get_page,
- .invalidate_page = zcache_cleancache_flush_page,
- .invalidate_inode = zcache_cleancache_flush_inode,
- .invalidate_fs = zcache_cleancache_flush_fs,
- .init_shared_fs = zcache_cleancache_init_shared_fs,
- .init_fs = zcache_cleancache_init_fs
-};
-
-struct cleancache_ops zcache_cleancache_register_ops(void)
-{
- struct cleancache_ops old_ops =
- cleancache_register_ops(&zcache_cleancache_ops);
-
- return old_ops;
-}
-
-static int __init qcache_init(void)
-{
- int ret = 0;
- struct qcache_info *qc = &qcache_info;
- struct fmem_data *fdp;
- int bitmap_size;
- unsigned int cpu;
- struct cleancache_ops old_ops;
-
-#ifdef CONFIG_SYSFS
- ret = sysfs_create_group(mm_kobj, &qcache_attr_group);
- if (ret) {
- pr_err("qcache: can't create sysfs\n");
- goto out;
- }
-#endif /* CONFIG_SYSFS */
-
- fdp = fmem_get_info();
- qc->addr = fdp->virt;
- qc->pages = fdp->size >> PAGE_SHIFT;
- if (!qc->pages)
- goto out;
-
- tmem_register_hostops(&zcache_hostops);
- tmem_register_pamops(&zcache_pamops);
- for_each_online_cpu(cpu) {
- per_cpu(zcache_dstmem, cpu) = (void *)__get_free_pages(
- GFP_KERNEL | __GFP_REPEAT,
- LZO_DSTMEM_PAGE_ORDER),
- per_cpu(zcache_workmem, cpu) =
- kzalloc(LZO1X_MEM_COMPRESS,
- GFP_KERNEL | __GFP_REPEAT);
- }
- zcache_objnode_cache = kmem_cache_create("zcache_objnode",
- sizeof(struct tmem_objnode), 0, 0, NULL);
- zcache_obj_cache = kmem_cache_create("zcache_obj",
- sizeof(struct tmem_obj), 0, 0, NULL);
- ret = zcache_new_client(LOCAL_CLIENT);
- if (ret) {
- pr_err("qcache: can't create client\n");
- goto out;
- }
-
- zbud_init();
- old_ops = zcache_cleancache_register_ops();
- pr_info("qcache: cleancache enabled using kernel "
- "transcendent memory and compression buddies\n");
- if (old_ops.init_fs != NULL)
- pr_warning("qcache: cleancache_ops overridden");
-
-
- bitmap_size = BITS_TO_LONGS(qc->pages) * sizeof(long);
-
- qc->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
- if (!qc->bitmap) {
- pr_info("can't allocate qcache bitmap!\n");
- ret = -ENOMEM;
- goto out;
- }
- spin_lock_init(&qc->lock);
-
- fmem_set_state(FMEM_T_STATE);
-
-out:
- return ret;
-}
-
-module_init(qcache_init)
diff --git a/drivers/staging/qcache/tmem.c b/drivers/staging/qcache/tmem.c
deleted file mode 100644
index 40f2246..0000000
--- a/drivers/staging/qcache/tmem.c
+++ /dev/null
@@ -1,833 +0,0 @@
-/*
- * In-kernel transcendent memory (generic implementation)
- *
- * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
- * Copyright (c) 2011, The Linux Foundation. All rights reserved.
- *
- * The primary purpose of Transcedent Memory ("tmem") is to map object-oriented
- * "handles" (triples containing a pool id, and object id, and an index), to
- * pages in a page-accessible memory (PAM). Tmem references the PAM pages via
- * an abstract "pampd" (PAM page-descriptor), which can be operated on by a
- * set of functions (pamops). Each pampd contains some representation of
- * PAGE_SIZE bytes worth of data. Tmem must support potentially millions of
- * pages and must be able to insert, find, and delete these pages at a
- * potential frequency of thousands per second concurrently across many CPUs,
- * (and, if used with KVM, across many vcpus across many guests).
- * Tmem is tracked with a hierarchy of data structures, organized by
- * the elements in a handle-tuple: pool_id, object_id, and page index.
- * One or more "clients" (e.g. guests) each provide one or more tmem_pools.
- * Each pool, contains a hash table of rb_trees of tmem_objs. Each
- * tmem_obj contains a radix-tree-like tree of pointers, with intermediate
- * nodes called tmem_objnodes. Each leaf pointer in this tree points to
- * a pampd, which is accessible only through a small set of callbacks
- * registered by the PAM implementation (see tmem_register_pamops). Tmem
- * does all memory allocation via a set of callbacks registered by the tmem
- * host implementation (e.g. see tmem_register_hostops).
- */
-
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/atomic.h>
-
-#include "tmem.h"
-
-/* data structure sentinels used for debugging... see tmem.h */
-#define POOL_SENTINEL 0x87658765
-#define OBJ_SENTINEL 0x12345678
-#define OBJNODE_SENTINEL 0xfedcba09
-
-static bool tmem_enabled;
-
-static void lock_tmem_state(void)
-{
- lock_fmem_state();
-}
-
-static void unlock_tmem_state(void)
-{
- unlock_fmem_state();
-}
-
-/*
- * A tmem host implementation must use this function to register callbacks
- * for memory allocation.
- */
-static struct tmem_hostops tmem_hostops;
-
-static void tmem_objnode_tree_init(void);
-
-void tmem_register_hostops(struct tmem_hostops *m)
-{
- tmem_objnode_tree_init();
- tmem_hostops = *m;
-}
-
-/*
- * A tmem host implementation must use this function to register
- * callbacks for a page-accessible memory (PAM) implementation
- */
-static struct tmem_pamops tmem_pamops;
-
-void tmem_register_pamops(struct tmem_pamops *m)
-{
- tmem_pamops = *m;
-}
-
-/*
- * Oid's are potentially very sparse and tmem_objs may have an indeterminately
- * short life, being added and deleted at a relatively high frequency.
- * So an rb_tree is an ideal data structure to manage tmem_objs. But because
- * of the potentially huge number of tmem_objs, each pool manages a hashtable
- * of rb_trees to reduce search, insert, delete, and rebalancing time.
- * Each hashbucket also has a lock to manage concurrent access.
- *
- * The following routines manage tmem_objs. When any tmem_obj is accessed,
- * the hashbucket lock must be held.
- */
-
-/* searches for object==oid in pool, returns locked object if found */
-static struct tmem_obj *tmem_obj_find(struct tmem_hashbucket *hb,
- struct tmem_oid *oidp)
-{
- struct rb_node *rbnode;
- struct tmem_obj *obj;
-
- rbnode = hb->obj_rb_root.rb_node;
- while (rbnode) {
- BUG_ON(RB_EMPTY_NODE(rbnode));
- obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node);
- switch (tmem_oid_compare(oidp, &obj->oid)) {
- case 0: /* equal */
- goto out;
- case -1:
- rbnode = rbnode->rb_left;
- break;
- case 1:
- rbnode = rbnode->rb_right;
- break;
- }
- }
- obj = NULL;
-out:
- return obj;
-}
-
-static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *);
-
-/* free an object that has no more pampds in it */
-static void tmem_obj_free(struct tmem_obj *obj, struct tmem_hashbucket *hb)
-{
- struct tmem_pool *pool;
-
- BUG_ON(obj == NULL);
- ASSERT_SENTINEL(obj, OBJ);
- BUG_ON(obj->pampd_count > 0);
- pool = obj->pool;
- BUG_ON(pool == NULL);
- if (obj->objnode_tree_root != NULL) /* may be "stump" with no leaves */
- tmem_pampd_destroy_all_in_obj(obj);
- BUG_ON(obj->objnode_tree_root != NULL);
- BUG_ON((long)obj->objnode_count != 0);
- atomic_dec(&pool->obj_count);
- BUG_ON(atomic_read(&pool->obj_count) < 0);
- INVERT_SENTINEL(obj, OBJ);
- obj->pool = NULL;
- tmem_oid_set_invalid(&obj->oid);
- rb_erase(&obj->rb_tree_node, &hb->obj_rb_root);
-}
-
-/*
- * initialize, and insert an tmem_object_root (called only if find failed)
- */
-static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb,
- struct tmem_pool *pool,
- struct tmem_oid *oidp)
-{
- struct rb_root *root = &hb->obj_rb_root;
- struct rb_node **new = &(root->rb_node), *parent = NULL;
- struct tmem_obj *this;
-
- BUG_ON(pool == NULL);
- atomic_inc(&pool->obj_count);
- obj->objnode_tree_height = 0;
- obj->objnode_tree_root = NULL;
- obj->pool = pool;
- obj->oid = *oidp;
- obj->objnode_count = 0;
- obj->pampd_count = 0;
- (*tmem_pamops.new_obj)(obj);
- SET_SENTINEL(obj, OBJ);
- while (*new) {
- BUG_ON(RB_EMPTY_NODE(*new));
- this = rb_entry(*new, struct tmem_obj, rb_tree_node);
- parent = *new;
- switch (tmem_oid_compare(oidp, &this->oid)) {
- case 0:
- BUG(); /* already present; should never happen! */
- break;
- case -1:
- new = &(*new)->rb_left;
- break;
- case 1:
- new = &(*new)->rb_right;
- break;
- }
- }
- rb_link_node(&obj->rb_tree_node, parent, new);
- rb_insert_color(&obj->rb_tree_node, root);
-}
-
-/*
- * Tmem is managed as a set of tmem_pools with certain attributes, such as
- * "ephemeral" vs "persistent". These attributes apply to all tmem_objs
- * and all pampds that belong to a tmem_pool. A tmem_pool is created
- * or deleted relatively rarely (for example, when a filesystem is
- * mounted or unmounted.
- */
-
-/* flush all data from a pool and, optionally, free it */
-static void tmem_pool_flush(struct tmem_pool *pool, bool destroy)
-{
- struct rb_node *rbnode;
- struct tmem_obj *obj;
- struct tmem_hashbucket *hb = &pool->hashbucket[0];
- int i;
-
- BUG_ON(pool == NULL);
- for (i = 0; i < TMEM_HASH_BUCKETS; i++, hb++) {
- spin_lock(&hb->lock);
- rbnode = rb_first(&hb->obj_rb_root);
- while (rbnode != NULL) {
- obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node);
- rbnode = rb_next(rbnode);
- tmem_pampd_destroy_all_in_obj(obj);
- tmem_obj_free(obj, hb);
- (*tmem_hostops.obj_free)(obj, pool);
- }
- spin_unlock(&hb->lock);
- }
- if (destroy)
- list_del(&pool->pool_list);
-}
-
-/*
- * A tmem_obj contains a radix-tree-like tree in which the intermediate
- * nodes are called tmem_objnodes. (The kernel lib/radix-tree.c implementation
- * is very specialized and tuned for specific uses and is not particularly
- * suited for use from this code, though some code from the core algorithms has
- * been reused, thus the copyright notices below). Each tmem_objnode contains
- * a set of pointers which point to either a set of intermediate tmem_objnodes
- * or a set of of pampds.
- *
- * Portions Copyright (C) 2001 Momchil Velikov
- * Portions Copyright (C) 2001 Christoph Hellwig
- * Portions Copyright (C) 2005 SGI, Christoph Lameter <clameter@sgi.com>
- */
-
-struct tmem_objnode_tree_path {
- struct tmem_objnode *objnode;
- int offset;
-};
-
-/* objnode height_to_maxindex translation */
-static unsigned long tmem_objnode_tree_h2max[OBJNODE_TREE_MAX_PATH + 1];
-
-static void tmem_objnode_tree_init(void)
-{
- unsigned int ht, tmp;
-
- for (ht = 0; ht < ARRAY_SIZE(tmem_objnode_tree_h2max); ht++) {
- tmp = ht * OBJNODE_TREE_MAP_SHIFT;
- if (tmp >= OBJNODE_TREE_INDEX_BITS)
- tmem_objnode_tree_h2max[ht] = ~0UL;
- else
- tmem_objnode_tree_h2max[ht] =
- (~0UL >> (OBJNODE_TREE_INDEX_BITS - tmp - 1)) >> 1;
- }
-}
-
-static struct tmem_objnode *tmem_objnode_alloc(struct tmem_obj *obj)
-{
- struct tmem_objnode *objnode;
-
- ASSERT_SENTINEL(obj, OBJ);
- BUG_ON(obj->pool == NULL);
- ASSERT_SENTINEL(obj->pool, POOL);
- objnode = (*tmem_hostops.objnode_alloc)(obj->pool);
- if (unlikely(objnode == NULL))
- goto out;
- objnode->obj = obj;
- SET_SENTINEL(objnode, OBJNODE);
- memset(&objnode->slots, 0, sizeof(objnode->slots));
- objnode->slots_in_use = 0;
- obj->objnode_count++;
-out:
- return objnode;
-}
-
-static void tmem_objnode_free(struct tmem_objnode *objnode)
-{
- struct tmem_pool *pool;
- int i;
-
- BUG_ON(objnode == NULL);
- for (i = 0; i < OBJNODE_TREE_MAP_SIZE; i++)
- BUG_ON(objnode->slots[i] != NULL);
- ASSERT_SENTINEL(objnode, OBJNODE);
- INVERT_SENTINEL(objnode, OBJNODE);
- BUG_ON(objnode->obj == NULL);
- ASSERT_SENTINEL(objnode->obj, OBJ);
- pool = objnode->obj->pool;
- BUG_ON(pool == NULL);
- ASSERT_SENTINEL(pool, POOL);
- objnode->obj->objnode_count--;
- objnode->obj = NULL;
- (*tmem_hostops.objnode_free)(objnode, pool);
-}
-
-/*
- * lookup index in object and return associated pampd (or NULL if not found)
- */
-static void **__tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index)
-{
- unsigned int height, shift;
- struct tmem_objnode **slot = NULL;
-
- BUG_ON(obj == NULL);
- ASSERT_SENTINEL(obj, OBJ);
- BUG_ON(obj->pool == NULL);
- ASSERT_SENTINEL(obj->pool, POOL);
-
- height = obj->objnode_tree_height;
- if (index > tmem_objnode_tree_h2max[obj->objnode_tree_height])
- goto out;
- if (height == 0 && obj->objnode_tree_root) {
- slot = &obj->objnode_tree_root;
- goto out;
- }
- shift = (height-1) * OBJNODE_TREE_MAP_SHIFT;
- slot = &obj->objnode_tree_root;
- while (height > 0) {
- if (*slot == NULL)
- goto out;
- slot = (struct tmem_objnode **)
- ((*slot)->slots +
- ((index >> shift) & OBJNODE_TREE_MAP_MASK));
- shift -= OBJNODE_TREE_MAP_SHIFT;
- height--;
- }
-out:
- return slot != NULL ? (void **)slot : NULL;
-}
-
-static void *tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index)
-{
- struct tmem_objnode **slot;
-
- slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index);
- return slot != NULL ? *slot : NULL;
-}
-
-static void *tmem_pampd_replace_in_obj(struct tmem_obj *obj, uint32_t index,
- void *new_pampd)
-{
- struct tmem_objnode **slot;
- void *ret = NULL;
-
- slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index);
- if ((slot != NULL) && (*slot != NULL)) {
- void *old_pampd = *(void **)slot;
- *(void **)slot = new_pampd;
- (*tmem_pamops.free)(old_pampd, obj->pool, NULL, 0);
- ret = new_pampd;
- }
- return ret;
-}
-
-static int tmem_pampd_add_to_obj(struct tmem_obj *obj, uint32_t index,
- void *pampd)
-{
- int ret = 0;
- struct tmem_objnode *objnode = NULL, *newnode, *slot;
- unsigned int height, shift;
- int offset = 0;
-
- /* if necessary, extend the tree to be higher */
- if (index > tmem_objnode_tree_h2max[obj->objnode_tree_height]) {
- height = obj->objnode_tree_height + 1;
- if (index > tmem_objnode_tree_h2max[height])
- while (index > tmem_objnode_tree_h2max[height])
- height++;
- if (obj->objnode_tree_root == NULL) {
- obj->objnode_tree_height = height;
- goto insert;
- }
- do {
- newnode = tmem_objnode_alloc(obj);
- if (!newnode) {
- ret = -ENOMEM;
- goto out;
- }
- newnode->slots[0] = obj->objnode_tree_root;
- newnode->slots_in_use = 1;
- obj->objnode_tree_root = newnode;
- obj->objnode_tree_height++;
- } while (height > obj->objnode_tree_height);
- }
-insert:
- slot = obj->objnode_tree_root;
- height = obj->objnode_tree_height;
- shift = (height-1) * OBJNODE_TREE_MAP_SHIFT;
- while (height > 0) {
- if (slot == NULL) {
- /* add a child objnode. */
- slot = tmem_objnode_alloc(obj);
- if (!slot) {
- ret = -ENOMEM;
- goto out;
- }
- if (objnode) {
-
- objnode->slots[offset] = slot;
- objnode->slots_in_use++;
- } else
- obj->objnode_tree_root = slot;
- }
- /* go down a level */
- offset = (index >> shift) & OBJNODE_TREE_MAP_MASK;
- objnode = slot;
- slot = objnode->slots[offset];
- shift -= OBJNODE_TREE_MAP_SHIFT;
- height--;
- }
- BUG_ON(slot != NULL);
- if (objnode) {
- objnode->slots_in_use++;
- objnode->slots[offset] = pampd;
- } else
- obj->objnode_tree_root = pampd;
- obj->pampd_count++;
-out:
- return ret;
-}
-
-static void *tmem_pampd_delete_from_obj(struct tmem_obj *obj, uint32_t index)
-{
- struct tmem_objnode_tree_path path[OBJNODE_TREE_MAX_PATH + 1];
- struct tmem_objnode_tree_path *pathp = path;
- struct tmem_objnode *slot = NULL;
- unsigned int height, shift;
- int offset;
-
- BUG_ON(obj == NULL);
- ASSERT_SENTINEL(obj, OBJ);
- BUG_ON(obj->pool == NULL);
- ASSERT_SENTINEL(obj->pool, POOL);
- height = obj->objnode_tree_height;
- if (index > tmem_objnode_tree_h2max[height])
- goto out;
- slot = obj->objnode_tree_root;
- if (height == 0 && obj->objnode_tree_root) {
- obj->objnode_tree_root = NULL;
- goto out;
- }
- shift = (height - 1) * OBJNODE_TREE_MAP_SHIFT;
- pathp->objnode = NULL;
- do {
- if (slot == NULL)
- goto out;
- pathp++;
- offset = (index >> shift) & OBJNODE_TREE_MAP_MASK;
- pathp->offset = offset;
- pathp->objnode = slot;
- slot = slot->slots[offset];
- shift -= OBJNODE_TREE_MAP_SHIFT;
- height--;
- } while (height > 0);
- if (slot == NULL)
- goto out;
- while (pathp->objnode) {
- pathp->objnode->slots[pathp->offset] = NULL;
- pathp->objnode->slots_in_use--;
- if (pathp->objnode->slots_in_use) {
- if (pathp->objnode == obj->objnode_tree_root) {
- while (obj->objnode_tree_height > 0 &&
- obj->objnode_tree_root->slots_in_use == 1 &&
- obj->objnode_tree_root->slots[0]) {
- struct tmem_objnode *to_free =
- obj->objnode_tree_root;
-
- obj->objnode_tree_root =
- to_free->slots[0];
- obj->objnode_tree_height--;
- to_free->slots[0] = NULL;
- to_free->slots_in_use = 0;
- tmem_objnode_free(to_free);
- }
- }
- goto out;
- }
- tmem_objnode_free(pathp->objnode); /* 0 slots used, free it */
- pathp--;
- }
- obj->objnode_tree_height = 0;
- obj->objnode_tree_root = NULL;
-
-out:
- if (slot != NULL)
- obj->pampd_count--;
- BUG_ON(obj->pampd_count < 0);
- return slot;
-}
-
-/* recursively walk the objnode_tree destroying pampds and objnodes */
-static void tmem_objnode_node_destroy(struct tmem_obj *obj,
- struct tmem_objnode *objnode,
- unsigned int ht)
-{
- int i;
-
- if (ht == 0)
- return;
- for (i = 0; i < OBJNODE_TREE_MAP_SIZE; i++) {
- if (objnode->slots[i]) {
- if (ht == 1) {
- obj->pampd_count--;
- (*tmem_pamops.free)(objnode->slots[i],
- obj->pool, NULL, 0);
- objnode->slots[i] = NULL;
- continue;
- }
- tmem_objnode_node_destroy(obj, objnode->slots[i], ht-1);
- tmem_objnode_free(objnode->slots[i]);
- objnode->slots[i] = NULL;
- }
- }
-}
-
-static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj)
-{
- if (obj->objnode_tree_root == NULL)
- return;
- if (obj->objnode_tree_height == 0) {
- obj->pampd_count--;
- (*tmem_pamops.free)(obj->objnode_tree_root, obj->pool, NULL, 0);
- } else {
- tmem_objnode_node_destroy(obj, obj->objnode_tree_root,
- obj->objnode_tree_height);
- tmem_objnode_free(obj->objnode_tree_root);
- obj->objnode_tree_height = 0;
- }
- obj->objnode_tree_root = NULL;
- (*tmem_pamops.free_obj)(obj->pool, obj);
-}
-
-/*
- * Tmem is operated on by a set of well-defined actions:
- * "put", "get", "flush", "flush_object", "new pool" and "destroy pool".
- * (The tmem ABI allows for subpages and exchanges but these operations
- * are not included in this implementation.)
- *
- * These "tmem core" operations are implemented in the following functions.
- */
-
-/*
- * "Put" a page, e.g. copy a page from the kernel into newly allocated
- * PAM space (if such space is available). Tmem_put is complicated by
- * a corner case: What if a page with matching handle already exists in
- * tmem? To guarantee coherency, one of two actions is necessary: Either
- * the data for the page must be overwritten, or the page must be
- * "flushed" so that the data is not accessible to a subsequent "get".
- * Since these "duplicate puts" are relatively rare, this implementation
- * always flushes for simplicity.
- */
-int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
- char *data, size_t size, bool raw, bool ephemeral)
-{
- struct tmem_obj *obj = NULL, *objfound = NULL, *objnew = NULL;
- void *pampd = NULL, *pampd_del = NULL;
- int ret = -ENOMEM;
- struct tmem_hashbucket *hb;
-
- lock_tmem_state();
- if (!tmem_enabled)
- goto disabled;
- hb = &pool->hashbucket[tmem_oid_hash(oidp)];
- spin_lock(&hb->lock);
- obj = objfound = tmem_obj_find(hb, oidp);
- if (obj != NULL) {
- pampd = tmem_pampd_lookup_in_obj(objfound, index);
- if (pampd != NULL) {
- /* if found, is a dup put, flush the old one */
- pampd_del = tmem_pampd_delete_from_obj(obj, index);
- BUG_ON(pampd_del != pampd);
- (*tmem_pamops.free)(pampd, pool, oidp, index);
- if (obj->pampd_count == 0) {
- objnew = obj;
- objfound = NULL;
- }
- pampd = NULL;
- }
- } else {
- obj = objnew = (*tmem_hostops.obj_alloc)(pool);
- if (unlikely(obj == NULL)) {
- ret = -ENOMEM;
- goto out;
- }
- tmem_obj_init(obj, hb, pool, oidp);
- }
- BUG_ON(obj == NULL);
- BUG_ON(((objnew != obj) && (objfound != obj)) || (objnew == objfound));
- pampd = (*tmem_pamops.create)(data, size, raw, ephemeral,
- obj->pool, &obj->oid, index);
- if (unlikely(pampd == NULL))
- goto free;
- ret = tmem_pampd_add_to_obj(obj, index, pampd);
- if (unlikely(ret == -ENOMEM))
- /* may have partially built objnode tree ("stump") */
- goto delete_and_free;
- goto out;
-
-delete_and_free:
- (void)tmem_pampd_delete_from_obj(obj, index);
-free:
- if (pampd)
- (*tmem_pamops.free)(pampd, pool, NULL, 0);
- if (objnew) {
- tmem_obj_free(objnew, hb);
- (*tmem_hostops.obj_free)(objnew, pool);
- }
-out:
- spin_unlock(&hb->lock);
-disabled:
- unlock_tmem_state();
- return ret;
-}
-
-/*
- * "Get" a page, e.g. if one can be found, copy the tmem page with the
- * matching handle from PAM space to the kernel. By tmem definition,
- * when a "get" is successful on an ephemeral page, the page is "flushed",
- * and when a "get" is successful on a persistent page, the page is retained
- * in tmem. Note that to preserve
- * coherency, "get" can never be skipped if tmem contains the data.
- * That is, if a get is done with a certain handle and fails, any
- * subsequent "get" must also fail (unless of course there is a
- * "put" done with the same handle).
-
- */
-int tmem_get(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
- char *data, size_t *size, bool raw, int get_and_free)
-{
- struct tmem_obj *obj;
- void *pampd;
- bool ephemeral = is_ephemeral(pool);
- int ret = -1;
- struct tmem_hashbucket *hb;
- bool free = (get_and_free == 1) || ((get_and_free == 0) && ephemeral);
- bool lock_held = false;
-
- lock_tmem_state();
- if (!tmem_enabled)
- goto disabled;
- hb = &pool->hashbucket[tmem_oid_hash(oidp)];
- spin_lock(&hb->lock);
- lock_held = true;
- obj = tmem_obj_find(hb, oidp);
- if (obj == NULL)
- goto out;
- if (free)
- pampd = tmem_pampd_delete_from_obj(obj, index);
- else
- pampd = tmem_pampd_lookup_in_obj(obj, index);
- if (pampd == NULL)
- goto out;
- if (free) {
- if (obj->pampd_count == 0) {
- tmem_obj_free(obj, hb);
- (*tmem_hostops.obj_free)(obj, pool);
- obj = NULL;
- }
- }
- if (tmem_pamops.is_remote(pampd)) {
- lock_held = false;
- spin_unlock(&hb->lock);
- }
- if (free)
- ret = (*tmem_pamops.get_data_and_free)(
- data, size, raw, pampd, pool, oidp, index);
- else
- ret = (*tmem_pamops.get_data)(
- data, size, raw, pampd, pool, oidp, index);
- if (ret < 0)
- goto out;
- ret = 0;
-out:
- if (lock_held)
- spin_unlock(&hb->lock);
-disabled:
- unlock_tmem_state();
- return ret;
-}
-
-/*
- * If a page in tmem matches the handle, "flush" this page from tmem such
- * that any subsequent "get" does not succeed (unless, of course, there
- * was another "put" with the same handle).
- */
-int tmem_flush_page(struct tmem_pool *pool,
- struct tmem_oid *oidp, uint32_t index)
-{
- struct tmem_obj *obj;
- void *pampd;
- int ret = -1;
- struct tmem_hashbucket *hb;
-
- hb = &pool->hashbucket[tmem_oid_hash(oidp)];
- spin_lock(&hb->lock);
- obj = tmem_obj_find(hb, oidp);
- if (obj == NULL)
- goto out;
- pampd = tmem_pampd_delete_from_obj(obj, index);
- if (pampd == NULL)
- goto out;
- (*tmem_pamops.free)(pampd, pool, oidp, index);
- if (obj->pampd_count == 0) {
- tmem_obj_free(obj, hb);
- (*tmem_hostops.obj_free)(obj, pool);
- }
- ret = 0;
-
-out:
- spin_unlock(&hb->lock);
- return ret;
-}
-
-/*
- * If a page in tmem matches the handle, replace the page so that any
- * subsequent "get" gets the new page. Returns 0 if
- * there was a page to replace, else returns -1.
- */
-int tmem_replace(struct tmem_pool *pool, struct tmem_oid *oidp,
- uint32_t index, void *new_pampd)
-{
- struct tmem_obj *obj;
- int ret = -1;
- struct tmem_hashbucket *hb;
-
- lock_tmem_state();
- if (!tmem_enabled)
- goto disabled;
- hb = &pool->hashbucket[tmem_oid_hash(oidp)];
- spin_lock(&hb->lock);
- obj = tmem_obj_find(hb, oidp);
- if (obj == NULL)
- goto out;
- new_pampd = tmem_pampd_replace_in_obj(obj, index, new_pampd);
- ret = (*tmem_pamops.replace_in_obj)(new_pampd, obj);
-out:
- spin_unlock(&hb->lock);
-disabled:
- unlock_tmem_state();
- return ret;
-}
-
-/*
- * "Flush" all pages in tmem matching this oid.
- */
-int tmem_flush_object(struct tmem_pool *pool, struct tmem_oid *oidp)
-{
- struct tmem_obj *obj;
- struct tmem_hashbucket *hb;
- int ret = -1;
-
- hb = &pool->hashbucket[tmem_oid_hash(oidp)];
- spin_lock(&hb->lock);
- obj = tmem_obj_find(hb, oidp);
- if (obj == NULL)
- goto out;
- tmem_pampd_destroy_all_in_obj(obj);
- tmem_obj_free(obj, hb);
- (*tmem_hostops.obj_free)(obj, pool);
- ret = 0;
-
-out:
- spin_unlock(&hb->lock);
- return ret;
-}
-
-/*
- * "Flush" all pages (and tmem_objs) from this tmem_pool and disable
- * all subsequent access to this tmem_pool.
- */
-int tmem_destroy_pool(struct tmem_pool *pool)
-{
- int ret = -1;
-
- if (pool == NULL)
- goto out;
- tmem_pool_flush(pool, 1);
- ret = 0;
-out:
- return ret;
-}
-
-int tmem_flush_pool(struct tmem_pool *pool)
-{
- int ret = -1;
-
- if (pool == NULL)
- goto out;
- tmem_pool_flush(pool, 0);
- ret = 0;
-out:
- return ret;
-}
-
-static LIST_HEAD(tmem_global_pool_list);
-
-/*
- * Create a new tmem_pool with the provided flag and return
- * a pool id provided by the tmem host implementation.
- */
-void tmem_new_pool(struct tmem_pool *pool, uint32_t flags)
-{
- int persistent = flags & TMEM_POOL_PERSIST;
- int shared = flags & TMEM_POOL_SHARED;
- struct tmem_hashbucket *hb = &pool->hashbucket[0];
- int i;
-
- for (i = 0; i < TMEM_HASH_BUCKETS; i++, hb++) {
- hb->obj_rb_root = RB_ROOT;
- spin_lock_init(&hb->lock);
- }
- INIT_LIST_HEAD(&pool->pool_list);
- atomic_set(&pool->obj_count, 0);
- SET_SENTINEL(pool, POOL);
- list_add_tail(&pool->pool_list, &tmem_global_pool_list);
- pool->persistent = persistent;
- pool->shared = shared;
-}
-
-/* The following must be called with tmem state locked */
-static void tmem_cleanup(void)
-{
- (*tmem_hostops.flush_all_obj)();
-}
-
-void tmem_enable(void)
-{
- pr_info("turning tmem on\n");
- tmem_enabled = true;
-
- (*tmem_hostops.control)(false);
-}
-
-void tmem_disable(void)
-{
- pr_info("turning tmem off\n");
- tmem_enabled = false;
-
- tmem_cleanup();
- (*tmem_hostops.control)(true);
-}
diff --git a/drivers/staging/qcache/tmem.h b/drivers/staging/qcache/tmem.h
deleted file mode 100644
index 359c201..0000000
--- a/drivers/staging/qcache/tmem.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * tmem.h
- *
- * Transcendent memory
- *
- * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
- */
-
-#ifndef _TMEM_H_
-#define _TMEM_H_
-
-#include <linux/types.h>
-#include <linux/highmem.h>
-#include <linux/hash.h>
-#include <linux/atomic.h>
-#include <linux/fmem.h>
-
-/*
- * These are pre-defined by the Xen<->Linux ABI
- */
-#define TMEM_PUT_PAGE 4
-#define TMEM_GET_PAGE 5
-#define TMEM_FLUSH_PAGE 6
-#define TMEM_FLUSH_OBJECT 7
-#define TMEM_POOL_PERSIST 1
-#define TMEM_POOL_SHARED 2
-#define TMEM_POOL_PRECOMPRESSED 4
-#define TMEM_POOL_PAGESIZE_SHIFT 4
-#define TMEM_POOL_PAGESIZE_MASK 0xf
-#define TMEM_POOL_RESERVED_BITS 0x00ffff00
-
-/*
- * sentinels have proven very useful for debugging but can be removed
- * or disabled before final merge.
- */
-#define SENTINELS
-#ifdef SENTINELS
-#define DECL_SENTINEL uint32_t sentinel;
-#define SET_SENTINEL(_x, _y) (_x->sentinel = _y##_SENTINEL)
-#define INVERT_SENTINEL(_x, _y) (_x->sentinel = ~_y##_SENTINEL)
-#define ASSERT_SENTINEL(_x, _y) WARN_ON(_x->sentinel != _y##_SENTINEL)
-#define ASSERT_INVERTED_SENTINEL(_x, _y) WARN_ON(_x->sentinel != ~_y##_SENTINEL)
-#else
-#define DECL_SENTINEL
-#define SET_SENTINEL(_x, _y) do { } while (0)
-#define INVERT_SENTINEL(_x, _y) do { } while (0)
-#define ASSERT_SENTINEL(_x, _y) do { } while (0)
-#define ASSERT_INVERTED_SENTINEL(_x, _y) do { } while (0)
-#endif
-
-/*
- * A pool is the highest-level data structure managed by tmem and
- * usually corresponds to a large independent set of pages such as
- * a filesystem. Each pool has an id, and certain attributes and counters.
- * It also contains a set of hash buckets, each of which contains an rbtree
- * of objects and a lock to manage concurrency within the pool.
- */
-
-#define TMEM_HASH_BUCKET_BITS 8
-#define TMEM_HASH_BUCKETS (1<<TMEM_HASH_BUCKET_BITS)
-
-struct tmem_hashbucket {
- struct rb_root obj_rb_root;
- spinlock_t lock;
-};
-
-struct tmem_pool {
- void *client; /* "up" for some clients, avoids table lookup */
- struct list_head pool_list;
- uint32_t pool_id;
- bool persistent;
- bool shared;
- atomic_t obj_count;
- atomic_t refcount;
- struct tmem_hashbucket hashbucket[TMEM_HASH_BUCKETS];
- DECL_SENTINEL
-};
-
-#define is_persistent(_p) (_p->persistent)
-#define is_ephemeral(_p) (!(_p->persistent))
-
-/*
- * An object id ("oid") is large: 192-bits (to ensure, for example, files
- * in a modern filesystem can be uniquely identified).
- */
-
-struct tmem_oid {
- uint64_t oid[3];
-};
-
-static inline void tmem_oid_set_invalid(struct tmem_oid *oidp)
-{
- oidp->oid[0] = oidp->oid[1] = oidp->oid[2] = -1UL;
-}
-
-static inline bool tmem_oid_valid(struct tmem_oid *oidp)
-{
- return oidp->oid[0] != -1UL || oidp->oid[1] != -1UL ||
- oidp->oid[2] != -1UL;
-}
-
-static inline int tmem_oid_compare(struct tmem_oid *left,
- struct tmem_oid *right)
-{
- int ret;
-
- if (left->oid[2] == right->oid[2]) {
- if (left->oid[1] == right->oid[1]) {
- if (left->oid[0] == right->oid[0])
- ret = 0;
- else if (left->oid[0] < right->oid[0])
- ret = -1;
- else
- return 1;
- } else if (left->oid[1] < right->oid[1])
- ret = -1;
- else
- ret = 1;
- } else if (left->oid[2] < right->oid[2])
- ret = -1;
- else
- ret = 1;
- return ret;
-}
-
-static inline unsigned tmem_oid_hash(struct tmem_oid *oidp)
-{
- return hash_long(oidp->oid[0] ^ oidp->oid[1] ^ oidp->oid[2],
- TMEM_HASH_BUCKET_BITS);
-}
-
-/*
- * A tmem_obj contains an identifier (oid), pointers to the parent
- * pool and the rb_tree to which it belongs, counters, and an ordered
- * set of pampds, structured in a radix-tree-like tree. The intermediate
- * nodes of the tree are called tmem_objnodes.
- */
-
-struct tmem_objnode;
-
-struct tmem_obj {
- struct tmem_oid oid;
- struct tmem_pool *pool;
- struct rb_node rb_tree_node;
- struct tmem_objnode *objnode_tree_root;
- unsigned int objnode_tree_height;
- unsigned long objnode_count;
- long pampd_count;
- void *extra; /* for private use by pampd implementation */
- DECL_SENTINEL
-};
-
-#define OBJNODE_TREE_MAP_SHIFT 6
-#define OBJNODE_TREE_MAP_SIZE (1UL << OBJNODE_TREE_MAP_SHIFT)
-#define OBJNODE_TREE_MAP_MASK (OBJNODE_TREE_MAP_SIZE-1)
-#define OBJNODE_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long))
-#define OBJNODE_TREE_MAX_PATH \
- (OBJNODE_TREE_INDEX_BITS/OBJNODE_TREE_MAP_SHIFT + 2)
-
-struct tmem_objnode {
- struct tmem_obj *obj;
- DECL_SENTINEL
- void *slots[OBJNODE_TREE_MAP_SIZE];
- unsigned int slots_in_use;
-};
-
-/* pampd abstract datatype methods provided by the PAM implementation */
-struct tmem_pamops {
- void *(*create)(char *, size_t, bool, int,
- struct tmem_pool *, struct tmem_oid *, uint32_t);
- int (*get_data)(char *, size_t *, bool, void *, struct tmem_pool *,
- struct tmem_oid *, uint32_t);
- int (*get_data_and_free)(char *, size_t *, bool, void *,
- struct tmem_pool *, struct tmem_oid *,
- uint32_t);
- void (*free)(void *, struct tmem_pool *, struct tmem_oid *, uint32_t);
- void (*free_obj)(struct tmem_pool *, struct tmem_obj *);
- bool (*is_remote)(void *);
- void (*new_obj)(struct tmem_obj *);
- int (*replace_in_obj)(void *, struct tmem_obj *);
-};
-extern void tmem_register_pamops(struct tmem_pamops *m);
-
-/* memory allocation methods provided by the host implementation */
-struct tmem_hostops {
- struct tmem_obj *(*obj_alloc)(struct tmem_pool *);
- void (*obj_free)(struct tmem_obj *, struct tmem_pool *);
- struct tmem_objnode *(*objnode_alloc)(struct tmem_pool *);
- void (*objnode_free)(struct tmem_objnode *, struct tmem_pool *);
- void (*flush_all_obj)(void);
- void (*control)(bool);
-};
-extern void tmem_register_hostops(struct tmem_hostops *m);
-
-/* core tmem accessor functions */
-extern int tmem_put(struct tmem_pool *, struct tmem_oid *, uint32_t index,
- char *, size_t, bool, bool);
-extern int tmem_get(struct tmem_pool *, struct tmem_oid *, uint32_t index,
- char *, size_t *, bool, int);
-extern int tmem_replace(struct tmem_pool *, struct tmem_oid *, uint32_t index,
- void *);
-extern int tmem_flush_page(struct tmem_pool *, struct tmem_oid *,
- uint32_t index);
-extern int tmem_flush_object(struct tmem_pool *, struct tmem_oid *);
-extern int tmem_destroy_pool(struct tmem_pool *);
-extern int tmem_flush_pool(struct tmem_pool *);
-extern void tmem_new_pool(struct tmem_pool *, uint32_t);
-
-extern void tmem_enable(void);
-extern void tmem_disable(void);
-#endif /* _TMEM_H */
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 52608af..6933163 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -77,6 +77,9 @@
#define TSENS_EEPROM_8X10_1(n) ((n) + 0x1a4)
#define TSENS_EEPROM_8X10_1_OFFSET 8
+#define TSENS_EEPROM_8X10_2(n) ((n) + 0x1a8)
+#define TSENS_EEPROM_8X10_SPARE_1(n) ((n) + 0xd8)
+#define TSENS_EEPROM_8X10_SPARE_2(n) ((n) + 0xdc)
/* TSENS calibration Mask data */
#define TSENS_BASE1_MASK 0xff
@@ -207,6 +210,8 @@
#define TSENS_8X10_TSENS_CAL_SEL 0x70000000
#define TSENS1_8X10_POINT1_MASK 0x3f
#define TSENS1_8X10_POINT2_MASK 0xfc0
+#define TSENS_8X10_REDUN_SEL_MASK 0x6000000
+#define TSENS_8X10_REDUN_SEL_SHIFT 25
#define TSENS_BIT_APPEND 0x3
#define TSENS_CAL_DEGC_POINT1 30
@@ -559,6 +564,9 @@
unsigned int reg_cntl;
int code, hi_code, lo_code, code_err_chk, sensor_sw_id = 0, rc = 0;
+ if (!tm_sensor || trip < 0 || !temp)
+ return -EINVAL;
+
rc = tsens_get_sw_id_mapping(tm_sensor->sensor_hw_num, &sensor_sw_id);
if (rc < 0) {
pr_err("tsens mapping index not found\n");
@@ -721,17 +729,30 @@
int i, tsens_base0_data = 0, tsens0_point1 = 0, tsens1_point1 = 0;
int tsens0_point2 = 0, tsens1_point2 = 0;
int tsens_base1_data = 0, tsens_calibration_mode = 0;
- uint32_t calib_data[2];
+ uint32_t calib_data[2], calib_redun_sel;
uint32_t calib_tsens_point1_data[2], calib_tsens_point2_data[2];
if (tmdev->calibration_less_mode)
goto calibration_less_mode;
- calib_data[0] = readl_relaxed(
+ calib_redun_sel = readl_relaxed(
+ TSENS_EEPROM_8X10_2(tmdev->tsens_calib_addr));
+ calib_redun_sel = calib_redun_sel & TSENS_8X10_REDUN_SEL_MASK;
+ calib_redun_sel >>= TSENS_8X10_REDUN_SEL_SHIFT;
+ pr_debug("calib_redun_sel:%x\n", calib_redun_sel);
+
+ if (calib_redun_sel == TSENS_QFPROM_BACKUP_SEL) {
+ calib_data[0] = readl_relaxed(
+ TSENS_EEPROM_8X10_SPARE_1(tmdev->tsens_calib_addr));
+ calib_data[1] = readl_relaxed(
+ TSENS_EEPROM_8X10_SPARE_2(tmdev->tsens_calib_addr));
+ } else {
+ calib_data[0] = readl_relaxed(
TSENS_EEPROM_8X10_1(tmdev->tsens_calib_addr));
- calib_data[1] = readl_relaxed(
- (TSENS_EEPROM_8X10_1(tmdev->tsens_calib_addr) +
+ calib_data[1] = readl_relaxed(
+ (TSENS_EEPROM_8X10_1(tmdev->tsens_calib_addr) +
TSENS_EEPROM_8X10_1_OFFSET));
+ }
tsens_calibration_mode = (calib_data[0] & TSENS_8X10_TSENS_CAL_SEL)
>> TSENS_8X10_CAL_SEL_SHIFT;
@@ -1325,7 +1346,7 @@
static int get_device_tree_data(struct platform_device *pdev)
{
- const struct device_node *of_node = pdev->dev.of_node;
+ struct device_node *of_node = pdev->dev.of_node;
struct resource *res_mem = NULL;
u32 *tsens_slope_data;
u32 *sensor_id;
@@ -1353,8 +1374,13 @@
return rc;
};
- tsens_calib_mode = of_get_property(of_node,
- "qcom,calib-mode", NULL);
+ rc = of_property_read_string(of_node,
+ "qcom,calib-mode", &tsens_calib_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "missing calib-mode\n");
+ return -ENODEV;
+ }
+
if (!strncmp(tsens_calib_mode, "fuse_map1", 9))
calib_type = TSENS_CALIB_FUSE_MAP_8974;
else if (!strncmp(tsens_calib_mode, "fuse_map2", 9))
diff --git a/drivers/tty/n_smux.c b/drivers/tty/n_smux.c
index d1545dc..3794aa9 100644
--- a/drivers/tty/n_smux.c
+++ b/drivers/tty/n_smux.c
@@ -1187,8 +1187,11 @@
enable_powerdown = 1;
ch->local_state = SMUX_LCH_LOCAL_OPENED;
- if (ch->remote_state == SMUX_LCH_REMOTE_OPENED)
+ if (ch->remote_state == SMUX_LCH_REMOTE_OPENED) {
schedule_notify(lcid, SMUX_CONNECTED, NULL);
+ if (!(list_empty(&ch->tx_queue)))
+ list_channel(ch);
+ }
ret = 0;
} else if (ch->remote_mode == SMUX_LCH_MODE_REMOTE_LOOPBACK) {
SMUX_DBG("smux: Remote loopback OPEN ACK received\n");
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 7f669d8..9e22afd 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -192,7 +192,7 @@
ktime_t clk_off_delay;
enum msm_hs_clk_states_e clk_state;
enum msm_hs_clk_req_off_state_e clk_req_off_state;
-
+ atomic_t clk_count;
struct msm_hs_wakeup wakeup;
struct wake_lock dma_wake_lock; /* held while any DMA active */
@@ -241,6 +241,7 @@
static void msm_hs_start_rx_locked(struct uart_port *uport);
static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr);
static void flip_insert_work(struct work_struct *work);
+static void msm_hs_bus_voting(struct msm_hs_port *msm_uport, unsigned int vote);
#define UARTDM_TO_MSM(uart_port) \
container_of((uart_port), struct msm_hs_port, uport)
@@ -287,6 +288,61 @@
return ret;
}
+static int msm_hs_clock_vote(struct msm_hs_port *msm_uport)
+{
+ int rc = 0;
+
+ if (1 == atomic_inc_return(&msm_uport->clk_count)) {
+ msm_hs_bus_voting(msm_uport, BUS_SCALING);
+ /* Turn on core clk and iface clk */
+ rc = clk_prepare_enable(msm_uport->clk);
+ if (rc) {
+ dev_err(msm_uport->uport.dev,
+ "%s: Could not turn on core clk [%d]\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (msm_uport->pclk) {
+ rc = clk_prepare_enable(msm_uport->pclk);
+ if (rc) {
+ clk_disable_unprepare(msm_uport->clk);
+ dev_err(msm_uport->uport.dev,
+ "%s: Could not turn on pclk [%d]\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+ msm_uport->clk_state = MSM_HS_CLK_ON;
+ }
+
+
+ return rc;
+}
+
+static void msm_hs_clock_unvote(struct msm_hs_port *msm_uport)
+{
+ int rc = atomic_dec_return(&msm_uport->clk_count);
+
+ if (rc < 0) {
+ msm_hs_bus_voting(msm_uport, BUS_RESET);
+ WARN(rc, "msm_uport->clk_count < 0!");
+ dev_err(msm_uport->uport.dev,
+ "%s: Clocks count invalid [%d]\n", __func__,
+ atomic_read(&msm_uport->clk_count));
+ return;
+ }
+
+ if (0 == rc) {
+ msm_hs_bus_voting(msm_uport, BUS_RESET);
+ /* Turn off the core clk and iface clk*/
+ clk_disable_unprepare(msm_uport->clk);
+ if (msm_uport->pclk)
+ clk_disable_unprepare(msm_uport->pclk);
+ msm_uport->clk_state = MSM_HS_CLK_OFF;
+ }
+}
+
static ssize_t show_clock(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -432,11 +488,7 @@
unsigned long flags;
int ret = 0;
- msm_hs_bus_voting(msm_uport, BUS_SCALING);
-
- clk_prepare_enable(msm_uport->clk);
- if (msm_uport->pclk)
- clk_prepare_enable(msm_uport->pclk);
+ msm_hs_clock_vote(msm_uport);
if (val) {
spin_lock_irqsave(&uport->lock, flags);
@@ -461,11 +513,8 @@
}
/* Calling CLOCK API. Hence mb() requires here. */
mb();
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
- msm_hs_bus_voting(msm_uport, BUS_RESET);
+ msm_hs_clock_unvote(msm_uport);
return 0;
}
@@ -476,23 +525,16 @@
unsigned long flags;
int ret = 0;
- msm_hs_bus_voting(msm_uport, BUS_SCALING);
-
- clk_prepare_enable(msm_uport->clk);
- if (msm_uport->pclk)
- clk_prepare_enable(msm_uport->pclk);
+ msm_hs_clock_vote(msm_uport);
spin_lock_irqsave(&uport->lock, flags);
ret = msm_hs_read(&msm_uport->uport, UARTDM_MR2_ADDR);
spin_unlock_irqrestore(&uport->lock, flags);
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
+ msm_hs_clock_unvote(msm_uport);
*val = (ret & UARTDM_MR2_LOOP_MODE_BMSK) ? 1 : 0;
- msm_hs_bus_voting(msm_uport, BUS_RESET);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(loopback_enable_fops, msm_serial_loopback_enable_get,
@@ -585,22 +627,12 @@
return ret;
}
- ret = clk_prepare_enable(msm_uport->clk);
+ ret = msm_hs_clock_vote(msm_uport);
if (ret) {
printk(KERN_ERR "Error could not turn on UART clk\n");
return ret;
}
- if (msm_uport->pclk) {
- ret = clk_prepare_enable(msm_uport->pclk);
- if (ret) {
- clk_disable_unprepare(msm_uport->clk);
- dev_err(uport->dev,
- "Error could not turn on UART pclk\n");
- return ret;
- }
- }
- msm_uport->clk_state = MSM_HS_CLK_ON;
return 0;
}
@@ -1040,8 +1072,12 @@
{
unsigned int data;
unsigned int ret = 0;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ msm_hs_clock_vote(msm_uport);
data = msm_hs_read(uport, UARTDM_SR_ADDR);
+ msm_hs_clock_unvote(msm_uport);
+
if (data & UARTDM_SR_TXEMT_BMSK)
ret = TIOCSER_TEMT;
@@ -1658,10 +1694,13 @@
unsigned int mctrl)
{
unsigned long flags;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ msm_hs_clock_vote(msm_uport);
spin_lock_irqsave(&uport->lock, flags);
msm_hs_set_mctrl_locked(uport, mctrl);
spin_unlock_irqrestore(&uport->lock, flags);
+ msm_hs_clock_unvote(msm_uport);
}
EXPORT_SYMBOL(msm_hs_set_mctrl);
@@ -1825,11 +1864,7 @@
spin_unlock_irqrestore(&uport->lock, flags);
/* we really want to clock off */
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
-
- msm_uport->clk_state = MSM_HS_CLK_OFF;
+ msm_hs_clock_unvote(msm_uport);
spin_lock_irqsave(&uport->lock, flags);
if (use_low_power_wakeup(msm_uport)) {
@@ -2020,25 +2055,13 @@
disable_irq_nosync(msm_uport->wakeup.irq);
spin_unlock_irqrestore(&uport->lock, flags);
- /* Vote for PNOC BUS Scaling */
- msm_hs_bus_voting(msm_uport, BUS_SCALING);
-
- ret = clk_prepare_enable(msm_uport->clk);
+ ret = msm_hs_clock_vote(msm_uport);
if (ret) {
dev_err(uport->dev, "Clock ON Failure"
"For UART CLK Stalling HSUART\n");
break;
}
- if (msm_uport->pclk) {
- ret = clk_prepare_enable(msm_uport->pclk);
- if (unlikely(ret)) {
- clk_disable_unprepare(msm_uport->clk);
- dev_err(uport->dev, "Clock ON Failure"
- "For UART Pclk Stalling HSUART\n");
- break;
- }
- }
spin_lock_irqsave(&uport->lock, flags);
/* else fall-through */
case MSM_HS_CLK_REQUEST_OFF:
@@ -2375,8 +2398,7 @@
disable_irq(msm_uport->wakeup.irq);
}
- /* Vote for PNOC BUS Scaling */
- msm_hs_bus_voting(msm_uport, BUS_SCALING);
+ msm_hs_clock_vote(msm_uport);
spin_lock_irqsave(&uport->lock, flags);
@@ -2384,6 +2406,8 @@
spin_unlock_irqrestore(&uport->lock, flags);
+ msm_hs_clock_unvote(msm_uport);
+
pm_runtime_enable(uport->dev);
return 0;
@@ -2403,9 +2427,7 @@
if (is_blsp_uart(msm_uport))
msm_hs_unconfig_uart_gpios(uport);
deinit_uart_clk:
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
+ msm_hs_clock_unvote(msm_uport);
wake_unlock(&msm_uport->dma_wake_lock);
return ret;
@@ -2835,7 +2857,7 @@
struct resource *core_resource;
struct resource *bam_resource;
struct resource *resource;
- int core_irqres, bam_irqres;
+ int core_irqres, bam_irqres, wakeup_irqres;
struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
if (pdev->dev.of_node) {
@@ -2894,6 +2916,7 @@
IORESOURCE_MEM, "bam_mem");
core_irqres = platform_get_irq_byname(pdev, "core_irq");
bam_irqres = platform_get_irq_byname(pdev, "bam_irq");
+ wakeup_irqres = platform_get_irq_byname(pdev, "wakeup_irq");
if (!core_resource) {
pr_err("Invalid core HSUART Resources.\n");
@@ -2913,6 +2936,8 @@
pr_err("Invalid bam irqres Resources.\n");
return -ENXIO;
}
+ if (!wakeup_irqres)
+ pr_debug("Wakeup irq not specified.\n");
uport->mapbase = core_resource->start;
@@ -2933,6 +2958,7 @@
uport->irq = core_irqres;
msm_uport->bam_irq = bam_irqres;
+ pdata->wakeup_irq = wakeup_irqres;
msm_uport->bus_scale_table = msm_bus_cl_get_pdata(pdev);
if (!msm_uport->bus_scale_table) {
@@ -3053,6 +3079,8 @@
INIT_WORK(&msm_uport->disconnect_rx_endpoint,
hsuart_disconnect_rx_endpoint_work);
mutex_init(&msm_uport->clk_mutex);
+ atomic_set(&msm_uport->clk_count, 0);
+
/* Initialize SPS HW connected with UART core */
if (is_blsp_uart(msm_uport)) {
@@ -3063,11 +3091,7 @@
}
}
- msm_hs_bus_voting(msm_uport, BUS_SCALING);
-
- clk_prepare_enable(msm_uport->clk);
- if (msm_uport->pclk)
- clk_prepare_enable(msm_uport->pclk);
+ msm_hs_clock_vote(msm_uport);
ret = uartdm_init_port(uport);
if (unlikely(ret)) {
@@ -3102,19 +3126,12 @@
uport->line = pdata->userid;
ret = uart_add_one_port(&msm_hs_driver, uport);
if (!ret) {
- msm_hs_bus_voting(msm_uport, BUS_RESET);
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
+ msm_hs_clock_unvote(msm_uport);
return ret;
}
err_clock:
-
- msm_hs_bus_voting(msm_uport, BUS_RESET);
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
+ msm_hs_clock_unvote(msm_uport);
destroy_mutex:
mutex_destroy(&msm_uport->clk_mutex);
@@ -3185,6 +3202,7 @@
struct msm_hs_tx *tx = &msm_uport->tx;
struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
+ msm_hs_clock_vote(msm_uport);
if (msm_uport->tx.dma_in_flight) {
if (!is_blsp_uart(msm_uport)) {
spin_lock_irqsave(&uport->lock, flags);
@@ -3236,16 +3254,12 @@
*/
mb();
- /* Reset PNOC Bus Scaling */
- msm_hs_bus_voting(msm_uport, BUS_RESET);
-
if (msm_uport->clk_state != MSM_HS_CLK_OFF) {
/* to balance clk_state */
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
+ msm_hs_clock_unvote(msm_uport);
wake_unlock(&msm_uport->dma_wake_lock);
}
+ msm_hs_clock_unvote(msm_uport);
msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
dma_unmap_single(uport->dev, msm_uport->tx.dma_base,
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index a5f586b..0f8a7f0 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -34,7 +34,9 @@
#include <linux/usb/gadget.h>
#include <linux/qpnp-misc.h>
#include <linux/usb/msm_hsusb.h>
+#include <linux/usb/msm_ext_chg.h>
#include <linux/regulator/consumer.h>
+#include <linux/pm_wakeup.h>
#include <linux/power_supply.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/cdev.h>
@@ -165,7 +167,7 @@
struct dwc3_msm {
struct device *dev;
void __iomem *base;
- u32 resource_size;
+ struct resource *io_res;
int dbm_num_eps;
u8 ep_num_mapping[DBM_MAX_EPS];
const struct usb_ep_ops *original_ep_ops[DWC3_ENDPOINTS_NUM];
@@ -182,6 +184,7 @@
struct regulator *hsusb_vddcx;
struct regulator *ssusb_1p8;
struct regulator *ssusb_vddcx;
+ struct regulator *dwc3_gdsc;
/* VBUS regulator if no OTG and running in host only mode */
struct regulator *vbus_otg;
@@ -194,7 +197,6 @@
bool lpm_irq_seen;
struct delayed_work resume_work;
struct work_struct restart_usb_work;
- struct wake_lock wlock;
struct dwc3_charger charger;
struct usb_phy *otg_xceiv;
struct delayed_work chg_work;
@@ -244,8 +246,6 @@
#define USB_SSPHY_1P8_VOL_MAX 1800000 /* uV */
#define USB_SSPHY_1P8_HPM_LOAD 23000 /* uA */
-static struct dwc3_msm *context;
-
static struct usb_ext_notification *usb_ext;
/**
@@ -341,8 +341,8 @@
tmp &= mask; /* clear other bits */
if (tmp != val)
- dev_err(context->dev, "%s: write: %x to QSCRATCH: %x FAILED\n",
- __func__, val, offset);
+ pr_err("%s: write: %x to QSCRATCH: %x FAILED\n",
+ __func__, val, offset);
}
/**
@@ -411,12 +411,12 @@
* Return DBM EP number according to usb endpoint number.
*
*/
-static int dwc3_msm_find_matching_dbm_ep(u8 usb_ep)
+static int dwc3_msm_find_matching_dbm_ep(struct dwc3_msm *mdwc, u8 usb_ep)
{
int i;
- for (i = 0; i < context->dbm_num_eps; i++)
- if (context->ep_num_mapping[i] == usb_ep)
+ for (i = 0; i < mdwc->dbm_num_eps; i++)
+ if (mdwc->ep_num_mapping[i] == usb_ep)
return i;
return -ENODEV; /* Not found */
@@ -426,13 +426,13 @@
* Return number of configured DBM endpoints.
*
*/
-static int dwc3_msm_configured_dbm_ep_num(void)
+static int dwc3_msm_configured_dbm_ep_num(struct dwc3_msm *mdwc)
{
int i;
int count = 0;
- for (i = 0; i < context->dbm_num_eps; i++)
- if (context->ep_num_mapping[i])
+ for (i = 0; i < mdwc->dbm_num_eps; i++)
+ if (mdwc->ep_num_mapping[i])
count++;
return count;
@@ -446,12 +446,13 @@
* @size - size of the event buffer.
*
*/
-static int dwc3_msm_event_buffer_config(u32 addr, u16 size)
+static int dwc3_msm_event_buffer_config(struct dwc3_msm *mdwc,
+ u32 addr, u16 size)
{
- dev_dbg(context->dev, "%s\n", __func__);
+ dev_dbg(mdwc->dev, "%s\n", __func__);
- dwc3_msm_write_reg(context->base, DBM_GEVNTADR, addr);
- dwc3_msm_write_reg_field(context->base, DBM_GEVNTSIZ,
+ dwc3_msm_write_reg(mdwc->base, DBM_GEVNTADR, addr);
+ dwc3_msm_write_reg_field(mdwc->base, DBM_GEVNTSIZ,
DBM_GEVNTSIZ_MASK, size);
return 0;
@@ -461,19 +462,19 @@
* Reset the DBM registers upon initialization.
*
*/
-static int dwc3_msm_dbm_soft_reset(int enter_reset)
+static int dwc3_msm_dbm_soft_reset(struct dwc3_msm *mdwc, int enter_reset)
{
- dev_dbg(context->dev, "%s\n", __func__);
+ dev_dbg(mdwc->dev, "%s\n", __func__);
if (enter_reset) {
- dev_dbg(context->dev, "enter DBM reset\n");
- dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+ dev_dbg(mdwc->dev, "enter DBM reset\n");
+ dwc3_msm_write_reg_field(mdwc->base, DBM_SOFT_RESET,
DBM_SFT_RST_MASK, 1);
} else {
- dev_dbg(context->dev, "exit DBM reset\n");
- dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+ dev_dbg(mdwc->dev, "exit DBM reset\n");
+ dwc3_msm_write_reg_field(mdwc->base, DBM_SOFT_RESET,
DBM_SFT_RST_MASK, 0);
/*enable DBM*/
- dwc3_msm_write_reg_field(context->base, QSCRATCH_GENERAL_CFG,
+ dwc3_msm_write_reg_field(mdwc->base, QSCRATCH_GENERAL_CFG,
DBM_EN_MASK, 0x1);
}
@@ -490,21 +491,21 @@
* @enter_reset - should we enter a reset state or get out of it.
*
*/
-static int dwc3_msm_dbm_ep_soft_reset(u8 dbm_ep, bool enter_reset)
+static int dwc3_msm_dbm_ep_soft_reset(struct dwc3_msm *mdwc,
+ u8 dbm_ep, bool enter_reset)
{
- dev_dbg(context->dev, "%s\n", __func__);
+ dev_dbg(mdwc->dev, "%s\n", __func__);
- if (dbm_ep >= context->dbm_num_eps) {
- dev_err(context->dev,
- "%s: Invalid DBM ep index\n", __func__);
+ if (dbm_ep >= mdwc->dbm_num_eps) {
+ dev_err(mdwc->dev, "%s: Invalid DBM ep index\n", __func__);
return -ENODEV;
}
if (enter_reset) {
- dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+ dwc3_msm_write_reg_field(mdwc->base, DBM_SOFT_RESET,
DBM_SFT_RST_EPS_MASK & 1 << dbm_ep, 1);
} else {
- dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+ dwc3_msm_write_reg_field(mdwc->base, DBM_SOFT_RESET,
DBM_SFT_RST_EPS_MASK & 1 << dbm_ep, 0);
}
@@ -523,43 +524,43 @@
*
* @return int - DBM ep number.
*/
-static int dwc3_msm_dbm_ep_config(u8 usb_ep, u8 bam_pipe,
+static int dwc3_msm_dbm_ep_config(struct dwc3_msm *mdwc, u8 usb_ep, u8 bam_pipe,
bool producer, bool disable_wb,
bool internal_mem, bool ioc)
{
u8 dbm_ep;
u32 ep_cfg;
- dev_dbg(context->dev, "%s\n", __func__);
+ dev_dbg(mdwc->dev, "%s\n", __func__);
- dbm_ep = dwc3_msm_find_matching_dbm_ep(usb_ep);
+ dbm_ep = dwc3_msm_find_matching_dbm_ep(mdwc, usb_ep);
if (dbm_ep < 0) {
- dev_err(context->dev,
+ dev_err(mdwc->dev,
"%s: Invalid usb ep index\n", __func__);
return -ENODEV;
}
/* First, reset the dbm endpoint */
- dwc3_msm_dbm_ep_soft_reset(dbm_ep, 0);
+ dwc3_msm_dbm_ep_soft_reset(mdwc, dbm_ep, 0);
/* Set ioc bit for dbm_ep if needed */
- dwc3_msm_write_reg_field(context->base, DBM_DBG_CNFG,
+ dwc3_msm_write_reg_field(mdwc->base, DBM_DBG_CNFG,
DBM_ENABLE_IOC_MASK & 1 << dbm_ep, ioc ? 1 : 0);
ep_cfg = (producer ? DBM_PRODUCER : 0) |
(disable_wb ? DBM_DISABLE_WB : 0) |
(internal_mem ? DBM_INT_RAM_ACC : 0);
- dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep),
+ dwc3_msm_write_reg_field(mdwc->base, DBM_EP_CFG(dbm_ep),
DBM_PRODUCER | DBM_DISABLE_WB | DBM_INT_RAM_ACC, ep_cfg >> 8);
- dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep), USB3_EPNUM,
+ dwc3_msm_write_reg_field(mdwc->base, DBM_EP_CFG(dbm_ep), USB3_EPNUM,
usb_ep);
- dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep),
+ dwc3_msm_write_reg_field(mdwc->base, DBM_EP_CFG(dbm_ep),
DBM_BAM_PIPE_NUM, bam_pipe);
- dwc3_msm_write_reg_field(context->base, DBM_PIPE_CFG, 0x000000ff,
+ dwc3_msm_write_reg_field(mdwc->base, DBM_PIPE_CFG, 0x000000ff,
0xe4);
- dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep), DBM_EN_EP,
+ dwc3_msm_write_reg_field(mdwc->base, DBM_EP_CFG(dbm_ep), DBM_EN_EP,
1);
return dbm_ep;
@@ -571,35 +572,34 @@
* @usb_ep - USB ep number.
*
*/
-static int dwc3_msm_dbm_ep_unconfig(u8 usb_ep)
+static int dwc3_msm_dbm_ep_unconfig(struct dwc3_msm *mdwc, u8 usb_ep)
{
u8 dbm_ep;
u32 data;
- dev_dbg(context->dev, "%s\n", __func__);
+ dev_dbg(mdwc->dev, "%s\n", __func__);
- dbm_ep = dwc3_msm_find_matching_dbm_ep(usb_ep);
+ dbm_ep = dwc3_msm_find_matching_dbm_ep(mdwc, usb_ep);
if (dbm_ep < 0) {
- dev_err(context->dev,
- "%s: Invalid usb ep index\n", __func__);
+ dev_err(mdwc->dev, "%s: Invalid usb ep index\n", __func__);
return -ENODEV;
}
- context->ep_num_mapping[dbm_ep] = 0;
+ mdwc->ep_num_mapping[dbm_ep] = 0;
- data = dwc3_msm_read_reg(context->base, DBM_EP_CFG(dbm_ep));
+ data = dwc3_msm_read_reg(mdwc->base, DBM_EP_CFG(dbm_ep));
data &= (~0x1);
- dwc3_msm_write_reg(context->base, DBM_EP_CFG(dbm_ep), data);
+ dwc3_msm_write_reg(mdwc->base, DBM_EP_CFG(dbm_ep), data);
/* Reset the dbm endpoint */
- dwc3_msm_dbm_ep_soft_reset(dbm_ep, true);
+ dwc3_msm_dbm_ep_soft_reset(mdwc, dbm_ep, true);
/*
* 10 usec delay is required before deasserting DBM endpoint reset
* according to hardware programming guide.
*/
udelay(10);
- dwc3_msm_dbm_ep_soft_reset(dbm_ep, false);
+ dwc3_msm_dbm_ep_soft_reset(mdwc, dbm_ep, false);
return 0;
}
@@ -618,15 +618,17 @@
{
u8 dbm_ep;
struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
u8 bam_pipe = dst_pipe_idx;
- dev_dbg(context->dev, "%s\n", __func__);
+ dev_dbg(mdwc->dev, "%s\n", __func__);
dbm_ep = bam_pipe;
- context->ep_num_mapping[dbm_ep] = dep->number;
+ mdwc->ep_num_mapping[dbm_ep] = dep->number;
- dwc3_msm_write_reg(context->base, DBM_DATA_FIFO(dbm_ep), addr);
- dwc3_msm_write_reg_field(context->base, DBM_DATA_FIFO_SIZE(dbm_ep),
+ dwc3_msm_write_reg(mdwc->base, DBM_DATA_FIFO(dbm_ep), addr);
+ dwc3_msm_write_reg_field(mdwc->base, DBM_DATA_FIFO_SIZE(dbm_ep),
DBM_DATA_FIFO_SIZE_MASK, size);
return 0;
@@ -646,12 +648,12 @@
struct usb_request *request)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
struct dwc3_msm_req_complete *req_complete = NULL;
/* Find original request complete function and remove it from list */
- list_for_each_entry(req_complete,
- &context->req_complete_list,
- list_item) {
+ list_for_each_entry(req_complete, &mdwc->req_complete_list, list_item) {
if (req_complete->req == request)
break;
}
@@ -670,14 +672,14 @@
dep->busy_slot++;
/* Unconfigure dbm ep */
- dwc3_msm_dbm_ep_unconfig(dep->number);
+ dwc3_msm_dbm_ep_unconfig(mdwc, dep->number);
/*
* If this is the last endpoint we unconfigured, than reset also
* the event buffers.
*/
- if (0 == dwc3_msm_configured_dbm_ep_num())
- dwc3_msm_event_buffer_config(0, 0);
+ if (0 == dwc3_msm_configured_dbm_ep_num(mdwc))
+ dwc3_msm_event_buffer_config(mdwc, 0, 0);
/*
* Call original complete function, notice that dwc->lock is already
@@ -790,6 +792,7 @@
struct dwc3_request *req = to_dwc3_request(request);
struct dwc3_ep *dep = to_dwc3_ep(ep);
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
struct dwc3_msm_req_complete *req_complete;
unsigned long flags;
int ret = 0;
@@ -802,23 +805,23 @@
if (!(request->udc_priv & MSM_SPS_MODE)) {
/* Not SPS mode, call original queue */
- dev_vdbg(dwc->dev, "%s: not sps mode, use regular queue\n",
+ dev_vdbg(mdwc->dev, "%s: not sps mode, use regular queue\n",
__func__);
- return (context->original_ep_ops[dep->number])->queue(ep,
+ return (mdwc->original_ep_ops[dep->number])->queue(ep,
request,
gfp_flags);
}
if (!dep->endpoint.desc) {
- dev_err(dwc->dev,
+ dev_err(mdwc->dev,
"%s: trying to queue request %p to disabled ep %s\n",
__func__, request, ep->name);
return -EPERM;
}
if (dep->number == 0 || dep->number == 1) {
- dev_err(dwc->dev,
+ dev_err(mdwc->dev,
"%s: trying to queue dbm request %p to control ep %s\n",
__func__, request, ep->name);
return -EPERM;
@@ -827,7 +830,7 @@
if (dep->busy_slot != dep->free_slot || !list_empty(&dep->request_list)
|| !list_empty(&dep->req_queued)) {
- dev_err(dwc->dev,
+ dev_err(mdwc->dev,
"%s: trying to queue dbm request %p tp ep %s\n",
__func__, request, ep->name);
return -EPERM;
@@ -842,12 +845,12 @@
*/
req_complete = kzalloc(sizeof(*req_complete), GFP_KERNEL);
if (!req_complete) {
- dev_err(dep->dwc->dev, "%s: not enough memory\n", __func__);
+ dev_err(mdwc->dev, "%s: not enough memory\n", __func__);
return -ENOMEM;
}
req_complete->req = request;
req_complete->orig_complete = request->complete;
- list_add_tail(&req_complete->list_item, &context->req_complete_list);
+ list_add_tail(&req_complete->list_item, &mdwc->req_complete_list);
request->complete = dwc3_msm_req_complete_func;
/*
@@ -859,11 +862,11 @@
internal_mem = ((request->udc_priv & MSM_INTERNAL_MEM) ? true : false);
ioc = ((request->udc_priv & MSM_ETD_IOC) ? true : false);
- ret = dwc3_msm_dbm_ep_config(dep->number,
+ ret = dwc3_msm_dbm_ep_config(mdwc, dep->number,
bam_pipe, producer,
disable_wb, internal_mem, ioc);
if (ret < 0) {
- dev_err(context->dev,
+ dev_err(mdwc->dev,
"error %d after calling dwc3_msm_dbm_ep_config\n",
ret);
return ret;
@@ -883,13 +886,13 @@
ret = __dwc3_msm_ep_queue(dep, req);
spin_unlock_irqrestore(&dwc->lock, flags);
if (ret < 0) {
- dev_err(context->dev,
+ dev_err(mdwc->dev,
"error %d after calling __dwc3_msm_ep_queue\n", ret);
return ret;
}
speed = dwc3_readl(dwc->regs, DWC3_DSTS) & DWC3_DSTS_CONNECTSPD;
- dwc3_msm_write_reg(context->base, DBM_GEN_CFG, speed >> 2);
+ dwc3_msm_write_reg(mdwc->base, DBM_GEN_CFG, speed >> 2);
return 0;
}
@@ -911,25 +914,27 @@
int msm_ep_config(struct usb_ep *ep)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
struct usb_ep_ops *new_ep_ops;
- dwc3_msm_event_buffer_config(dwc3_msm_read_reg(context->base,
- DWC3_GEVNTADRLO(0)),
- dwc3_msm_read_reg(context->base, DWC3_GEVNTSIZ(0)));
+ dwc3_msm_event_buffer_config(mdwc,
+ dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTADRLO(0)),
+ dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTSIZ(0)));
/* Save original ep ops for future restore*/
- if (context->original_ep_ops[dep->number]) {
- dev_err(context->dev,
+ if (mdwc->original_ep_ops[dep->number]) {
+ dev_err(mdwc->dev,
"ep [%s,%d] already configured as msm endpoint\n",
ep->name, dep->number);
return -EPERM;
}
- context->original_ep_ops[dep->number] = ep->ops;
+ mdwc->original_ep_ops[dep->number] = ep->ops;
/* Set new usb ops as we like */
new_ep_ops = kzalloc(sizeof(struct usb_ep_ops), GFP_KERNEL);
if (!new_ep_ops) {
- dev_err(context->dev,
+ dev_err(mdwc->dev,
"%s: unable to allocate mem for new usb ep ops\n",
__func__);
return -ENOMEM;
@@ -961,18 +966,20 @@
int msm_ep_unconfig(struct usb_ep *ep)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
struct usb_ep_ops *old_ep_ops;
/* Restore original ep ops */
- if (!context->original_ep_ops[dep->number]) {
- dev_err(context->dev,
+ if (!mdwc->original_ep_ops[dep->number]) {
+ dev_err(mdwc->dev,
"ep [%s,%d] was not configured as msm endpoint\n",
ep->name, dep->number);
return -EINVAL;
}
old_ep_ops = (struct usb_ep_ops *)ep->ops;
- ep->ops = context->original_ep_ops[dep->number];
- context->original_ep_ops[dep->number] = NULL;
+ ep->ops = mdwc->original_ep_ops[dep->number];
+ mdwc->original_ep_ops[dep->number] = NULL;
kfree(old_ep_ops);
/*
@@ -1017,14 +1024,16 @@
* This performs full hardware reset and re-initialization which
* might be required by some DBM client driver during uninit/cleanup.
*/
-void msm_dwc3_restart_usb_session(void)
+void msm_dwc3_restart_usb_session(struct usb_gadget *gadget)
{
- struct dwc3_msm *mdwc = context;
+ struct dwc3 *dwc = container_of(gadget, struct dwc3, gadget);
+ struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
+
+ if (mdwc)
+ return;
dev_dbg(mdwc->dev, "%s\n", __func__);
queue_work(system_nrt_wq, &mdwc->restart_usb_work);
-
- return;
}
EXPORT_SYMBOL(msm_dwc3_restart_usb_session);
@@ -1056,10 +1065,9 @@
EXPORT_SYMBOL(msm_register_usb_ext_notification);
/* HSPHY */
-static int dwc3_hsusb_config_vddcx(int high)
+static int dwc3_hsusb_config_vddcx(struct dwc3_msm *dwc, int high)
{
int min_vol, max_vol, ret;
- struct dwc3_msm *dwc = context;
max_vol = dwc->vdd_high_vol_level;
min_vol = high ? dwc->vdd_low_vol_level : dwc->vdd_no_vol_level;
@@ -1075,10 +1083,9 @@
return ret;
}
-static int dwc3_hsusb_ldo_init(int init)
+static int dwc3_hsusb_ldo_init(struct dwc3_msm *dwc, int init)
{
int rc = 0;
- struct dwc3_msm *dwc = context;
if (!init) {
regulator_set_voltage(dwc->hsusb_1p8, 0, USB_HSPHY_1P8_VOL_MAX);
@@ -1119,10 +1126,9 @@
return rc;
}
-static int dwc3_hsusb_ldo_enable(int on)
+static int dwc3_hsusb_ldo_enable(struct dwc3_msm *dwc, int on)
{
int rc = 0;
- struct dwc3_msm *dwc = context;
dev_dbg(dwc->dev, "reg (%s)\n", on ? "HPM" : "LPM");
@@ -1180,10 +1186,9 @@
}
/* SSPHY */
-static int dwc3_ssusb_config_vddcx(int high)
+static int dwc3_ssusb_config_vddcx(struct dwc3_msm *dwc, int high)
{
int min_vol, max_vol, ret;
- struct dwc3_msm *dwc = context;
max_vol = dwc->vdd_high_vol_level;
min_vol = high ? dwc->vdd_low_vol_level : dwc->vdd_no_vol_level;
@@ -1199,10 +1204,9 @@
}
/* 3.3v supply not needed for SS PHY */
-static int dwc3_ssusb_ldo_init(int init)
+static int dwc3_ssusb_ldo_init(struct dwc3_msm *dwc, int init)
{
int rc = 0;
- struct dwc3_msm *dwc = context;
if (!init) {
regulator_set_voltage(dwc->ssusb_1p8, 0, USB_SSPHY_1P8_VOL_MAX);
@@ -1222,12 +1226,11 @@
return rc;
}
-static int dwc3_ssusb_ldo_enable(int on)
+static int dwc3_ssusb_ldo_enable(struct dwc3_msm *dwc, int on)
{
int rc = 0;
- struct dwc3_msm *dwc = context;
- dev_dbg(context->dev, "reg (%s)\n", on ? "HPM" : "LPM");
+ dev_dbg(dwc->dev, "reg (%s)\n", on ? "HPM" : "LPM");
if (!on)
goto disable_regulators;
@@ -1260,10 +1263,40 @@
return rc < 0 ? rc : 0;
}
-static int dwc3_msm_link_clk_reset(bool assert)
+/*
+ * Config Global Distributed Switch Controller (GDSC)
+ * to support controller power collapse
+ */
+static int dwc3_msm_config_gdsc(struct dwc3_msm *mdwc, int on)
{
int ret = 0;
- struct dwc3_msm *mdwc = context;
+
+ if (IS_ERR(mdwc->dwc3_gdsc))
+ return 0;
+
+ if (!mdwc->dwc3_gdsc) {
+ mdwc->dwc3_gdsc = devm_regulator_get(mdwc->dev,
+ "USB3_GDSC");
+ if (IS_ERR(mdwc->dwc3_gdsc))
+ return 0;
+ }
+
+ if (on) {
+ ret = regulator_enable(mdwc->dwc3_gdsc);
+ if (ret) {
+ dev_err(mdwc->dev, "unable to enable usb3 gdsc\n");
+ return ret;
+ }
+ } else {
+ regulator_disable(mdwc->dwc3_gdsc);
+ }
+
+ return 0;
+}
+
+static int dwc3_msm_link_clk_reset(struct dwc3_msm *mdwc, bool assert)
+{
+ int ret = 0;
if (assert) {
/* Using asynchronous block reset to the hardware */
@@ -1289,7 +1322,7 @@
}
/* Reinitialize SSPHY parameters by overriding using QSCRATCH CR interface */
-static void dwc3_msm_ss_phy_reg_init(struct dwc3_msm *msm)
+static void dwc3_msm_ss_phy_reg_init(struct dwc3_msm *mdwc)
{
u32 data = 0;
@@ -1298,14 +1331,14 @@
* in HS mode instead of SS mode. Workaround it by asserting
* LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode
*/
- data = dwc3_msm_ssusb_read_phycreg(msm->base, 0x102D);
+ data = dwc3_msm_ssusb_read_phycreg(mdwc->base, 0x102D);
data |= (1 << 7);
- dwc3_msm_ssusb_write_phycreg(msm->base, 0x102D, data);
+ dwc3_msm_ssusb_write_phycreg(mdwc->base, 0x102D, data);
- data = dwc3_msm_ssusb_read_phycreg(msm->base, 0x1010);
+ data = dwc3_msm_ssusb_read_phycreg(mdwc->base, 0x1010);
data &= ~0xFF0;
data |= 0x20;
- dwc3_msm_ssusb_write_phycreg(msm->base, 0x1010, data);
+ dwc3_msm_ssusb_write_phycreg(mdwc->base, 0x1010, data);
/*
* Fix RX Equalization setting as follows
@@ -1314,13 +1347,13 @@
* LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
* LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
*/
- data = dwc3_msm_ssusb_read_phycreg(msm->base, 0x1006);
+ data = dwc3_msm_ssusb_read_phycreg(mdwc->base, 0x1006);
data &= ~(1 << 6);
data |= (1 << 7);
data &= ~(0x7 << 8);
data |= (0x3 << 8);
data |= (0x1 << 11);
- dwc3_msm_ssusb_write_phycreg(msm->base, 0x1006, data);
+ dwc3_msm_ssusb_write_phycreg(mdwc->base, 0x1006, data);
/*
* Set EQ and TX launch amplitudes as follows
@@ -1328,12 +1361,12 @@
* LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
* LANE0.TX_OVRD_DRV_LO.EN set to 1.
*/
- data = dwc3_msm_ssusb_read_phycreg(msm->base, 0x1002);
+ data = dwc3_msm_ssusb_read_phycreg(mdwc->base, 0x1002);
data &= ~0x3F80;
data |= (0x16 << 7);
data &= ~0x7F;
data |= (0x7F | (1 << 14));
- dwc3_msm_ssusb_write_phycreg(msm->base, 0x1002, data);
+ dwc3_msm_ssusb_write_phycreg(mdwc->base, 0x1002, data);
/*
* Set the QSCRATCH SS_PHY_PARAM_CTRL1 parameters as follows
@@ -1341,73 +1374,73 @@
* TX_DEEMPH_3_5DB [13:8] to 22
* LOS_BIAS [2:0] to 0x5
*/
- dwc3_msm_write_readback(msm->base, SS_PHY_PARAM_CTRL_1,
+ dwc3_msm_write_readback(mdwc->base, SS_PHY_PARAM_CTRL_1,
0x07f03f07, 0x07f01605);
}
/* Initialize QSCRATCH registers for HSPHY and SSPHY operation */
-static void dwc3_msm_qscratch_reg_init(struct dwc3_msm *msm)
+static void dwc3_msm_qscratch_reg_init(struct dwc3_msm *mdwc)
{
/* SSPHY Initialization: Use ref_clk from pads and set its parameters */
- dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210002);
+ dwc3_msm_write_reg(mdwc->base, SS_PHY_CTRL_REG, 0x10210002);
msleep(30);
/* Assert SSPHY reset */
- dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210082);
+ dwc3_msm_write_reg(mdwc->base, SS_PHY_CTRL_REG, 0x10210082);
usleep_range(2000, 2200);
/* De-assert SSPHY reset - power and ref_clock must be ON */
- dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210002);
+ dwc3_msm_write_reg(mdwc->base, SS_PHY_CTRL_REG, 0x10210002);
usleep_range(2000, 2200);
/* Ref clock must be stable now, enable ref clock for HS mode */
- dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210102);
+ dwc3_msm_write_reg(mdwc->base, SS_PHY_CTRL_REG, 0x10210102);
usleep_range(2000, 2200);
/*
* HSPHY Initialization: Enable UTMI clock and clamp enable HVINTs,
* and disable RETENTION (power-on default is ENABLED)
*/
- dwc3_msm_write_reg(msm->base, HS_PHY_CTRL_REG, 0x5220bb2);
+ dwc3_msm_write_reg(mdwc->base, HS_PHY_CTRL_REG, 0x5220bb2);
usleep_range(2000, 2200);
/* Disable (bypass) VBUS and ID filters */
- dwc3_msm_write_reg(msm->base, QSCRATCH_GENERAL_CFG, 0x78);
+ dwc3_msm_write_reg(mdwc->base, QSCRATCH_GENERAL_CFG, 0x78);
/*
* write HSPHY init value to QSCRATCH reg to set HSPHY parameters like
* VBUS valid threshold, disconnect valid threshold, DC voltage level,
* preempasis and rise/fall time.
*/
if (override_phy_init)
- msm->hsphy_init_seq = override_phy_init;
- if (msm->hsphy_init_seq)
- dwc3_msm_write_readback(msm->base,
+ mdwc->hsphy_init_seq = override_phy_init;
+ if (mdwc->hsphy_init_seq)
+ dwc3_msm_write_readback(mdwc->base,
PARAMETER_OVERRIDE_X_REG, 0x03FFFFFF,
- msm->hsphy_init_seq & 0x03FFFFFF);
+ mdwc->hsphy_init_seq & 0x03FFFFFF);
/* Enable master clock for RAMs to allow BAM to access RAMs when
* RAM clock gating is enabled via DWC3's GCTL. Otherwise, issues
* are seen where RAM clocks get turned OFF in SS mode
*/
- dwc3_msm_write_reg(msm->base, CGCTL_REG,
- dwc3_msm_read_reg(msm->base, CGCTL_REG) | 0x18);
+ dwc3_msm_write_reg(mdwc->base, CGCTL_REG,
+ dwc3_msm_read_reg(mdwc->base, CGCTL_REG) | 0x18);
- dwc3_msm_ss_phy_reg_init(msm);
+ dwc3_msm_ss_phy_reg_init(mdwc);
/*
* This is required to restore the POR value after userspace
* is done with charger detection.
*/
- msm->qscratch_ctl_val = dwc3_msm_read_reg(msm->base, QSCRATCH_CTRL_REG);
+ mdwc->qscratch_ctl_val =
+ dwc3_msm_read_reg(mdwc->base, QSCRATCH_CTRL_REG);
}
-static void dwc3_msm_block_reset(bool core_reset)
+static void dwc3_msm_block_reset(struct dwc3_ext_xceiv *xceiv, bool core_reset)
{
-
- struct dwc3_msm *mdwc = context;
+ struct dwc3_msm *mdwc = container_of(xceiv, struct dwc3_msm, ext_xceiv);
int ret = 0;
if (core_reset) {
- ret = dwc3_msm_link_clk_reset(1);
+ ret = dwc3_msm_link_clk_reset(mdwc, 1);
if (ret)
return;
usleep_range(1000, 1200);
- ret = dwc3_msm_link_clk_reset(0);
+ ret = dwc3_msm_link_clk_reset(mdwc, 0);
if (ret)
return;
@@ -1418,9 +1451,9 @@
}
/* Reset the DBM */
- dwc3_msm_dbm_soft_reset(1);
+ dwc3_msm_dbm_soft_reset(mdwc, 1);
usleep_range(1000, 1200);
- dwc3_msm_dbm_soft_reset(0);
+ dwc3_msm_dbm_soft_reset(mdwc, 0);
}
static void dwc3_chg_enable_secondary_det(struct dwc3_msm *mdwc)
@@ -1525,7 +1558,7 @@
case DWC3_DCP_CHARGER: return "USB_DCP_CHARGER";
case DWC3_CDP_CHARGER: return "USB_CDP_CHARGER";
case DWC3_PROPRIETARY_CHARGER: return "USB_PROPRIETARY_CHARGER";
- case DWC3_UNSUPPORTED_CHARGER: return "INVALID_CHARGER";
+ case DWC3_FLOATED_CHARGER: return "USB_FLOATED_CHARGER";
default: return "UNKNOWN_CHARGER";
}
}
@@ -1539,6 +1572,7 @@
{
struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, chg_work.work);
bool is_dcd = false, tmout, vout;
+ static bool dcd;
unsigned long delay;
dev_dbg(mdwc->dev, "chg detection work\n");
@@ -1554,19 +1588,15 @@
is_dcd = dwc3_chg_check_dcd(mdwc);
tmout = ++mdwc->dcd_retries == DWC3_CHG_DCD_MAX_RETRIES;
if (is_dcd || tmout) {
+ if (is_dcd)
+ dcd = true;
+ else
+ dcd = false;
dwc3_chg_disable_dcd(mdwc);
+ usleep_range(1000, 1200);
if (dwc3_chg_det_check_linestate(mdwc)) {
- dwc3_chg_enable_primary_det(mdwc);
- usleep_range(1000, 1200);
- vout = dwc3_chg_det_check_output(mdwc);
- if (!vout)
- mdwc->charger.chg_type =
- DWC3_UNSUPPORTED_CHARGER;
- else
- mdwc->charger.chg_type =
+ mdwc->charger.chg_type =
DWC3_PROPRIETARY_CHARGER;
- dwc3_msm_write_reg(mdwc->base,
- CHARGING_DET_CTRL_REG, 0x0);
mdwc->chg_state = USB_CHG_STATE_DETECTED;
delay = 0;
break;
@@ -1585,7 +1615,15 @@
delay = DWC3_CHG_SECONDARY_DET_TIME;
mdwc->chg_state = USB_CHG_STATE_PRIMARY_DONE;
} else {
- mdwc->charger.chg_type = DWC3_SDP_CHARGER;
+ /*
+ * Detect floating charger only if propreitary
+ * charger detection is enabled.
+ */
+ if (!dcd && prop_chg_detect)
+ mdwc->charger.chg_type =
+ DWC3_FLOATED_CHARGER;
+ else
+ mdwc->charger.chg_type = DWC3_SDP_CHARGER;
mdwc->chg_state = USB_CHG_STATE_DETECTED;
delay = 0;
}
@@ -1626,7 +1664,7 @@
static void dwc3_start_chg_det(struct dwc3_charger *charger, bool start)
{
- struct dwc3_msm *mdwc = context;
+ struct dwc3_msm *mdwc = container_of(charger, struct dwc3_msm, charger);
if (start == false) {
dev_dbg(mdwc->dev, "canceling charging detection work\n");
@@ -1729,6 +1767,11 @@
/* make sure above writes are completed before turning off clocks */
wmb();
+
+ /* remove vote for controller power collapse */
+ if (!host_bus_suspend)
+ dwc3_msm_config_gdsc(mdwc, 0);
+
if (!host_bus_suspend || !host_ss_active) {
clk_disable_unprepare(mdwc->core_clk);
mdwc->lpm_flags |= MDWC3_CORECLK_OFF;
@@ -1753,13 +1796,13 @@
if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability && !dcp &&
!host_bus_suspend)
- dwc3_hsusb_ldo_enable(0);
+ dwc3_hsusb_ldo_enable(mdwc, 0);
- dwc3_ssusb_ldo_enable(0);
- dwc3_ssusb_config_vddcx(0);
+ dwc3_ssusb_ldo_enable(mdwc, 0);
+ dwc3_ssusb_config_vddcx(mdwc, 0);
if (!host_bus_suspend && !dcp)
- dwc3_hsusb_config_vddcx(0);
- wake_unlock(&mdwc->wlock);
+ dwc3_hsusb_config_vddcx(mdwc, 0);
+ pm_relax(mdwc->dev);
atomic_set(&mdwc->in_lpm, 1);
dev_info(mdwc->dev, "DWC3 in low power mode\n");
@@ -1787,7 +1830,7 @@
return 0;
}
- wake_lock(&mdwc->wlock);
+ pm_stay_awake(mdwc->dev);
if (mdwc->bus_perf_client) {
ret = msm_bus_scale_client_update_request(
@@ -1809,18 +1852,22 @@
mdwc->lpm_flags &= ~MDWC3_TCXO_SHUTDOWN;
}
+ /* add vote for controller power collapse */
+ if (!host_bus_suspend)
+ dwc3_msm_config_gdsc(mdwc, 1);
+
if (!host_bus_suspend)
clk_prepare_enable(mdwc->utmi_clk);
if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability && !dcp &&
!host_bus_suspend)
- dwc3_hsusb_ldo_enable(1);
+ dwc3_hsusb_ldo_enable(mdwc, 1);
- dwc3_ssusb_ldo_enable(1);
- dwc3_ssusb_config_vddcx(1);
+ dwc3_ssusb_ldo_enable(mdwc, 1);
+ dwc3_ssusb_config_vddcx(mdwc, 1);
if (!host_bus_suspend && !dcp)
- dwc3_hsusb_config_vddcx(1);
+ dwc3_hsusb_config_vddcx(mdwc, 1);
clk_prepare_enable(mdwc->ref_clk);
usleep_range(1000, 1200);
@@ -2174,9 +2221,9 @@
static void dwc3_init_adc_work(struct work_struct *w);
-static void dwc3_ext_notify_online(int on)
+static void dwc3_ext_notify_online(void *ctx, int on)
{
- struct dwc3_msm *mdwc = context;
+ struct dwc3_msm *mdwc = ctx;
bool notify_otg = false;
if (!mdwc) {
@@ -2224,13 +2271,16 @@
disable_irq(mdwc->pmic_id_irq);
ret = usb_ext->notify(usb_ext->ctxt, mdwc->id_state,
- dwc3_ext_notify_online);
+ dwc3_ext_notify_online, mdwc);
dev_dbg(mdwc->dev, "%s: external handler returned %d\n",
__func__, ret);
if (mdwc->pmic_id_irq) {
+ unsigned long flags;
+ local_irq_save(flags);
/* ID may have changed while IRQ disabled; update it */
mdwc->id_state = !!irq_read_line(mdwc->pmic_id_irq);
+ local_irq_restore(flags);
enable_irq(mdwc->pmic_id_irq);
}
@@ -2317,7 +2367,12 @@
static ssize_t adc_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%s\n", context->id_adc_detect ?
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+
+ if (!mdwc)
+ return -EINVAL;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", mdwc->id_adc_detect ?
"enabled" : "disabled");
}
@@ -2325,13 +2380,18 @@
struct device_attribute *attr, const char
*buf, size_t size)
{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+
+ if (!mdwc)
+ return -EINVAL;
+
if (!strnicmp(buf, "enable", 6)) {
- if (!context->id_adc_detect)
- dwc3_init_adc_work(&context->init_adc_work.work);
+ if (!mdwc->id_adc_detect)
+ dwc3_init_adc_work(&mdwc->init_adc_work.work);
return size;
} else if (!strnicmp(buf, "disable", 7)) {
qpnp_adc_tm_usbid_end();
- context->id_adc_detect = false;
+ mdwc->id_adc_detect = false;
return size;
}
@@ -2343,55 +2403,78 @@
static int dwc3_msm_ext_chg_open(struct inode *inode, struct file *file)
{
- struct dwc3_msm *mdwc = context;
+ struct dwc3_msm *mdwc =
+ container_of(inode->i_cdev, struct dwc3_msm, ext_chg_cdev);
pr_debug("dwc3-msm ext chg open\n");
-
+ file->private_data = mdwc;
mdwc->ext_chg_opened = true;
+
return 0;
}
-static ssize_t
-dwc3_msm_ext_chg_write(struct file *file, const char __user *ubuf,
- size_t size, loff_t *pos)
+static long
+dwc3_msm_ext_chg_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct dwc3_msm *mdwc = context;
- char kbuf[16];
+ struct dwc3_msm *mdwc = file->private_data;
+ struct msm_usb_chg_info info = {0};
+ int ret = 0, val;
- memset(kbuf, 0x00, sizeof(kbuf));
- if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, size)))
- return -EFAULT;
+ switch (cmd) {
+ case MSM_USB_EXT_CHG_INFO:
+ info.chg_block_type = USB_CHG_BLOCK_QSCRATCH;
+ info.page_offset = (mdwc->io_res->start +
+ QSCRATCH_REG_OFFSET) & ~PAGE_MASK;
+ /*
+ * The charger block register address space is only
+ * 512 bytes. But mmap() works on PAGE granularity.
+ */
+ info.length = PAGE_SIZE;
- pr_debug("%s: buf = %s\n", __func__, kbuf);
-
- if (!strncmp(kbuf, "enable", 6)) {
- pr_info("%s: on\n", __func__);
- if (mdwc->charger.chg_type == DWC3_DCP_CHARGER) {
- pm_runtime_get_sync(mdwc->dev);
+ if (copy_to_user((void __user *)arg, &info, sizeof(info))) {
+ pr_err("%s: copy to user failed\n\n", __func__);
+ ret = -EFAULT;
+ }
+ break;
+ case MSM_USB_EXT_CHG_BLOCK_LPM:
+ if (get_user(val, (int __user *)arg)) {
+ pr_err("%s: get_user failed\n\n", __func__);
+ ret = -EFAULT;
+ break;
+ }
+ pr_debug("%s: LPM block request %d\n", __func__, val);
+ if (val) { /* block LPM */
+ if (mdwc->charger.chg_type == DWC3_DCP_CHARGER) {
+ pm_runtime_get_sync(mdwc->dev);
+ } else {
+ mdwc->ext_chg_active = false;
+ complete(&mdwc->ext_chg_wait);
+ ret = -ENODEV;
+ }
} else {
mdwc->ext_chg_active = false;
complete(&mdwc->ext_chg_wait);
- return -ENODEV;
+ pm_runtime_put(mdwc->dev);
}
- } else if (!strncmp(kbuf, "disable", 7)) {
- pr_info("%s: off\n", __func__);
- mdwc->ext_chg_active = false;
- complete(&mdwc->ext_chg_wait);
- pm_runtime_put(mdwc->dev);
- } else {
- return -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
}
- return size;
+ return ret;
}
static int dwc3_msm_ext_chg_mmap(struct file *file, struct vm_area_struct *vma)
{
+ struct dwc3_msm *mdwc = file->private_data;
unsigned long vsize = vma->vm_end - vma->vm_start;
int ret;
- pr_debug("%s: size = %lu %x\n", __func__, vsize, (int) vma->vm_pgoff);
+ if (vma->vm_pgoff != 0 || vsize > PAGE_SIZE)
+ return -EINVAL;
+ vma->vm_pgoff = __phys_to_pfn(mdwc->io_res->start +
+ QSCRATCH_REG_OFFSET);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
@@ -2404,7 +2487,7 @@
static int dwc3_msm_ext_chg_release(struct inode *inode, struct file *file)
{
- struct dwc3_msm *mdwc = context;
+ struct dwc3_msm *mdwc = file->private_data;
pr_debug("dwc3-msm ext chg release\n");
@@ -2416,7 +2499,7 @@
static const struct file_operations dwc3_msm_ext_chg_fops = {
.owner = THIS_MODULE,
.open = dwc3_msm_ext_chg_open,
- .write = dwc3_msm_ext_chg_write,
+ .unlocked_ioctl = dwc3_msm_ext_chg_ioctl,
.mmap = dwc3_msm_ext_chg_mmap,
.release = dwc3_msm_ext_chg_release,
};
@@ -2469,7 +2552,7 @@
static int __devinit dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
- struct dwc3_msm *msm;
+ struct dwc3_msm *mdwc;
struct resource *res;
void __iomem *tcsr;
unsigned long flags;
@@ -2477,32 +2560,38 @@
int len = 0;
u32 tmp[3];
- msm = devm_kzalloc(&pdev->dev, sizeof(*msm), GFP_KERNEL);
- if (!msm) {
+ mdwc = devm_kzalloc(&pdev->dev, sizeof(*mdwc), GFP_KERNEL);
+ if (!mdwc) {
dev_err(&pdev->dev, "not enough memory\n");
return -ENOMEM;
}
- platform_set_drvdata(pdev, msm);
- context = msm;
- msm->dev = &pdev->dev;
+ platform_set_drvdata(pdev, mdwc);
+ mdwc->dev = &pdev->dev;
- INIT_LIST_HEAD(&msm->req_complete_list);
- INIT_DELAYED_WORK(&msm->chg_work, dwc3_chg_detect_work);
- INIT_DELAYED_WORK(&msm->resume_work, dwc3_resume_work);
- INIT_WORK(&msm->restart_usb_work, dwc3_restart_usb_work);
- INIT_WORK(&msm->id_work, dwc3_id_work);
- INIT_DELAYED_WORK(&msm->init_adc_work, dwc3_init_adc_work);
- init_completion(&msm->ext_chg_wait);
+ INIT_LIST_HEAD(&mdwc->req_complete_list);
+ INIT_DELAYED_WORK(&mdwc->chg_work, dwc3_chg_detect_work);
+ INIT_DELAYED_WORK(&mdwc->resume_work, dwc3_resume_work);
+ INIT_WORK(&mdwc->restart_usb_work, dwc3_restart_usb_work);
+ INIT_WORK(&mdwc->id_work, dwc3_id_work);
+ INIT_DELAYED_WORK(&mdwc->init_adc_work, dwc3_init_adc_work);
+ init_completion(&mdwc->ext_chg_wait);
- msm->xo_clk = clk_get(&pdev->dev, "xo");
- if (IS_ERR(msm->xo_clk)) {
- dev_err(&pdev->dev, "%s unable to get TCXO buffer handle\n",
- __func__);
- return PTR_ERR(msm->xo_clk);
+ ret = dwc3_msm_config_gdsc(mdwc, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to configure usb3 gdsc\n");
+ return ret;
}
- ret = clk_prepare_enable(msm->xo_clk);
+ mdwc->xo_clk = clk_get(&pdev->dev, "xo");
+ if (IS_ERR(mdwc->xo_clk)) {
+ dev_err(&pdev->dev, "%s unable to get TCXO buffer handle\n",
+ __func__);
+ ret = PTR_ERR(mdwc->xo_clk);
+ goto disable_dwc3_gdsc;
+ }
+
+ ret = clk_prepare_enable(mdwc->xo_clk);
if (ret) {
dev_err(&pdev->dev, "%s failed to vote for TCXO buffer%d\n",
__func__, ret);
@@ -2513,62 +2602,62 @@
* DWC3 Core requires its CORE CLK (aka master / bus clk) to
* run at 125Mhz in SSUSB mode and >60MHZ for HSUSB mode.
*/
- msm->core_clk = devm_clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(msm->core_clk)) {
+ mdwc->core_clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(mdwc->core_clk)) {
dev_err(&pdev->dev, "failed to get core_clk\n");
- ret = PTR_ERR(msm->core_clk);
+ ret = PTR_ERR(mdwc->core_clk);
goto disable_xo;
}
- clk_set_rate(msm->core_clk, 125000000);
- clk_prepare_enable(msm->core_clk);
+ clk_set_rate(mdwc->core_clk, 125000000);
+ clk_prepare_enable(mdwc->core_clk);
- msm->iface_clk = devm_clk_get(&pdev->dev, "iface_clk");
- if (IS_ERR(msm->iface_clk)) {
+ mdwc->iface_clk = devm_clk_get(&pdev->dev, "iface_clk");
+ if (IS_ERR(mdwc->iface_clk)) {
dev_err(&pdev->dev, "failed to get iface_clk\n");
- ret = PTR_ERR(msm->iface_clk);
+ ret = PTR_ERR(mdwc->iface_clk);
goto disable_core_clk;
}
- clk_prepare_enable(msm->iface_clk);
+ clk_prepare_enable(mdwc->iface_clk);
- msm->sleep_clk = devm_clk_get(&pdev->dev, "sleep_clk");
- if (IS_ERR(msm->sleep_clk)) {
+ mdwc->sleep_clk = devm_clk_get(&pdev->dev, "sleep_clk");
+ if (IS_ERR(mdwc->sleep_clk)) {
dev_err(&pdev->dev, "failed to get sleep_clk\n");
- ret = PTR_ERR(msm->sleep_clk);
+ ret = PTR_ERR(mdwc->sleep_clk);
goto disable_iface_clk;
}
- clk_prepare_enable(msm->sleep_clk);
+ clk_prepare_enable(mdwc->sleep_clk);
- msm->hsphy_sleep_clk = devm_clk_get(&pdev->dev, "sleep_a_clk");
- if (IS_ERR(msm->hsphy_sleep_clk)) {
+ mdwc->hsphy_sleep_clk = devm_clk_get(&pdev->dev, "sleep_a_clk");
+ if (IS_ERR(mdwc->hsphy_sleep_clk)) {
dev_err(&pdev->dev, "failed to get sleep_a_clk\n");
- ret = PTR_ERR(msm->hsphy_sleep_clk);
+ ret = PTR_ERR(mdwc->hsphy_sleep_clk);
goto disable_sleep_clk;
}
- clk_prepare_enable(msm->hsphy_sleep_clk);
+ clk_prepare_enable(mdwc->hsphy_sleep_clk);
- msm->utmi_clk = devm_clk_get(&pdev->dev, "utmi_clk");
- if (IS_ERR(msm->utmi_clk)) {
+ mdwc->utmi_clk = devm_clk_get(&pdev->dev, "utmi_clk");
+ if (IS_ERR(mdwc->utmi_clk)) {
dev_err(&pdev->dev, "failed to get utmi_clk\n");
- ret = PTR_ERR(msm->utmi_clk);
+ ret = PTR_ERR(mdwc->utmi_clk);
goto disable_sleep_a_clk;
}
- clk_prepare_enable(msm->utmi_clk);
+ clk_prepare_enable(mdwc->utmi_clk);
- msm->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
- if (IS_ERR(msm->ref_clk)) {
+ mdwc->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
+ if (IS_ERR(mdwc->ref_clk)) {
dev_err(&pdev->dev, "failed to get ref_clk\n");
- ret = PTR_ERR(msm->ref_clk);
+ ret = PTR_ERR(mdwc->ref_clk);
goto disable_utmi_clk;
}
- clk_prepare_enable(msm->ref_clk);
+ clk_prepare_enable(mdwc->ref_clk);
of_get_property(node, "qcom,vdd-voltage-level", &len);
if (len == sizeof(tmp)) {
of_property_read_u32_array(node, "qcom,vdd-voltage-level",
tmp, len/sizeof(*tmp));
- msm->vdd_no_vol_level = tmp[0];
- msm->vdd_low_vol_level = tmp[1];
- msm->vdd_high_vol_level = tmp[2];
+ mdwc->vdd_no_vol_level = tmp[0];
+ mdwc->vdd_low_vol_level = tmp[1];
+ mdwc->vdd_high_vol_level = tmp[2];
} else {
dev_err(&pdev->dev, "no qcom,vdd-voltage-level property\n");
ret = -EINVAL;
@@ -2576,99 +2665,100 @@
}
/* SS PHY */
- msm->ssusb_vddcx = devm_regulator_get(&pdev->dev, "ssusb_vdd_dig");
- if (IS_ERR(msm->ssusb_vddcx)) {
+ mdwc->ssusb_vddcx = devm_regulator_get(&pdev->dev, "ssusb_vdd_dig");
+ if (IS_ERR(mdwc->ssusb_vddcx)) {
dev_err(&pdev->dev, "unable to get ssusb vddcx\n");
- ret = PTR_ERR(msm->ssusb_vddcx);
+ ret = PTR_ERR(mdwc->ssusb_vddcx);
goto disable_ref_clk;
}
- ret = dwc3_ssusb_config_vddcx(1);
+ ret = dwc3_ssusb_config_vddcx(mdwc, 1);
if (ret) {
dev_err(&pdev->dev, "ssusb vddcx configuration failed\n");
goto disable_ref_clk;
}
- ret = regulator_enable(context->ssusb_vddcx);
+ ret = regulator_enable(mdwc->ssusb_vddcx);
if (ret) {
dev_err(&pdev->dev, "unable to enable the ssusb vddcx\n");
goto unconfig_ss_vddcx;
}
- ret = dwc3_ssusb_ldo_init(1);
+ ret = dwc3_ssusb_ldo_init(mdwc, 1);
if (ret) {
dev_err(&pdev->dev, "ssusb vreg configuration failed\n");
goto disable_ss_vddcx;
}
- ret = dwc3_ssusb_ldo_enable(1);
+ ret = dwc3_ssusb_ldo_enable(mdwc, 1);
if (ret) {
dev_err(&pdev->dev, "ssusb vreg enable failed\n");
goto free_ss_ldo_init;
}
/* HS PHY */
- msm->hsusb_vddcx = devm_regulator_get(&pdev->dev, "hsusb_vdd_dig");
- if (IS_ERR(msm->hsusb_vddcx)) {
+ mdwc->hsusb_vddcx = devm_regulator_get(&pdev->dev, "hsusb_vdd_dig");
+ if (IS_ERR(mdwc->hsusb_vddcx)) {
dev_err(&pdev->dev, "unable to get hsusb vddcx\n");
- ret = PTR_ERR(msm->hsusb_vddcx);
+ ret = PTR_ERR(mdwc->hsusb_vddcx);
goto disable_ss_ldo;
}
- ret = dwc3_hsusb_config_vddcx(1);
+ ret = dwc3_hsusb_config_vddcx(mdwc, 1);
if (ret) {
dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
goto disable_ss_ldo;
}
- ret = regulator_enable(context->hsusb_vddcx);
+ ret = regulator_enable(mdwc->hsusb_vddcx);
if (ret) {
dev_err(&pdev->dev, "unable to enable the hsusb vddcx\n");
goto unconfig_hs_vddcx;
}
- ret = dwc3_hsusb_ldo_init(1);
+ ret = dwc3_hsusb_ldo_init(mdwc, 1);
if (ret) {
dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
goto disable_hs_vddcx;
}
- ret = dwc3_hsusb_ldo_enable(1);
+ ret = dwc3_hsusb_ldo_enable(mdwc, 1);
if (ret) {
dev_err(&pdev->dev, "hsusb vreg enable failed\n");
goto free_hs_ldo_init;
}
- msm->id_state = msm->ext_xceiv.id = DWC3_ID_FLOAT;
- msm->ext_xceiv.otg_capability = of_property_read_bool(node,
+ mdwc->id_state = mdwc->ext_xceiv.id = DWC3_ID_FLOAT;
+ mdwc->ext_xceiv.otg_capability = of_property_read_bool(node,
"qcom,otg-capability");
- msm->charger.charging_disabled = of_property_read_bool(node,
+ mdwc->charger.charging_disabled = of_property_read_bool(node,
"qcom,charging-disabled");
- msm->charger.skip_chg_detect = of_property_read_bool(node,
+ mdwc->charger.skip_chg_detect = of_property_read_bool(node,
"qcom,skip-charger-detection");
/*
* DWC3 has separate IRQ line for OTG events (ID/BSV) and for
* DP and DM linestate transitions during low power mode.
*/
- msm->hs_phy_irq = platform_get_irq_byname(pdev, "hs_phy_irq");
- if (msm->hs_phy_irq < 0) {
+ mdwc->hs_phy_irq = platform_get_irq_byname(pdev, "hs_phy_irq");
+ if (mdwc->hs_phy_irq < 0) {
dev_dbg(&pdev->dev, "pget_irq for hs_phy_irq failed\n");
- msm->hs_phy_irq = 0;
+ mdwc->hs_phy_irq = 0;
} else {
- ret = devm_request_irq(&pdev->dev, msm->hs_phy_irq,
+ ret = devm_request_irq(&pdev->dev, mdwc->hs_phy_irq,
msm_dwc3_irq, IRQF_TRIGGER_RISING,
- "msm_dwc3", msm);
+ "msm_dwc3", mdwc);
if (ret) {
dev_err(&pdev->dev, "irqreq HSPHYINT failed\n");
goto disable_hs_ldo;
}
- enable_irq_wake(msm->hs_phy_irq);
+ enable_irq_wake(mdwc->hs_phy_irq);
}
- if (msm->ext_xceiv.otg_capability) {
- msm->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq");
- if (msm->pmic_id_irq > 0) {
+ if (mdwc->ext_xceiv.otg_capability) {
+ mdwc->pmic_id_irq =
+ platform_get_irq_byname(pdev, "pmic_id_irq");
+ if (mdwc->pmic_id_irq > 0) {
/* check if PMIC ID IRQ is supported */
ret = qpnp_misc_irqs_available(&pdev->dev);
@@ -2676,14 +2766,15 @@
/* qpnp hasn't probed yet; defer dwc probe */
goto disable_hs_ldo;
} else if (ret == 0) {
- msm->pmic_id_irq = 0;
+ mdwc->pmic_id_irq = 0;
} else {
ret = devm_request_irq(&pdev->dev,
- msm->pmic_id_irq,
+ mdwc->pmic_id_irq,
dwc3_pmic_id_irq,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
- "dwc3_msm_pmic_id", msm);
+ "dwc3_msm_pmic_id",
+ mdwc);
if (ret) {
dev_err(&pdev->dev, "irqreq IDINT failed\n");
goto disable_hs_ldo;
@@ -2691,21 +2782,21 @@
local_irq_save(flags);
/* Update initial ID state */
- msm->id_state =
- !!irq_read_line(msm->pmic_id_irq);
- if (msm->id_state == DWC3_ID_GROUND)
+ mdwc->id_state =
+ !!irq_read_line(mdwc->pmic_id_irq);
+ if (mdwc->id_state == DWC3_ID_GROUND)
queue_work(system_nrt_wq,
- &msm->id_work);
+ &mdwc->id_work);
local_irq_restore(flags);
- enable_irq_wake(msm->pmic_id_irq);
+ enable_irq_wake(mdwc->pmic_id_irq);
}
}
- if (msm->pmic_id_irq <= 0) {
+ if (mdwc->pmic_id_irq <= 0) {
/* If no PMIC ID IRQ, use ADC for ID pin detection */
- queue_work(system_nrt_wq, &msm->init_adc_work.work);
+ queue_work(system_nrt_wq, &mdwc->init_adc_work.work);
device_create_file(&pdev->dev, &dev_attr_adc_enable);
- msm->pmic_id_irq = 0;
+ mdwc->pmic_id_irq = 0;
}
}
@@ -2735,59 +2826,60 @@
goto disable_hs_ldo;
}
- msm->base = devm_ioremap_nocache(&pdev->dev, res->start,
+ mdwc->base = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
- if (!msm->base) {
+ if (!mdwc->base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENODEV;
goto disable_hs_ldo;
}
- msm->resource_size = resource_size(res);
+ mdwc->io_res = res; /* used to calculate chg block offset */
if (of_property_read_u32(node, "qcom,dwc-hsphy-init",
- &msm->hsphy_init_seq))
+ &mdwc->hsphy_init_seq))
dev_dbg(&pdev->dev, "unable to read hsphy init seq\n");
- else if (!msm->hsphy_init_seq)
+ else if (!mdwc->hsphy_init_seq)
dev_warn(&pdev->dev, "incorrect hsphyinitseq.Using PORvalue\n");
- dwc3_msm_qscratch_reg_init(msm);
+ dwc3_msm_qscratch_reg_init(mdwc);
- pm_runtime_set_active(msm->dev);
- pm_runtime_enable(msm->dev);
+ pm_runtime_set_active(mdwc->dev);
+ pm_runtime_enable(mdwc->dev);
if (of_property_read_u32(node, "qcom,dwc-usb3-msm-dbm-eps",
- &msm->dbm_num_eps)) {
+ &mdwc->dbm_num_eps)) {
dev_err(&pdev->dev,
"unable to read platform data num of dbm eps\n");
- msm->dbm_num_eps = DBM_MAX_EPS;
+ mdwc->dbm_num_eps = DBM_MAX_EPS;
}
- if (msm->dbm_num_eps > DBM_MAX_EPS) {
+ if (mdwc->dbm_num_eps > DBM_MAX_EPS) {
dev_err(&pdev->dev,
"Driver doesn't support number of DBM EPs. "
"max: %d, dbm_num_eps: %d\n",
- DBM_MAX_EPS, msm->dbm_num_eps);
+ DBM_MAX_EPS, mdwc->dbm_num_eps);
ret = -ENODEV;
goto disable_hs_ldo;
}
/* usb_psy required only for vbus_notifications or charging support */
- if (msm->ext_xceiv.otg_capability || !msm->charger.charging_disabled) {
- msm->usb_psy.name = "usb";
- msm->usb_psy.type = POWER_SUPPLY_TYPE_USB;
- msm->usb_psy.supplied_to = dwc3_msm_pm_power_supplied_to;
- msm->usb_psy.num_supplicants = ARRAY_SIZE(
+ if (mdwc->ext_xceiv.otg_capability ||
+ !mdwc->charger.charging_disabled) {
+ mdwc->usb_psy.name = "usb";
+ mdwc->usb_psy.type = POWER_SUPPLY_TYPE_USB;
+ mdwc->usb_psy.supplied_to = dwc3_msm_pm_power_supplied_to;
+ mdwc->usb_psy.num_supplicants = ARRAY_SIZE(
dwc3_msm_pm_power_supplied_to);
- msm->usb_psy.properties = dwc3_msm_pm_power_props_usb;
- msm->usb_psy.num_properties =
+ mdwc->usb_psy.properties = dwc3_msm_pm_power_props_usb;
+ mdwc->usb_psy.num_properties =
ARRAY_SIZE(dwc3_msm_pm_power_props_usb);
- msm->usb_psy.get_property = dwc3_msm_power_get_property_usb;
- msm->usb_psy.set_property = dwc3_msm_power_set_property_usb;
- msm->usb_psy.external_power_changed =
+ mdwc->usb_psy.get_property = dwc3_msm_power_get_property_usb;
+ mdwc->usb_psy.set_property = dwc3_msm_power_set_property_usb;
+ mdwc->usb_psy.external_power_changed =
dwc3_msm_external_power_changed;
- ret = power_supply_register(&pdev->dev, &msm->usb_psy);
+ ret = power_supply_register(&pdev->dev, &mdwc->usb_psy);
if (ret < 0) {
dev_err(&pdev->dev,
"%s:power_supply_register usb failed\n",
@@ -2805,27 +2897,28 @@
}
}
- msm->bus_scale_table = msm_bus_cl_get_pdata(pdev);
- if (!msm->bus_scale_table) {
+ mdwc->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+ if (!mdwc->bus_scale_table) {
dev_err(&pdev->dev, "bus scaling is disabled\n");
} else {
- msm->bus_perf_client =
- msm_bus_scale_register_client(msm->bus_scale_table);
+ mdwc->bus_perf_client =
+ msm_bus_scale_register_client(mdwc->bus_scale_table);
ret = msm_bus_scale_client_update_request(
- msm->bus_perf_client, 1);
+ mdwc->bus_perf_client, 1);
if (ret)
dev_err(&pdev->dev, "Failed to vote for bus scaling\n");
}
- msm->otg_xceiv = usb_get_transceiver();
+ mdwc->otg_xceiv = usb_get_transceiver();
/* Register with OTG if present, ignore USB2 OTG using other PHY */
- if (msm->otg_xceiv && !(msm->otg_xceiv->flags & ENABLE_SECONDARY_PHY)) {
+ if (mdwc->otg_xceiv &&
+ !(mdwc->otg_xceiv->flags & ENABLE_SECONDARY_PHY)) {
/* Skip charger detection for simulator targets */
- if (!msm->charger.skip_chg_detect) {
- msm->charger.start_detection = dwc3_start_chg_det;
- ret = dwc3_set_charger(msm->otg_xceiv->otg,
- &msm->charger);
- if (ret || !msm->charger.notify_detection_complete) {
+ if (!mdwc->charger.skip_chg_detect) {
+ mdwc->charger.start_detection = dwc3_start_chg_det;
+ ret = dwc3_set_charger(mdwc->otg_xceiv->otg,
+ &mdwc->charger);
+ if (ret || !mdwc->charger.notify_detection_complete) {
dev_err(&pdev->dev,
"failed to register charger: %d\n",
ret);
@@ -2833,125 +2926,130 @@
}
}
- if (msm->ext_xceiv.otg_capability)
- msm->ext_xceiv.ext_block_reset = dwc3_msm_block_reset;
- ret = dwc3_set_ext_xceiv(msm->otg_xceiv->otg, &msm->ext_xceiv);
- if (ret || !msm->ext_xceiv.notify_ext_events) {
+ if (mdwc->ext_xceiv.otg_capability)
+ mdwc->ext_xceiv.ext_block_reset = dwc3_msm_block_reset;
+ ret = dwc3_set_ext_xceiv(mdwc->otg_xceiv->otg,
+ &mdwc->ext_xceiv);
+ if (ret || !mdwc->ext_xceiv.notify_ext_events) {
dev_err(&pdev->dev, "failed to register xceiver: %d\n",
ret);
goto put_xcvr;
}
} else {
dev_dbg(&pdev->dev, "No OTG, DWC3 running in host only mode\n");
- msm->host_mode = 1;
- msm->vbus_otg = devm_regulator_get(&pdev->dev, "vbus_dwc3");
- if (IS_ERR(msm->vbus_otg)) {
+ mdwc->host_mode = 1;
+ mdwc->vbus_otg = devm_regulator_get(&pdev->dev, "vbus_dwc3");
+ if (IS_ERR(mdwc->vbus_otg)) {
dev_dbg(&pdev->dev, "Failed to get vbus regulator\n");
- msm->vbus_otg = 0;
+ mdwc->vbus_otg = 0;
} else {
- ret = regulator_enable(msm->vbus_otg);
+ ret = regulator_enable(mdwc->vbus_otg);
if (ret) {
- msm->vbus_otg = 0;
+ mdwc->vbus_otg = 0;
dev_err(&pdev->dev, "Failed to enable vbus_otg\n");
}
}
- msm->otg_xceiv = NULL;
+ mdwc->otg_xceiv = NULL;
}
- if (msm->ext_xceiv.otg_capability && msm->charger.start_detection) {
- ret = dwc3_msm_setup_cdev(msm);
+ if (mdwc->ext_xceiv.otg_capability && mdwc->charger.start_detection) {
+ ret = dwc3_msm_setup_cdev(mdwc);
if (ret)
dev_err(&pdev->dev, "Fail to setup dwc3 setup cdev\n");
}
- wake_lock_init(&msm->wlock, WAKE_LOCK_SUSPEND, "msm_dwc3");
- wake_lock(&msm->wlock);
- dwc3_debugfs_init(msm);
+ device_init_wakeup(mdwc->dev, 1);
+ pm_stay_awake(mdwc->dev);
+ dwc3_debugfs_init(mdwc);
return 0;
put_xcvr:
- usb_put_transceiver(msm->otg_xceiv);
+ usb_put_transceiver(mdwc->otg_xceiv);
put_psupply:
- if (msm->usb_psy.dev)
- power_supply_unregister(&msm->usb_psy);
+ if (mdwc->usb_psy.dev)
+ power_supply_unregister(&mdwc->usb_psy);
disable_hs_ldo:
- dwc3_hsusb_ldo_enable(0);
+ dwc3_hsusb_ldo_enable(mdwc, 0);
free_hs_ldo_init:
- dwc3_hsusb_ldo_init(0);
+ dwc3_hsusb_ldo_init(mdwc, 0);
disable_hs_vddcx:
- regulator_disable(context->hsusb_vddcx);
+ regulator_disable(mdwc->hsusb_vddcx);
unconfig_hs_vddcx:
- dwc3_hsusb_config_vddcx(0);
+ dwc3_hsusb_config_vddcx(mdwc, 0);
disable_ss_ldo:
- dwc3_ssusb_ldo_enable(0);
+ dwc3_ssusb_ldo_enable(mdwc, 0);
free_ss_ldo_init:
- dwc3_ssusb_ldo_init(0);
+ dwc3_ssusb_ldo_init(mdwc, 0);
disable_ss_vddcx:
- regulator_disable(context->ssusb_vddcx);
+ regulator_disable(mdwc->ssusb_vddcx);
unconfig_ss_vddcx:
- dwc3_ssusb_config_vddcx(0);
+ dwc3_ssusb_config_vddcx(mdwc, 0);
disable_ref_clk:
- clk_disable_unprepare(msm->ref_clk);
+ clk_disable_unprepare(mdwc->ref_clk);
disable_utmi_clk:
- clk_disable_unprepare(msm->utmi_clk);
+ clk_disable_unprepare(mdwc->utmi_clk);
disable_sleep_a_clk:
- clk_disable_unprepare(msm->hsphy_sleep_clk);
+ clk_disable_unprepare(mdwc->hsphy_sleep_clk);
disable_sleep_clk:
- clk_disable_unprepare(msm->sleep_clk);
+ clk_disable_unprepare(mdwc->sleep_clk);
disable_iface_clk:
- clk_disable_unprepare(msm->iface_clk);
+ clk_disable_unprepare(mdwc->iface_clk);
disable_core_clk:
- clk_disable_unprepare(msm->core_clk);
+ clk_disable_unprepare(mdwc->core_clk);
disable_xo:
- clk_disable_unprepare(msm->xo_clk);
+ clk_disable_unprepare(mdwc->xo_clk);
put_xo:
- clk_put(msm->xo_clk);
+ clk_put(mdwc->xo_clk);
+disable_dwc3_gdsc:
+ dwc3_msm_config_gdsc(mdwc, 0);
return ret;
}
static int __devexit dwc3_msm_remove(struct platform_device *pdev)
{
- struct dwc3_msm *msm = platform_get_drvdata(pdev);
+ struct dwc3_msm *mdwc = platform_get_drvdata(pdev);
- if (!msm->ext_chg_device) {
- device_destroy(msm->ext_chg_class, msm->ext_chg_dev);
- cdev_del(&msm->ext_chg_cdev);
- class_destroy(msm->ext_chg_class);
- unregister_chrdev_region(msm->ext_chg_dev, 1);
+ if (!mdwc->ext_chg_device) {
+ device_destroy(mdwc->ext_chg_class, mdwc->ext_chg_dev);
+ cdev_del(&mdwc->ext_chg_cdev);
+ class_destroy(mdwc->ext_chg_class);
+ unregister_chrdev_region(mdwc->ext_chg_dev, 1);
}
- if (msm->id_adc_detect)
+ if (mdwc->id_adc_detect)
qpnp_adc_tm_usbid_end();
if (dwc3_debugfs_root)
debugfs_remove_recursive(dwc3_debugfs_root);
- if (msm->otg_xceiv) {
- dwc3_start_chg_det(&msm->charger, false);
- usb_put_transceiver(msm->otg_xceiv);
+ if (mdwc->otg_xceiv) {
+ dwc3_start_chg_det(&mdwc->charger, false);
+ usb_put_transceiver(mdwc->otg_xceiv);
}
- if (msm->usb_psy.dev)
- power_supply_unregister(&msm->usb_psy);
- if (msm->vbus_otg)
- regulator_disable(msm->vbus_otg);
+ if (mdwc->usb_psy.dev)
+ power_supply_unregister(&mdwc->usb_psy);
+ if (mdwc->vbus_otg)
+ regulator_disable(mdwc->vbus_otg);
- pm_runtime_disable(msm->dev);
- wake_lock_destroy(&msm->wlock);
+ pm_runtime_disable(mdwc->dev);
+ device_init_wakeup(mdwc->dev, 0);
- dwc3_hsusb_ldo_enable(0);
- dwc3_hsusb_ldo_init(0);
- regulator_disable(msm->hsusb_vddcx);
- dwc3_hsusb_config_vddcx(0);
- dwc3_ssusb_ldo_enable(0);
- dwc3_ssusb_ldo_init(0);
- regulator_disable(msm->ssusb_vddcx);
- dwc3_ssusb_config_vddcx(0);
- clk_disable_unprepare(msm->core_clk);
- clk_disable_unprepare(msm->iface_clk);
- clk_disable_unprepare(msm->sleep_clk);
- clk_disable_unprepare(msm->hsphy_sleep_clk);
- clk_disable_unprepare(msm->ref_clk);
- clk_disable_unprepare(msm->xo_clk);
- clk_put(msm->xo_clk);
+ dwc3_hsusb_ldo_enable(mdwc, 0);
+ dwc3_hsusb_ldo_init(mdwc, 0);
+ regulator_disable(mdwc->hsusb_vddcx);
+ dwc3_hsusb_config_vddcx(mdwc, 0);
+ dwc3_ssusb_ldo_enable(mdwc, 0);
+ dwc3_ssusb_ldo_init(mdwc, 0);
+ regulator_disable(mdwc->ssusb_vddcx);
+ dwc3_ssusb_config_vddcx(mdwc, 0);
+ clk_disable_unprepare(mdwc->core_clk);
+ clk_disable_unprepare(mdwc->iface_clk);
+ clk_disable_unprepare(mdwc->sleep_clk);
+ clk_disable_unprepare(mdwc->hsphy_sleep_clk);
+ clk_disable_unprepare(mdwc->ref_clk);
+ clk_disable_unprepare(mdwc->xo_clk);
+ clk_put(mdwc->xo_clk);
+
+ dwc3_msm_config_gdsc(mdwc, 0);
return 0;
}
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index 98c9b4c..a7bea63 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -218,7 +218,7 @@
*/
if (ext_xceiv && ext_xceiv->otg_capability &&
ext_xceiv->ext_block_reset)
- ext_xceiv->ext_block_reset(true);
+ ext_xceiv->ext_block_reset(ext_xceiv, true);
dwc3_otg_set_peripheral_regs(dotg);
@@ -285,7 +285,7 @@
* DBM reset is required, hence perform only DBM reset here */
if (ext_xceiv && ext_xceiv->otg_capability &&
ext_xceiv->ext_block_reset)
- ext_xceiv->ext_block_reset(false);
+ ext_xceiv->ext_block_reset(ext_xceiv, false);
dwc3_otg_set_peripheral_regs(dotg);
usb_gadget_vbus_connect(otg->gadget);
@@ -726,8 +726,17 @@
phy->state = OTG_STATE_B_PERIPHERAL;
work = 1;
break;
- case DWC3_UNSUPPORTED_CHARGER:
+ case DWC3_FLOATED_CHARGER:
dotg->charger_retry_count++;
+ /*
+ * In case of floating charger, if
+ * retry count equal to max retry count
+ * notify PMIC about floating charger
+ * and put Hw in low power mode. Else
+ * perform charger detection again by
+ * calling start_detection() with false
+ * and then with true argument.
+ */
if (dotg->charger_retry_count ==
max_chgr_retry_count) {
dwc3_otg_set_power(phy, 0);
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
index b00468e..7adf874 100644
--- a/drivers/usb/dwc3/dwc3_otg.h
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -64,6 +64,7 @@
* IDEV_CHG_MAX can be drawn irrespective of USB state.
* DWC3_PROPRIETARY_CHARGER A proprietary charger pull DP and DM to specific
* voltages between 2.0-3.3v for identification.
+ * DWC3_FLOATED_CHARGER Non standard charger whose data lines are floating.
*/
enum dwc3_chg_type {
DWC3_INVALID_CHARGER = 0,
@@ -71,7 +72,7 @@
DWC3_DCP_CHARGER,
DWC3_CDP_CHARGER,
DWC3_PROPRIETARY_CHARGER,
- DWC3_UNSUPPORTED_CHARGER,
+ DWC3_FLOATED_CHARGER,
};
struct dwc3_charger {
@@ -113,7 +114,8 @@
void (*notify_ext_events)(struct usb_otg *otg,
enum dwc3_ext_events ext_event);
/* for block reset USB core */
- void (*ext_block_reset)(bool core_reset);
+ void (*ext_block_reset)(struct dwc3_ext_xceiv *ext_xceiv,
+ bool core_reset);
};
/* for external transceiver driver */
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 36a43c3..38b1967 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -671,6 +671,9 @@
#define MAX_XPORT_STR_LEN 50
static char rmnet_transports[MAX_XPORT_STR_LEN];
+/*rmnet transport name string - "rmnet_hsic[,rmnet_hsusb]" */
+static char rmnet_xport_names[MAX_XPORT_STR_LEN];
+
static void rmnet_function_cleanup(struct android_usb_function *f)
{
frmnet_cleanup();
@@ -683,18 +686,28 @@
int err = 0;
char *ctrl_name;
char *data_name;
+ char *tname = NULL;
char buf[MAX_XPORT_STR_LEN], *b;
+ char xport_name_buf[MAX_XPORT_STR_LEN], *tb;
static int rmnet_initialized, ports;
if (!rmnet_initialized) {
rmnet_initialized = 1;
strlcpy(buf, rmnet_transports, sizeof(buf));
b = strim(buf);
+
+ strlcpy(xport_name_buf, rmnet_xport_names,
+ sizeof(xport_name_buf));
+ tb = strim(xport_name_buf);
+
while (b) {
ctrl_name = strsep(&b, ",");
data_name = strsep(&b, ",");
if (ctrl_name && data_name) {
- err = frmnet_init_port(ctrl_name, data_name);
+ if (tb)
+ tname = strsep(&tb, ",");
+ err = frmnet_init_port(ctrl_name, data_name,
+ tname);
if (err) {
pr_err("rmnet: Cannot open ctrl port:"
"'%s' data port:'%s'\n",
@@ -738,12 +751,34 @@
return size;
}
+static ssize_t rmnet_xport_names_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_xport_names);
+}
+
+static ssize_t rmnet_xport_names_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(rmnet_xport_names, buff, sizeof(rmnet_xport_names));
+
+ return size;
+}
+
static struct device_attribute dev_attr_rmnet_transports =
__ATTR(transports, S_IRUGO | S_IWUSR,
rmnet_transports_show,
rmnet_transports_store);
+
+static struct device_attribute dev_attr_rmnet_xport_names =
+ __ATTR(transport_names, S_IRUGO | S_IWUSR,
+ rmnet_xport_names_show,
+ rmnet_xport_names_store);
+
static struct device_attribute *rmnet_function_attributes[] = {
&dev_attr_rmnet_transports,
+ &dev_attr_rmnet_xport_names,
NULL };
static struct android_usb_function rmnet_function = {
@@ -1199,9 +1234,33 @@
return size;
}
+/*enabled FSERIAL transport names - "serial_hsic[,serial_hsusb]"*/
+static char serial_xport_names[32];
+static ssize_t serial_xport_names_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(serial_xport_names, buff, sizeof(serial_xport_names));
+
+ return size;
+}
+
+static ssize_t serial_xport_names_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", serial_xport_names);
+}
+
static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store);
-static struct device_attribute *serial_function_attributes[] =
- { &dev_attr_transports, NULL };
+static struct device_attribute dev_attr_serial_xport_names =
+ __ATTR(transport_names, S_IRUGO | S_IWUSR,
+ serial_xport_names_show,
+ serial_xport_names_store);
+
+static struct device_attribute *serial_function_attributes[] = {
+ &dev_attr_transports,
+ &dev_attr_serial_xport_names,
+ NULL };
static void serial_function_cleanup(struct android_usb_function *f)
{
@@ -1211,8 +1270,8 @@
static int serial_function_bind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
- char *name;
- char buf[32], *b;
+ char *name, *xport_name = NULL;
+ char buf[32], *b, xport_name_buf[32], *tb;
int err = -1, i;
static int serial_initialized = 0, ports = 0;
@@ -1223,11 +1282,16 @@
strlcpy(buf, serial_transports, sizeof(buf));
b = strim(buf);
+ strlcpy(xport_name_buf, serial_xport_names, sizeof(xport_name_buf));
+ tb = strim(xport_name_buf);
+
while (b) {
name = strsep(&b, ",");
if (name) {
- err = gserial_init_port(ports, name);
+ if (tb)
+ xport_name = strsep(&tb, ",");
+ err = gserial_init_port(ports, name, xport_name);
if (err) {
pr_err("serial: Cannot open port '%s'", name);
goto out;
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index 88074b9..a9c073b 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -69,6 +69,26 @@
ULPI_CLR(ULPI_MISC_A));
}
+/* Link power management will reduce power consumption by
+ * short time HW suspend/resume.
+ */
+static void ci13xxx_msm_set_l1(struct ci13xxx *udc)
+{
+ int temp;
+ struct device *dev = udc->gadget.dev.parent;
+
+ dev_dbg(dev, "Enable link power management\n");
+
+ /* Enable remote wakeup and L1 for IN EPs */
+ writel_relaxed(0xffff0000, USB_L1_EP_CTRL);
+
+ temp = readl_relaxed(USB_L1_CONFIG);
+ temp |= L1_CONFIG_LPM_EN | L1_CONFIG_REMOTE_WAKEUP |
+ L1_CONFIG_GATE_SYS_CLK | L1_CONFIG_PHY_LPM |
+ L1_CONFIG_PLL;
+ writel_relaxed(temp, USB_L1_CONFIG);
+}
+
static void ci13xxx_msm_connect(void)
{
struct ci13xxx *udc = _udc;
@@ -109,6 +129,9 @@
writel_relaxed(0, USB_AHBBURST);
writel_relaxed(0x08, USB_AHBMODE);
+ if (udc->gadget.l1_supported)
+ ci13xxx_msm_set_l1(udc);
+
if (phy && (phy->flags & ENABLE_SECONDARY_PHY)) {
int temp;
@@ -234,6 +257,7 @@
struct resource *res;
int ret;
struct ci13xxx_platform_data *pdata = pdev->dev.platform_data;
+ bool is_l1_supported = false;
dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
@@ -245,6 +269,8 @@
else
ci13xxx_msm_udc_driver.nz_itc =
1 << (pdata->log2_itc-1);
+
+ is_l1_supported = pdata->l1_supported;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -265,6 +291,8 @@
goto iounmap;
}
+ _udc->gadget.l1_supported = is_l1_supported;
+
_udc_ctxt.irq = platform_get_irq(pdev, 0);
if (_udc_ctxt.irq < 0) {
dev_err(&pdev->dev, "IRQ not found\n");
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index e7074a2..3f8d924 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -1853,6 +1853,14 @@
spin_lock_irqsave(mep->lock, flags);
+
+ if (_udc && (!_udc->vbus_active || _udc->suspended)) {
+ pr_debug("ep%d%s prime timer when vbus_active=%d,suspend=%d\n",
+ mep->num, mep->dir ? "IN" : "OUT",
+ _udc->vbus_active, _udc->suspended);
+ goto out;
+ }
+
if (!hw_cread(CAP_ENDPTPRIME, BIT(n)))
goto out;
@@ -2197,6 +2205,9 @@
if (mEp == NULL)
return -EINVAL;
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
+
hw_ep_flush(mEp->num, mEp->dir);
while (!list_empty(&mEp->qh.queue)) {
@@ -2926,8 +2937,6 @@
/* only internal SW should disable ctrl endpts */
- del_timer(&mEp->prime_timer);
- mEp->prime_timer_count = 0;
direction = mEp->dir;
do {
dbg_event(_usb_addr(mEp), "DISABLE", 0);
@@ -3282,8 +3291,6 @@
spin_lock_irqsave(mEp->lock, flags);
- del_timer(&mEp->prime_timer);
- mEp->prime_timer_count = 0;
dbg_event(_usb_addr(mEp), "FFLUSH", 0);
/*
* _ep_nuke() takes care of flushing the endpoint.
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index de4a233..27d67e3 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -515,7 +515,8 @@
/*
* A SuperSpeed device shall include the USB2.0 extension descriptor
- * and shall support LPM when operating in USB2.0 HS mode.
+ * and shall support LPM when operating in USB2.0 HS mode, as well as
+ * a HS device when operating in USB2.1 HS mode.
*/
usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
bos->bNumDeviceCaps++;
@@ -525,33 +526,37 @@
usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT);
- /*
- * The Superspeed USB Capability descriptor shall be implemented by all
- * SuperSpeed devices.
- */
- ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
- bos->bNumDeviceCaps++;
- le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
- ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
- ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
- ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
- ss_cap->bmAttributes = 0; /* LTM is not supported yet */
- ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
- USB_FULL_SPEED_OPERATION |
- USB_HIGH_SPEED_OPERATION |
- USB_5GBPS_OPERATION);
- ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+ if (gadget_is_superspeed(cdev->gadget)) {
+ /*
+ * The Superspeed USB Capability descriptor shall be
+ * implemented by all SuperSpeed devices.
+ */
+ ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
+ ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+ ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+ ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+ ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
+ USB_FULL_SPEED_OPERATION |
+ USB_HIGH_SPEED_OPERATION |
+ USB_5GBPS_OPERATION);
+ ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
- /* Get Controller configuration */
- if (cdev->gadget->ops->get_config_params)
- cdev->gadget->ops->get_config_params(&dcd_config_params);
- else {
- dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
- dcd_config_params.bU2DevExitLat =
- cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ /* Get Controller configuration */
+ if (cdev->gadget->ops->get_config_params)
+ cdev->gadget->ops->get_config_params
+ (&dcd_config_params);
+ else {
+ dcd_config_params.bU1devExitLat =
+ USB_DEFAULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU2DevExitLat =
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ }
+ ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+ ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
}
- ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
- ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
return le16_to_cpu(bos->wTotalLength);
}
@@ -1124,6 +1129,9 @@
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
DBG(cdev, "Config SS device in HS\n");
}
+ } else if (gadget->l1_supported) {
+ cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+ DBG(cdev, "Config HS device with LPM(L1)\n");
}
value = min(w_length, (u16) sizeof cdev->desc);
@@ -1164,7 +1172,8 @@
value = min(w_length, (u16) value);
break;
case USB_DT_BOS:
- if (gadget_is_superspeed(gadget)) {
+ if (gadget_is_superspeed(gadget) ||
+ gadget->l1_supported) {
value = bos_desc(cdev);
value = min(w_length, (u16) value);
}
@@ -1371,6 +1380,10 @@
reset_config(cdev);
if (composite->disconnect)
composite->disconnect(cdev);
+ if (cdev->delayed_status != 0) {
+ INFO(cdev, "delayed status mismatch..resetting\n");
+ cdev->delayed_status = 0;
+ }
spin_unlock_irqrestore(&cdev->lock, flags);
}
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
index 3355e19..d1b911a 100644
--- a/drivers/usb/gadget/f_diag.c
+++ b/drivers/usb/gadget/f_diag.c
@@ -642,6 +642,8 @@
if (ctxt->ch && ctxt->ch->priv_usb == ctxt)
ctxt->ch->priv_usb = NULL;
list_del(&ctxt->list_item);
+ /* Free any pending USB requests from last session */
+ free_reqs(ctxt);
kfree(ctxt);
}
diff --git a/drivers/usb/gadget/f_qdss.c b/drivers/usb/gadget/f_qdss.c
index cece500..f90967f 100644
--- a/drivers/usb/gadget/f_qdss.c
+++ b/drivers/usb/gadget/f_qdss.c
@@ -790,6 +790,7 @@
void usb_qdss_close(struct usb_qdss_ch *ch)
{
struct f_qdss *qdss = ch->priv_usb;
+ struct usb_gadget *gadget = qdss->cdev->gadget;
unsigned long flags;
pr_debug("usb_qdss_close\n");
@@ -801,7 +802,7 @@
ch->app_conn = 0;
spin_unlock_irqrestore(&d_lock, flags);
- msm_dwc3_restart_usb_session();
+ msm_dwc3_restart_usb_session(gadget);
}
EXPORT_SYMBOL(usb_qdss_close);
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 6dcc3b4..6bfa203 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -1230,7 +1230,8 @@
no_data_hsuart_ports = 0;
}
-static int frmnet_init_port(const char *ctrl_name, const char *data_name)
+static int frmnet_init_port(const char *ctrl_name, const char *data_name,
+ const char *port_name)
{
struct f_rmnet *dev;
struct rmnet_ports *rmnet_port;
@@ -1272,6 +1273,7 @@
no_ctrl_qti_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_ctrl_set_port_name(port_name, ctrl_name);
rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
no_ctrl_hsic_ports++;
break;
@@ -1299,6 +1301,7 @@
no_data_bam2bam_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_data_set_port_name(port_name, data_name);
rmnet_port->data_xport_num = no_data_hsic_ports;
no_data_hsic_ports++;
break;
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 74dba07..57cbc03 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -985,9 +985,11 @@
/**
* gserial_init_port - bind a gserial_port to its transport
*/
-static int gserial_init_port(int port_num, const char *name)
+static int gserial_init_port(int port_num, const char *name,
+ const char *port_name)
{
enum transport_type transport;
+ int ret = 0;
if (port_num >= GSERIAL_NO_PORTS)
return -ENODEV;
@@ -1013,6 +1015,9 @@
no_smd_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_ctrl_set_port_name(port_name, name);
+ ghsic_data_set_port_name(port_name, name);
+
/*client port number will be updated in gport_setup*/
no_hsic_sports++;
break;
@@ -1028,5 +1033,5 @@
nr_ports++;
- return 0;
+ return ret;
}
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 022e641..a775459 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -730,11 +730,11 @@
int ret;
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
- teth_bridge_disconnect();
ret = usb_bam_disconnect_ipa(&d->ipa_params);
if (ret)
pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
__func__, ret);
+ teth_bridge_disconnect();
}
}
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
index d470edf..577a4fe 100644
--- a/drivers/usb/gadget/u_bam_data.c
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -177,13 +177,14 @@
int ret;
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
- if (d->func_type == USB_FUNC_MBIM)
- teth_bridge_disconnect();
if (d->func_type == USB_FUNC_ECM)
ecm_ipa_disconnect(d->ipa_params.priv);
ret = usb_bam_disconnect_ipa(&d->ipa_params);
if (ret)
pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
+ if (d->func_type == USB_FUNC_MBIM)
+ teth_bridge_disconnect();
+
}
}
@@ -199,7 +200,6 @@
int ret;
pr_debug("%s: Connect workqueue started", __func__);
- usb_bam_reset_complete();
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
if (d->func_type == USB_FUNC_MBIM) {
@@ -267,17 +267,18 @@
}
}
} else { /* transport type is USB_GADGET_XPORT_BAM2BAM */
- ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
- if (ret) {
- pr_err("usb_bam_connect (src) failed: err:%d\n", ret);
- return;
+ usb_bam_reset_complete();
+ ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
+ if (ret) {
+ pr_err("usb_bam_connect (src) failed: err:%d\n", ret);
+ return;
+ }
+ ret = usb_bam_connect(d->dst_connection_idx, &d->dst_pipe_idx);
+ if (ret) {
+ pr_err("usb_bam_connect (dst) failed: err:%d\n", ret);
+ return;
+ }
}
- ret = usb_bam_connect(d->dst_connection_idx, &d->dst_pipe_idx);
- if (ret) {
- pr_err("usb_bam_connect (dst) failed: err:%d\n", ret);
- return;
- }
-}
if (!port->port_usb) {
pr_err("port_usb is NULL");
diff --git a/drivers/usb/gadget/u_ctrl_hsic.c b/drivers/usb/gadget/u_ctrl_hsic.c
index 0f93ad4..6143d1b 100644
--- a/drivers/usb/gadget/u_ctrl_hsic.c
+++ b/drivers/usb/gadget/u_ctrl_hsic.c
@@ -36,12 +36,6 @@
static unsigned int no_ctrl_ports;
-static const char *ctrl_bridge_names[] = {
- "dun_ctrl_hsic0",
- "rmnet_ctrl_hsic0"
-};
-
-#define CTRL_BRIDGE_NAME_MAX_LEN 20
#define READ_BUF_LEN 1024
#define CH_OPENED 0
@@ -83,6 +77,7 @@
static struct {
struct gctrl_port *port;
struct platform_driver pdrv;
+ char port_name[BRIDGE_NAME_MAX_LEN];
} gctrl_ports[NUM_PORTS];
static int ghsic_ctrl_receive(void *dev, void *buf, size_t actual)
@@ -336,19 +331,35 @@
gser->send_modem_ctrl_bits(gser, ctrl_bits);
}
+static int ghsic_ctrl_get_port_id(const char *pdev_name)
+{
+ struct gctrl_port *port;
+ int i;
+
+ for (i = 0; i < no_ctrl_ports; i++) {
+ port = gctrl_ports[i].port;
+ if (!strncmp(port->brdg.name, pdev_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int ghsic_ctrl_probe(struct platform_device *pdev)
{
struct gctrl_port *port;
unsigned long flags;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_ctrl_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_ctrl_get_port_id(pdev->name);
+ if (id < 0 || id >= no_ctrl_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gctrl_ports[pdev->id].port;
+ port = gctrl_ports[id].port;
set_bit(CH_READY, &port->bridge_sts);
/* if usb is online, start read */
@@ -366,15 +377,17 @@
struct gserial *gser = NULL;
struct grmnet *gr = NULL;
unsigned long flags;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_ctrl_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_ctrl_get_port_id(pdev->name);
+ if (id < 0 || id >= no_ctrl_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gctrl_ports[pdev->id].port;
+ port = gctrl_ports[id].port;
spin_lock_irqsave(&port->port_lock, flags);
if (!port->port_usb) {
@@ -421,15 +434,17 @@
{
struct gctrl_port *port;
struct platform_driver *pdrv;
+ char *name;
port = kzalloc(sizeof(struct gctrl_port), GFP_KERNEL);
if (!port)
return -ENOMEM;
- port->wq = create_singlethread_workqueue(ctrl_bridge_names[portno]);
+ name = gctrl_ports[portno].port_name;
+
+ port->wq = create_singlethread_workqueue(name);
if (!port->wq) {
- pr_err("%s: Unable to create workqueue:%s\n",
- __func__, ctrl_bridge_names[portno]);
+ pr_err("%s: Unable to create workqueue:%s\n", __func__, name);
return -ENOMEM;
}
@@ -441,7 +456,7 @@
INIT_WORK(&port->connect_w, ghsic_ctrl_connect_w);
INIT_WORK(&port->disconnect_w, gctrl_disconnect_w);
- port->brdg.ch_id = portno;
+ port->brdg.name = name;
port->brdg.ctx = port;
port->brdg.ops.send_pkt = ghsic_ctrl_receive;
if (port->gtype == USB_GADGET_SERIAL)
@@ -451,7 +466,7 @@
pdrv = &gctrl_ports[portno].pdrv;
pdrv->probe = ghsic_ctrl_probe;
pdrv->remove = ghsic_ctrl_remove;
- pdrv->driver.name = ctrl_bridge_names[portno];
+ pdrv->driver.name = name;
pdrv->driver.owner = THIS_MODULE;
platform_driver_register(pdrv);
@@ -461,6 +476,31 @@
return 0;
}
+/*portname will be used to find the bridge channel index*/
+void ghsic_ctrl_set_port_name(const char *name, const char *xport_type)
+{
+ static unsigned int port_num;
+
+ if (port_num >= NUM_PORTS) {
+ pr_err("%s: setting xport name for invalid port num %d\n",
+ __func__, port_num);
+ return;
+ }
+
+ /*if no xport name is passed set it to xport type e.g. hsic*/
+ if (!name)
+ strlcpy(gctrl_ports[port_num].port_name, xport_type,
+ BRIDGE_NAME_MAX_LEN);
+ else
+ strlcpy(gctrl_ports[port_num].port_name, name,
+ BRIDGE_NAME_MAX_LEN);
+
+ /*append _ctrl to get ctrl bridge name e.g. serial_hsic_ctrl*/
+ strlcat(gctrl_ports[port_num].port_name, "_ctrl", BRIDGE_NAME_MAX_LEN);
+
+ port_num++;
+}
+
int ghsic_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
{
int first_port_id = no_ctrl_ports;
diff --git a/drivers/usb/gadget/u_data_hsic.c b/drivers/usb/gadget/u_data_hsic.c
index 3932bbc..92653db 100644
--- a/drivers/usb/gadget/u_data_hsic.c
+++ b/drivers/usb/gadget/u_data_hsic.c
@@ -25,13 +25,6 @@
static unsigned int no_data_ports;
-static const char *data_bridge_names[] = {
- "dun_data_hsic0",
- "rmnet_data_hsic0"
-};
-
-#define DATA_BRIDGE_NAME_MAX_LEN 20
-
#define GHSIC_DATA_RMNET_RX_Q_SIZE 50
#define GHSIC_DATA_RMNET_TX_Q_SIZE 300
#define GHSIC_DATA_SERIAL_RX_Q_SIZE 10
@@ -130,6 +123,7 @@
static struct {
struct gdata_port *port;
struct platform_driver pdrv;
+ char port_name[BRIDGE_NAME_MAX_LEN];
} gdata_ports[NUM_PORTS];
static unsigned int get_timestamp(void);
@@ -150,22 +144,25 @@
static int ghsic_data_alloc_requests(struct usb_ep *ep, struct list_head *head,
int num,
void (*cb)(struct usb_ep *ep, struct usb_request *),
- gfp_t flags)
+ spinlock_t *lock)
{
int i;
struct usb_request *req;
+ unsigned long flags;
pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
ep->name, head, num, cb);
for (i = 0; i < num; i++) {
- req = usb_ep_alloc_request(ep, flags);
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!req) {
pr_debug("%s: req allocated:%d\n", __func__, i);
return list_empty(head) ? -ENOMEM : 0;
}
req->complete = cb;
+ spin_lock_irqsave(lock, flags);
list_add(&req->list, head);
+ spin_unlock_irqrestore(lock, flags);
}
return 0;
@@ -438,20 +435,23 @@
req = list_first_entry(&port->rx_idle,
struct usb_request, list);
+ list_del(&req->list);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
created = get_timestamp();
- skb = alloc_skb(ghsic_data_rx_req_size, GFP_ATOMIC);
- if (!skb)
+ skb = alloc_skb(ghsic_data_rx_req_size, GFP_KERNEL);
+ if (!skb) {
+ spin_lock_irqsave(&port->rx_lock, flags);
+ list_add(&req->list, &port->rx_idle);
break;
+ }
info = (struct timestamp_info *)skb->cb;
info->created = created;
- list_del(&req->list);
req->buf = skb->data;
req->length = ghsic_data_rx_req_size;
req->context = skb;
info->rx_queued = get_timestamp();
- spin_unlock_irqrestore(&port->rx_lock, flags);
ret = usb_ep_queue(ep, req, GFP_KERNEL);
spin_lock_irqsave(&port->rx_lock, flags);
if (ret) {
@@ -472,7 +472,7 @@
static void ghsic_data_start_io(struct gdata_port *port)
{
unsigned long flags;
- struct usb_ep *ep;
+ struct usb_ep *ep_out, *ep_in;
int ret;
pr_debug("%s: port:%p\n", __func__, port);
@@ -481,37 +481,37 @@
return;
spin_lock_irqsave(&port->rx_lock, flags);
- ep = port->out;
- if (!ep) {
- spin_unlock_irqrestore(&port->rx_lock, flags);
+ ep_out = port->out;
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ if (!ep_out)
return;
- }
- ret = ghsic_data_alloc_requests(ep, &port->rx_idle,
- port->rx_q_size, ghsic_data_epout_complete, GFP_ATOMIC);
+ ret = ghsic_data_alloc_requests(ep_out, &port->rx_idle,
+ port->rx_q_size, ghsic_data_epout_complete, &port->rx_lock);
if (ret) {
pr_err("%s: rx req allocation failed\n", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ ep_in = port->in;
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ if (!ep_in) {
+ spin_lock_irqsave(&port->rx_lock, flags);
+ ghsic_data_free_requests(ep_out, &port->rx_idle);
spin_unlock_irqrestore(&port->rx_lock, flags);
return;
}
- spin_unlock_irqrestore(&port->rx_lock, flags);
- spin_lock_irqsave(&port->tx_lock, flags);
- ep = port->in;
- if (!ep) {
- spin_unlock_irqrestore(&port->tx_lock, flags);
- return;
- }
-
- ret = ghsic_data_alloc_requests(ep, &port->tx_idle,
- port->tx_q_size, ghsic_data_epin_complete, GFP_ATOMIC);
+ ret = ghsic_data_alloc_requests(ep_in, &port->tx_idle,
+ port->tx_q_size, ghsic_data_epin_complete, &port->tx_lock);
if (ret) {
pr_err("%s: tx req allocation failed\n", __func__);
- ghsic_data_free_requests(ep, &port->rx_idle);
- spin_unlock_irqrestore(&port->tx_lock, flags);
+ spin_lock_irqsave(&port->rx_lock, flags);
+ ghsic_data_free_requests(ep_out, &port->rx_idle);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
return;
}
- spin_unlock_irqrestore(&port->tx_lock, flags);
/* queue out requests */
ghsic_data_start_rx(port);
@@ -586,19 +586,35 @@
spin_unlock_irqrestore(&port->rx_lock, flags);
}
+static int ghsic_data_get_port_id(const char *pdev_name)
+{
+ struct gdata_port *port;
+ int i;
+
+ for (i = 0; i < no_data_ports; i++) {
+ port = gdata_ports[i].port;
+ if (!strncmp(port->brdg.name, pdev_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int ghsic_data_probe(struct platform_device *pdev)
{
struct gdata_port *port;
+ int id;
- pr_debug("%s: name:%s no_data_ports= %d\n",
- __func__, pdev->name, no_data_ports);
+ pr_debug("%s: name:%s no_data_ports= %d\n", __func__, pdev->name,
+ no_data_ports);
- if (pdev->id >= no_data_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_data_get_port_id(pdev->name);
+ if (id < 0 || id >= no_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gdata_ports[pdev->id].port;
+ port = gdata_ports[id].port;
set_bit(CH_READY, &port->bridge_sts);
/* if usb is online, try opening bridge */
@@ -614,15 +630,17 @@
struct gdata_port *port;
struct usb_ep *ep_in;
struct usb_ep *ep_out;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_data_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_data_get_port_id(pdev->name);
+ if (id < 0 || id >= no_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gdata_ports[pdev->id].port;
+ port = gdata_ports[id].port;
ep_in = port->in;
if (ep_in)
@@ -632,6 +650,9 @@
if (ep_out)
usb_ep_fifo_flush(ep_out);
+ /* cancel pending writes to MDM */
+ cancel_work_sync(&port->write_tomdm_w);
+
ghsic_data_free_buffers(port);
cancel_work_sync(&port->connect_w);
@@ -658,15 +679,17 @@
{
struct gdata_port *port;
struct platform_driver *pdrv;
+ char *name;
port = kzalloc(sizeof(struct gdata_port), GFP_KERNEL);
if (!port)
return -ENOMEM;
- port->wq = create_singlethread_workqueue(data_bridge_names[port_num]);
+ name = gdata_ports[port_num].port_name;
+
+ port->wq = create_singlethread_workqueue(name);
if (!port->wq) {
- pr_err("%s: Unable to create workqueue:%s\n",
- __func__, data_bridge_names[port_num]);
+ pr_err("%s: Unable to create workqueue:%s\n", __func__, name);
kfree(port);
return -ENOMEM;
}
@@ -688,7 +711,7 @@
skb_queue_head_init(&port->rx_skb_q);
port->gtype = gtype;
- port->brdg.ch_id = port_num;
+ port->brdg.name = name;
port->brdg.ctx = port;
port->brdg.ops.send_pkt = ghsic_data_receive;
port->brdg.ops.unthrottle_tx = ghsic_data_unthrottle_tx;
@@ -697,7 +720,7 @@
pdrv = &gdata_ports[port_num].pdrv;
pdrv->probe = ghsic_data_probe;
pdrv->remove = ghsic_data_remove;
- pdrv->driver.name = data_bridge_names[port_num];
+ pdrv->driver.name = name;
pdrv->driver.owner = THIS_MODULE;
platform_driver_register(pdrv);
@@ -846,7 +869,7 @@
}
#if defined(CONFIG_DEBUG_FS)
-#define DEBUG_BUF_SIZE 1024
+#define DEBUG_DATA_BUF_SIZE 4096
static unsigned int record_timestamp;
module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
@@ -917,7 +940,7 @@
if (!record_timestamp)
return 0;
- buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_DATA_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -927,7 +950,7 @@
for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
continue;
- j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+ j += scnprintf(buf + j, DEBUG_DATA_BUF_SIZE - j,
"%s\n", dbg_data.buf[i]);
}
@@ -955,7 +978,7 @@
int i;
int temp = 0;
- buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_DATA_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -966,7 +989,7 @@
pdrv = &gdata_ports[i].pdrv;
spin_lock_irqsave(&port->rx_lock, flags);
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ temp += scnprintf(buf + temp, DEBUG_DATA_BUF_SIZE - temp,
"\nName: %s\n"
"#PORT:%d port#: %p\n"
"data_ch_open: %d\n"
@@ -991,7 +1014,7 @@
spin_unlock_irqrestore(&port->rx_lock, flags);
spin_lock_irqsave(&port->tx_lock, flags);
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ temp += scnprintf(buf + temp, DEBUG_DATA_BUF_SIZE - temp,
"\n******DL INFO******\n\n"
"dpkts_to_usbhost: %lu\n"
"tx_buf_len: %u\n"
@@ -1094,6 +1117,31 @@
#endif
+/*portname will be used to find the bridge channel index*/
+void ghsic_data_set_port_name(const char *name, const char *xport_type)
+{
+ static unsigned int port_num;
+
+ if (port_num >= NUM_PORTS) {
+ pr_err("%s: setting xport name for invalid port num %d\n",
+ __func__, port_num);
+ return;
+ }
+
+ /*if no xport name is passed set it to xport type e.g. hsic*/
+ if (!name)
+ strlcpy(gdata_ports[port_num].port_name, xport_type,
+ BRIDGE_NAME_MAX_LEN);
+ else
+ strlcpy(gdata_ports[port_num].port_name, name,
+ BRIDGE_NAME_MAX_LEN);
+
+ /*append _data to get data bridge name: e.g. serial_hsic_data*/
+ strlcat(gdata_ports[port_num].port_name, "_data", BRIDGE_NAME_MAX_LEN);
+
+ port_num++;
+}
+
int ghsic_data_setup(unsigned num_ports, enum gadget_type gtype)
{
int first_port_id = no_data_ports;
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 9961d00..325c2b5 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -557,6 +557,11 @@
switch (retval) {
default:
DBG(dev, "tx queue err %d\n", retval);
+ new_req->length = 0;
+ spin_lock(&dev->req_lock);
+ list_add_tail(&new_req->list,
+ &dev->tx_reqs);
+ spin_unlock(&dev->req_lock);
break;
case 0:
spin_lock(&dev->req_lock);
@@ -566,7 +571,13 @@
}
} else {
spin_lock(&dev->req_lock);
- list_add(&new_req->list, &dev->tx_reqs);
+ /*
+ * Put the idle request at the back of the
+ * queue. The xmit function will put the
+ * unfinished request at the beginning of the
+ * queue.
+ */
+ list_add_tail(&new_req->list, &dev->tx_reqs);
spin_unlock(&dev->req_lock);
}
} else {
@@ -787,6 +798,8 @@
if (retval) {
if (!multi_pkt_xfer)
dev_kfree_skb_any(skb);
+ else
+ req->length = 0;
drop:
dev->net->stats.tx_dropped++;
spin_lock_irqsave(&dev->req_lock, flags);
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 14c3323..ef45d49 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -1895,6 +1895,8 @@
"hsic,consider-ipa-handshake"));
pdata->ahb_async_bridge_bypass = of_property_read_bool(node,
"qcom,ahb-async-bridge-bypass");
+ pdata->disable_cerr = of_property_read_bool(node,
+ "hsic,disable-cerr");
return pdata;
}
@@ -1982,8 +1984,10 @@
mehci->ehci.pool_64_bit_align = pdata->pool_64_bit_align;
mehci->enable_hbm = pdata->enable_hbm;
- if (pdata)
+ if (pdata) {
mehci->ehci.log2_irq_thresh = pdata->log2_irq_thresh;
+ mehci->ehci.disable_cerr = pdata->disable_cerr;
+ }
ret = msm_hsic_init_gdsc(mehci, 1);
if (ret) {
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 0b89dd0..8b1f3a8 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -1,6 +1,6 @@
/* ehci-msm.c - HSUSB Host Controller Driver Implementation
*
- * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
*
* Partly derived from ehci-fsl.c and ehci-hcd.c
* Copyright (c) 2000-2004 by David Brownell
@@ -48,7 +48,7 @@
return retval;
/* bursts of unspecified length. */
- writel(0, USB_AHBBURST);
+ writel_relaxed(0, USB_AHBBURST);
/* Use the AHB transactor */
writel_relaxed(0x08, USB_AHBMODE);
/* Disable streaming mode and select host mode */
@@ -60,6 +60,10 @@
USB_PHY_CTRL2);
}
+ /* Disable ULPI_TX_PKT_EN_CLR_FIX which is valid only for HSIC */
+ writel_relaxed(readl_relaxed(USB_GENCONFIG2) & ~(1<<19),
+ USB_GENCONFIG2);
+
ehci_port_power(ehci, 1);
return 0;
}
@@ -260,15 +264,23 @@
static int ehci_msm_pm_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ int ret;
dev_dbg(dev, "ehci-msm PM resume\n");
if (!hcd->rh_registered)
return 0;
- ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
+ /* Notify OTG to bring hw out of LPM before restoring wakeup flags */
+ ret = usb_phy_set_suspend(phy, 0);
+ if (ret)
+ return ret;
- return usb_phy_set_suspend(phy, 0);
+ ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
+ /* Resume root-hub to handle USB event if any else initiate LPM again */
+ usb_hcd_resume_root_hub(hcd);
+
+ return ret;
}
#endif
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index 6727996..0aecaad 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -25,9 +25,12 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <linux/wakelock.h>
+#include <linux/pm_wakeup.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/irq.h>
#include <linux/usb/ulpi.h>
#include <linux/usb/msm_hsusb_hw.h>
@@ -36,6 +39,7 @@
#include <mach/clk.h>
#include <mach/msm_xo.h>
#include <mach/msm_iomap.h>
+#include <linux/debugfs.h>
#define MSM_USB_BASE (hcd->regs)
@@ -62,11 +66,16 @@
bool pmic_gpio_dp_irq_enabled;
uint32_t pmic_gpio_int_cnt;
atomic_t pm_usage_cnt;
- struct wake_lock wlock;
+ struct wakeup_source ws;
struct work_struct phy_susp_fail_work;
int async_irq;
bool async_irq_enabled;
uint32_t async_int_cnt;
+ int resume_gpio;
+
+ int wakeup_int_cnt;
+ bool wakeup_irq_enabled;
+ int wakeup_irq;
};
static inline struct msm_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
@@ -601,8 +610,8 @@
msm_ehci_vbus_power(mhcd, 1);
}
-#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
-#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
+#define PHY_SUSP_TIMEOUT_MSEC 500
+#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
#ifdef CONFIG_PM_SLEEP
static int msm_ehci_suspend(struct msm_hcd *mhcd)
@@ -619,24 +628,42 @@
disable_irq(hcd->irq);
- /* Set the PHCD bit, only if it is not set by the controller.
- * PHY may take some time or even fail to enter into low power
- * mode (LPM). Hence poll for 500 msec and reset the PHY and link
- * in failure case.
+ /* make sure we don't race against a remote wakeup */
+ if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) ||
+ readl_relaxed(USB_PORTSC) & PORT_RESUME) {
+ dev_dbg(mhcd->dev, "wakeup pending, aborting suspend\n");
+ enable_irq(hcd->irq);
+ return -EBUSY;
+ }
+
+ /* If port is enabled wait 5ms for PHCD to come up. Reset PHY
+ * and link if it fails to do so.
+ * If port is not enabled set the PHCD bit and poll for it to
+ * come up with in 500ms. Reset phy and link if it fails to do so.
*/
portsc = readl_relaxed(USB_PORTSC);
- if (!(portsc & PORTSC_PHCD)) {
- writel_relaxed(portsc | PORTSC_PHCD,
- USB_PORTSC);
+ if (portsc & PORT_PE) {
- timeout = jiffies + usecs_to_jiffies(PHY_SUSPEND_TIMEOUT_USEC);
+ usleep_range(5000, 5000);
+
+ if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) {
+ dev_err(mhcd->dev,
+ "Unable to suspend PHY. portsc: %8x\n",
+ readl_relaxed(USB_PORTSC));
+ goto reset_phy_and_link;
+ }
+ } else {
+ writel_relaxed(portsc | PORTSC_PHCD, USB_PORTSC);
+
+ timeout = jiffies + msecs_to_jiffies(PHY_SUSP_TIMEOUT_MSEC);
while (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) {
if (time_after(jiffies, timeout)) {
- dev_err(mhcd->dev, "Unable to suspend PHY\n");
- schedule_work(&mhcd->phy_susp_fail_work);
- return -ETIMEDOUT;
+ dev_err(mhcd->dev,
+ "Unable to suspend PHY. portsc: %8x\n",
+ readl_relaxed(USB_PORTSC));
+ goto reset_phy_and_link;
}
- udelay(1);
+ usleep_range(10000, 10000);
}
}
@@ -645,9 +672,14 @@
* power mode (LPM). This interrupt is level triggered. So USB IRQ
* line must be disabled till async interrupt enable bit is cleared
* in USBCMD register. Assert STP (ULPI interface STOP signal) to
- * block data communication from PHY.
+ * block data communication from PHY. Enable asynchronous interrupt
+ * only when wakeup gpio IRQ is not present.
*/
- writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
+ if (mhcd->wakeup_irq)
+ writel_relaxed(readl_relaxed(USB_USBCMD) | ULPI_STP_CTRL,
+ USB_USBCMD);
+ else
+ writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
ULPI_STP_CTRL, USB_USBCMD);
/*
@@ -673,6 +705,13 @@
atomic_set(&mhcd->in_lpm, 1);
enable_irq(hcd->irq);
+
+ if (mhcd->wakeup_irq) {
+ mhcd->wakeup_irq_enabled = 1;
+ enable_irq_wake(mhcd->wakeup_irq);
+ enable_irq(mhcd->wakeup_irq);
+ }
+
if (mhcd->pmic_gpio_dp_irq) {
mhcd->pmic_gpio_dp_irq_enabled = 1;
enable_irq_wake(mhcd->pmic_gpio_dp_irq);
@@ -683,11 +722,15 @@
enable_irq_wake(mhcd->async_irq);
enable_irq(mhcd->async_irq);
}
- wake_unlock(&mhcd->wlock);
+ pm_relax(mhcd->dev);
dev_info(mhcd->dev, "EHCI USB in low power mode\n");
return 0;
+
+reset_phy_and_link:
+ schedule_work(&mhcd->phy_susp_fail_work);
+ return -ETIMEDOUT;
}
static int msm_ehci_resume(struct msm_hcd *mhcd)
@@ -708,15 +751,24 @@
disable_irq_nosync(mhcd->pmic_gpio_dp_irq);
mhcd->pmic_gpio_dp_irq_enabled = 0;
}
+
spin_lock_irqsave(&mhcd->wakeup_lock, flags);
if (mhcd->async_irq_enabled) {
disable_irq_wake(mhcd->async_irq);
disable_irq_nosync(mhcd->async_irq);
mhcd->async_irq_enabled = 0;
}
+
+ if (mhcd->wakeup_irq) {
+ if (mhcd->wakeup_irq_enabled) {
+ disable_irq_wake(mhcd->wakeup_irq);
+ disable_irq_nosync(mhcd->wakeup_irq);
+ mhcd->wakeup_irq_enabled = 0;
+ }
+ }
spin_unlock_irqrestore(&mhcd->wakeup_lock, flags);
- wake_lock(&mhcd->wlock);
+ pm_stay_awake(mhcd->dev);
/* Vote for TCXO when waking up the phy */
if (!IS_ERR(mhcd->xo_clk)) {
@@ -783,6 +835,7 @@
struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
if (atomic_read(&mhcd->in_lpm)) {
+ dev_dbg(mhcd->dev, "phy async intr\n");
disable_irq_nosync(hcd->irq);
mhcd->async_int = true;
pm_runtime_get(mhcd->dev);
@@ -801,7 +854,7 @@
dev_dbg(mhcd->dev, "%s: hsusb host remote wakeup interrupt cnt: %u\n",
__func__, mhcd->async_int_cnt);
- wake_lock(&mhcd->wlock);
+ pm_stay_awake(mhcd->dev);
spin_lock(&mhcd->wakeup_lock);
if (mhcd->async_irq_enabled) {
@@ -832,7 +885,7 @@
__func__, mhcd->pmic_gpio_int_cnt);
- wake_lock(&mhcd->wlock);
+ pm_stay_awake(mhcd->dev);
if (mhcd->pmic_gpio_dp_irq_enabled) {
mhcd->pmic_gpio_dp_irq_enabled = 0;
@@ -896,6 +949,149 @@
return 0;
}
+static int msm_ehci_bus_resume_with_gpio(struct usb_hcd *hcd)
+{
+ struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+ int ret;
+
+ gpio_direction_output(mhcd->resume_gpio, 1);
+
+ ret = ehci_bus_resume(hcd);
+
+ gpio_direction_output(mhcd->resume_gpio, 0);
+
+ return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static u32 addr;
+#define BUF_SIZE 32
+static ssize_t debug_read_phy_data(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_hcd *mhcd = file->private_data;
+ char *kbuf;
+ size_t c = 0;
+ u32 data = 0;
+ int ret = 0;
+
+ kbuf = kzalloc(sizeof(char) * BUF_SIZE, GFP_KERNEL);
+ pm_runtime_get(mhcd->dev);
+ data = msm_ulpi_read(mhcd, addr);
+ pm_runtime_put(mhcd->dev);
+ if (data < 0) {
+ dev_err(mhcd->dev,
+ "%s(): ulpi read timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ c = scnprintf(kbuf, BUF_SIZE, "addr: 0x%x: data: 0x%x\n", addr, data);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, kbuf, c);
+
+ kfree(kbuf);
+
+ return ret;
+}
+
+static ssize_t debug_write_phy_data(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_hcd *mhcd = file->private_data;
+ char kbuf[10];
+ u32 data = 0;
+
+ memset(kbuf, 0, 10);
+
+ if (copy_from_user(kbuf, buf, count > 10 ? 10 : count))
+ return -EFAULT;
+
+ if (sscanf(kbuf, "%x", &data) != 1)
+ return -EINVAL;
+
+ pm_runtime_get(mhcd->dev);
+ if (msm_ulpi_write(mhcd, data, addr) < 0) {
+ dev_err(mhcd->dev,
+ "%s(): ulpi write timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+ pm_runtime_put(mhcd->dev);
+
+ return count;
+}
+
+static ssize_t debug_phy_write_addr(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char kbuf[10];
+ u32 temp;
+
+ memset(kbuf, 0, 10);
+
+ if (copy_from_user(kbuf, buf, count > 10 ? 10 : count))
+ return -EFAULT;
+
+ if (sscanf(kbuf, "%x", &temp) != 1)
+ return -EINVAL;
+
+ if (temp > 0x3F)
+ return -EINVAL;
+
+ addr = temp;
+
+ return count;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+const struct file_operations debug_rw_phy_ops = {
+ .open = debug_open,
+ .read = debug_read_phy_data,
+ .write = debug_write_phy_data,
+};
+
+const struct file_operations debug_write_phy_ops = {
+ .open = debug_open,
+ .write = debug_phy_write_addr,
+};
+
+static struct dentry *dent_ehci;
+
+static int ehci_debugfs_init(struct msm_hcd *mhcd)
+{
+ struct dentry *debug_phy_data;
+ struct dentry *debug_phy_addr;
+
+ dent_ehci = debugfs_create_dir(dev_name(mhcd->dev), 0);
+ if (IS_ERR(dent_ehci))
+ return -ENOENT;
+
+ debug_phy_data = debugfs_create_file("phy_reg_data", 0666,
+ dent_ehci, mhcd, &debug_rw_phy_ops);
+ if (!debug_phy_data) {
+ debugfs_remove(dent_ehci);
+ return -ENOENT;
+ }
+
+ debug_phy_addr = debugfs_create_file("phy_reg_addr", 0666,
+ dent_ehci, mhcd, &debug_write_phy_ops);
+ if (!debug_phy_addr) {
+ debugfs_remove_recursive(dent_ehci);
+ return -ENOENT;
+ }
+ return 0;
+}
+#else
+static int ehci_debugfs_init(struct msm_hcd *mhcd)
+{
+ return 0;
+}
+#endif
+
static struct hc_driver msm_hc2_driver = {
.description = hcd_name,
.product_desc = "Qualcomm EHCI Host Controller",
@@ -942,6 +1138,46 @@
.bus_resume = ehci_bus_resume,
};
+static irqreturn_t msm_hsusb_wakeup_irq(int irq, void *data)
+{
+ struct msm_hcd *mhcd = data;
+ int ret;
+
+ mhcd->wakeup_int_cnt++;
+ dev_dbg(mhcd->dev, "%s: hsic remote wakeup interrupt cnt: %u\n",
+ __func__, mhcd->wakeup_int_cnt);
+
+ pm_stay_awake(mhcd->dev);
+
+ spin_lock(&mhcd->wakeup_lock);
+ if (mhcd->wakeup_irq_enabled) {
+ mhcd->wakeup_irq_enabled = 0;
+ disable_irq_wake(irq);
+ disable_irq_nosync(irq);
+ }
+ spin_unlock(&mhcd->wakeup_lock);
+
+ if (!atomic_read(&mhcd->pm_usage_cnt)) {
+ ret = pm_runtime_get(mhcd->dev);
+ /*
+ * controller runtime resume can race with us.
+ * if we are active (ret == 1) or resuming
+ * (ret == -EINPROGRESS), decrement the
+ * PM usage counter before returning.
+ */
+ if ((ret == 1) || (ret == -EINPROGRESS)) {
+ pm_runtime_put_noidle(mhcd->dev);
+ } else {
+ /* Let khubd know of hub port status change */
+ if (mhcd->ehci.no_selective_suspend)
+ mhcd->ehci.suspended_ports = 1;
+ atomic_set(&mhcd->pm_usage_cnt, 1);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
static int msm_ehci_init_clocks(struct msm_hcd *mhcd, u32 init)
{
int ret = 0;
@@ -1022,6 +1258,12 @@
"qcom,usb2-enable-hsphy2");
of_property_read_u32(node, "qcom,usb2-power-budget",
&pdata->power_budget);
+ pdata->no_selective_suspend = of_property_read_bool(node,
+ "qcom,no-selective-suspend");
+ pdata->resume_gpio = of_get_named_gpio(node, "qcom,resume-gpio", 0);
+ if (pdata->resume_gpio < 0)
+ pdata->resume_gpio = 0;
+
return pdata;
}
@@ -1045,6 +1287,8 @@
if (!pdev->dev.platform_data)
dev_dbg(&pdev->dev, "No platform data given\n");
+ pdata = pdev->dev.platform_data;
+
if (!pdev->dev.dma_mask)
pdev->dev.dma_mask = &ehci_msm_dma_mask;
if (!pdev->dev.coherent_dma_mask)
@@ -1122,6 +1366,22 @@
goto free_xo_handle;
}
+ if (pdata && pdata->resume_gpio) {
+ mhcd->resume_gpio = pdata->resume_gpio;
+ ret = gpio_request(mhcd->resume_gpio, "hsusb_resume");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "resume gpio(%d) request failed:%d\n",
+ mhcd->resume_gpio, ret);
+ mhcd->resume_gpio = 0;
+ } else {
+ msm_hc2_driver.bus_resume =
+ msm_ehci_bus_resume_with_gpio;
+ }
+ }
+
+ spin_lock_init(&mhcd->wakeup_lock);
+
ret = msm_ehci_init_clocks(mhcd, 1);
if (ret) {
dev_err(&pdev->dev, "unable to initialize clocks\n");
@@ -1177,9 +1437,33 @@
!irq_read_line(pdata->dock_connect_irq)))
msm_ehci_vbus_power(mhcd, 1);
+ /* For peripherals directly conneted to downstream port of root hub
+ * and require to drive suspend and resume by controller driver instead
+ * of root hub.
+ */
+ if (pdata)
+ mhcd->ehci.no_selective_suspend = pdata->no_selective_suspend;
+
+ mhcd->wakeup_irq = platform_get_irq_byname(pdev, "wakeup_irq");
+ if (mhcd->wakeup_irq > 0) {
+ dev_dbg(&pdev->dev, "wakeup irq:%d\n", mhcd->wakeup_irq);
+
+ irq_set_status_flags(mhcd->wakeup_irq, IRQ_NOAUTOEN);
+ ret = request_irq(mhcd->wakeup_irq, msm_hsusb_wakeup_irq,
+ IRQF_TRIGGER_HIGH,
+ "msm_hsusb_wakeup", mhcd);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq(%d) failed:%d\n",
+ mhcd->wakeup_irq, ret);
+ mhcd->wakeup_irq = 0;
+ }
+ } else {
+ mhcd->wakeup_irq = 0;
+ }
+
device_init_wakeup(&pdev->dev, 1);
- wake_lock_init(&mhcd->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
- wake_lock(&mhcd->wlock);
+ wakeup_source_init(&mhcd->ws, dev_name(&pdev->dev));
+ pm_stay_awake(mhcd->dev);
INIT_WORK(&mhcd->phy_susp_fail_work, msm_ehci_phy_susp_fail_work);
/*
* This pdev->dev is assigned parent of root-hub by USB core,
@@ -1205,6 +1489,9 @@
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ if (ehci_debugfs_init(mhcd) < 0)
+ dev_err(mhcd->dev, "%s: debugfs init failed\n", __func__);
+
return 0;
vbus_deinit:
@@ -1218,6 +1505,8 @@
deinit_clocks:
msm_ehci_init_clocks(mhcd, 0);
devote_xo_handle:
+ if (mhcd->resume_gpio)
+ gpio_free(mhcd->resume_gpio);
if (!IS_ERR(mhcd->xo_clk))
clk_disable_unprepare(mhcd->xo_clk);
else
@@ -1253,6 +1542,16 @@
disable_irq_wake(mhcd->async_irq);
free_irq(mhcd->async_irq, mhcd);
}
+
+ if (mhcd->wakeup_irq) {
+ if (mhcd->wakeup_irq_enabled)
+ disable_irq_wake(mhcd->wakeup_irq);
+ free_irq(mhcd->wakeup_irq, mhcd);
+ }
+
+ if (mhcd->resume_gpio)
+ gpio_free(mhcd->resume_gpio);
+
device_init_wakeup(&pdev->dev, 0);
pm_runtime_set_suspended(&pdev->dev);
@@ -1271,10 +1570,13 @@
msm_ehci_init_vddcx(mhcd, 0);
msm_ehci_init_clocks(mhcd, 0);
- wake_lock_destroy(&mhcd->wlock);
+ wakeup_source_trash(&mhcd->ws);
iounmap(hcd->regs);
usb_put_hcd(hcd);
+#if defined(CONFIG_DEBUG_FS)
+ debugfs_remove_recursive(dent_ehci);
+#endif
return 0;
}
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index db49c07..b029be2 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -637,7 +637,8 @@
qtd->urb = urb;
token = QTD_STS_ACTIVE;
- token |= (EHCI_TUNE_CERR << 10);
+ if (!ehci->disable_cerr)
+ token |= (EHCI_TUNE_CERR << 10);
/* for split transactions, SplitXState initialized to zero */
len = urb->transfer_buffer_length;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index edf2a73..0498a6a 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -154,6 +154,7 @@
unsigned susp_sof_bug:1; /*Chip Idea HC*/
unsigned resume_sof_bug:1;/*Chip Idea HC*/
unsigned reset_sof_bug:1; /*Chip Idea HC*/
+ bool disable_cerr;
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 717103d..1b6d15e 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -21,6 +21,7 @@
*/
#include <linux/gfp.h>
+#include <linux/slab.h>
#include <asm/unaligned.h>
#include "xhci.h"
@@ -472,6 +473,149 @@
}
}
+static void xhci_single_step_completion(struct urb *urb)
+{
+ struct completion *done = urb->context;
+
+ complete(done);
+}
+
+/*
+ * Allocate a URB and initialize the various fields of it.
+ * This API is used by the single_step_set_feature test of
+ * EHSET where IN packet of the GetDescriptor request is
+ * sent 15secs after the SETUP packet.
+ * Return NULL if failed.
+ */
+static struct urb *xhci_request_single_step_set_feature_urb(
+ struct usb_device *udev,
+ void *dr,
+ void *buf,
+ struct completion *done)
+{
+ struct urb *urb;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ struct usb_host_endpoint *ep;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return NULL;
+
+ urb->pipe = usb_rcvctrlpipe(udev, 0);
+ ep = udev->ep_in[usb_pipeendpoint(urb->pipe)];
+ if (!ep) {
+ usb_free_urb(urb);
+ return NULL;
+ }
+
+ /*
+ * Initialize the various URB fields as these are used by the HCD
+ * driver to queue it and as well as when completion happens.
+ */
+ urb->ep = ep;
+ urb->dev = udev;
+ urb->setup_packet = dr;
+ urb->transfer_buffer = buf;
+ urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
+ urb->complete = xhci_single_step_completion;
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->transfer_flags = URB_DIR_IN;
+ usb_get_urb(urb);
+ atomic_inc(&urb->use_count);
+ atomic_inc(&urb->dev->urbnum);
+ usb_hcd_map_urb_for_dma(hcd, urb, GFP_KERNEL);
+ urb->context = done;
+ return urb;
+}
+
+/*
+ * This function implements the USB_PORT_FEAT_TEST handling of the
+ * SINGLE_STEP_SET_FEATURE test mode as defined in the Embedded
+ * High-Speed Electrical Test (EHSET) specification. This simply
+ * issues a GetDescriptor control transfer, with an inserted 15-second
+ * delay after the end of the SETUP stage and before the IN token of
+ * the DATA stage is set. The idea is that this gives the test operator
+ * enough time to configure the oscilloscope to perform a measurement
+ * of the response time between the DATA and ACK packets that follow.
+ */
+static int xhci_ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
+{
+ int retval;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ struct usb_device *udev;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct usb_device_descriptor *buf;
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ /* Obtain udev of the rhub's child port */
+ udev = hcd->self.root_hub->children[port];
+ if (!udev) {
+ xhci_err(xhci, "No device attached to the RootHub\n");
+ return -ENODEV;
+ }
+ buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ if (!dr) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ /* Fill Setup packet for GetDescriptor */
+ dr->bRequestType = USB_DIR_IN;
+ dr->bRequest = USB_REQ_GET_DESCRIPTOR;
+ dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
+ dr->wIndex = 0;
+ dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
+ urb = xhci_request_single_step_set_feature_urb(udev, dr, buf, &done);
+ if (!urb)
+ goto cleanup;
+
+ /* Now complete just the SETUP stage */
+ spin_lock_irqsave(&xhci->lock, flags);
+ retval = xhci_submit_single_step_set_feature(hcd, urb, 1);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ if (retval)
+ goto out1;
+
+ if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
+ usb_kill_urb(urb);
+ retval = -ETIMEDOUT;
+ xhci_err(xhci, "%s SETUP stage timed out on ep0\n", __func__);
+ goto out1;
+ }
+
+ /* Sleep for 15 seconds; HC will send SOFs during this period */
+ msleep(15 * 1000);
+
+ /* Complete remaining DATA and status stages. Re-use same URB */
+ urb->status = -EINPROGRESS;
+ usb_get_urb(urb);
+ atomic_inc(&urb->use_count);
+ atomic_inc(&urb->dev->urbnum);
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ retval = xhci_submit_single_step_set_feature(hcd, urb, 0);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ if (!retval && !wait_for_completion_timeout(&done,
+ msecs_to_jiffies(2000))) {
+ usb_kill_urb(urb);
+ retval = -ETIMEDOUT;
+ xhci_err(xhci, "%s IN stage timed out on ep0\n", __func__);
+ }
+out1:
+ usb_free_urb(urb);
+cleanup:
+ kfree(dr);
+ kfree(buf);
+ return retval;
+}
+
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
@@ -778,6 +922,11 @@
temp = xhci_readl(xhci, port_array[wIndex] + 1);
temp |= test_mode << 28;
xhci_writel(xhci, temp, port_array[wIndex] + 1);
+ } else if (test_mode == 6) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ retval = xhci_ehset_single_step_set_feature(hcd,
+ wIndex);
+ spin_lock_irqsave(&xhci->lock, flags);
} else {
goto error;
}
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 46b5ce4..cc0c1e0 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -37,9 +37,12 @@
if (!pdata)
return;
- else if (pdata->vendor == SYNOPSIS_DWC3_VENDOR &&
- pdata->revision < 0x230A)
+
+ if (pdata->vendor == SYNOPSIS_DWC3_VENDOR && pdata->revision < 0x230A)
xhci->quirks |= XHCI_PORTSC_DELAY;
+
+ if (pdata->vendor == SYNOPSIS_DWC3_VENDOR && pdata->revision == 0x250A)
+ xhci->quirks |= XHCI_RESET_DELAY;
}
/* called during probe() after chip reset completes */
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 323b481..b3f3fa8 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3215,6 +3215,150 @@
return 0;
}
+/*
+ * Variant of xhci_queue_ctrl_tx() used to implement EHSET
+ * SINGLE_STEP_SET_FEATURE test mode. It differs in that the control
+ * transfer is broken up so that the SETUP stage can happen and call
+ * the URB's completion handler before the DATA/STATUS stages are
+ * executed by the xHC hardware. This assumes the control transfer is a
+ * GetDescriptor, with a DATA stage in the IN direction, and an OUT
+ * STATUS stage.
+ *
+ * This function is called twice, usually with a 15-second delay in between.
+ * - with is_setup==true, the SETUP stage for the control request
+ * (GetDescriptor) is queued in the TRB ring and sent to HW immediately
+ * - with is_setup==false, the DATA and STATUS TRBs are queued and exceuted
+ *
+ * Caller must have locked xhci->lock
+ */
+int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, struct urb *urb,
+ int is_setup)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_ring *ep_ring;
+ int num_trbs;
+ int ret;
+ unsigned int slot_id, ep_index;
+ struct usb_ctrlrequest *setup;
+ struct xhci_generic_trb *start_trb;
+ int start_cycle;
+ u32 field, length_field;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
+
+ ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
+ if (!ep_ring)
+ return -EINVAL;
+
+ /* Need buffer for data stage */
+ if (urb->transfer_buffer_length <= 0)
+ return -EINVAL;
+
+ /*
+ * Need to copy setup packet into setup TRB, so we can't use the setup
+ * DMA address.
+ */
+ if (!urb->setup_packet)
+ return -EINVAL;
+ setup = (struct usb_ctrlrequest *) urb->setup_packet;
+
+ slot_id = urb->dev->slot_id;
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+
+ urb_priv = kzalloc(sizeof(struct urb_priv) +
+ sizeof(struct xhci_td *), GFP_ATOMIC);
+ if (!urb_priv)
+ return -ENOMEM;
+
+ td = urb_priv->td[0] = kzalloc(sizeof(struct xhci_td), GFP_ATOMIC);
+ if (!td) {
+ kfree(urb_priv);
+ return -ENOMEM;
+ }
+
+ urb_priv->length = 1;
+ urb_priv->td_cnt = 0;
+ urb->hcpriv = urb_priv;
+
+ num_trbs = is_setup ? 1 : 2;
+
+ ret = prepare_transfer(xhci, xhci->devs[slot_id],
+ ep_index, urb->stream_id,
+ num_trbs, urb, 0, GFP_ATOMIC);
+ if (ret < 0) {
+ kfree(td);
+ kfree(urb_priv);
+ return ret;
+ }
+
+ /*
+ * Don't give the first TRB to the hardware (by toggling the cycle bit)
+ * until we've finished creating all the other TRBs. The ring's cycle
+ * state may change as we enqueue the other TRBs, so save it too.
+ */
+ start_trb = &ep_ring->enqueue->generic;
+ start_cycle = ep_ring->cycle_state;
+
+ if (is_setup) {
+ /* Queue only the setup TRB */
+ field = TRB_IDT | TRB_IOC | TRB_TYPE(TRB_SETUP);
+ if (start_cycle == 0)
+ field |= 0x1;
+
+ /* xHCI 1.0 6.4.1.2.1: Transfer Type field */
+ if (xhci->hci_version == 0x100) {
+ if (setup->bRequestType & USB_DIR_IN)
+ field |= TRB_TX_TYPE(TRB_DATA_IN);
+ else
+ field |= TRB_TX_TYPE(TRB_DATA_OUT);
+ }
+
+ /* Save the DMA address of the last TRB in the TD */
+ td->last_trb = ep_ring->enqueue;
+
+ queue_trb(xhci, ep_ring, false,
+ setup->bRequestType | setup->bRequest << 8 |
+ le16_to_cpu(setup->wValue) << 16,
+ le16_to_cpu(setup->wIndex) |
+ le16_to_cpu(setup->wLength) << 16,
+ TRB_LEN(8) | TRB_INTR_TARGET(0),
+ field);
+ } else {
+ /* Queue data TRB */
+ field = TRB_ISP | TRB_TYPE(TRB_DATA);
+ if (start_cycle == 0)
+ field |= 0x1;
+ if (setup->bRequestType & USB_DIR_IN)
+ field |= TRB_DIR_IN;
+
+ length_field = TRB_LEN(urb->transfer_buffer_length) |
+ xhci_td_remainder(urb->transfer_buffer_length) |
+ TRB_INTR_TARGET(0);
+ queue_trb(xhci, ep_ring, true,
+ lower_32_bits(urb->transfer_dma),
+ upper_32_bits(urb->transfer_dma),
+ length_field,
+ field);
+
+ /* Save the DMA address of the last TRB in the TD */
+ td->last_trb = ep_ring->enqueue;
+
+ /* Queue status TRB */
+ field = TRB_IOC | TRB_TYPE(TRB_STATUS);
+ if (!(setup->bRequestType & USB_DIR_IN))
+ field |= TRB_DIR_IN;
+
+ queue_trb(xhci, ep_ring, false,
+ 0,
+ 0,
+ TRB_INTR_TARGET(0),
+ field | ep_ring->cycle_state);
+ }
+
+ giveback_first_trb(xhci, slot_id, ep_index, 0, start_cycle, start_trb);
+ return 0;
+}
+
static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
struct urb *urb, int i)
{
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index df41b4f..ad09139 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4055,6 +4055,9 @@
retval = xhci_reset(xhci);
if (retval)
goto error;
+
+ if (xhci->quirks & XHCI_RESET_DELAY)
+ usleep_range(350, 1000);
xhci_dbg(xhci, "Reset complete\n");
temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 8f3651b..1ba51c2 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1497,6 +1497,14 @@
* (16.66 ns x 5 = 84ns) ~100ns after writing to the PORTSC register.
*/
#define XHCI_PORTSC_DELAY (1 << 10)
+/*
+ * In Synopsis DWC3 controller, XHCI RESET takes some time complete. If PIPE
+ * RESET is not complete by the time USBCMD.RUN bit is set then HC fails to
+ * carry out SS transfers.
+ *
+ * The workaround is to give worst case pipe delay ~350us after resetting HC
+ */
+#define XHCI_RESET_DELAY (1 << 11)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1823,4 +1831,8 @@
struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx);
struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_index);
+/* EHSET */
+int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, struct urb *urb,
+ int is_setup);
+
#endif /* __LINUX_XHCI_HCD_H */
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index 1ee1c8e..22e2a25 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -45,6 +45,7 @@
struct diag_bridge_ops *ops;
struct platform_device *pdev;
unsigned default_autosusp_delay;
+ int id;
/* debugging counters */
unsigned long bytes_to_host;
@@ -92,7 +93,7 @@
static void diag_bridge_delete(struct kref *kref)
{
struct diag_bridge *dev = container_of(kref, struct diag_bridge, kref);
- int id = dev->pdev->id;
+ int id = dev->id;
usb_put_dev(dev->udev);
__dev[id] = 0;
@@ -470,6 +471,7 @@
return -ENOMEM;
}
__dev[devid] = dev;
+ dev->id = devid;
dev->udev = usb_get_dev(interface_to_usbdev(ifc));
dev->ifc = ifc;
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
index 2a61501..75fce2f 100644
--- a/drivers/usb/misc/mdm_ctrl_bridge.c
+++ b/drivers/usb/misc/mdm_ctrl_bridge.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
@@ -26,11 +26,6 @@
#include <asm/unaligned.h>
#include <mach/usb_bridge.h>
-static const char *ctrl_bridge_names[] = {
- "dun_ctrl_hsic0",
- "rmnet_ctrl_hsic0"
-};
-
/* polling interval for Interrupt ep */
#define HS_INTERVAL 7
#define FS_LS_INTERVAL 3
@@ -40,10 +35,18 @@
#define SUSPENDED BIT(0)
+enum ctrl_bridge_rx_state {
+ RX_IDLE, /* inturb is not queued */
+ RX_WAIT, /* inturb is queued and waiting for data */
+ RX_BUSY, /* inturb is completed. processing RX */
+};
+
struct ctrl_bridge {
struct usb_device *udev;
struct usb_interface *intf;
+ char *name;
+
unsigned int int_pipe;
struct urb *inturb;
void *intbuf;
@@ -66,6 +69,9 @@
/* output control lines (DTR, RTS) */
unsigned int cbits_tomdm;
+ spinlock_t lock;
+ enum ctrl_bridge_rx_state rx_state;
+
/* counters */
unsigned int snd_encap_cmd;
unsigned int get_encap_res;
@@ -76,8 +82,19 @@
static struct ctrl_bridge *__dev[MAX_BRIDGE_DEVICES];
-/* counter used for indexing ctrl bridge devices */
-static int ch_id;
+static int get_ctrl_bridge_chid(char *xport_name)
+{
+ struct ctrl_bridge *dev;
+ int i;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!strncmp(dev->name, xport_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -ENODEV;
+}
unsigned int ctrl_bridge_get_cbits_tohost(unsigned int id)
{
@@ -125,12 +142,43 @@
}
EXPORT_SYMBOL(ctrl_bridge_set_cbits);
+static int ctrl_bridge_start_read(struct ctrl_bridge *dev, gfp_t gfp_flags)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ if (!dev->inturb) {
+ dev_err(&dev->intf->dev, "%s: inturb is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ retval = usb_submit_urb(dev->inturb, gfp_flags);
+ if (retval < 0 && retval != -EPERM) {
+ dev_err(&dev->intf->dev,
+ "%s error submitting int urb %d\n",
+ __func__, retval);
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (retval)
+ dev->rx_state = RX_IDLE;
+ else
+ dev->rx_state = RX_WAIT;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return retval;
+}
+
static void resp_avail_cb(struct urb *urb)
{
struct ctrl_bridge *dev = urb->context;
- int status = 0;
int resubmit_urb = 1;
struct bridge *brdg = dev->brdg;
+ unsigned long flags;
+
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ return;
switch (urb->status) {
case 0:
@@ -158,15 +206,14 @@
if (resubmit_urb) {
/*re- submit int urb to check response available*/
- usb_anchor_urb(dev->inturb, &dev->tx_submitted);
- status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
- if (status) {
- dev_err(&dev->intf->dev,
- "%s: Error re-submitting Int URB %d\n",
- __func__, status);
- usb_unanchor_urb(dev->inturb);
- }
+ ctrl_bridge_start_read(dev, GFP_ATOMIC);
+ } else {
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_IDLE;
+ spin_unlock_irqrestore(&dev->lock, flags);
}
+
+ usb_autopm_put_interface_async(dev->intf);
}
static void notification_available_cb(struct urb *urb)
@@ -177,6 +224,15 @@
struct bridge *brdg = dev->brdg;
unsigned int ctrl_bits;
unsigned char *data;
+ unsigned long flags;
+
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ return;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_IDLE;
+ spin_unlock_irqrestore(&dev->lock, flags);
switch (urb->status) {
case 0:
@@ -204,7 +260,11 @@
switch (ctrl->bNotificationType) {
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_BUSY;
+ spin_unlock_irqrestore(&dev->lock, flags);
dev->resp_avail++;
+ usb_autopm_get_interface_no_resume(dev->intf);
usb_fill_control_urb(dev->readurb, dev->udev,
usb_rcvctrlpipe(dev->udev, 0),
(unsigned char *)dev->in_ctlreq,
@@ -212,13 +272,12 @@
DEFAULT_READ_URB_LENGTH,
resp_avail_cb, dev);
- usb_anchor_urb(dev->readurb, &dev->tx_submitted);
status = usb_submit_urb(dev->readurb, GFP_ATOMIC);
if (status) {
dev_err(&dev->intf->dev,
"%s: Error submitting Read URB %d\n",
__func__, status);
- usb_unanchor_urb(dev->readurb);
+ usb_autopm_put_interface_async(dev->intf);
goto resubmit_int_urb;
}
return;
@@ -242,56 +301,28 @@
}
resubmit_int_urb:
- usb_anchor_urb(urb, &dev->tx_submitted);
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status) {
- dev_err(&dev->intf->dev, "%s: Error re-submitting Int URB %d\n",
- __func__, status);
- usb_unanchor_urb(urb);
- }
-}
-
-static int ctrl_bridge_start_read(struct ctrl_bridge *dev)
-{
- int retval = 0;
-
- if (!dev->inturb) {
- dev_err(&dev->intf->dev, "%s: inturb is NULL\n", __func__);
- return -ENODEV;
- }
-
- if (!dev->inturb->anchor) {
- usb_anchor_urb(dev->inturb, &dev->tx_submitted);
- retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
- if (retval < 0) {
- dev_err(&dev->intf->dev,
- "%s error submitting int urb %d\n",
- __func__, retval);
- usb_unanchor_urb(dev->inturb);
- }
- }
-
- return retval;
+ ctrl_bridge_start_read(dev, GFP_ATOMIC);
}
int ctrl_bridge_open(struct bridge *brdg)
{
struct ctrl_bridge *dev;
+ int ch_id;
if (!brdg) {
err("bridge is null\n");
return -EINVAL;
}
- if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
- return -EINVAL;
-
- dev = __dev[brdg->ch_id];
- if (!dev) {
- err("dev is null\n");
- return -ENODEV;
+ ch_id = get_ctrl_bridge_chid(brdg->name);
+ if (ch_id < 0 || ch_id >= MAX_BRIDGE_DEVICES) {
+ err("%s: %s dev not found\n", __func__, brdg->name);
+ return ch_id;
}
+ brdg->ch_id = ch_id;
+
+ dev = __dev[ch_id];
dev->brdg = brdg;
dev->snd_encap_cmd = 0;
dev->get_encap_res = 0;
@@ -337,7 +368,13 @@
kfree(urb->transfer_buffer);
kfree(urb->setup_packet);
usb_free_urb(urb);
- usb_autopm_put_interface_async(dev->intf);
+
+ /* if we are here after device disconnect
+ * usb_unbind_interface() takes care of
+ * residual pm_autopm_get_interface_* calls
+ */
+ if (urb->dev->state != USB_STATE_NOTATTACHED)
+ usb_autopm_put_interface_async(dev->intf);
}
int ctrl_bridge_write(unsigned int id, char *data, size_t size)
@@ -346,6 +383,7 @@
struct urb *writeurb;
struct usb_ctrlrequest *out_ctlreq;
struct ctrl_bridge *dev;
+ unsigned long flags;
if (id >= MAX_BRIDGE_DEVICES) {
result = -EINVAL;
@@ -414,12 +452,15 @@
goto free_ctrlreq;
}
+ spin_lock_irqsave(&dev->lock, flags);
if (test_bit(SUSPENDED, &dev->flags)) {
usb_anchor_urb(writeurb, &dev->tx_deferred);
+ spin_unlock_irqrestore(&dev->lock, flags);
goto deferred;
}
usb_anchor_urb(writeurb, &dev->tx_submitted);
+ spin_unlock_irqrestore(&dev->lock, flags);
result = usb_submit_urb(writeurb, GFP_ATOMIC);
if (result < 0) {
dev_err(&dev->intf->dev, "%s: submit URB error %d\n",
@@ -446,6 +487,7 @@
int ctrl_bridge_suspend(unsigned int id)
{
struct ctrl_bridge *dev;
+ unsigned long flags;
if (id >= MAX_BRIDGE_DEVICES)
return -EINVAL;
@@ -454,8 +496,27 @@
if (!dev)
return -ENODEV;
+ spin_lock_irqsave(&dev->lock, flags);
+ if (!usb_anchor_empty(&dev->tx_submitted) || dev->rx_state == RX_BUSY) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_kill_urb(dev->inturb);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->rx_state != RX_IDLE) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ if (!usb_anchor_empty(&dev->tx_submitted)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ ctrl_bridge_start_read(dev, GFP_KERNEL);
+ return -EBUSY;
+ }
set_bit(SUSPENDED, &dev->flags);
- usb_kill_anchored_urbs(&dev->tx_submitted);
+ spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
@@ -464,6 +525,8 @@
{
struct ctrl_bridge *dev;
struct urb *urb;
+ unsigned long flags;
+ int ret;
if (id >= MAX_BRIDGE_DEVICES)
return -EINVAL;
@@ -472,12 +535,19 @@
if (!dev)
return -ENODEV;
- if (!test_and_clear_bit(SUSPENDED, &dev->flags))
+ if (!test_bit(SUSPENDED, &dev->flags))
return 0;
+ spin_lock_irqsave(&dev->lock, flags);
/* submit pending write requests */
while ((urb = usb_get_from_anchor(&dev->tx_deferred))) {
- int ret;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ /*
+ * usb_get_from_anchor() does not drop the
+ * ref count incremented by the usb_anchro_urb()
+ * called in Tx submission path. Let us do it.
+ */
+ usb_put_urb(urb);
usb_anchor_urb(urb, &dev->tx_submitted);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {
@@ -487,9 +557,12 @@
usb_free_urb(urb);
usb_autopm_put_interface_async(dev->intf);
}
+ spin_lock_irqsave(&dev->lock, flags);
}
+ clear_bit(SUSPENDED, &dev->flags);
+ spin_unlock_irqrestore(&dev->lock, flags);
- return ctrl_bridge_start_read(dev);
+ return ctrl_bridge_start_read(dev, GFP_KERNEL);
}
#if defined(CONFIG_DEBUG_FS)
@@ -507,7 +580,7 @@
if (!buf)
return -ENOMEM;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -522,7 +595,7 @@
"cbits_tomdm: %d\n"
"cbits_tohost: %d\n"
"suspended: %d\n",
- dev->pdev->name, dev,
+ dev->name, dev,
dev->snd_encap_cmd,
dev->get_encap_res,
dev->resp_avail,
@@ -546,7 +619,7 @@
struct ctrl_bridge *dev;
int i;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -593,7 +666,7 @@
int
ctrl_bridge_probe(struct usb_interface *ifc, struct usb_host_endpoint *int_in,
- int id)
+ char *name, int id)
{
struct ctrl_bridge *dev;
struct usb_device *udev;
@@ -604,28 +677,28 @@
udev = interface_to_usbdev(ifc);
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = __dev[id];
if (!dev) {
- dev_err(&ifc->dev, "%s: unable to allocate dev\n",
- __func__);
- return -ENOMEM;
- }
- dev->pdev = platform_device_alloc(ctrl_bridge_names[id], id);
- if (!dev->pdev) {
- dev_err(&ifc->dev, "%s: unable to allocate platform device\n",
- __func__);
- retval = -ENOMEM;
- goto nomem;
+ pr_err("%s:device not found\n", __func__);
+ return -ENODEV;
}
+ dev->name = name;
+
+ dev->pdev = platform_device_alloc(name, -1);
+ if (!dev->pdev) {
+ retval = -ENOMEM;
+ dev_err(&ifc->dev, "%s: unable to allocate platform device\n",
+ __func__);
+ goto free_name;
+ }
+
+ dev->flags = 0;
dev->udev = udev;
dev->int_pipe = usb_rcvintpipe(udev,
int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
dev->intf = ifc;
- init_usb_anchor(&dev->tx_submitted);
- init_usb_anchor(&dev->tx_deferred);
-
/*use max pkt size from ep desc*/
ep = &dev->intf->cur_altsetting->endpoint[0].desc;
@@ -633,7 +706,7 @@
if (!dev->inturb) {
dev_err(&ifc->dev, "%s: error allocating int urb\n", __func__);
retval = -ENOMEM;
- goto pdev_del;
+ goto pdev_put;
}
wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
@@ -685,14 +758,24 @@
dev->intf->cur_altsetting->desc.bInterfaceNumber;
dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
- __dev[id] = dev;
+ retval = platform_device_add(dev->pdev);
+ if (retval) {
+ dev_err(&ifc->dev, "%s:fail to add pdev\n", __func__);
+ goto free_ctrlreq;
+ }
- platform_device_add(dev->pdev);
+ retval = ctrl_bridge_start_read(dev, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ifc->dev, "%s:fail to start reading\n", __func__);
+ goto pdev_del;
+ }
- ch_id++;
+ return 0;
- return ctrl_bridge_start_read(dev);
-
+pdev_del:
+ platform_device_del(dev->pdev);
+free_ctrlreq:
+ kfree(dev->in_ctlreq);
free_rbuf:
kfree(dev->readbuf);
free_rurb:
@@ -701,10 +784,10 @@
kfree(dev->intbuf);
free_inturb:
usb_free_urb(dev->inturb);
-pdev_del:
- platform_device_unregister(dev->pdev);
-nomem:
- kfree(dev);
+pdev_put:
+ platform_device_put(dev->pdev);
+free_name:
+ dev->name = "none";
return retval;
}
@@ -715,9 +798,18 @@
dev_dbg(&dev->intf->dev, "%s:\n", __func__);
+ /*set device name to none to get correct channel id
+ * at the time of bridge open
+ */
+ dev->name = "none";
+
platform_device_unregister(dev->pdev);
- usb_unlink_anchored_urbs(&dev->tx_submitted);
+ usb_scuttle_anchored_urbs(&dev->tx_deferred);
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+
+ usb_kill_urb(dev->inturb);
+ usb_kill_urb(dev->readurb);
kfree(dev->in_ctlreq);
kfree(dev->readbuf);
@@ -725,26 +817,54 @@
usb_free_urb(dev->readurb);
usb_free_urb(dev->inturb);
-
- __dev[id] = NULL;
- ch_id--;
-
- kfree(dev);
}
-static int __init ctrl_bridge_init(void)
+int ctrl_bridge_init(void)
{
+ struct ctrl_bridge *dev;
+ int i;
+ int retval = 0;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("%s: unable to allocate dev\n", __func__);
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ /*transport name will be set during probe*/
+ dev->name = "none";
+
+ spin_lock_init(&dev->lock);
+ init_usb_anchor(&dev->tx_submitted);
+ init_usb_anchor(&dev->tx_deferred);
+
+ __dev[i] = dev;
+ }
+
ctrl_bridge_debugfs_init();
return 0;
-}
-module_init(ctrl_bridge_init);
-static void __exit ctrl_bridge_exit(void)
+error:
+ while (--i >= 0) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+
+ return retval;
+}
+
+void ctrl_bridge_exit(void)
{
- ctrl_bridge_debugfs_exit();
-}
-module_exit(ctrl_bridge_exit);
+ int i;
-MODULE_DESCRIPTION("Qualcomm modem control bridge driver");
-MODULE_LICENSE("GPL v2");
+ ctrl_bridge_debugfs_exit();
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+}
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
index fcbf0e1..eea217a 100644
--- a/drivers/usb/misc/mdm_data_bridge.c
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -29,11 +29,37 @@
#define FLOW_CTRL_DISABLE 300
#define FLOW_CTRL_SUPPORT 1
-static const char *data_bridge_names[] = {
- "dun_data_hsic0",
- "rmnet_data_hsic0"
+#define BRIDGE_DATA_IDX 0
+#define BRIDGE_CTRL_IDX 1
+
+/*for xport : HSIC*/
+static const char * const serial_hsic_bridge_names[] = {
+ "serial_hsic_data",
+ "serial_hsic_ctrl",
};
+static const char * const rmnet_hsic_bridge_names[] = {
+ "rmnet_hsic_data",
+ "rmnet_hsic_ctrl",
+};
+
+/*for xport : HSUSB*/
+static const char * const serial_hsusb_bridge_names[] = {
+ "serial_hsusb_data",
+ "serial_hsusb_ctrl",
+};
+
+static const char * const rmnet_hsusb_bridge_names[] = {
+ "rmnet_hsusb_data",
+ "rmnet_hsusb_ctrl",
+};
+
+/* since driver supports multiple instances, on smp systems
+ * probe might get called from multiple cores, hence use lock
+ * to identify unclaimed bridge device instance
+ */
+static DEFINE_MUTEX(brdg_claim_lock);
+
static struct workqueue_struct *bridge_wq;
static unsigned int fctrl_support = FLOW_CTRL_SUPPORT;
@@ -54,14 +80,16 @@
static unsigned tx_urb_mult = 20;
module_param(tx_urb_mult, uint, S_IRUGO|S_IWUSR);
-#define TX_HALT BIT(0)
-#define RX_HALT BIT(1)
-#define SUSPENDED BIT(2)
+#define TX_HALT 0
+#define RX_HALT 1
+#define SUSPENDED 2
+#define CLAIMED 3
struct data_bridge {
struct usb_interface *intf;
struct usb_device *udev;
int id;
+ char *name;
unsigned int bulk_in;
unsigned int bulk_out;
@@ -71,9 +99,6 @@
struct usb_anchor tx_active;
struct usb_anchor rx_active;
- /* keep track of outgoing URBs during suspend */
- struct usb_anchor delayed;
-
struct list_head rx_idle;
struct sk_buff_head rx_done;
@@ -102,14 +127,45 @@
static struct data_bridge *__dev[MAX_BRIDGE_DEVICES];
-/* counter used for indexing data bridge devices */
-static int ch_id;
-
static unsigned int get_timestamp(void);
static void dbg_timestamp(char *, struct sk_buff *);
static int submit_rx_urb(struct data_bridge *dev, struct urb *urb,
gfp_t flags);
+/* Find an unclaimed bridge device instance */
+static int get_bridge_dev_idx(void)
+{
+ struct data_bridge *dev;
+ int i;
+
+ mutex_lock(&brdg_claim_lock);
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!test_bit(CLAIMED, &dev->flags)) {
+ set_bit(CLAIMED, &dev->flags);
+ mutex_unlock(&brdg_claim_lock);
+ return i;
+ }
+ }
+ mutex_unlock(&brdg_claim_lock);
+
+ return -ENODEV;
+}
+
+static int get_data_bridge_chid(char *xport_name)
+{
+ struct data_bridge *dev;
+ int i;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!strncmp(dev->name, xport_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -ENODEV;
+}
+
static inline bool rx_halted(struct data_bridge *dev)
{
return test_bit(RX_HALT, &dev->flags);
@@ -120,6 +176,22 @@
return test_bit(RX_THROTTLED, &brdg->flags);
}
+static void free_rx_urbs(struct data_bridge *dev)
+{
+ struct list_head *head;
+ struct urb *rx_urb;
+ unsigned long flags;
+
+ head = &dev->rx_idle;
+ spin_lock_irqsave(&dev->rx_done.lock, flags);
+ while (!list_empty(head)) {
+ rx_urb = list_entry(head->next, struct urb, urb_list);
+ list_del(&rx_urb->urb_list);
+ usb_free_urb(rx_urb);
+ }
+ spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+}
+
int data_bridge_unthrottle_rx(unsigned int id)
{
struct data_bridge *dev;
@@ -193,6 +265,10 @@
struct data_bridge *dev = info->dev;
bool queue = 0;
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ urb->status = -ECONNRESET;
+
brdg = dev->brdg;
skb_put(skb, urb->actual_length);
@@ -280,35 +356,45 @@
{
int i;
struct urb *rx_urb;
+ int retval = 0;
for (i = 0; i < max_rx_urbs; i++) {
rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!rx_urb)
- return -ENOMEM;
+ if (!rx_urb) {
+ retval = -ENOMEM;
+ goto free_urbs;
+ }
list_add_tail(&rx_urb->urb_list, &dev->rx_idle);
}
- return 0;
+
+ return 0;
+
+free_urbs:
+ free_rx_urbs(dev);
+ return retval;
}
int data_bridge_open(struct bridge *brdg)
{
struct data_bridge *dev;
+ int ch_id;
if (!brdg) {
err("bridge is null\n");
return -EINVAL;
}
- if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
- return -EINVAL;
-
- dev = __dev[brdg->ch_id];
- if (!dev) {
- err("dev is null\n");
- return -ENODEV;
+ ch_id = get_data_bridge_chid(brdg->name);
+ if (ch_id < 0 || ch_id >= MAX_BRIDGE_DEVICES) {
+ err("%s: %s dev not found\n", __func__, brdg->name);
+ return ch_id;
}
+ brdg->ch_id = ch_id;
+
+ dev = __dev[ch_id];
+
dev_dbg(&dev->intf->dev, "%s: dev:%p\n", __func__, dev);
dev->brdg = brdg;
@@ -346,9 +432,8 @@
cancel_work_sync(&dev->kevent);
cancel_work_sync(&dev->process_rx_w);
- usb_unlink_anchored_urbs(&dev->tx_active);
- usb_unlink_anchored_urbs(&dev->rx_active);
- usb_unlink_anchored_urbs(&dev->delayed);
+ usb_kill_anchored_urbs(&dev->tx_active);
+ usb_kill_anchored_urbs(&dev->rx_active);
spin_lock_irqsave(&dev->rx_done.lock, flags);
while ((skb = __skb_dequeue(&dev->rx_done)))
@@ -457,7 +542,12 @@
brdg->ops.unthrottle_tx(brdg->ctx);
}
- usb_autopm_put_interface_async(dev->intf);
+ /* if we are here after device disconnect
+ * usb_unbind_interface() takes care of
+ * residual pm_autopm_get_interface_* calls
+ */
+ if (urb->dev->state != USB_STATE_NOTATTACHED)
+ usb_autopm_put_interface_async(dev->intf);
}
int data_bridge_write(unsigned int id, struct sk_buff *skb)
@@ -502,11 +592,6 @@
txurb->transfer_flags |= URB_ZERO_PACKET;
- if (test_bit(SUSPENDED, &dev->flags)) {
- usb_anchor_urb(txurb, &dev->delayed);
- goto free_urb;
- }
-
pending = atomic_inc_return(&dev->pending_txurbs);
usb_anchor_urb(txurb, &dev->tx_active);
@@ -546,110 +631,66 @@
}
EXPORT_SYMBOL(data_bridge_write);
-static int data_bridge_resume(struct data_bridge *dev)
+static int bridge_resume(struct usb_interface *iface)
{
- struct urb *urb;
- int retval;
+ int retval = 0;
+ struct data_bridge *dev = usb_get_intfdata(iface);
- if (!test_and_clear_bit(SUSPENDED, &dev->flags))
- return 0;
-
- while ((urb = usb_get_from_anchor(&dev->delayed))) {
- usb_anchor_urb(urb, &dev->tx_active);
- atomic_inc(&dev->pending_txurbs);
- retval = usb_submit_urb(urb, GFP_ATOMIC);
- if (retval < 0) {
- atomic_dec(&dev->pending_txurbs);
- usb_unanchor_urb(urb);
-
- /* TODO: need to free urb data */
- usb_scuttle_anchored_urbs(&dev->delayed);
- break;
- }
- dev->to_modem++;
- dev->txurb_drp_cnt--;
- }
+ clear_bit(SUSPENDED, &dev->flags);
if (dev->brdg)
queue_work(dev->wq, &dev->process_rx_w);
- return 0;
-}
-
-static int bridge_resume(struct usb_interface *iface)
-{
- int retval = 0;
- int oldstate;
- struct data_bridge *dev = usb_get_intfdata(iface);
-
- oldstate = iface->dev.power.power_state.event;
- iface->dev.power.power_state.event = PM_EVENT_ON;
-
- if (oldstate & PM_EVENT_SUSPEND) {
- retval = data_bridge_resume(dev);
- if (!retval)
- retval = ctrl_bridge_resume(dev->id);
- }
+ retval = ctrl_bridge_resume(dev->id);
return retval;
}
-static int data_bridge_suspend(struct data_bridge *dev, pm_message_t message)
-{
- if (atomic_read(&dev->pending_txurbs) &&
- (message.event & PM_EVENT_AUTO))
- return -EBUSY;
-
- set_bit(SUSPENDED, &dev->flags);
-
- usb_kill_anchored_urbs(&dev->tx_active);
- usb_kill_anchored_urbs(&dev->rx_active);
-
- return 0;
-}
-
static int bridge_suspend(struct usb_interface *intf, pm_message_t message)
{
int retval;
struct data_bridge *dev = usb_get_intfdata(intf);
- retval = data_bridge_suspend(dev, message);
- if (!retval) {
- retval = ctrl_bridge_suspend(dev->id);
- intf->dev.power.power_state.event = message.event;
- }
+ if (atomic_read(&dev->pending_txurbs))
+ return -EBUSY;
- return retval;
+ retval = ctrl_bridge_suspend(dev->id);
+ if (retval)
+ return retval;
+
+ set_bit(SUSPENDED, &dev->flags);
+ usb_kill_anchored_urbs(&dev->rx_active);
+
+ return 0;
}
static int data_bridge_probe(struct usb_interface *iface,
struct usb_host_endpoint *bulk_in,
- struct usb_host_endpoint *bulk_out, int id)
+ struct usb_host_endpoint *bulk_out, char *name, int id)
{
struct data_bridge *dev;
+ int retval;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = __dev[id];
if (!dev) {
- err("%s: unable to allocate dev\n", __func__);
- return -ENOMEM;
+ err("%s: device not found\n", __func__);
+ return -ENODEV;
}
- dev->pdev = platform_device_alloc(data_bridge_names[id], id);
+ dev->pdev = platform_device_alloc(name, -1);
if (!dev->pdev) {
err("%s: unable to allocate platform device\n", __func__);
kfree(dev);
return -ENOMEM;
}
- init_usb_anchor(&dev->tx_active);
- init_usb_anchor(&dev->rx_active);
- init_usb_anchor(&dev->delayed);
+ /*clear all bits except claimed bit*/
+ clear_bit(RX_HALT, &dev->flags);
+ clear_bit(TX_HALT, &dev->flags);
+ clear_bit(SUSPENDED, &dev->flags);
- INIT_LIST_HEAD(&dev->rx_idle);
- skb_queue_head_init(&dev->rx_done);
-
- dev->wq = bridge_wq;
dev->id = id;
+ dev->name = name;
dev->udev = interface_to_usbdev(iface);
dev->intf = iface;
@@ -661,13 +702,12 @@
usb_set_intfdata(iface, dev);
- INIT_WORK(&dev->kevent, defer_kevent);
- INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
-
- __dev[id] = dev;
-
/*allocate list of rx urbs*/
- data_bridge_prepare_rx(dev);
+ retval = data_bridge_prepare_rx(dev);
+ if (retval) {
+ platform_device_put(dev->pdev);
+ return retval;
+ }
platform_device_add(dev->pdev);
@@ -675,7 +715,7 @@
}
#if defined(CONFIG_DEBUG_FS)
-#define DEBUG_BUF_SIZE 1024
+#define DEBUG_BUF_SIZE 4096
static unsigned int record_timestamp;
module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
@@ -746,7 +786,7 @@
if (!record_timestamp)
return 0;
- buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -756,7 +796,7 @@
for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
continue;
- j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+ j += scnprintf(buf + j, DEBUG_BUF_SIZE - j,
"%s\n", dbg_data.buf[i]);
}
@@ -786,7 +826,7 @@
if (!buf)
return -ENOMEM;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -806,7 +846,7 @@
"suspended: %d\n"
"TX_HALT: %d\n"
"RX_HALT: %d\n",
- dev->pdev->name, dev,
+ dev->name, dev,
atomic_read(&dev->pending_txurbs),
dev->txurb_drp_cnt,
dev->to_host,
@@ -836,7 +876,7 @@
struct data_bridge *dev;
int i;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -913,9 +953,8 @@
int i;
int status = 0;
int numends;
- unsigned int iface_num;
-
- iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+ int ch_id;
+ char **bname = (char **)id->driver_info;
if (iface->num_altsetting != 1) {
err("%s invalid num_altsetting %u\n",
@@ -923,9 +962,6 @@
return -EINVAL;
}
- if (!test_bit(iface_num, &id->driver_info))
- return -ENODEV;
-
udev = interface_to_usbdev(iface);
usb_get_dev(udev);
@@ -953,27 +989,32 @@
goto out;
}
- status = data_bridge_probe(iface, bulk_in, bulk_out, ch_id);
+ ch_id = get_bridge_dev_idx();
+ if (ch_id < 0) {
+ err("%s all bridge channels claimed. Probe failed\n", __func__);
+ return -ENODEV;
+ }
+
+ status = data_bridge_probe(iface, bulk_in, bulk_out,
+ bname[BRIDGE_DATA_IDX], ch_id);
if (status < 0) {
dev_err(&iface->dev, "data_bridge_probe failed %d\n", status);
goto out;
}
- status = ctrl_bridge_probe(iface, int_in, ch_id);
+ status = ctrl_bridge_probe(iface, int_in, bname[BRIDGE_CTRL_IDX],
+ ch_id);
if (status < 0) {
dev_err(&iface->dev, "ctrl_bridge_probe failed %d\n", status);
- goto free_data_bridge;
+ goto error;
}
- ch_id++;
-
return 0;
-free_data_bridge:
+error:
platform_device_unregister(__dev[ch_id]->pdev);
+ free_rx_urbs(__dev[ch_id]);
usb_set_intfdata(iface, NULL);
- kfree(__dev[ch_id]);
- __dev[ch_id] = NULL;
out:
usb_put_dev(udev);
@@ -983,57 +1024,65 @@
static void bridge_disconnect(struct usb_interface *intf)
{
struct data_bridge *dev = usb_get_intfdata(intf);
- struct list_head *head;
- struct urb *rx_urb;
- unsigned long flags;
if (!dev) {
err("%s: data device not found\n", __func__);
return;
}
- ch_id--;
+ /*set device name to none to get correct channel id
+ * at the time of bridge open
+ */
+ dev->name = "none";
+
ctrl_bridge_disconnect(dev->id);
platform_device_unregister(dev->pdev);
usb_set_intfdata(intf, NULL);
- __dev[dev->id] = NULL;
- /*free rx urbs*/
- head = &dev->rx_idle;
- spin_lock_irqsave(&dev->rx_done.lock, flags);
- while (!list_empty(head)) {
- rx_urb = list_entry(head->next, struct urb, urb_list);
- list_del(&rx_urb->urb_list);
- usb_free_urb(rx_urb);
- }
- spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+ free_rx_urbs(dev);
usb_put_dev(dev->udev);
- kfree(dev);
+
+ clear_bit(CLAIMED, &dev->flags);
}
-/*bit position represents interface number*/
-#define PID9001_IFACE_MASK 0xC
-#define PID9034_IFACE_MASK 0xC
-#define PID9048_IFACE_MASK 0x18
-#define PID904C_IFACE_MASK 0x28
-#define PID9075_IFACE_MASK 0x28
-
+/*driver info stores data/ctrl bridge name used to match bridge xport name*/
static const struct usb_device_id bridge_ids[] = {
- { USB_DEVICE(0x5c6, 0x9001),
- .driver_info = PID9001_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9001, 2),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9034),
- .driver_info = PID9034_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9001, 3),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9048),
- .driver_info = PID9048_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9034, 2),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x904c),
- .driver_info = PID904C_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9034, 3),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9075),
- .driver_info = PID9075_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 4),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904c, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904c, 5),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 5),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 3),
+ .driver_info = (unsigned long)serial_hsusb_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 4),
+ .driver_info = (unsigned long)rmnet_hsusb_bridge_names,
},
{ } /* Terminating entry */
@@ -1052,31 +1101,83 @@
static int __init bridge_init(void)
{
- int ret;
+ struct data_bridge *dev;
+ int ret;
+ int i = 0;
+
+ ret = ctrl_bridge_init();
+ if (ret)
+ return ret;
+
+ bridge_wq = create_singlethread_workqueue("mdm_bridge");
+ if (!bridge_wq) {
+ pr_err("%s: Unable to create workqueue:bridge\n", __func__);
+ ret = -ENOMEM;
+ goto free_ctrl;
+ }
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ err("%s: unable to allocate dev\n", __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ dev->wq = bridge_wq;
+
+ /*transport name will be set during probe*/
+ dev->name = "none";
+
+ init_usb_anchor(&dev->tx_active);
+ init_usb_anchor(&dev->rx_active);
+
+ INIT_LIST_HEAD(&dev->rx_idle);
+
+ skb_queue_head_init(&dev->rx_done);
+
+ INIT_WORK(&dev->kevent, defer_kevent);
+ INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
+
+ __dev[i] = dev;
+ }
ret = usb_register(&bridge_driver);
if (ret) {
err("%s: unable to register mdm_bridge driver", __func__);
- return ret;
- }
-
- bridge_wq = create_singlethread_workqueue("mdm_bridge");
- if (!bridge_wq) {
- usb_deregister(&bridge_driver);
- pr_err("%s: Unable to create workqueue:bridge\n", __func__);
- return -ENOMEM;
+ goto error;
}
data_bridge_debugfs_init();
return 0;
+
+error:
+ while (--i >= 0) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+ destroy_workqueue(bridge_wq);
+free_ctrl:
+ ctrl_bridge_exit();
+ return ret;
}
static void __exit bridge_exit(void)
{
+ int i;
+
+ usb_deregister(&bridge_driver);
data_bridge_debugfs_exit();
destroy_workqueue(bridge_wq);
- usb_deregister(&bridge_driver);
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+
+ ctrl_bridge_exit();
}
module_init(bridge_init);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index a074e2d..fa1932b 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -36,6 +36,7 @@
#include <linux/usb/quirks.h>
#include <linux/usb/msm_hsusb.h>
#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/msm_ext_chg.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/pm8xxx/pm8921-charger.h>
#include <linux/mfd/pm8xxx/misc.h>
@@ -87,6 +88,11 @@
MODULE_PARM_DESC(lpm_disconnect_thresh,
"Delay before entering LPM on USB disconnect");
+static bool floated_charger_enable;
+module_param(floated_charger_enable , bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(floated_charger_enable,
+ "Whether to enable floated charger");
+
static DECLARE_COMPLETION(pmic_vbus_init);
static struct msm_otg *the_msm_otg;
static bool debug_aca_enabled;
@@ -851,6 +857,24 @@
return 0;
}
+static void msm_otg_bus_vote(struct msm_otg *motg, enum usb_bus_vote vote)
+{
+ int ret;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ /* Check if target allows min_vote to be same as no_vote */
+ if (vote >= pdata->bus_scale_table->num_usecases)
+ vote = USB_NO_PERF_VOTE;
+
+ if (motg->bus_perf_client) {
+ ret = msm_bus_scale_client_update_request(
+ motg->bus_perf_client, vote);
+ if (ret)
+ dev_err(motg->phy.dev, "%s: Failed to vote (%d)\n"
+ "for bus bw %d\n", __func__, vote, ret);
+ }
+}
+
#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
@@ -862,9 +886,10 @@
struct msm_otg_platform_data *pdata = motg->pdata;
int cnt = 0;
bool host_bus_suspend, device_bus_suspend, dcp, prop_charger;
+ bool floated_charger;
u32 phy_ctrl_val = 0, cmd_val;
unsigned ret;
- u32 portsc;
+ u32 portsc, config2;
if (atomic_read(&motg->in_lpm))
return 0;
@@ -880,6 +905,18 @@
motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
dcp = motg->chg_type == USB_DCP_CHARGER;
prop_charger = motg->chg_type == USB_PROPRIETARY_CHARGER;
+ floated_charger = motg->chg_type == USB_FLOATED_CHARGER;
+
+ /* Enable line state difference wakeup fix for only device and host
+ * bus suspend scenarios. Otherwise PHY can not be suspended when
+ * a charger that pulls DP/DM high is connected.
+ */
+ config2 = readl_relaxed(USB_GENCONFIG2);
+ if (device_bus_suspend)
+ config2 |= GENCFG2_LINESTATE_DIFF_WAKEUP_EN;
+ else
+ config2 &= ~GENCFG2_LINESTATE_DIFF_WAKEUP_EN;
+ writel_relaxed(config2, USB_GENCONFIG2);
/*
* Abort suspend when,
@@ -888,7 +925,8 @@
*/
if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
- !dcp && !prop_charger) || test_bit(A_BUS_REQ, &motg->inputs)) {
+ !dcp && !prop_charger && !floated_charger) ||
+ test_bit(A_BUS_REQ, &motg->inputs)) {
enable_irq(motg->irq);
return -EBUSY;
}
@@ -966,8 +1004,8 @@
* BC1.2 spec mandates PD to enable VDP_SRC when charging from DCP.
* PHY retention and collapse can not happen with VDP_SRC enabled.
*/
- if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend &&
- !device_bus_suspend && !dcp) {
+ if (motg->caps & ALLOW_PHY_RETENTION && !device_bus_suspend && !dcp &&
+ (!host_bus_suspend || motg->caps & ALLOW_HOST_PHY_RETENTION)) {
phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
if (motg->pdata->otg_control == OTG_PHY_CONTROL) {
/* Enable PHY HV interrupts to wake MPM/Link */
@@ -978,7 +1016,8 @@
else
phy_ctrl_val |= PHY_OTGSESSVLDHV_INTEN;
}
-
+ if (host_bus_suspend)
+ phy_ctrl_val |= PHY_CLAMP_DPDMSE_EN;
writel_relaxed(phy_ctrl_val & ~PHY_RETEN, USB_PHY_CTRL);
motg->lpm_flags |= PHY_RETENTIONED;
}
@@ -995,7 +1034,7 @@
}
/* usb phy no more require TCXO clock, hence vote for TCXO disable */
- if (!host_bus_suspend) {
+ if (!host_bus_suspend || (motg->caps & ALLOW_HOST_PHY_RETENTION)) {
if (!IS_ERR(motg->xo_clk)) {
clk_disable_unprepare(motg->xo_clk);
motg->lpm_flags |= XO_SHUTDOWN;
@@ -1036,10 +1075,17 @@
if (pdata->otg_control == OTG_PHY_CONTROL &&
pdata->mpm_otgsessvld_int)
msm_mpm_set_pin_wake(pdata->mpm_otgsessvld_int, 1);
+ if (host_bus_suspend && pdata->mpm_dpshv_int)
+ msm_mpm_set_pin_wake(pdata->mpm_dpshv_int, 1);
+ if (host_bus_suspend && pdata->mpm_dmshv_int)
+ msm_mpm_set_pin_wake(pdata->mpm_dmshv_int, 1);
}
if (bus)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
+
+ motg->host_bus_suspend = host_bus_suspend;
atomic_set(&motg->in_lpm, 1);
/* Enable ASYNC IRQ (if present) during LPM */
if (motg->async_irq)
@@ -1068,6 +1114,9 @@
disable_irq(motg->irq);
wake_lock(&motg->wlock);
+ /* Some platforms require BUS vote to enable/disable clocks */
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+
/* Vote for TCXO when waking up the phy */
if (motg->lpm_flags & XO_SHUTDOWN) {
if (!IS_ERR(motg->xo_clk)) {
@@ -1104,6 +1153,7 @@
/* Disable PHY HV interrupts */
phy_ctrl_val &=
~(PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
+ phy_ctrl_val &= ~(PHY_CLAMP_DPDMSE_EN);
writel_relaxed(phy_ctrl_val, USB_PHY_CTRL);
motg->lpm_flags &= ~PHY_RETENTIONED;
}
@@ -1151,6 +1201,10 @@
if (pdata->otg_control == OTG_PHY_CONTROL &&
pdata->mpm_otgsessvld_int)
msm_mpm_set_pin_wake(pdata->mpm_otgsessvld_int, 0);
+ if (motg->host_bus_suspend && pdata->mpm_dpshv_int)
+ msm_mpm_set_pin_wake(pdata->mpm_dpshv_int, 0);
+ if (motg->host_bus_suspend && pdata->mpm_dmshv_int)
+ msm_mpm_set_pin_wake(pdata->mpm_dmshv_int, 0);
}
if (bus)
set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
@@ -1550,7 +1604,6 @@
static void msm_otg_start_peripheral(struct usb_otg *otg, int on)
{
- int ret;
struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
struct msm_otg_platform_data *pdata = motg->pdata;
@@ -1568,29 +1621,18 @@
pdata->setup_gpio(OTG_STATE_B_PERIPHERAL);
/* Configure BUS performance parameters for MAX bandwidth */
- if (motg->bus_perf_client && debug_bus_voting_enabled) {
- ret = msm_bus_scale_client_update_request(
- motg->bus_perf_client, 1);
- if (ret)
- dev_err(motg->phy.dev, "%s: Failed to vote for "
- "bus bandwidth %d\n", __func__, ret);
- }
+ if (debug_bus_voting_enabled)
+ msm_otg_bus_vote(motg, USB_MAX_PERF_VOTE);
+
usb_gadget_vbus_connect(otg->gadget);
} else {
dev_dbg(otg->phy->dev, "gadget off\n");
usb_gadget_vbus_disconnect(otg->gadget);
/* Configure BUS performance parameters to default */
- if (motg->bus_perf_client) {
- ret = msm_bus_scale_client_update_request(
- motg->bus_perf_client, 0);
- if (ret)
- dev_err(motg->phy.dev, "%s: Failed to devote "
- "for bus bw %d\n", __func__, ret);
- }
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
if (pdata->setup_gpio)
pdata->setup_gpio(OTG_STATE_UNDEFINED);
}
-
}
static int msm_otg_set_peripheral(struct usb_otg *otg,
@@ -2177,6 +2219,8 @@
/* Clear alt interrupt latch and enable bits */
ulpi_write(phy, 0x1F, 0x92);
ulpi_write(phy, 0x1F, 0x95);
+ /* re-enable DP and DM pull down resistors */
+ ulpi_write(phy, 0x6, 0xB);
break;
default:
break;
@@ -2200,6 +2244,7 @@
case USB_ACA_C_CHARGER: return "USB_ACA_C_CHARGER";
case USB_ACA_DOCK_CHARGER: return "USB_ACA_DOCK_CHARGER";
case USB_PROPRIETARY_CHARGER: return "USB_PROPRIETARY_CHARGER";
+ case USB_FLOATED_CHARGER: return "USB_FLOATED_CHARGER";
default: return "INVALID_CHARGER";
}
}
@@ -2213,6 +2258,7 @@
struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
struct usb_phy *phy = &motg->phy;
bool is_dcd = false, tmout, vout, is_aca;
+ static bool dcd;
u32 line_state, dm_vlgc;
unsigned long delay;
@@ -2257,6 +2303,10 @@
motg->dcd_time += MSM_CHG_DCD_POLL_TIME;
tmout = motg->dcd_time >= MSM_CHG_DCD_TIMEOUT;
if (is_dcd || tmout) {
+ if (is_dcd)
+ dcd = true;
+ else
+ dcd = false;
msm_chg_disable_dcd(motg);
msm_chg_enable_primary_det(motg);
delay = MSM_CHG_PRIMARY_DET_TIME;
@@ -2295,6 +2345,8 @@
if (line_state) /* DP > VLGC or/and DM > VLGC */
motg->chg_type = USB_PROPRIETARY_CHARGER;
+ else if (!dcd && floated_charger_enable)
+ motg->chg_type = USB_FLOATED_CHARGER;
else
motg->chg_type = USB_SDP_CHARGER;
@@ -2410,11 +2462,46 @@
}
}
+static void msm_otg_wait_for_ext_chg_done(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ unsigned long t;
+
+ /*
+ * Defer next cable connect event till external charger
+ * detection is completed.
+ */
+
+ if (motg->ext_chg_active) {
+
+ pr_debug("before msm_otg ext chg wait\n");
+
+ t = wait_for_completion_timeout(&motg->ext_chg_wait,
+ msecs_to_jiffies(3000));
+ if (!t)
+ pr_err("msm_otg ext chg wait timeout\n");
+ else
+ pr_debug("msm_otg ext chg wait done\n");
+ }
+
+ if (motg->ext_chg_opened) {
+ if (phy->flags & ENABLE_DP_MANUAL_PULLUP) {
+ ulpi_write(phy, ULPI_MISC_A_VBUSVLDEXT |
+ ULPI_MISC_A_VBUSVLDEXTSEL,
+ ULPI_CLR(ULPI_MISC_A));
+ }
+ /* clear charging register bits */
+ ulpi_write(phy, 0x3F, 0x86);
+ /* re-enable DP and DM pull-down resistors*/
+ ulpi_write(phy, 0x6, 0xB);
+ }
+}
+
static void msm_otg_sm_work(struct work_struct *w)
{
struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
struct usb_otg *otg = motg->phy.otg;
- bool work = 0, srp_reqd;
+ bool work = 0, srp_reqd, dcp;
pm_runtime_resume(otg->phy->dev);
pr_debug("%s work\n", otg_state_string(otg->phy->state));
@@ -2464,10 +2551,20 @@
case USB_DCP_CHARGER:
/* Enable VDP_SRC */
ulpi_write(otg->phy, 0x2, 0x85);
+ if (motg->ext_chg_opened) {
+ init_completion(
+ &motg->ext_chg_wait);
+ motg->ext_chg_active = true;
+ }
/* fall through */
case USB_PROPRIETARY_CHARGER:
msm_otg_notify_charger(motg,
IDEV_CHG_MAX);
+ pm_runtime_put_sync(otg->phy->dev);
+ break;
+ case USB_FLOATED_CHARGER:
+ msm_otg_notify_charger(motg,
+ IDEV_CHG_MAX);
pm_runtime_put_noidle(otg->phy->dev);
pm_runtime_suspend(otg->phy->dev);
break;
@@ -2523,9 +2620,12 @@
clear_bit(B_FALSE_SDP, &motg->inputs);
clear_bit(A_BUS_REQ, &motg->inputs);
cancel_delayed_work_sync(&motg->chg_work);
+ dcp = (motg->chg_type == USB_DCP_CHARGER);
motg->chg_state = USB_CHG_STATE_UNDEFINED;
motg->chg_type = USB_INVALID_CHARGER;
msm_otg_notify_charger(motg, 0);
+ if (dcp)
+ msm_otg_wait_for_ext_chg_done(motg);
msm_otg_reset(otg->phy);
/*
* There is a small window where ID interrupt
@@ -3469,7 +3569,6 @@
size_t count, loff_t *ppos)
{
char buf[8];
- int ret;
struct seq_file *s = file->private_data;
struct msm_otg *motg = s->private;
@@ -3483,13 +3582,7 @@
debug_bus_voting_enabled = true;
} else {
debug_bus_voting_enabled = false;
- if (motg->bus_perf_client) {
- ret = msm_bus_scale_client_update_request(
- motg->bus_perf_client, 0);
- if (ret)
- dev_err(motg->phy.dev, "%s: Failed to devote "
- "for bus bw %d\n", __func__, ret);
- }
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
}
return count;
@@ -3515,6 +3608,9 @@
case POWER_SUPPLY_PROP_ONLINE:
val->intval = motg->online;
break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = psy->type;
+ break;
default:
return -EINVAL;
}
@@ -3539,6 +3635,9 @@
case POWER_SUPPLY_PROP_CURRENT_MAX:
motg->current_max = val->intval;
break;
+ case POWER_SUPPLY_PROP_TYPE:
+ psy->type = val->intval;
+ break;
default:
return -EINVAL;
}
@@ -3571,6 +3670,7 @@
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_TYPE,
};
const struct file_operations msm_otg_bus_fops = {
@@ -3712,6 +3812,7 @@
ofdev->dev.platform_data;
ci_pdata.log2_itc = otg_pdata->log2_itc;
ci_pdata.usb_core_id = 0;
+ ci_pdata.l1_supported = otg_pdata->l1_supported;
retval = platform_device_add_data(pdev, &ci_pdata,
sizeof(ci_pdata));
if (retval)
@@ -3790,6 +3891,168 @@
return 0;
}
+static int msm_otg_ext_chg_open(struct inode *inode, struct file *file)
+{
+ struct msm_otg *motg = the_msm_otg;
+
+ pr_debug("msm_otg ext chg open\n");
+
+ motg->ext_chg_opened = true;
+ file->private_data = (void *)motg;
+ return 0;
+}
+
+static long
+msm_otg_ext_chg_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct msm_otg *motg = file->private_data;
+ struct msm_usb_chg_info info = {0};
+ int ret = 0, val;
+
+ switch (cmd) {
+ case MSM_USB_EXT_CHG_INFO:
+ info.chg_block_type = USB_CHG_BLOCK_ULPI;
+ info.page_offset = motg->io_res->start & ~PAGE_MASK;
+ /* mmap() works on PAGE granularity */
+ info.length = PAGE_SIZE;
+
+ if (copy_to_user((void __user *)arg, &info, sizeof(info))) {
+ pr_err("%s: copy to user failed\n\n", __func__);
+ ret = -EFAULT;
+ }
+ break;
+ case MSM_USB_EXT_CHG_BLOCK_LPM:
+ if (get_user(val, (int __user *)arg)) {
+ pr_err("%s: get_user failed\n\n", __func__);
+ ret = -EFAULT;
+ break;
+ }
+ pr_debug("%s: LPM block request %d\n", __func__, val);
+ if (val) { /* block LPM */
+ if (motg->chg_type == USB_DCP_CHARGER) {
+ /*
+ * If device is already suspended, resume it.
+ * The PM usage counter is incremented in
+ * runtime resume method. if device is not
+ * suspended, cancel the scheduled suspend
+ * and increment the PM usage counter.
+ */
+ if (pm_runtime_suspended(motg->phy.dev))
+ pm_runtime_resume(motg->phy.dev);
+ else
+ pm_runtime_get_sync(motg->phy.dev);
+ } else {
+ motg->ext_chg_active = false;
+ complete(&motg->ext_chg_wait);
+ ret = -ENODEV;
+ }
+ } else {
+ motg->ext_chg_active = false;
+ complete(&motg->ext_chg_wait);
+ pm_runtime_put(motg->phy.dev);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int msm_otg_ext_chg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct msm_otg *motg = file->private_data;
+ unsigned long vsize = vma->vm_end - vma->vm_start;
+ int ret;
+
+ if (vma->vm_pgoff || vsize > PAGE_SIZE)
+ return -EINVAL;
+
+ vma->vm_pgoff = __phys_to_pfn(motg->io_res->start);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vsize, vma->vm_page_prot);
+ if (ret < 0) {
+ pr_err("%s: failed with return val %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int msm_otg_ext_chg_release(struct inode *inode, struct file *file)
+{
+ struct msm_otg *motg = file->private_data;
+
+ pr_debug("msm_otg ext chg release\n");
+
+ motg->ext_chg_opened = false;
+
+ return 0;
+}
+
+static const struct file_operations msm_otg_ext_chg_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_otg_ext_chg_open,
+ .unlocked_ioctl = msm_otg_ext_chg_ioctl,
+ .mmap = msm_otg_ext_chg_mmap,
+ .release = msm_otg_ext_chg_release,
+};
+
+static int msm_otg_setup_ext_chg_cdev(struct msm_otg *motg)
+{
+ int ret;
+
+ if (motg->pdata->enable_sec_phy || motg->pdata->mode == USB_HOST ||
+ motg->pdata->otg_control != OTG_PMIC_CONTROL ||
+ psy != &motg->usb_psy) {
+ pr_debug("usb ext chg is not supported by msm otg\n");
+ return -ENODEV;
+ }
+
+ ret = alloc_chrdev_region(&motg->ext_chg_dev, 0, 1, "usb_ext_chg");
+ if (ret < 0) {
+ pr_err("Fail to allocate usb ext char dev region\n");
+ return ret;
+ }
+ motg->ext_chg_class = class_create(THIS_MODULE, "msm_ext_chg");
+ if (ret < 0) {
+ pr_err("Fail to create usb ext chg class\n");
+ goto unreg_chrdev;
+ }
+ cdev_init(&motg->ext_chg_cdev, &msm_otg_ext_chg_fops);
+ motg->ext_chg_cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&motg->ext_chg_cdev, motg->ext_chg_dev, 1);
+ if (ret < 0) {
+ pr_err("Fail to add usb ext chg cdev\n");
+ goto destroy_class;
+ }
+ motg->ext_chg_device = device_create(motg->ext_chg_class,
+ NULL, motg->ext_chg_dev, NULL,
+ "usb_ext_chg");
+ if (IS_ERR(motg->ext_chg_device)) {
+ pr_err("Fail to create usb ext chg device\n");
+ ret = PTR_ERR(motg->ext_chg_device);
+ motg->ext_chg_device = NULL;
+ goto del_cdev;
+ }
+
+ init_completion(&motg->ext_chg_wait);
+ pr_debug("msm otg ext chg cdev setup success\n");
+ return 0;
+
+del_cdev:
+ cdev_del(&motg->ext_chg_cdev);
+destroy_class:
+ class_destroy(motg->ext_chg_class);
+unreg_chrdev:
+ unregister_chrdev_region(motg->ext_chg_dev, 1);
+
+ return ret;
+}
+
struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -3839,10 +4102,17 @@
of_property_read_u32(node, "qcom,hsusb-log2-itc",
&pdata->log2_itc);
+ of_property_read_u32(node, "qcom,hsusb-otg-mpm-dpsehv-int",
+ &pdata->mpm_dpshv_int);
+ of_property_read_u32(node, "qcom,hsusb-otg-mpm-dmsehv-int",
+ &pdata->mpm_dmshv_int);
pdata->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq");
if (pdata->pmic_id_irq < 0)
pdata->pmic_id_irq = 0;
+ pdata->l1_supported = of_property_read_bool(node,
+ "qcom,hsusb-l1-supported");
+
return pdata;
}
@@ -3881,17 +4151,17 @@
pdata = pdev->dev.platform_data;
}
- motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+ motg = devm_kzalloc(&pdev->dev, sizeof(struct msm_otg), GFP_KERNEL);
if (!motg) {
dev_err(&pdev->dev, "unable to allocate msm_otg\n");
return -ENOMEM;
}
- motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+ motg->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+ GFP_KERNEL);
if (!motg->phy.otg) {
dev_err(&pdev->dev, "unable to allocate usb_otg\n");
- ret = -ENOMEM;
- goto free_motg;
+ return -ENOMEM;
}
the_msm_otg = motg;
@@ -3899,6 +4169,19 @@
phy = &motg->phy;
phy->dev = &pdev->dev;
+ if (motg->pdata->bus_scale_table) {
+ motg->bus_perf_client =
+ msm_bus_scale_register_client(motg->pdata->bus_scale_table);
+ if (!motg->bus_perf_client) {
+ dev_err(motg->phy.dev, "%s: Failed to register BUS\n"
+ "scaling client!!\n", __func__);
+ } else {
+ debug_bus_voting_enabled = true;
+ /* Some platforms require BUS vote to control clocks */
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+ }
+ }
+
/*
* ACA ID_GND threshold range is overlapped with OTG ID_FLOAT. Hence
* PHY treat ACA ID_GND as float and no interrupt is generated. But
@@ -3907,7 +4190,7 @@
if (aca_enabled() && motg->pdata->otg_control != OTG_PMIC_CONTROL) {
dev_err(&pdev->dev, "ACA can not be enabled without PMIC\n");
ret = -EINVAL;
- goto free_otg;
+ goto devote_bus_bw;
}
/* initialize reset counter */
@@ -3971,6 +4254,7 @@
goto put_pclk;
}
+ motg->io_res = res;
motg->regs = ioremap(res->start, resource_size(res));
if (!motg->regs) {
dev_err(&pdev->dev, "ioremap failed\n");
@@ -4124,6 +4408,11 @@
if (pdata->otg_control == OTG_PHY_CONTROL && pdata->mpm_otgsessvld_int)
msm_mpm_enable_pin(pdata->mpm_otgsessvld_int, 1);
+ if (pdata->mpm_dpshv_int)
+ msm_mpm_enable_pin(pdata->mpm_dpshv_int, 1);
+ if (pdata->mpm_dmshv_int)
+ msm_mpm_enable_pin(pdata->mpm_dmshv_int, 1);
+
phy->init = msm_otg_reset;
phy->set_power = msm_otg_set_power;
phy->set_suspend = msm_otg_set_suspend;
@@ -4187,6 +4476,9 @@
if (motg->pdata->otg_control == OTG_PHY_CONTROL)
motg->caps = ALLOW_PHY_RETENTION |
ALLOW_PHY_REGULATORS_LPM;
+
+ if (motg->pdata->mpm_dpshv_int || motg->pdata->mpm_dmshv_int)
+ motg->caps |= ALLOW_HOST_PHY_RETENTION;
}
if (motg->pdata->enable_lpm_on_dev_suspend)
@@ -4202,16 +4494,6 @@
pm_runtime_use_autosuspend(&pdev->dev);
}
- if (motg->pdata->bus_scale_table) {
- motg->bus_perf_client =
- msm_bus_scale_register_client(motg->pdata->bus_scale_table);
- if (!motg->bus_perf_client)
- dev_err(motg->phy.dev, "%s: Failed to register BUS "
- "scaling client!!\n", __func__);
- else
- debug_bus_voting_enabled = true;
- }
-
motg->usb_psy.name = "usb";
motg->usb_psy.type = POWER_SUPPLY_TYPE_USB;
motg->usb_psy.supplied_to = otg_pm_power_supplied_to;
@@ -4236,6 +4518,10 @@
if (legacy_power_supply && pdata->otg_control == OTG_PMIC_CONTROL)
pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
+ ret = msm_otg_setup_ext_chg_cdev(motg);
+ if (ret)
+ dev_dbg(&pdev->dev, "fail to setup cdev\n");
+
return 0;
remove_phy:
@@ -4279,10 +4565,12 @@
clk_put(motg->clk);
if (!IS_ERR(motg->phy_reset_clk))
clk_put(motg->phy_reset_clk);
-free_otg:
- kfree(motg->phy.otg);
-free_motg:
- kfree(motg);
+devote_bus_bw:
+ if (motg->bus_perf_client) {
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
+ msm_bus_scale_unregister_client(motg->bus_perf_client);
+ }
+
return ret;
}
@@ -4295,6 +4583,13 @@
if (phy->otg->host || phy->otg->gadget)
return -EBUSY;
+ if (!motg->ext_chg_device) {
+ device_destroy(motg->ext_chg_class, motg->ext_chg_dev);
+ cdev_del(&motg->ext_chg_cdev);
+ class_destroy(motg->ext_chg_class);
+ unregister_chrdev_region(motg->ext_chg_dev, 1);
+ }
+
if (pdev->dev.of_node)
msm_otg_setup_devices(pdev, motg->pdata->mode, false);
if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
@@ -4322,6 +4617,11 @@
motg->pdata->mpm_otgsessvld_int)
msm_mpm_enable_pin(motg->pdata->mpm_otgsessvld_int, 0);
+ if (motg->pdata->mpm_dpshv_int)
+ msm_mpm_enable_pin(motg->pdata->mpm_dpshv_int, 0);
+ if (motg->pdata->mpm_dmshv_int)
+ msm_mpm_enable_pin(motg->pdata->mpm_dmshv_int, 0);
+
/*
* Put PHY in low power mode.
*/
@@ -4363,11 +4663,11 @@
clk_put(motg->clk);
clk_put(motg->core_clk);
- if (motg->bus_perf_client)
+ if (motg->bus_perf_client) {
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
msm_bus_scale_unregister_client(motg->bus_perf_client);
+ }
- kfree(motg->phy.otg);
- kfree(motg);
return 0;
}
@@ -4381,8 +4681,25 @@
if (phy->state == OTG_STATE_UNDEFINED)
return -EAGAIN;
- else
- return 0;
+
+ if (motg->ext_chg_active) {
+ dev_dbg(dev, "Deferring LPM\n");
+ /*
+ * Charger detection may happen in user space.
+ * Delay entering LPM by 3 sec. Otherwise we
+ * have to exit LPM when user space begins
+ * charger detection.
+ *
+ * This timer will be canceled when user space
+ * votes against LPM by incrementing PM usage
+ * counter. We enter low power mode when
+ * PM usage counter is decremented.
+ */
+ pm_schedule_suspend(dev, 3000);
+ return -EAGAIN;
+ }
+
+ return 0;
}
static int msm_otg_runtime_suspend(struct device *dev)
diff --git a/drivers/video/msm/mdss/dsi_io_v2.c b/drivers/video/msm/mdss/dsi_io_v2.c
index 273fb54..f0ad511 100644
--- a/drivers/video/msm/mdss/dsi_io_v2.c
+++ b/drivers/video/msm/mdss/dsi_io_v2.c
@@ -320,7 +320,7 @@
static void msm_dsi_phy_regulator_init(unsigned char *ctrl_base,
struct mdss_dsi_phy_ctrl *pd)
{
- MIPI_OUTP(ctrl_base + DSI_DSIPHY_LDO_CNTRL, 0x04);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_LDO_CNTRL, 0x25);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_0, pd->regulator[0]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_1, pd->regulator[1]);
MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_2, pd->regulator[2]);
diff --git a/drivers/video/msm/mdss/dsi_v2.c b/drivers/video/msm/mdss/dsi_v2.c
index 5833796..1a9059c 100644
--- a/drivers/video/msm/mdss/dsi_v2.c
+++ b/drivers/video/msm/mdss/dsi_v2.c
@@ -26,15 +26,6 @@
static int dsi_off(struct mdss_panel_data *pdata)
{
int rc = 0;
- if (!panel_common_data || !pdata)
- return -ENODEV;
-
- if (dsi_intf.op_mode_config)
- dsi_intf.op_mode_config(DSI_CMD_MODE, pdata);
-
- pr_debug("panel off commands\n");
- if (panel_common_data->off)
- panel_common_data->off(pdata);
pr_debug("turn off dsi controller\n");
if (dsi_intf.off)
@@ -44,11 +35,6 @@
pr_err("mdss_dsi_off DSI failed %d\n", rc);
return rc;
}
-
- pr_debug("turn off panel power\n");
- if (panel_common_data->reset)
- panel_common_data->reset(pdata, 0);
-
return rc;
}
@@ -56,13 +42,7 @@
{
int rc = 0;
- pr_debug("dsi_on\n");
-
- if (!panel_common_data || !pdata)
- return -ENODEV;
-
-
- pr_debug("dsi_on DSI controller ont\n");
+ pr_debug("dsi_on DSI controller on\n");
if (dsi_intf.on)
rc = dsi_intf.on(pdata);
@@ -70,17 +50,34 @@
pr_err("mdss_dsi_on DSI failed %d\n", rc);
return rc;
}
- pr_debug("dsi_on power on panel\n");
- if (panel_common_data->reset)
- panel_common_data->reset(pdata, 1);
+ return rc;
+}
+static int dsi_panel_handler(struct mdss_panel_data *pdata, int enable)
+{
+ int rc = 0;
- pr_debug("dsi_on DSI panel ont\n");
- if (panel_common_data->on)
- rc = panel_common_data->on(pdata);
+ pr_debug("dsi_panel_handler enable=%d\n", enable);
+ if (!panel_common_data || !pdata)
+ return -ENODEV;
- if (rc) {
- pr_err("mdss_dsi_on panel failed %d\n", rc);
- return rc;
+ if (enable) {
+ if (panel_common_data->reset)
+ panel_common_data->reset(pdata, 1);
+
+ if (panel_common_data->on)
+ rc = panel_common_data->on(pdata);
+
+ if (rc)
+ pr_err("dsi_panel_handler panel on failed %d\n", rc);
+ } else {
+ if (dsi_intf.op_mode_config)
+ dsi_intf.op_mode_config(DSI_CMD_MODE, pdata);
+
+ if (panel_common_data->off)
+ panel_common_data->off(pdata);
+
+ if (panel_common_data->reset)
+ panel_common_data->reset(pdata, 0);
}
return rc;
}
@@ -96,12 +93,18 @@
}
switch (event) {
- case MDSS_EVENT_PANEL_ON:
+ case MDSS_EVENT_UNBLANK:
rc = dsi_on(pdata);
break;
- case MDSS_EVENT_PANEL_OFF:
+ case MDSS_EVENT_BLANK:
rc = dsi_off(pdata);
break;
+ case MDSS_EVENT_PANEL_ON:
+ rc = dsi_panel_handler(pdata, 1);
+ break;
+ case MDSS_EVENT_PANEL_OFF:
+ rc = dsi_panel_handler(pdata, 0);
+ break;
default:
pr_debug("%s: unhandled event=%d\n", __func__, event);
break;
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c
index e5b6603..66418db 100644
--- a/drivers/video/msm/mdss/mdp3.c
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -161,12 +161,19 @@
{
int i = 0;
struct mdp3_hw_resource *mdata = (struct mdp3_hw_resource *)ptr;
- u32 mdp_interrupt = MDP3_REG_READ(MDP3_REG_INTR_STATUS);
+ u32 mdp_interrupt = 0;
+ spin_lock(&mdata->irq_lock);
+ if (!mdata->irq_mask) {
+ pr_err("spurious interrupt\n");
+ spin_unlock(&mdata->irq_lock);
+ return IRQ_HANDLED;
+ }
+
+ mdp_interrupt = MDP3_REG_READ(MDP3_REG_INTR_STATUS);
MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_interrupt);
pr_debug("mdp3_irq_handler irq=%d\n", mdp_interrupt);
- spin_lock(&mdata->irq_lock);
mdp_interrupt &= mdata->irq_mask;
while (mdp_interrupt && i < MDP3_MAX_INTR) {
@@ -183,7 +190,6 @@
void mdp3_irq_enable(int type)
{
unsigned long flag;
- int irqEnabled = 0;
pr_debug("mdp3_irq_enable type=%d\n", type);
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
@@ -193,11 +199,10 @@
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
return;
}
- irqEnabled = mdp3_res->irq_mask;
+
mdp3_res->irq_mask |= BIT(type);
MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
- if (!irqEnabled)
- enable_irq(mdp3_res->irq);
+
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
}
@@ -220,8 +225,6 @@
if (mdp3_res->irq_ref_count[type] == 0) {
mdp3_res->irq_mask &= ~BIT(type);
MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
- if (!mdp3_res->irq_mask)
- disable_irq_nosync(mdp3_res->irq);
}
}
@@ -229,7 +232,7 @@
{
unsigned long flag;
- pr_debug("interrupt %d callback n", type);
+ pr_debug("interrupt %d callback\n", type);
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
if (cb)
mdp3_res->callbacks[type] = *cb;
@@ -240,6 +243,30 @@
return 0;
}
+void mdp3_irq_register(void)
+{
+ unsigned long flag;
+
+ pr_debug("mdp3_irq_register\n");
+ spin_lock_irqsave(&mdp3_res->irq_lock, flag);
+ enable_irq(mdp3_res->irq);
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
+}
+
+void mdp3_irq_deregister(void)
+{
+ unsigned long flag;
+
+ pr_debug("mdp3_irq_deregister\n");
+ spin_lock_irqsave(&mdp3_res->irq_lock, flag);
+ memset(mdp3_res->irq_ref_count, 0, sizeof(u32) * MDP3_MAX_INTR);
+ mdp3_res->irq_mask = 0;
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0);
+ MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, 0xfffffff);
+ disable_irq_nosync(mdp3_res->irq);
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
+}
+
static int mdp3_bus_scale_register(void)
{
int i;
@@ -539,13 +566,6 @@
return 0;
}
-static int mdp3_iommu_fault_handler(struct iommu_domain *domain,
- struct device *dev, unsigned long iova, int flags, void *token)
-{
- pr_err("MDP IOMMU page fault: iova 0x%lx\n", iova);
- return 0;
-}
-
int mdp3_iommu_attach(int context)
{
struct mdp3_iommu_ctx_map *context_map;
@@ -621,9 +641,6 @@
else
return PTR_ERR(mdp3_iommu_domains[i].domain);
}
- iommu_set_fault_handler(mdp3_iommu_domains[i].domain,
- mdp3_iommu_fault_handler,
- NULL);
}
mdp3_res->domains = mdp3_iommu_domains;
@@ -826,20 +843,16 @@
struct ion_client *iclient = mdp3_res->ion_client;
int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
- if (!data->srcp_file) {
- pr_debug("No img to put\n");
- return 0;
- }
- if (data->flags & MDP_BLIT_SRC_GEM) {
- pr_debug("memory source MDP_BLIT_SRC_GEM\n");
- } else if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
- pr_debug("fb mem buf=0x%x\n", data->addr);
+ if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
+ pr_info("mdp3_put_img fb mem buf=0x%x\n", data->addr);
fput_light(data->srcp_file, data->p_need);
data->srcp_file = NULL;
- } else {
+ } else if (!IS_ERR_OR_NULL(data->srcp_ihdl)) {
ion_unmap_iommu(iclient, data->srcp_ihdl, dom, 0);
ion_free(iclient, data->srcp_ihdl);
data->srcp_ihdl = NULL;
+ } else {
+ return -EINVAL;
}
return 0;
}
@@ -855,16 +868,9 @@
start = (unsigned long *) &data->addr;
len = (unsigned long *) &data->len;
- data->flags |= img->flags;
+ data->flags = img->flags;
data->p_need = 0;
- if (img->flags & MDP_BLIT_SRC_GEM) {
- data->srcp_file = NULL;
- ret = kgsl_gem_obj_addr(img->memory_id, (int) img->priv,
- &data->addr, &data->len);
- if (!ret)
- goto done;
- }
if (img->flags & MDP_MEMORY_ID_TYPE_FB) {
file = fget_light(img->memory_id, &data->p_need);
if (file == NULL) {
@@ -889,8 +895,7 @@
data->srcp_file = file;
if (!ret)
goto done;
- }
- if (iclient) {
+ } else if (iclient) {
data->srcp_ihdl = ion_import_dma_buf(iclient, img->memory_id);
if (IS_ERR_OR_NULL(data->srcp_ihdl)) {
pr_err("error on ion_import_fd\n");
@@ -977,6 +982,92 @@
return xres * bpp;
}
+static int mdp3_fbmem_alloc(struct msm_fb_data_type *mfd)
+{
+ int ret = -ENOMEM, dom;
+ void *virt = NULL;
+ unsigned long phys = 0;
+ size_t size;
+ u32 yres = mfd->fbi->var.yres_virtual;
+
+ size = PAGE_ALIGN(mfd->fbi->fix.line_length * yres);
+
+ if (mfd->index != 0) {
+ mfd->fbi->screen_base = virt;
+ mfd->fbi->fix.smem_start = phys;
+ mfd->fbi->fix.smem_len = 0;
+ return 0;
+ }
+
+ mdp3_res->ion_handle = ion_alloc(mdp3_res->ion_client, size,
+ SZ_1M,
+ ION_HEAP(ION_QSECOM_HEAP_ID), 0);
+
+ if (!IS_ERR_OR_NULL(mdp3_res->ion_handle)) {
+ virt = ion_map_kernel(mdp3_res->ion_client,
+ mdp3_res->ion_handle);
+ if (IS_ERR(virt)) {
+ pr_err("%s map kernel error\n", __func__);
+ goto ion_map_kernel_err;
+ }
+
+ ret = ion_phys(mdp3_res->ion_client, mdp3_res->ion_handle,
+ &phys, &size);
+ if (ret) {
+ pr_err("%s ion_phys error\n", __func__);
+ goto ion_map_phys_err;
+ }
+ } else {
+ pr_err("%s ion alloc fail\n", __func__);
+ mdp3_res->ion_handle = NULL;
+ return -ENOMEM;
+ }
+
+ dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
+
+ ret = ion_map_iommu(mdp3_res->ion_client, mdp3_res->ion_handle,
+ dom, 0, SZ_4K, 0, &mfd->iova,
+ (unsigned long *)&size, 0, 0);
+
+ if (ret) {
+ pr_err("%s map IOMMU error\n", __func__);
+ goto ion_map_phys_err;
+ }
+
+ pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n",
+ size, virt, phys, mfd->index);
+
+ mfd->fbi->screen_base = virt;
+ mfd->fbi->fix.smem_start = phys;
+ mfd->fbi->fix.smem_len = size;
+ return 0;
+
+ion_map_phys_err:
+ ion_unmap_kernel(mdp3_res->ion_client, mdp3_res->ion_handle);
+ion_map_kernel_err:
+ ion_free(mdp3_res->ion_client, mdp3_res->ion_handle);
+ mdp3_res->ion_handle = NULL;
+ return -ENOMEM;
+}
+
+void mdp3_fbmem_free(struct msm_fb_data_type *mfd)
+{
+ pr_info("mdp3_fbmem_free\n");
+ if (mdp3_res->ion_handle) {
+ int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
+
+ ion_unmap_kernel(mdp3_res->ion_client, mdp3_res->ion_handle);
+ ion_unmap_iommu(mdp3_res->ion_client, mdp3_res->ion_handle,
+ dom, 0);
+ ion_free(mdp3_res->ion_client, mdp3_res->ion_handle);
+ mdp3_res->ion_handle = NULL;
+ mfd->fbi->screen_base = 0;
+ mfd->fbi->fix.smem_start = 0;
+ mfd->fbi->fix.smem_len = 0;
+ mfd->iova = 0;
+ }
+}
+
struct mdp3_dma *mdp3_get_dma_pipe(int capability)
{
int i;
@@ -1018,6 +1109,7 @@
static struct msm_mdp_interface mdp3_interface = {
.init_fnc = mdp3_init,
.fb_mem_get_iommu_domain = mdp3_fb_mem_get_iommu_domain,
+ .fb_mem_alloc_fnc = mdp3_fbmem_alloc,
.fb_stride = mdp3_fb_stride,
};
diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h
index 1afae01..d29e5b6 100644
--- a/drivers/video/msm/mdss/mdp3.h
+++ b/drivers/video/msm/mdss/mdp3.h
@@ -110,6 +110,7 @@
struct ion_client *ion_client;
struct mdp3_iommu_domain_map *domains;
struct mdp3_iommu_ctx_map *iommu_contexts;
+ struct ion_handle *ion_handle;
struct mdp3_dma dma[MDP3_DMA_MAX];
struct mdp3_intf intf[MDP3_DMA_OUTPUT_SEL_MAX];
@@ -141,6 +142,8 @@
void mdp3_irq_disable(int type);
void mdp3_irq_disable_nosync(int type);
int mdp3_set_intr_callback(u32 type, struct mdp3_intr_cb *cb);
+void mdp3_irq_register(void);
+void mdp3_irq_deregister(void);
int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate, int client);
int mdp3_clk_enable(int enable);
int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota);
@@ -148,6 +151,7 @@
int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data);
int mdp3_iommu_enable(int client);
int mdp3_iommu_disable(int client);
+void mdp3_fbmem_free(struct msm_fb_data_type *mfd);
#define MDP3_REG_WRITE(addr, val) writel_relaxed(val, mdp3_res->mdp_base + addr)
#define MDP3_REG_READ(addr) readl_relaxed(mdp3_res->mdp_base + addr)
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index 339b888..7edf3d2 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -30,6 +30,8 @@
static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd);
static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx);
+static int mdp3_histogram_stop(struct mdp3_session_data *session,
+ u32 block);
static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq)
{
@@ -425,8 +427,10 @@
}
panel = mdp3_session->panel;
- if (panel->event_handler)
- rc = panel->event_handler(panel, MDSS_EVENT_PANEL_ON, NULL);
+ if (panel->event_handler) {
+ rc = panel->event_handler(panel, MDSS_EVENT_UNBLANK, NULL);
+ rc |= panel->event_handler(panel, MDSS_EVENT_PANEL_ON, NULL);
+ }
if (rc) {
pr_err("fail to turn on the panel\n");
goto on_error;
@@ -437,6 +441,8 @@
goto on_error;
}
+ mdp3_irq_register();
+
rc = mdp3_ctrl_dma_init(mfd, mdp3_session->dma);
if (rc) {
pr_err("dma init failed\n");
@@ -455,6 +461,10 @@
goto on_error;
}
+ if (panel->set_backlight)
+ panel->set_backlight(panel, panel->panel_info.bl_max);
+
+ pr_debug("mdp3_ctrl_on dma start\n");
if (mfd->fbi->screen_base) {
rc = mdp3_session->dma->start(mdp3_session->dma,
mdp3_session->intf);
@@ -486,6 +496,7 @@
return -ENODEV;
}
+ panel = mdp3_session->panel;
mutex_lock(&mdp3_session->lock);
if (!mdp3_session->status) {
@@ -493,34 +504,46 @@
goto off_error;
}
- pr_debug("mdp3_ctrl_off stop mdp3 dma engine\n");
+ mdp3_histogram_stop(mdp3_session, MDP_BLOCK_DMA_P);
- rc = mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
+ pr_debug("mdp3_ctrl_off turn panel off\n");
+ if (panel->set_backlight)
+ panel->set_backlight(panel, 0);
- if (rc)
- pr_err("fail to stop the MDP3 dma\n");
-
- pr_debug("mdp3_ctrl_off stop dsi panel and controller\n");
- panel = mdp3_session->panel;
if (panel->event_handler)
rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF, NULL);
if (rc)
pr_err("fail to turn off the panel\n");
- pr_debug("mdp3_ctrl_off release bus and clock\n");
- rc = mdp3_ctrl_res_req_bus(mfd, 0);
+ rc = mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
if (rc)
- pr_err("mdp bus resource release failed\n");
+ pr_err("fail to stop the MDP3 dma\n");
+
+ mdp3_irq_deregister();
+
+ pr_debug("mdp3_ctrl_off stop clock\n");
rc = mdp3_ctrl_res_req_clk(mfd, 0);
if (rc)
pr_err("mdp clock resource release failed\n");
+ pr_debug("mdp3_ctrl_off stop dsi controller\n");
+ if (panel->event_handler)
+ rc = panel->event_handler(panel, MDSS_EVENT_BLANK, NULL);
+ if (rc)
+ pr_err("fail to turn off the panel\n");
+
+ pr_debug("mdp3_ctrl_off release bus\n");
+ rc = mdp3_ctrl_res_req_bus(mfd, 0);
+ if (rc)
+ pr_err("mdp bus resource release failed\n");
+
rc = mdp3_iommu_disable(MDP3_CLIENT_DMA_P);
if (rc)
pr_err("fail to dettach MDP DMA SMMU\n");
off_error:
mdp3_session->status = 0;
+ mdp3_bufq_deinit(&mdp3_session->bufq_out);
mutex_unlock(&mdp3_session->lock);
if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST)
mdp3_overlay_unset(mfd, mdp3_session->overlay.id);
@@ -572,14 +595,11 @@
int rc = 0;
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
- mdp3_ctrl_pan_display(mfd);
-
mutex_lock(&mdp3_session->lock);
if (mdp3_session->overlay.id == ndx && ndx == 1) {
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
mdp3_bufq_deinit(&mdp3_session->bufq_in);
- mdp3_bufq_deinit(&mdp3_session->bufq_out);
} else {
rc = -EINVAL;
}
@@ -661,12 +681,17 @@
mdp3_bufq_push(&mdp3_session->bufq_out, data);
}
- if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) {
+ if (mdp3_bufq_count(&mdp3_session->bufq_out) > 2) {
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
mdp3_put_img(data);
- }
+ if (mfd->fbi->screen_base)
+ mdp3_fbmem_free(mfd);
+ }
mutex_unlock(&mdp3_session->lock);
+
+ mdss_fb_update_notify_update(mfd);
+
return rc;
}
@@ -709,7 +734,7 @@
mdp3_session->intf);
} else {
pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
- mdp3_session->intf->stop(mdp3_session->intf);
+ mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
}
pan_error:
mutex_unlock(&mdp3_session->lock);
@@ -738,6 +763,336 @@
return ret;
}
+static int mdp3_histogram_start(struct mdp3_session_data *session,
+ struct mdp_histogram_start_req *req)
+{
+ int ret;
+ struct mdp3_dma_histogram_config histo_config;
+
+ pr_debug("mdp3_histogram_start\n");
+ if (req->block != MDP_BLOCK_DMA_P ||
+ req->num_bins != MDP_HISTOGRAM_BIN_NUM) {
+ pr_err("mdp3_histogram_start invalid request\n");
+ return -EINVAL;
+ }
+
+ if (!session->dma->histo_op ||
+ !session->dma->config_histo) {
+ pr_err("mdp3_histogram_start not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (session->histo_status) {
+ pr_err("mdp3_histogram_start already started\n");
+ ret = -EBUSY;
+ goto histogram_start_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_RESET);
+ if (ret) {
+ pr_err("mdp3_histogram_start reset error\n");
+ goto histogram_start_err;
+ }
+
+ histo_config.frame_count = req->frame_cnt;
+ histo_config.bit_mask = req->bit_mask;
+ histo_config.auto_clear_en = 1;
+ histo_config.bit_mask_polarity = 0;
+ ret = session->dma->config_histo(session->dma, &histo_config);
+ if (ret) {
+ pr_err("mdp3_histogram_start config error\n");
+ goto histogram_start_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_START);
+ if (ret) {
+ pr_err("mdp3_histogram_start config error\n");
+ goto histogram_start_err;
+ }
+
+ session->histo_status = 1;
+
+histogram_start_err:
+ mutex_unlock(&session->histo_lock);
+ return ret;
+}
+
+static int mdp3_histogram_stop(struct mdp3_session_data *session,
+ u32 block)
+{
+ int ret;
+ pr_debug("mdp3_histogram_stop\n");
+
+ if (!session->dma->histo_op || block != MDP_BLOCK_DMA_P) {
+ pr_err("mdp3_histogram_stop not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (!session->histo_status) {
+ ret = 0;
+ goto histogram_stop_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_CANCEL);
+ if (ret)
+ pr_err("mdp3_histogram_stop error\n");
+
+ session->histo_status = 0;
+
+histogram_stop_err:
+ mutex_unlock(&session->histo_lock);
+ return ret;
+}
+
+static int mdp3_histogram_collect(struct mdp3_session_data *session,
+ struct mdp_histogram_data *hist)
+{
+ int ret;
+ struct mdp3_dma_histogram_data *mdp3_histo;
+
+ if (!session->dma->get_histo) {
+ pr_err("mdp3_histogram_collect not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (!session->histo_status) {
+ pr_err("mdp3_histogram_collect not started\n");
+ mutex_unlock(&session->histo_lock);
+ return -EPERM;
+ }
+
+ mutex_unlock(&session->histo_lock);
+
+ ret = session->dma->get_histo(session->dma);
+ if (ret) {
+ pr_debug("mdp3_histogram_collect error = %d\n", ret);
+ return ret;
+ }
+
+ mdp3_histo = &session->dma->histo_data;
+
+ ret = copy_to_user(hist->c0, mdp3_histo->r_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->c1, mdp3_histo->g_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->c2, mdp3_histo->b_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->extra_info, mdp3_histo->extra,
+ sizeof(uint32_t) * 2);
+ if (ret)
+ return ret;
+
+ hist->bin_cnt = MDP_HISTOGRAM_BIN_NUM;
+ hist->block = MDP_BLOCK_DMA_P;
+ return ret;
+}
+
+static int mdp3_bl_scale_config(struct msm_fb_data_type *mfd,
+ struct mdp_bl_scale_data *data)
+{
+ int ret = 0;
+ int curr_bl;
+ mutex_lock(&mfd->bl_lock);
+ curr_bl = mfd->bl_level;
+ mfd->bl_scale = data->scale;
+ mfd->bl_min_lvl = data->min_lvl;
+ pr_debug("update scale = %d, min_lvl = %d\n", mfd->bl_scale,
+ mfd->bl_min_lvl);
+
+ /* update current backlight to use new scaling*/
+ mdss_fb_set_backlight(mfd, curr_bl);
+ mutex_unlock(&mfd->bl_lock);
+ return ret;
+}
+
+static int mdp3_csc_config(struct mdp3_session_data *session,
+ struct mdp_csc_cfg_data *data)
+{
+ struct mdp3_dma_color_correct_config config;
+ struct mdp3_dma_ccs ccs;
+ int ret = -EINVAL;
+
+ if (!data->csc_data.csc_mv || !data->csc_data.csc_pre_bv ||
+ !data->csc_data.csc_post_bv || !data->csc_data.csc_pre_lv ||
+ !data->csc_data.csc_post_lv) {
+ pr_err("%s : Invalid csc vectors", __func__);
+ return -EINVAL;
+ }
+
+ session->cc_vect_sel = (session->cc_vect_sel + 1) % 2;
+
+ config.ccs_enable = 1;
+ config.ccs_sel = session->cc_vect_sel;
+ config.pre_limit_sel = session->cc_vect_sel;
+ config.post_limit_sel = session->cc_vect_sel;
+ config.pre_bias_sel = session->cc_vect_sel;
+ config.post_bias_sel = session->cc_vect_sel;
+ config.ccs_dirty = true;
+
+ ccs.mv = data->csc_data.csc_mv;
+ ccs.pre_bv = data->csc_data.csc_pre_bv;
+ ccs.post_bv = data->csc_data.csc_post_bv;
+ ccs.pre_lv = data->csc_data.csc_pre_lv;
+ ccs.post_lv = data->csc_data.csc_post_lv;
+
+ mutex_lock(&session->lock);
+ ret = session->dma->config_ccs(session->dma, &config, &ccs);
+ mutex_unlock(&session->lock);
+ return ret;
+}
+
+static int mdp3_pp_ioctl(struct msm_fb_data_type *mfd,
+ void __user *argp)
+{
+ int ret = -EINVAL;
+ struct msmfb_mdp_pp mdp_pp;
+ struct mdp3_session_data *mdp3_session;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EINVAL;
+
+ mdp3_session = mfd->mdp.private1;
+
+ ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp));
+ if (ret)
+ return ret;
+
+ switch (mdp_pp.op) {
+ case mdp_bl_scale_cfg:
+ ret = mdp3_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
+ &mdp_pp.data.bl_scale_data);
+ break;
+ case mdp_op_csc_cfg:
+ ret = mdp3_csc_config(mdp3_session,
+ &(mdp_pp.data.csc_cfg_data));
+ break;
+
+ default:
+ pr_err("Unsupported request to MDP_PP IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!ret)
+ ret = copy_to_user(argp, &mdp_pp, sizeof(struct msmfb_mdp_pp));
+ return ret;
+}
+
+static int mdp3_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd,
+ void __user *argp)
+{
+ int ret = -ENOSYS;
+ struct mdp_histogram_data hist;
+ struct mdp_histogram_start_req hist_req;
+ u32 block;
+ struct mdp3_session_data *mdp3_session;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EINVAL;
+
+ mdp3_session = mfd->mdp.private1;
+
+ switch (cmd) {
+ case MSMFB_HISTOGRAM_START:
+ ret = copy_from_user(&hist_req, argp, sizeof(hist_req));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_start(mdp3_session, &hist_req);
+ break;
+
+ case MSMFB_HISTOGRAM_STOP:
+ ret = copy_from_user(&block, argp, sizeof(int));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_stop(mdp3_session, block);
+ break;
+
+ case MSMFB_HISTOGRAM:
+ ret = copy_from_user(&hist, argp, sizeof(hist));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_collect(mdp3_session, &hist);
+ if (!ret)
+ ret = copy_to_user(argp, &hist, sizeof(hist));
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
+ struct fb_cmap *cmap)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ struct mdp3_dma_lut_config lut_config;
+ struct mdp3_dma_lut lut;
+ static u16 r[MDP_LUT_SIZE];
+ static u16 g[MDP_LUT_SIZE];
+ static u16 b[MDP_LUT_SIZE];
+
+ if (!mdp3_session->dma->config_lut)
+ return -EINVAL;
+
+ if (cmap->start + cmap->len > MDP_LUT_SIZE) {
+ pr_err("mdp3_ctrl_lut_update invalid arguments\n");
+ return -EINVAL;
+ }
+
+ rc = copy_from_user(r + cmap->start,
+ cmap->red, sizeof(u16)*cmap->len);
+ rc |= copy_from_user(g + cmap->start,
+ cmap->green, sizeof(u16)*cmap->len);
+ rc |= copy_from_user(b + cmap->start,
+ cmap->blue, sizeof(u16)*cmap->len);
+ if (rc)
+ return rc;
+
+ lut_config.lut_enable = 7;
+ lut_config.lut_sel = mdp3_session->lut_sel;
+ lut_config.lut_position = 0;
+ lut_config.lut_dirty = true;
+ lut.color0_lut = r;
+ lut.color1_lut = g;
+ lut.color2_lut = b;
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (!mdp3_session->status) {
+ pr_err("%s, display off!\n", __func__);
+ mutex_unlock(&mdp3_session->lock);
+ return -EPERM;
+ }
+
+ rc = mdp3_session->dma->config_lut(mdp3_session->dma, &lut_config,
+ &lut);
+ if (rc)
+ pr_err("mdp3_ctrl_lut_update failed\n");
+
+ mdp3_session->lut_sel = (mdp3_session->lut_sel + 1) % 2;
+
+ mutex_unlock(&mdp3_session->lock);
+ return rc;
+}
+
static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd,
u32 cmd, void __user *argp)
{
@@ -754,10 +1109,19 @@
if (!mdp3_session->status) {
pr_err("mdp3_ctrl_ioctl_handler, display off!\n");
- return -EINVAL;
+ return -EPERM;
}
switch (cmd) {
+ case MSMFB_MDP_PP:
+ rc = mdp3_pp_ioctl(mfd, argp);
+ break;
+ case MSMFB_HISTOGRAM_START:
+ case MSMFB_HISTOGRAM_STOP:
+ case MSMFB_HISTOGRAM:
+ rc = mdp3_histo_ioctl(mfd, cmd, argp);
+ break;
+
case MSMFB_VSYNC_CTRL:
case MSMFB_OVERLAY_VSYNC_CTRL:
if (!copy_from_user(&val, argp, sizeof(val))) {
@@ -836,6 +1200,7 @@
mdp3_interface->dma_fnc = mdp3_ctrl_pan_display;
mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler;
mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff;
+ mdp3_interface->lut_update = mdp3_ctrl_lut_update;
mdp3_session = kmalloc(sizeof(struct mdp3_session_data), GFP_KERNEL);
if (!mdp3_session) {
@@ -845,6 +1210,7 @@
memset(mdp3_session, 0, sizeof(struct mdp3_session_data));
mutex_init(&mdp3_session->lock);
INIT_WORK(&mdp3_session->vsync_work, mdp3_dispatch_vsync);
+ mutex_init(&mdp3_session->histo_lock);
mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
if (!mdp3_session->dma) {
rc = -ENODEV;
@@ -864,6 +1230,8 @@
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
mdp3_bufq_init(&mdp3_session->bufq_in);
mdp3_bufq_init(&mdp3_session->bufq_out);
+ mdp3_session->histo_status = 0;
+ mdp3_session->lut_sel = 0;
init_timer(&mdp3_session->vsync_timer);
mdp3_session->vsync_timer.function = mdp3_vsync_timer_func;
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index 0c2b0cd..9ea1c91 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -47,6 +47,10 @@
struct mdp3_buffer_queue bufq_in;
struct mdp3_buffer_queue bufq_out;
struct work_struct vsync_work;
+ int histo_status;
+ struct mutex histo_lock;
+ int lut_sel;
+ int cc_vect_sel;
};
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 3a46034..2e9c787 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -19,6 +19,10 @@
#define DMA_STOP_POLL_SLEEP_US 1000
#define DMA_STOP_POLL_TIMEOUT_US 16000
+#define DMA_HISTO_RESET_TIMEOUT_MS 40
+#define DMA_LUT_CONFIG_MASK 0xfffffbe8
+#define DMA_CCS_CONFIG_MASK 0xfffffc17
+#define HIST_WAIT_TIMEOUT(frame) ((75 * HZ * (frame)) / 1000)
static void mdp3_vsync_intr_handler(int type, void *arg)
{
@@ -45,12 +49,47 @@
mdp3_irq_disable_nosync(type);
}
+static void mdp3_hist_done_intr_handler(int type, void *arg)
+{
+ struct mdp3_dma *dma = (struct mdp3_dma *)arg;
+ u32 isr, mask;
+
+ isr = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_STATUS);
+ mask = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_ENABLE);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_CLEAR, isr);
+
+ isr &= mask;
+ if (isr == 0)
+ return;
+
+ if (isr & MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT) {
+ spin_lock(&dma->histo_lock);
+ dma->histo_state = MDP3_DMA_HISTO_STATE_READY;
+ complete(&dma->histo_comp);
+ spin_unlock(&dma->histo_lock);
+ }
+ if (isr & MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT) {
+ spin_lock(&dma->histo_lock);
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+ complete(&dma->histo_comp);
+ spin_unlock(&dma->histo_lock);
+ }
+}
+
void mdp3_dma_callback_enable(struct mdp3_dma *dma, int type)
{
int irq_bit;
pr_debug("mdp3_dma_callback_enable type=%d\n", type);
+ if (dma->dma_sel == MDP3_DMA_P) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE)
+ mdp3_irq_enable(MDP3_INTR_DMA_P_HISTO);
+
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_DONE)
+ mdp3_irq_enable(MDP3_INTR_DMA_P_HISTO);
+ }
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
@@ -79,6 +118,14 @@
pr_debug("mdp3_dma_callback_disable type=%d\n", type);
+ if (dma->dma_sel == MDP3_DMA_P) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE)
+ mdp3_irq_disable(MDP3_INTR_DMA_P_HISTO);
+
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_DONE)
+ mdp3_irq_disable(MDP3_INTR_DMA_P_HISTO);
+ }
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
@@ -101,7 +148,7 @@
static int mdp3_dma_callback_setup(struct mdp3_dma *dma)
{
- int rc;
+ int rc = 0;
struct mdp3_intr_cb vsync_cb = {
.cb = mdp3_vsync_intr_handler,
.data = dma,
@@ -112,14 +159,22 @@
.data = dma,
};
+ struct mdp3_intr_cb hist_cb = {
+ .cb = mdp3_hist_done_intr_handler,
+ .data = dma,
+ };
+
+ if (dma->dma_sel == MDP3_DMA_P)
+ rc = mdp3_set_intr_callback(MDP3_INTR_DMA_P_HISTO, &hist_cb);
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC)
- rc = mdp3_set_intr_callback(MDP3_INTR_LCDC_START_OF_FRAME,
+ rc |= mdp3_set_intr_callback(MDP3_INTR_LCDC_START_OF_FRAME,
&vsync_cb);
else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
int irq_bit = MDP3_INTR_SYNC_PRIMARY_LINE;
irq_bit += dma->dma_sel;
- rc = mdp3_set_intr_callback(irq_bit, &vsync_cb);
+ rc |= mdp3_set_intr_callback(irq_bit, &vsync_cb);
irq_bit = MDP3_INTR_DMA_P_DONE;
if (dma->dma_sel == MDP3_DMA_S)
irq_bit = MDP3_INTR_DMA_S_DONE;
@@ -128,6 +183,7 @@
pr_err("mdp3_dma_callback_setup not suppported interface\n");
rc = -ENODEV;
}
+
return rc;
}
@@ -163,6 +219,25 @@
}
}
+static void mdp3_dma_clk_auto_gating(struct mdp3_dma *dma, int enable)
+{
+ u32 cgc;
+ int clock_bit = 10;
+
+ clock_bit += dma->dma_sel;
+
+ if (enable) {
+ cgc = MDP3_REG_READ(MDP3_REG_CGC_EN);
+ cgc |= BIT(clock_bit);
+ MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc);
+
+ } else {
+ cgc = MDP3_REG_READ(MDP3_REG_CGC_EN);
+ cgc &= ~BIT(clock_bit);
+ MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc);
+ }
+}
+
static int mdp3_dma_sync_config(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config)
{
@@ -291,152 +366,158 @@
return 0;
}
+static void mdp3_ccs_update(struct mdp3_dma *dma)
+{
+ u32 cc_config;
+ int updated = 0;
+
+ cc_config = MDP3_REG_READ(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG);
+
+ if (dma->ccs_config.ccs_dirty) {
+ cc_config &= DMA_CCS_CONFIG_MASK;
+ if (dma->ccs_config.ccs_enable)
+ cc_config |= BIT(3);
+ else
+ cc_config &= ~BIT(3);
+ cc_config |= dma->ccs_config.ccs_sel << 5;
+ cc_config |= dma->ccs_config.pre_bias_sel << 6;
+ cc_config |= dma->ccs_config.post_bias_sel << 7;
+ cc_config |= dma->ccs_config.pre_limit_sel << 8;
+ cc_config |= dma->ccs_config.post_limit_sel << 9;
+ dma->ccs_config.ccs_dirty = false;
+ updated = 1;
+ }
+
+ if (dma->lut_config.lut_dirty) {
+ cc_config &= DMA_LUT_CONFIG_MASK;
+ cc_config |= dma->lut_config.lut_enable;
+ cc_config |= dma->lut_config.lut_position << 4;
+ cc_config |= dma->lut_config.lut_sel << 10;
+ dma->lut_config.lut_dirty = false;
+ updated = 1;
+ }
+ if (updated) {
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
+
+ /* Make sure ccs configuration update is done before continuing
+ with the DMA transfer */
+ wmb();
+ }
+}
+
static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
struct mdp3_dma_color_correct_config *config,
- struct mdp3_dma_ccs *ccs,
- struct mdp3_dma_lut *lut)
+ struct mdp3_dma_ccs *ccs)
{
int i;
- u32 addr, cc_config, color;
+ u32 addr;
- cc_config = config->lut_enable;
- if (config->ccs_enable)
- cc_config |= BIT(3);
- cc_config |= config->lut_position << 4;
- cc_config |= config->ccs_sel << 5;
- cc_config |= config->pre_bias_sel << 6;
- cc_config |= config->post_bias_sel << 7;
- cc_config |= config->pre_limit_sel << 8;
- cc_config |= config->post_limit_sel << 9;
- cc_config |= config->lut_sel << 10;
+ if (!ccs)
+ return -EINVAL;
- MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
-
- if (config->ccs_enable && ccs) {
- if (ccs->mv1) {
- addr = MDP3_REG_DMA_P_CSC_MV1;
- for (i = 0; i < 9; i++) {
- MDP3_REG_WRITE(addr, ccs->mv1[i]);
- addr += 4;
- }
- }
-
- if (ccs->mv2) {
+ if (config->ccs_enable) {
+ addr = MDP3_REG_DMA_P_CSC_MV1;
+ if (config->ccs_sel)
addr = MDP3_REG_DMA_P_CSC_MV2;
- for (i = 0; i < 9; i++) {
- MDP3_REG_WRITE(addr, ccs->mv2[i]);
- addr += 4;
- }
+ for (i = 0; i < 9; i++) {
+ MDP3_REG_WRITE(addr, ccs->mv[i]);
+ addr += 4;
}
- if (ccs->pre_bv1) {
- addr = MDP3_REG_DMA_P_CSC_PRE_BV1;
- for (i = 0; i < 3; i++) {
- MDP3_REG_WRITE(addr, ccs->pre_bv1[i]);
- addr += 4;
- }
- }
-
- if (ccs->pre_bv2) {
+ addr = MDP3_REG_DMA_P_CSC_PRE_BV1;
+ if (config->pre_bias_sel)
addr = MDP3_REG_DMA_P_CSC_PRE_BV2;
- for (i = 0; i < 3; i++) {
- MDP3_REG_WRITE(addr, ccs->pre_bv2[i]);
- addr += 4;
- }
+ for (i = 0; i < 3; i++) {
+ MDP3_REG_WRITE(addr, ccs->pre_bv[i]);
+ addr += 4;
}
- if (ccs->post_bv1) {
- addr = MDP3_REG_DMA_P_CSC_POST_BV1;
- for (i = 0; i < 3; i++) {
- MDP3_REG_WRITE(addr, ccs->post_bv1[i]);
- addr += 4;
- }
- }
-
- if (ccs->post_bv2) {
+ addr = MDP3_REG_DMA_P_CSC_POST_BV1;
+ if (config->post_bias_sel)
addr = MDP3_REG_DMA_P_CSC_POST_BV2;
- for (i = 0; i < 3; i++) {
- MDP3_REG_WRITE(addr, ccs->post_bv2[i]);
- addr += 4;
- }
+ for (i = 0; i < 3; i++) {
+ MDP3_REG_WRITE(addr, ccs->post_bv[i]);
+ addr += 4;
}
- if (ccs->pre_lv1) {
- addr = MDP3_REG_DMA_P_CSC_PRE_LV1;
- for (i = 0; i < 6; i++) {
- MDP3_REG_WRITE(addr, ccs->pre_lv1[i]);
- addr += 4;
- }
- }
-
- if (ccs->pre_lv2) {
+ addr = MDP3_REG_DMA_P_CSC_PRE_LV1;
+ if (config->pre_limit_sel)
addr = MDP3_REG_DMA_P_CSC_PRE_LV2;
- for (i = 0; i < 6; i++) {
- MDP3_REG_WRITE(addr, ccs->pre_lv2[i]);
- addr += 4;
- }
+ for (i = 0; i < 6; i++) {
+ MDP3_REG_WRITE(addr, ccs->pre_lv[i]);
+ addr += 4;
}
- if (ccs->post_lv1) {
- addr = MDP3_REG_DMA_P_CSC_POST_LV1;
- for (i = 0; i < 6; i++) {
- MDP3_REG_WRITE(addr, ccs->post_lv1[i]);
- addr += 4;
- }
- }
-
- if (ccs->post_lv2) {
+ addr = MDP3_REG_DMA_P_CSC_POST_LV1;
+ if (config->post_limit_sel)
addr = MDP3_REG_DMA_P_CSC_POST_LV2;
- for (i = 0; i < 6; i++) {
- MDP3_REG_WRITE(addr, ccs->post_lv2[i]);
- addr += 4;
- }
+ for (i = 0; i < 6; i++) {
+ MDP3_REG_WRITE(addr, ccs->post_lv[i]);
+ addr += 4;
}
}
+ dma->ccs_config = *config;
+
+ if (dma->output_config.out_sel != MDP3_DMA_OUTPUT_SEL_DSI_CMD)
+ mdp3_ccs_update(dma);
+
+ return 0;
+}
+
+static int mdp3_dmap_lut_config(struct mdp3_dma *dma,
+ struct mdp3_dma_lut_config *config,
+ struct mdp3_dma_lut *lut)
+{
+ u32 addr, color;
+ int i;
if (config->lut_enable && lut) {
- if (lut->color0_lut1 && lut->color1_lut1 && lut->color2_lut1) {
- addr = MDP3_REG_DMA_P_CSC_LUT1;
- for (i = 0; i < 256; i++) {
- color = lut->color0_lut1[i];
- color |= lut->color1_lut1[i] << 8;
- color |= lut->color2_lut1[i] << 16;
- MDP3_REG_WRITE(addr, color);
- addr += 4;
- }
- }
-
- if (lut->color0_lut2 && lut->color1_lut2 && lut->color2_lut2) {
+ addr = MDP3_REG_DMA_P_CSC_LUT1;
+ if (config->lut_sel)
addr = MDP3_REG_DMA_P_CSC_LUT2;
- for (i = 0; i < 256; i++) {
- color = lut->color0_lut2[i];
- color |= lut->color1_lut2[i] << 8;
- color |= lut->color2_lut2[i] << 16;
- MDP3_REG_WRITE(addr, color);
- addr += 4;
- }
+
+ for (i = 0; i < MDP_LUT_SIZE; i++) {
+ color = lut->color0_lut[i] & 0xff;
+ color |= (lut->color1_lut[i] & 0xff) << 8;
+ color |= (lut->color2_lut[i] & 0xff) << 16;
+ MDP3_REG_WRITE(addr, color);
+ addr += 4;
}
}
- dma->ccs_config = *config;
+ dma->lut_config = *config;
+
+ if (dma->output_config.out_sel != MDP3_DMA_OUTPUT_SEL_DSI_CMD)
+ mdp3_ccs_update(dma);
+
return 0;
}
static int mdp3_dmap_histo_config(struct mdp3_dma *dma,
struct mdp3_dma_histogram_config *histo_config)
{
- u32 hist_bit_mask, hist_control;
+ unsigned long flag;
+ u32 histo_bit_mask, histo_control;
+ u32 histo_isr_mask = MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT |
+ MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
if (histo_config->bit_mask_polarity)
- hist_bit_mask = BIT(31);
- hist_bit_mask |= histo_config->bit_mask;
+ histo_bit_mask = BIT(31);
+ histo_bit_mask |= histo_config->bit_mask;
if (histo_config->auto_clear_en)
- hist_control = BIT(0);
+ histo_control = BIT(0);
MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_FRAME_CNT,
histo_config->frame_count);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_BIT_MASK, hist_bit_mask);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CONTROL, hist_control);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_BIT_MASK, histo_bit_mask);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CONTROL, histo_control);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, histo_isr_mask);
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ dma->histogram_config = *histo_config;
return 0;
}
@@ -456,8 +537,10 @@
spin_lock_irqsave(&dma->dma_lock, flag);
MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf);
dma->source_config.buf = buf;
- if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD)
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ mdp3_ccs_update(dma);
MDP3_REG_WRITE(MDP3_REG_DMA_P_START, 1);
+ }
if (!intf->active) {
pr_debug("mdp3_dmap_update start interface\n");
@@ -518,78 +601,172 @@
return 0;
}
-static int mdp3_dmap_histo_get(struct mdp3_dma *dma,
- struct mdp3_dma_histogram_data *data)
+static int mdp3_dmap_histo_get(struct mdp3_dma *dma)
{
- int i;
- u32 addr, extra;
+ int i, state, timeout, ret;
+ u32 addr;
+ unsigned long flag;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+ state = dma->histo_state;
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ if (state != MDP3_DMA_HISTO_STATE_START &&
+ state != MDP3_DMA_HISTO_STATE_READY) {
+ pr_err("mdp3_dmap_histo_get invalid state %d\n", state);
+ return -EINVAL;
+ }
+
+ timeout = HIST_WAIT_TIMEOUT(dma->histogram_config.frame_count);
+ ret = wait_for_completion_killable_timeout(&dma->histo_comp, timeout);
+
+ if (ret == 0) {
+ pr_debug("mdp3_dmap_histo_get time out\n");
+ ret = -ETIMEDOUT;
+ } else if (ret < 0) {
+ pr_err("mdp3_dmap_histo_get interrupted\n");
+ }
+
+ if (ret < 0)
+ return ret;
+
+ if (dma->histo_state != MDP3_DMA_HISTO_STATE_READY) {
+ pr_err("mdp3_dmap_histo_get after dma shut down\n");
+ return -EPERM;
+ }
addr = MDP3_REG_DMA_P_HIST_R_DATA;
- for (i = 0; i < 32; i++) {
- data->r_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.r_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
addr = MDP3_REG_DMA_P_HIST_G_DATA;
- for (i = 0; i < 32; i++) {
- data->g_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.g_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
addr = MDP3_REG_DMA_P_HIST_B_DATA;
- for (i = 0; i < 32; i++) {
- data->b_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.b_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
- extra = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_0);
- data->r_min_value = (extra & 0x1F0000) >> 16;
- data->r_max_value = (extra & 0x1F000000) >> 24;
- extra = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_1);
- data->g_min_value = extra & 0x1F;
- data->g_max_value = (extra & 0x1F00) >> 8;
- data->b_min_value = (extra & 0x1F0000) >> 16;
- data->b_max_value = (extra & 0x1F000000) >> 24;
+ dma->histo_data.extra[0] =
+ MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_0);
+ dma->histo_data.extra[1] =
+ MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_1);
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+ init_completion(&dma->histo_comp);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_START;
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ return 0;
+}
+
+static int mdp3_dmap_histo_start(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+
+ if (dma->histo_state != MDP3_DMA_HISTO_STATE_IDLE)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ init_completion(&dma->histo_comp);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_START;
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_enable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_DONE);
+ return 0;
+
+}
+
+static int mdp3_dmap_histo_reset(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+ int ret;
+
+ if (dma->histo_state == MDP3_DMA_HISTO_STATE_START)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ init_completion(&dma->histo_comp);
+
+ mdp3_dma_clk_auto_gating(dma, 0);
+
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, BIT(0)|BIT(1));
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_RESET_SEQ_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_RESET;
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_enable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE);
+ ret = wait_for_completion_killable_timeout(&dma->histo_comp,
+ msecs_to_jiffies(DMA_HISTO_RESET_TIMEOUT_MS));
+
+ if (ret == 0) {
+ pr_err("mdp3_dmap_histo_reset time out\n");
+ ret = -ETIMEDOUT;
+ } else if (ret < 0) {
+ pr_err("mdp3_dmap_histo_reset interrupted\n");
+ } else {
+ ret = 0;
+ }
+ mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE);
+ mdp3_dma_clk_auto_gating(dma, 1);
+
+ return ret;
+}
+
+static int mdp3_dmap_histo_stop(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+ int cb_type = MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE |
+ MDP3_DMA_CALLBACK_TYPE_HIST_DONE;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CANCEL_REQ, 1);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, 0);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+ complete(&dma->histo_comp);
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_disable(dma, cb_type);
return 0;
}
static int mdp3_dmap_histo_op(struct mdp3_dma *dma, u32 op)
{
+ int ret;
+
switch (op) {
case MDP3_DMA_HISTO_OP_START:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ ret = mdp3_dmap_histo_start(dma);
break;
case MDP3_DMA_HISTO_OP_STOP:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_STOP_REQ, 1);
- break;
case MDP3_DMA_HISTO_OP_CANCEL:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CANCEL_REQ, 1);
+ ret = mdp3_dmap_histo_stop(dma);
break;
case MDP3_DMA_HISTO_OP_RESET:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_RESET_SEQ_START, 1);
+ ret = mdp3_dmap_histo_reset(dma);
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_status(struct mdp3_dma *dma, int *status)
-{
- *status = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_STATUS);
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_enable(struct mdp3_dma *dma, u32 mask)
-{
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, mask);
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_clear(struct mdp3_dma *dma, u32 mask)
-{
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_CLEAR, mask);
- return 0;
+ return ret;
}
static int mdp3_dma_start(struct mdp3_dma *dma, struct mdp3_intf *intf)
@@ -648,6 +825,7 @@
mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_VSYNC |
MDP3_DMA_CALLBACK_TYPE_DMA_DONE);
+ init_completion(&dma->dma_comp);
return ret;
}
@@ -667,13 +845,11 @@
dma->config_cursor = mdp3_dmap_cursor_config;
dma->config_ccs = mdp3_dmap_ccs_config;
dma->config_histo = mdp3_dmap_histo_config;
+ dma->config_lut = mdp3_dmap_lut_config;
dma->update = mdp3_dmap_update;
dma->update_cursor = mdp3_dmap_cursor_update;
dma->get_histo = mdp3_dmap_histo_get;
dma->histo_op = mdp3_dmap_histo_op;
- dma->histo_intr_status = mdp3_dmap_histo_intr_status;
- dma->histo_intr_enable = mdp3_dmap_histo_intr_enable;
- dma->histo_intr_clear = mdp3_dmap_histo_intr_clear;
dma->vsync_enable = mdp3_dma_vsync_enable;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
@@ -686,13 +862,11 @@
dma->config_cursor = NULL;
dma->config_ccs = NULL;
dma->config_histo = NULL;
+ dma->config_lut = NULL;
dma->update = mdp3_dmas_update;
dma->update_cursor = NULL;
dma->get_histo = NULL;
dma->histo_op = NULL;
- dma->histo_intr_status = NULL;
- dma->histo_intr_enable = NULL;
- dma->histo_intr_clear = NULL;
dma->vsync_enable = mdp3_dma_vsync_enable;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
@@ -704,10 +878,13 @@
}
spin_lock_init(&dma->dma_lock);
+ spin_lock_init(&dma->histo_lock);
init_completion(&dma->vsync_comp);
init_completion(&dma->dma_comp);
+ init_completion(&dma->histo_comp);
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
memset(&dma->cursor, 0, sizeof(dma->cursor));
memset(&dma->ccs_config, 0, sizeof(dma->ccs_config));
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index c7b5f25..c652818 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -16,6 +16,9 @@
#include <linux/sched.h>
+#define MDP_HISTOGRAM_BIN_NUM 32
+#define MDP_LUT_SIZE 256
+
enum {
MDP3_DMA_P,
MDP3_DMA_S,
@@ -119,8 +122,18 @@
};
enum {
+ MDP3_DMA_HISTO_STATE_UNKNOWN,
+ MDP3_DMA_HISTO_STATE_IDLE,
+ MDP3_DMA_HISTO_STATE_RESET,
+ MDP3_DMA_HISTO_STATE_START,
+ MDP3_DMA_HISTO_STATE_READY,
+};
+
+enum {
MDP3_DMA_CALLBACK_TYPE_VSYNC = 0x01,
MDP3_DMA_CALLBACK_TYPE_DMA_DONE = 0x02,
+ MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE = 0x04,
+ MDP3_DMA_CALLBACK_TYPE_HIST_DONE = 0x08,
};
struct mdp3_dma_source {
@@ -163,37 +176,34 @@
};
struct mdp3_dma_ccs {
- u32 *mv1; /*set1 matrix vector, 3x3 */
- u32 *mv2;
- u32 *pre_bv1; /*pre-bias vector for set1, 1x3*/
- u32 *pre_bv2;
- u32 *post_bv1; /*post-bias vecotr for set1, */
- u32 *post_bv2;
- u32 *pre_lv1; /*pre-limit vector for set 1, 1x6*/
- u32 *pre_lv2;
- u32 *post_lv1;
- u32 *post_lv2;
+ u32 *mv; /*set1 matrix vector, 3x3 */
+ u32 *pre_bv; /*pre-bias vector for set1, 1x3*/
+ u32 *post_bv; /*post-bias vecotr for set1, */
+ u32 *pre_lv; /*pre-limit vector for set 1, 1x6*/
+ u32 *post_lv;
};
struct mdp3_dma_lut {
- uint8_t *color0_lut1;
- uint8_t *color1_lut1;
- uint8_t *color2_lut1;
- uint8_t *color0_lut2;
- uint8_t *color1_lut2;
- uint8_t *color2_lut2;
+ u16 *color0_lut;
+ u16 *color1_lut;
+ u16 *color2_lut;
+};
+
+struct mdp3_dma_lut_config {
+ int lut_enable;
+ u32 lut_sel;
+ u32 lut_position;
+ bool lut_dirty;
};
struct mdp3_dma_color_correct_config {
int ccs_enable;
- int lut_enable;
- u32 lut_sel;
u32 post_limit_sel;
u32 pre_limit_sel;
u32 post_bias_sel;
u32 pre_bias_sel;
u32 ccs_sel;
- u32 lut_position;
+ bool ccs_dirty;
};
struct mdp3_dma_histogram_config {
@@ -204,15 +214,10 @@
};
struct mdp3_dma_histogram_data {
- uint8_t r_max_value;
- uint8_t r_min_value;
- uint8_t b_max_value;
- uint8_t b_min_value;
- uint8_t g_max_value;
- uint8_t g_min_value;
- uint8_t r_data[32];
- uint8_t g_data[32];
- uint8_t b_data[32];
+ u32 r_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 g_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 b_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 extra[2];
};
struct mdp3_vsync_notification {
@@ -229,8 +234,10 @@
int available;
spinlock_t dma_lock;
+ spinlock_t histo_lock;
struct completion vsync_comp;
struct completion dma_comp;
+ struct completion histo_comp;
struct mdp3_vsync_notification vsync_client;
struct mdp3_dma_output_config output_config;
@@ -238,7 +245,10 @@
struct mdp3_dma_cursor cursor;
struct mdp3_dma_color_correct_config ccs_config;
+ struct mdp3_dma_lut_config lut_config;
struct mdp3_dma_histogram_config histogram_config;
+ int histo_state;
+ struct mdp3_dma_histogram_data histo_data;
int (*start)(struct mdp3_dma *dma, struct mdp3_intf *intf);
@@ -249,27 +259,22 @@
int (*config_ccs)(struct mdp3_dma *dma,
struct mdp3_dma_color_correct_config *config,
- struct mdp3_dma_ccs *ccs,
+ struct mdp3_dma_ccs *ccs);
+
+ int (*config_lut)(struct mdp3_dma *dma,
+ struct mdp3_dma_lut_config *config,
struct mdp3_dma_lut *lut);
int (*update)(struct mdp3_dma *dma, void *buf, struct mdp3_intf *intf);
int (*update_cursor)(struct mdp3_dma *dma, int x, int y);
- int (*get_histo)(struct mdp3_dma *dma,
- struct mdp3_dma_histogram_data *data);
+ int (*get_histo)(struct mdp3_dma *dma);
int (*config_histo)(struct mdp3_dma *dma,
struct mdp3_dma_histogram_config *histo_config);
- int (*histo_op)(struct mdp3_dma *dma,
- u32 op);
-
- int (*histo_intr_status)(struct mdp3_dma *dma, int *status);
-
- int (*histo_intr_enable)(struct mdp3_dma *dma, u32 mask);
-
- int (*histo_intr_clear)(struct mdp3_dma *dma, u32 mask);
+ int (*histo_op)(struct mdp3_dma *dma, u32 op);
void (*vsync_enable)(struct mdp3_dma *dma,
struct mdp3_vsync_notification *vsync_client);
diff --git a/drivers/video/msm/mdss/mdp3_hwio.h b/drivers/video/msm/mdss/mdp3_hwio.h
index f10206f..4afa37c 100644
--- a/drivers/video/msm/mdss/mdp3_hwio.h
+++ b/drivers/video/msm/mdss/mdp3_hwio.h
@@ -60,6 +60,9 @@
#define MDP3_REG_EBI2_LCD0 0x003c
#define MDP3_REG_EBI2_LCD0_YSTRIDE 0x0050
+/*clock control*/
+#define MDP3_REG_CGC_EN 0x0100
+
/*DMA_P*/
#define MDP3_REG_DMA_P_CONFIG 0x90000
#define MDP3_REG_DMA_P_SIZE 0x90004
diff --git a/drivers/video/msm/mdss/mdp3_ppp.c b/drivers/video/msm/mdss/mdp3_ppp.c
index 48ffb72..afb2eb4 100644
--- a/drivers/video/msm/mdss/mdp3_ppp.c
+++ b/drivers/video/msm/mdss/mdp3_ppp.c
@@ -898,8 +898,8 @@
struct blit_req_list *req;
int i, rc;
- req = mdp3_ppp_next_req(&ppp_stat->req_q);
mutex_lock(&ppp_stat->config_ppp_mutex);
+ req = mdp3_ppp_next_req(&ppp_stat->req_q);
mdp3_iommu_enable(MDP3_CLIENT_PPP);
mdp3_ppp_turnon(mfd, 1);
diff --git a/drivers/video/msm/mdss/mdp3_ppp_hwio.c b/drivers/video/msm/mdss/mdp3_ppp_hwio.c
index 309effc..8dd3d55 100644
--- a/drivers/video/msm/mdss/mdp3_ppp_hwio.c
+++ b/drivers/video/msm/mdss/mdp3_ppp_hwio.c
@@ -57,6 +57,8 @@
int64_t Od;
int64_t Odprime;
int64_t Oreq;
+ int64_t init_phase_temp;
+ int64_t delta;
uint32_t mult;
/*
@@ -149,7 +151,24 @@
Oreq = (Osprime & int_mask) - one;
/* calculate initial phase */
- init_phase = (int)((Osprime - Oreq) >> 4);
+ init_phase_temp = Osprime - Oreq;
+ delta = ((int64_t) (org) << PQF_PLUS_4) - Oreq;
+ init_phase_temp -= delta;
+
+ /* limit to valid range before the left shift */
+ delta = (init_phase_temp & (1LL << 63)) ?
+ 4 : -4;
+ delta <<= PQF_PLUS_4;
+ while (abs((int)(init_phase_temp >>
+ PQF_PLUS_4)) > 4)
+ init_phase_temp += delta;
+
+ /*
+ * right shift to account for extra bits of
+ * precision
+ */
+ init_phase = (int)(init_phase_temp >> 4);
+
}
} else {
/*
@@ -181,7 +200,18 @@
Oreq = (Osprime & int_mask) - one;
/* calculate initial phase */
- init_phase = (int)((Osprime - Oreq) >> 4);
+ init_phase_temp = Osprime - Oreq;
+ delta = ((int64_t) (org) << PQF_PLUS_4) - Oreq;
+ init_phase_temp -= delta;
+
+ /* limit to valid range before the left shift */
+ delta = (init_phase_temp & (1LL << 63)) ? 4 : -4;
+ delta <<= PQF_PLUS_4;
+ while (abs((int)(init_phase_temp >> PQF_PLUS_4)) > 4)
+ init_phase_temp += delta;
+
+ /* right shift to account for extra bits of precision */
+ init_phase = (int)(init_phase_temp >> 4);
}
}
@@ -331,13 +361,43 @@
return rgb;
}
-static uint8_t *mdp_adjust_rot_addr(struct ppp_blit_op *iBuf,
+uint8_t *mdp_bg_adjust_rot_addr(struct ppp_blit_op *iBuf,
+ uint8_t *addr, uint32_t bpp, uint32_t uv)
+{
+ uint32_t dest_ystride = iBuf->bg.prop.width * bpp;
+ uint32_t h_slice = 1, min_val;
+
+ if (uv && ((iBuf->bg.color_fmt == MDP_Y_CBCR_H2V2) ||
+ (iBuf->bg.color_fmt == MDP_Y_CRCB_H2V2)))
+ h_slice = 2;
+
+ if (((iBuf->mdp_op & MDPOP_ROT90) == MDPOP_ROT90) ^
+ ((iBuf->mdp_op & MDPOP_LR) == MDPOP_LR)) {
+ min_val = (iBuf->bg.roi.width + iBuf->bg.roi.x) % 16;
+ if (!min_val)
+ min_val = 16;
+ addr +=
+ (iBuf->bg.roi.width -
+ MIN(min_val, iBuf->bg.roi.width)) * bpp;
+ }
+ if ((iBuf->mdp_op & MDPOP_UD) == MDPOP_UD) {
+ min_val = (iBuf->bg.roi.height + iBuf->bg.roi.y) % 16;
+ if (!min_val)
+ min_val = 16;
+ addr +=
+ ((iBuf->bg.roi.height -
+ MIN(min_val, iBuf->bg.roi.height))/h_slice) *
+ dest_ystride;
+ }
+
+ return addr;
+}
+
+uint8_t *mdp_dst_adjust_rot_addr(struct ppp_blit_op *iBuf,
uint8_t *addr, uint32_t bpp, uint32_t uv)
{
uint32_t dest_ystride = iBuf->dst.prop.width * bpp;
uint32_t h_slice = 1;
- if (0)
- return 0;
if (uv && ((iBuf->dst.color_fmt == MDP_Y_CBCR_H2V2) ||
(iBuf->dst.color_fmt == MDP_Y_CRCB_H2V2)))
@@ -350,16 +410,10 @@
MIN(16, iBuf->dst.roi.width)) * bpp;
}
if ((iBuf->mdp_op & MDPOP_UD) == MDPOP_UD) {
- if (1) {
- addr +=
- ((iBuf->dst.roi.height -
- MIN(16, iBuf->dst.roi.height))/h_slice) *
- dest_ystride;
- } else {
- addr +=
- (iBuf->dst.roi.width -
- MIN(16, iBuf->dst.roi.width)) * bpp;
- }
+ addr +=
+ ((iBuf->dst.roi.height -
+ MIN(16, iBuf->dst.roi.height))/h_slice) *
+ dest_ystride;
}
return addr;
@@ -378,8 +432,10 @@
img->p0 += (x + y * ALIGN(width, 32)) * bpp;
else
img->p0 += (x + y * width) * bpp;
- if (layer != 0)
- img->p0 = mdp_adjust_rot_addr(blit_op, img->p0, bpp, 0);
+ if (layer == 1)
+ img->p0 = mdp_bg_adjust_rot_addr(blit_op, img->p0, bpp, 0);
+ else if (layer == 2)
+ img->p0 = mdp_dst_adjust_rot_addr(blit_op, img->p0, bpp, 0);
if (img->p1) {
/*
@@ -394,8 +450,14 @@
else
img->p1 += ((x / h_slice) * h_slice +
((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp;
- if (layer != 0)
- img->p1 = mdp_adjust_rot_addr(blit_op, img->p1, bpp, 1);
+
+ if (layer == 1) {
+ img->p0 = mdp_bg_adjust_rot_addr(blit_op,
+ img->p0, bpp, 0);
+ } else if (layer == 2) {
+ img->p0 = mdp_dst_adjust_rot_addr(blit_op,
+ img->p0, bpp, 0);
+ }
}
}
@@ -1044,7 +1106,6 @@
}
if (*pppop_reg_ptr & PPP_OP_BLEND_ON) {
- blit_op->bg = blit_op->dst;
config_ppp_background(&blit_op->bg);
if (blit_op->dst.color_fmt == MDP_YCRYCB_H2V1) {
@@ -1152,9 +1213,11 @@
blit_op->dst.p1 = NULL;
}
+ blit_op->bg = blit_op->dst;
/* Jumping from Y-Plane to Chroma Plane */
/* first pixel addr calculation */
mdp_adjust_start_addr(blit_op, &blit_op->src, sv_slice, sh_slice, 0);
+ mdp_adjust_start_addr(blit_op, &blit_op->bg, dv_slice, dh_slice, 1);
mdp_adjust_start_addr(blit_op, &blit_op->dst, dv_slice, dh_slice, 2);
config_ppp_scale(blit_op, &ppp_operation_reg);
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h
index 1311dab..840af17 100644
--- a/drivers/video/msm/mdss/mdss.h
+++ b/drivers/video/msm/mdss/mdss.h
@@ -73,6 +73,7 @@
u32 irq_buzy;
u32 has_bwc;
u32 has_decimation;
+ u8 has_wfd_blk;
u32 mdp_irq_mask;
u32 mdp_hist_irq_mask;
@@ -88,6 +89,7 @@
u32 smp_mb_cnt;
u32 smp_mb_size;
+ u32 smp_mb_per_pipe;
u32 rot_block_size;
@@ -121,6 +123,7 @@
struct early_suspend early_suspend;
void *debug_data;
int current_bus_idx;
+ bool mixer_switched;
};
extern struct mdss_data_type *mdss_res;
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index 366209b..44a5e62 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -360,13 +360,13 @@
__func__, rc);
goto error;
}
- mp->vreg_config[i].peak_current = val_array[i];
+ mp->vreg_config[i].enable_load = val_array[i];
pr_debug("%s: %s min=%d, max=%d, pc=%d\n", __func__,
mp->vreg_config[i].vreg_name,
mp->vreg_config[i].min_voltage,
mp->vreg_config[i].max_voltage,
- mp->vreg_config[i].peak_current);
+ mp->vreg_config[i].enable_load);
}
devm_kfree(dev, val_array);
@@ -416,6 +416,14 @@
mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ ret = mdss_dsi_enable_bus_clocks(ctrl_pdata);
+ if (ret) {
+ pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__,
+ ret);
+ mdss_dsi_panel_power_on(pdata, 0);
+ return ret;
+ }
+
/* disable DSI phy */
mdss_dsi_phy_enable(ctrl_pdata, 0);
@@ -479,6 +487,7 @@
mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base));
mdss_dsi_phy_init(pdata);
+ mdss_dsi_disable_bus_clocks(ctrl_pdata);
mdss_dsi_clk_ctrl(ctrl_pdata, 1);
@@ -593,6 +602,13 @@
}
mdss_dsi_op_mode_config(mipi->mode, pdata);
+ if (pdata->panel_info.type == MIPI_CMD_PANEL) {
+ if (mipi->vsync_enable && mipi->hw_vsync_mode
+ && gpio_is_valid(ctrl_pdata->disp_te_gpio)) {
+ mdss_dsi_set_tear_on(ctrl_pdata);
+ }
+ }
+
pr_debug("%s-:\n", __func__);
return ret;
@@ -601,6 +617,7 @@
static int mdss_dsi_blank(struct mdss_panel_data *pdata)
{
int ret = 0;
+ struct mipi_panel_info *mipi;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
pr_debug("%s+:\n", __func__);
@@ -612,9 +629,17 @@
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
+ mipi = &pdata->panel_info.mipi;
mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata);
+ if (pdata->panel_info.type == MIPI_CMD_PANEL) {
+ if (mipi->vsync_enable && mipi->hw_vsync_mode
+ && gpio_is_valid(ctrl_pdata->disp_te_gpio)) {
+ mdss_dsi_set_tear_off(ctrl_pdata);
+ }
+ }
+
if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
ret = ctrl_pdata->off(pdata);
if (ret) {
@@ -1143,15 +1168,6 @@
return rc;
}
- rc = mdss_dsi_enable_bus_clocks(ctrl_pdata);
- if (rc) {
- pr_err("%s: failed to enable bus clocks. rc=%d\n",
- __func__, rc);
- rc = mdss_dsi_panel_power_on(
- &(ctrl_pdata->panel_data), 0);
- return rc;
- }
-
mdss_dsi_clk_ctrl(ctrl_pdata, 1);
ctrl_pdata->ctrl_state |=
(CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE);
diff --git a/drivers/video/msm/mdss/mdss_dsi.h b/drivers/video/msm/mdss/mdss_dsi.h
index 965a23f..8a8e4ca 100644
--- a/drivers/video/msm/mdss/mdss_dsi.h
+++ b/drivers/video/msm/mdss/mdss_dsi.h
@@ -382,6 +382,8 @@
void mdss_dsi_host_init(struct mipi_panel_info *pinfo,
struct mdss_panel_data *pdata);
+void mdss_dsi_set_tear_on(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_set_tear_off(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_op_mode_config(int mode,
struct mdss_panel_data *pdata);
void mdss_dsi_cmd_mode_ctrl(int enable);
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index 72e3c64..e682e69 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -80,6 +80,7 @@
mutex_lock(&ctrl->mutex);
if (enable) {
if (ctrl->clk_cnt == 0) {
+ mdss_dsi_enable_bus_clocks(ctrl);
mdss_dsi_prepare_clocks(ctrl);
mdss_dsi_clk_enable(ctrl);
}
@@ -90,6 +91,7 @@
if (ctrl->clk_cnt == 0) {
mdss_dsi_clk_disable(ctrl);
mdss_dsi_unprepare_clocks(ctrl);
+ mdss_dsi_disable_bus_clocks(ctrl);
}
}
}
@@ -1098,6 +1100,40 @@
pr_debug("%s: BTA done, status = %d\n", __func__, status);
}
+static char set_tear_on[2] = {0x35, 0x00};
+static struct dsi_cmd_desc dsi_tear_on_cmd = {
+ {DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_tear_on)}, set_tear_on};
+
+static char set_tear_off[2] = {0x34, 0x00};
+static struct dsi_cmd_desc dsi_tear_off_cmd = {
+ {DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(set_tear_off)}, set_tear_off};
+
+void mdss_dsi_set_tear_on(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ struct dcs_cmd_req cmdreq;
+
+ cmdreq.cmds = &dsi_tear_on_cmd;
+ cmdreq.cmds_cnt = 1;
+ cmdreq.flags = CMD_REQ_COMMIT;
+ cmdreq.rlen = 0;
+ cmdreq.cb = NULL;
+
+ mdss_dsi_cmdlist_put(ctrl, &cmdreq);
+}
+
+void mdss_dsi_set_tear_off(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ struct dcs_cmd_req cmdreq;
+
+ cmdreq.cmds = &dsi_tear_off_cmd;
+ cmdreq.cmds_cnt = 1;
+ cmdreq.flags = CMD_REQ_COMMIT;
+ cmdreq.rlen = 0;
+ cmdreq.cb = NULL;
+
+ mdss_dsi_cmdlist_put(ctrl, &cmdreq);
+}
+
int mdss_dsi_cmd_reg_tx(u32 data,
unsigned char *ctrl_base)
{
@@ -1173,6 +1209,8 @@
{
u32 dsi_ctrl, data;
int video_mode;
+ u32 left_dsi_ctrl = 0;
+ bool left_ctrl_restore = false;
if (ctrl->shared_pdata.broadcast_enable) {
if (ctrl->ndx == DSI_CTRL_0) {
@@ -1185,13 +1223,15 @@
if (ctrl->shared_pdata.broadcast_enable) {
if ((ctrl->ndx == DSI_CTRL_1)
&& (left_ctrl_pdata != NULL)) {
- dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
+ left_dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
+ 0x0004);
- video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
+ video_mode =
+ left_dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
if (video_mode) {
- data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
+ data = left_dsi_ctrl | 0x04; /* CMD_MODE_EN */
MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
data);
+ left_ctrl_restore = true;
}
}
}
@@ -1210,6 +1250,10 @@
mdss_dsi_cmds2buf_tx(ctrl, cmds, cnt);
+ if (left_ctrl_restore)
+ MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
+ left_dsi_ctrl); /*restore */
+
if (video_mode)
MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
dsi_ctrl); /* restore */
@@ -1244,6 +1288,45 @@
struct dsi_buf *tp, *rp;
int no_max_pkt_size;
char cmd;
+ u32 dsi_ctrl, data;
+ int video_mode;
+ u32 left_dsi_ctrl = 0;
+ bool left_ctrl_restore = false;
+
+ if (ctrl->shared_pdata.broadcast_enable) {
+ if (ctrl->ndx == DSI_CTRL_0) {
+ pr_debug("%s: Broadcast mode. 1st ctrl\n",
+ __func__);
+ return 0;
+ }
+ }
+
+ if (ctrl->shared_pdata.broadcast_enable) {
+ if ((ctrl->ndx == DSI_CTRL_1)
+ && (left_ctrl_pdata != NULL)) {
+ left_dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
+ + 0x0004);
+ video_mode = left_dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
+ if (video_mode) {
+ data = left_dsi_ctrl | 0x04; /* CMD_MODE_EN */
+ MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
+ data);
+ left_ctrl_restore = true;
+ }
+ }
+ }
+
+ /* turn on cmd mode
+ * for video mode, do not send cmds more than
+ * one pixel line, since it only transmit it
+ * during BLLP.
+ */
+ dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004);
+ video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
+ if (video_mode) {
+ data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004, data);
+ }
no_max_pkt_size = rx_flags & CMD_REQ_NO_MAX_PKT_SIZE;
if (no_max_pkt_size)
@@ -1342,6 +1425,13 @@
break;
}
+ if (left_ctrl_restore)
+ MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
+ left_dsi_ctrl); /*restore */
+ if (video_mode)
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
+ dsi_ctrl); /* restore */
+
return rp->len;
}
@@ -1638,7 +1728,7 @@
if (status) {
MIPI_OUTP(base + 0x0068, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
@@ -1649,7 +1739,7 @@
status = MIPI_INP(base + 0x00c0);/* DSI_TIMEOUT_STATUS */
if (status & 0x0111) {
MIPI_OUTP(base + 0x00c0, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
@@ -1661,7 +1751,7 @@
if (status & 0x011111) {
MIPI_OUTP(base + 0x00b4, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
@@ -1673,7 +1763,7 @@
if (status & 0x44444489) {
MIPI_OUTP(base + 0x000c, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
@@ -1685,7 +1775,7 @@
if (status & 0x80000000) {
MIPI_OUTP(base + 0x0008, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
diff --git a/drivers/video/msm/mdss/mdss_hdmi_edid.c b/drivers/video/msm/mdss/mdss_hdmi_edid.c
index 1876057..9a5b20b 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_edid.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_edid.c
@@ -854,7 +854,8 @@
static void hdmi_edid_get_display_vsd_3d_mode(const u8 *data_buf,
struct hdmi_edid_sink_data *sink_data, u32 num_of_cea_blocks)
{
- u8 len, offset, present_multi_3d, hdmi_vic_len, hdmi_3d_len;
+ u8 len, offset, present_multi_3d, hdmi_vic_len;
+ int hdmi_3d_len;
u16 structure_all, structure_mask;
const u8 *vsd = num_of_cea_blocks ?
hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET,
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 287f2cd..e4a6b86 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -87,6 +87,9 @@
static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl);
static int hdmi_tx_enable_power(struct hdmi_tx_ctrl *hdmi_ctrl,
enum hdmi_tx_power_module_type module, int enable);
+static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl,
+ bool wait_audio_tx);
+static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl);
struct mdss_hw hdmi_tx_hw = {
.hw_ndx = MDSS_HW_HDMI,
@@ -1676,10 +1679,11 @@
} /* hdmi_tx_powerdown_phy */
static int hdmi_tx_audio_acr_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
- bool enabled, int num_of_channels)
+ bool enabled)
{
/* Read first before writing */
u32 acr_pck_ctrl_reg;
+ u32 sample_rate;
struct dss_io_data *io = NULL;
if (!hdmi_ctrl) {
@@ -1687,6 +1691,8 @@
return -EINVAL;
}
+ sample_rate = hdmi_ctrl->audio_data.sample_rate;
+
io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
if (!io->base) {
DEV_ERR("%s: core io not inititalized\n", __func__);
@@ -1721,18 +1727,19 @@
return -EPERM;
}
- n = audio_acr->lut[hdmi_ctrl->audio_sample_rate].n;
- cts = audio_acr->lut[hdmi_ctrl->audio_sample_rate].cts;
- layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1;
+ n = audio_acr->lut[sample_rate].n;
+ cts = audio_acr->lut[sample_rate].cts;
+ layout = (MSM_HDMI_AUDIO_CHANNEL_2 ==
+ hdmi_ctrl->audio_data.channel_num) ? 0 : 1;
if (
- (AUDIO_SAMPLE_RATE_192KHZ == hdmi_ctrl->audio_sample_rate) ||
- (AUDIO_SAMPLE_RATE_176_4KHZ == hdmi_ctrl->audio_sample_rate)) {
+ (AUDIO_SAMPLE_RATE_192KHZ == sample_rate) ||
+ (AUDIO_SAMPLE_RATE_176_4KHZ == sample_rate)) {
multiplier = 4;
n >>= 2; /* divide N by 4 and use multiplier */
} else if (
- (AUDIO_SAMPLE_RATE_96KHZ == hdmi_ctrl->audio_sample_rate) ||
- (AUDIO_SAMPLE_RATE_88_2KHZ == hdmi_ctrl->audio_sample_rate)) {
+ (AUDIO_SAMPLE_RATE_96KHZ == sample_rate) ||
+ (AUDIO_SAMPLE_RATE_88_2KHZ == sample_rate)) {
multiplier = 2;
n >>= 1; /* divide N by 2 and use multiplier */
} else {
@@ -1743,12 +1750,16 @@
/* AUDIO_PRIORITY | SOURCE */
acr_pck_ctrl_reg |= 0x80000100;
+
+ /* Reset multiplier bits */
+ acr_pck_ctrl_reg &= ~(7 << 16);
+
/* N_MULTIPLE(multiplier) */
acr_pck_ctrl_reg |= (multiplier & 7) << 16;
- if ((AUDIO_SAMPLE_RATE_48KHZ == hdmi_ctrl->audio_sample_rate) ||
- (AUDIO_SAMPLE_RATE_96KHZ == hdmi_ctrl->audio_sample_rate) ||
- (AUDIO_SAMPLE_RATE_192KHZ == hdmi_ctrl->audio_sample_rate)) {
+ if ((AUDIO_SAMPLE_RATE_48KHZ == sample_rate) ||
+ (AUDIO_SAMPLE_RATE_96KHZ == sample_rate) ||
+ (AUDIO_SAMPLE_RATE_192KHZ == sample_rate)) {
/* SELECT(3) */
acr_pck_ctrl_reg |= 3 << 4;
/* CTS_48 */
@@ -1759,9 +1770,9 @@
/* N */
DSS_REG_W(io, HDMI_ACR_48_1, n);
} else if (
- (AUDIO_SAMPLE_RATE_44_1KHZ == hdmi_ctrl->audio_sample_rate) ||
- (AUDIO_SAMPLE_RATE_88_2KHZ == hdmi_ctrl->audio_sample_rate) ||
- (AUDIO_SAMPLE_RATE_176_4KHZ == hdmi_ctrl->audio_sample_rate)) {
+ (AUDIO_SAMPLE_RATE_44_1KHZ == sample_rate) ||
+ (AUDIO_SAMPLE_RATE_88_2KHZ == sample_rate) ||
+ (AUDIO_SAMPLE_RATE_176_4KHZ == sample_rate)) {
/* SELECT(2) */
acr_pck_ctrl_reg |= 2 << 4;
/* CTS_44 */
@@ -1800,12 +1811,15 @@
} /* hdmi_tx_audio_acr_setup */
static int hdmi_tx_audio_iframe_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
- bool enabled, u32 num_of_channels, u32 channel_allocation,
- u32 level_shift, bool down_mix)
+ bool enabled)
{
struct dss_io_data *io = NULL;
u32 channel_count = 1; /* Def to 2 channels -> Table 17 in CEA-D */
+ u32 num_of_channels;
+ u32 channel_allocation;
+ u32 level_shift;
+ u32 down_mix;
u32 check_sum, audio_info_0_reg, audio_info_1_reg;
u32 audio_info_ctrl_reg;
u32 aud_pck_ctrl_2_reg;
@@ -1816,6 +1830,11 @@
return -EINVAL;
}
+ num_of_channels = hdmi_ctrl->audio_data.channel_num;
+ channel_allocation = hdmi_ctrl->audio_data.spkr_alloc;
+ level_shift = hdmi_ctrl->audio_data.level_shift;
+ down_mix = hdmi_ctrl->audio_data.down_mix;
+
io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
if (!io->base) {
DEV_ERR("%s: core io not inititalized\n", __func__);
@@ -1926,8 +1945,8 @@
} /* hdmi_tx_audio_iframe_setup */
static int hdmi_tx_audio_info_setup(struct platform_device *pdev,
- u32 num_of_channels, u32 channel_allocation, u32 level_shift,
- bool down_mix)
+ u32 sample_rate, u32 num_of_channels, u32 channel_allocation,
+ u32 level_shift, bool down_mix)
{
int rc = 0;
struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev);
@@ -1937,10 +1956,31 @@
return -ENODEV;
}
- if (hdmi_ctrl->panel_power_on) {
- rc = hdmi_tx_audio_iframe_setup(hdmi_ctrl, true,
- num_of_channels, channel_allocation, level_shift,
- down_mix);
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) && hdmi_ctrl->panel_power_on) {
+
+ /* Map given sample rate to Enum */
+ if (sample_rate == 32000)
+ sample_rate = AUDIO_SAMPLE_RATE_32KHZ;
+ else if (sample_rate == 44100)
+ sample_rate = AUDIO_SAMPLE_RATE_44_1KHZ;
+ else if (sample_rate == 48000)
+ sample_rate = AUDIO_SAMPLE_RATE_48KHZ;
+ else if (sample_rate == 88200)
+ sample_rate = AUDIO_SAMPLE_RATE_88_2KHZ;
+ else if (sample_rate == 96000)
+ sample_rate = AUDIO_SAMPLE_RATE_96KHZ;
+ else if (sample_rate == 176400)
+ sample_rate = AUDIO_SAMPLE_RATE_176_4KHZ;
+ else if (sample_rate == 192000)
+ sample_rate = AUDIO_SAMPLE_RATE_192KHZ;
+
+ hdmi_ctrl->audio_data.sample_rate = sample_rate;
+ hdmi_ctrl->audio_data.channel_num = num_of_channels;
+ hdmi_ctrl->audio_data.spkr_alloc = channel_allocation;
+ hdmi_ctrl->audio_data.level_shift = level_shift;
+ hdmi_ctrl->audio_data.down_mix = down_mix;
+
+ rc = hdmi_tx_audio_setup(hdmi_ctrl);
if (rc)
DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed.rc=%d\n",
__func__, rc);
@@ -2047,7 +2087,6 @@
static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
{
int rc = 0;
- const int channels = MSM_HDMI_AUDIO_CHANNEL_2;
struct dss_io_data *io = NULL;
if (!hdmi_ctrl) {
@@ -2061,14 +2100,14 @@
return -EINVAL;
}
- rc = hdmi_tx_audio_acr_setup(hdmi_ctrl, true, channels);
+ rc = hdmi_tx_audio_acr_setup(hdmi_ctrl, true);
if (rc) {
DEV_ERR("%s: hdmi_tx_audio_acr_setup failed. rc=%d\n",
__func__, rc);
return rc;
}
- rc = hdmi_tx_audio_iframe_setup(hdmi_ctrl, true, channels, 0, 0, false);
+ rc = hdmi_tx_audio_iframe_setup(hdmi_ctrl, true);
if (rc) {
DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed. rc=%d\n",
__func__, rc);
@@ -2080,9 +2119,10 @@
return 0;
} /* hdmi_tx_audio_setup */
-static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl)
+static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl,
+ bool wait_audio_tx)
{
- u32 i, status, sleep_us, timeout_us, timeout_sec = 15;
+ u32 i = 0, status, sleep_us, timeout_us, timeout_sec = 15;
struct dss_io_data *io = NULL;
if (!hdmi_ctrl) {
@@ -2096,33 +2136,43 @@
return;
}
- /* Check if audio engine is turned off by QDSP or not */
- /* send off notification after every 1 sec for 15 seconds */
- for (i = 0; i < timeout_sec; i++) {
- sleep_us = 5000; /* Maximum time to sleep between two reads */
- timeout_us = 1000 * 1000; /* Total time for condition to meet */
+ if (wait_audio_tx) {
+ /* Check if audio engine is turned off by QDSP or not */
+ /* send off notification after every 1 sec for 15 seconds */
+ for (i = 0; i < timeout_sec; i++) {
+ /* Maximum time to sleep between two reads */
+ sleep_us = 5000;
+ /* Total time for condition to meet */
+ timeout_us = 1000 * 1000;
- if (readl_poll_timeout((io->base + HDMI_AUDIO_CFG),
- status, ((status & BIT(0)) == 0),
- sleep_us, timeout_us)) {
+ if (readl_poll_timeout((io->base + HDMI_AUDIO_CFG),
+ status, ((status & BIT(0)) == 0),
+ sleep_us, timeout_us)) {
- DEV_ERR("%s: audio still on after %d sec. try again\n",
+ DEV_ERR(
+ "%s: audio still on after %d sec. try again\n",
__func__, i+1);
- hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, true);
- continue;
+ hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0,
+ true);
+ continue;
+ }
+ break;
}
- break;
}
+
if (i == timeout_sec)
DEV_ERR("%s: Error: cannot turn off audio engine\n", __func__);
- if (hdmi_tx_audio_iframe_setup(hdmi_ctrl, false, 0, 0, 0, false))
+ if (hdmi_tx_audio_iframe_setup(hdmi_ctrl, false))
DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed.\n", __func__);
- if (hdmi_tx_audio_acr_setup(hdmi_ctrl, false, 0))
+ if (hdmi_tx_audio_acr_setup(hdmi_ctrl, false))
DEV_ERR("%s: hdmi_tx_audio_acr_setup failed.\n", __func__);
+ hdmi_ctrl->audio_data.sample_rate = AUDIO_SAMPLE_RATE_48KHZ;
+ hdmi_ctrl->audio_data.channel_num = MSM_HDMI_AUDIO_CHANNEL_2;
+
DEV_INFO("HDMI Audio: Disabled\n");
} /* hdmi_tx_audio_off */
@@ -2239,7 +2289,7 @@
if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) {
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false);
- hdmi_tx_audio_off(hdmi_ctrl);
+ hdmi_tx_audio_off(hdmi_ctrl, true);
}
hdmi_tx_powerdown_phy(hdmi_ctrl);
@@ -2250,6 +2300,8 @@
*/
hdmi_ctrl->pdata.power_data[HDMI_TX_CORE_PM].clk_config[0].rate = 0;
+ hdmi_cec_deconfig(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]);
+
hdmi_tx_core_off(hdmi_ctrl);
if (hdmi_ctrl->hpd_off_pending) {
@@ -2257,8 +2309,6 @@
hdmi_ctrl->hpd_off_pending = false;
}
- hdmi_cec_deconfig(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]);
-
mutex_lock(&hdmi_ctrl->mutex);
hdmi_ctrl->panel_power_on = false;
mutex_unlock(&hdmi_ctrl->mutex);
@@ -2656,7 +2706,8 @@
INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work);
- hdmi_ctrl->audio_sample_rate = AUDIO_SAMPLE_RATE_48KHZ;
+ hdmi_ctrl->audio_data.sample_rate = AUDIO_SAMPLE_RATE_48KHZ;
+ hdmi_ctrl->audio_data.channel_num = MSM_HDMI_AUDIO_CHANNEL_2;
hdmi_ctrl->sdev.name = "hdmi";
if (switch_dev_register(&hdmi_ctrl->sdev) < 0) {
@@ -3216,13 +3267,13 @@
__func__, hdmi_tx_pm_name(module_type), rc);
goto error;
}
- mp->vreg_config[j].peak_current = val_array[i];
+ mp->vreg_config[j].enable_load = val_array[i];
DEV_DBG("%s: %s min=%d, max=%d, pc=%d\n", __func__,
mp->vreg_config[j].vreg_name,
mp->vreg_config[j].min_voltage,
mp->vreg_config[j].max_voltage,
- mp->vreg_config[j].peak_current);
+ mp->vreg_config[j].enable_load);
ndx_mask >>= 1;
j++;
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index d4f8e67..18ee782 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -38,12 +38,20 @@
struct dss_module_power power_data[HDMI_TX_MAX_PM];
};
+struct hdmi_audio {
+ int sample_rate;
+ int channel_num;
+ int spkr_alloc;
+ int level_shift;
+ int down_mix;
+};
+
struct hdmi_tx_ctrl {
struct platform_device *pdev;
struct hdmi_tx_platform_data pdata;
struct mdss_panel_data panel_data;
- int audio_sample_rate;
+ struct hdmi_audio audio_data;
struct mutex mutex;
struct kobject *kobj;
diff --git a/drivers/video/msm/mdss/mdss_io_util.c b/drivers/video/msm/mdss/mdss_io_util.c
index 809db43..c862e78 100644
--- a/drivers/video/msm/mdss/mdss_io_util.c
+++ b/drivers/video/msm/mdss/mdss_io_util.c
@@ -13,6 +13,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/delay.h>
#include "mdss_io_util.h"
#define MAX_I2C_CMDS 16
@@ -160,19 +161,6 @@
curr_vreg->vreg_name);
goto vreg_set_voltage_fail;
}
- if (curr_vreg->peak_current >= 0) {
- rc = regulator_set_optimum_mode(
- curr_vreg->vreg,
- curr_vreg->peak_current);
- if (rc < 0) {
- DEV_ERR(
- "%pS->%s: %s set opt m fail\n",
- __builtin_return_address(0),
- __func__,
- curr_vreg->vreg_name);
- goto vreg_set_opt_mode_fail;
- }
- }
}
}
} else {
@@ -183,10 +171,6 @@
curr_vreg->vreg) > 0)
? DSS_REG_LDO : DSS_REG_VS;
if (type == DSS_REG_LDO) {
- if (curr_vreg->peak_current >= 0) {
- regulator_set_optimum_mode(
- curr_vreg->vreg, 0);
- }
regulator_set_voltage(curr_vreg->vreg,
0, curr_vreg->max_voltage);
}
@@ -201,10 +185,6 @@
if (type == DSS_REG_LDO)
regulator_set_optimum_mode(curr_vreg->vreg, 0);
-vreg_set_opt_mode_fail:
-if (type == DSS_REG_LDO)
- regulator_set_voltage(curr_vreg->vreg, 0, curr_vreg->max_voltage);
-
vreg_set_voltage_fail:
regulator_put(curr_vreg->vreg);
curr_vreg->vreg = NULL;
@@ -229,9 +209,19 @@
DEV_ERR("%pS->%s: %s regulator error. rc=%d\n",
__builtin_return_address(0), __func__,
in_vreg[i].vreg_name, rc);
- goto disable_vreg;
+ goto vreg_set_opt_mode_fail;
+ }
+ msleep(in_vreg[i].pre_on_sleep);
+ rc = regulator_set_optimum_mode(in_vreg[i].vreg,
+ in_vreg[i].enable_load);
+ if (rc < 0) {
+ DEV_ERR("%pS->%s: %s set opt m fail\n",
+ __builtin_return_address(0), __func__,
+ in_vreg[i].vreg_name);
+ goto vreg_set_opt_mode_fail;
}
rc = regulator_enable(in_vreg[i].vreg);
+ msleep(in_vreg[i].post_on_sleep);
if (rc < 0) {
DEV_ERR("%pS->%s: %s enable failed\n",
__builtin_return_address(0), __func__,
@@ -241,14 +231,28 @@
}
} else {
for (i = num_vreg-1; i >= 0; i--)
- if (regulator_is_enabled(in_vreg[i].vreg))
+ if (regulator_is_enabled(in_vreg[i].vreg)) {
+ msleep(in_vreg[i].pre_off_sleep);
+ regulator_set_optimum_mode(in_vreg[i].vreg,
+ in_vreg[i].disable_load);
regulator_disable(in_vreg[i].vreg);
+ msleep(in_vreg[i].post_off_sleep);
+ }
}
return rc;
disable_vreg:
- for (i--; i >= 0; i--)
+ regulator_set_optimum_mode(in_vreg[i].vreg, in_vreg[i].disable_load);
+
+vreg_set_opt_mode_fail:
+ for (i--; i >= 0; i--) {
+ msleep(in_vreg[i].pre_off_sleep);
+ regulator_set_optimum_mode(in_vreg[i].vreg,
+ in_vreg[i].disable_load);
regulator_disable(in_vreg[i].vreg);
+ msleep(in_vreg[i].post_off_sleep);
+ }
+
return rc;
} /* msm_dss_enable_vreg */
diff --git a/drivers/video/msm/mdss/mdss_io_util.h b/drivers/video/msm/mdss/mdss_io_util.h
index 23341d6..6ad21e8 100644
--- a/drivers/video/msm/mdss/mdss_io_util.h
+++ b/drivers/video/msm/mdss/mdss_io_util.h
@@ -52,7 +52,12 @@
char vreg_name[32];
int min_voltage;
int max_voltage;
- int peak_current;
+ int enable_load;
+ int disable_load;
+ int pre_on_sleep;
+ int post_on_sleep;
+ int pre_off_sleep;
+ int post_off_sleep;
};
struct dss_gpio {
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index c4bf67e..0a41ef8 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -68,6 +68,10 @@
.fb_stride = mdss_mdp_fb_stride,
};
+#define DEFAULT_TOTAL_RGB_PIPES 3
+#define DEFAULT_TOTAL_VIG_PIPES 3
+#define DEFAULT_TOTAL_DMA_PIPES 2
+
#define IB_QUOTA 800000000
#define AB_QUOTA 800000000
@@ -703,13 +707,6 @@
return 0;
}
-static int mdss_iommu_fault_handler(struct iommu_domain *domain,
- struct device *dev, unsigned long iova, int flags, void *token)
-{
- pr_err("MDP IOMMU page fault: iova 0x%lx\n", iova);
- return 0;
-}
-
int mdss_iommu_attach(struct mdss_data_type *mdata)
{
struct iommu_domain *domain;
@@ -796,7 +793,6 @@
iomap->domain_idx);
return -EINVAL;
}
- iommu_set_fault_handler(domain, mdss_iommu_fault_handler, NULL);
iomap->ctx = msm_iommu_get_ctx(iomap->ctx_name);
if (!iomap->ctx) {
@@ -1154,9 +1150,9 @@
static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev)
{
- u32 npipes, off;
+ u32 npipes, dma_off;
int rc = 0;
- u32 nids = 0;
+ u32 nids = 0, setup_cnt = 0, len;
u32 *offsets = NULL, *ftch_id = NULL;
struct mdss_data_type *mdata = platform_get_drvdata(pdev);
@@ -1184,17 +1180,41 @@
offsets = kzalloc(sizeof(u32) * npipes, GFP_KERNEL);
if (!offsets) {
- pr_err("no mem assigned: kzalloc fail\n");
+ pr_err("no mem assigned for offsets: kzalloc fail\n");
return -ENOMEM;
}
ftch_id = kzalloc(sizeof(u32) * nids, GFP_KERNEL);
if (!ftch_id) {
- pr_err("no mem assigned: kzalloc fail\n");
+ pr_err("no mem assigned for ftch_id: kzalloc fail\n");
rc = -ENOMEM;
goto ftch_alloc_fail;
}
+ mdata->vig_pipes = devm_kzalloc(&mdata->pdev->dev,
+ sizeof(struct mdss_mdp_pipe) * mdata->nvig_pipes, GFP_KERNEL);
+ if (!mdata->vig_pipes) {
+ pr_err("no mem for vig_pipes: kzalloc fail\n");
+ rc = -ENOMEM;
+ goto vig_alloc_fail;
+ }
+
+ mdata->rgb_pipes = devm_kzalloc(&mdata->pdev->dev,
+ sizeof(struct mdss_mdp_pipe) * mdata->nrgb_pipes, GFP_KERNEL);
+ if (!mdata->rgb_pipes) {
+ pr_err("no mem for rgb_pipes: kzalloc fail\n");
+ rc = -ENOMEM;
+ goto rgb_alloc_fail;
+ }
+
+ mdata->dma_pipes = devm_kzalloc(&mdata->pdev->dev,
+ sizeof(struct mdss_mdp_pipe) * mdata->ndma_pipes, GFP_KERNEL);
+ if (!mdata->dma_pipes) {
+ pr_err("no mem for dma_pipes: kzalloc fail\n");
+ rc = -ENOMEM;
+ goto dma_alloc_fail;
+ }
+
rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-vig-fetch-id",
ftch_id, mdata->nvig_pipes);
if (rc)
@@ -1203,47 +1223,91 @@
rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-vig-off",
offsets, mdata->nvig_pipes);
if (rc)
- goto parse_done;
+ goto parse_fail;
- rc = mdss_mdp_pipe_addr_setup(mdata, offsets, ftch_id,
- MDSS_MDP_PIPE_TYPE_VIG, MDSS_MDP_SSPP_VIG0, mdata->nvig_pipes);
+ len = min_t(int, DEFAULT_TOTAL_VIG_PIPES, (int)mdata->nvig_pipes);
+ rc = mdss_mdp_pipe_addr_setup(mdata, mdata->vig_pipes, offsets, ftch_id,
+ MDSS_MDP_PIPE_TYPE_VIG, MDSS_MDP_SSPP_VIG0, len);
if (rc)
- goto parse_done;
+ goto parse_fail;
+
+ setup_cnt += len;
rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-rgb-fetch-id",
ftch_id + mdata->nvig_pipes, mdata->nrgb_pipes);
if (rc)
- goto parse_done;
+ goto parse_fail;
rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-rgb-off",
offsets + mdata->nvig_pipes, mdata->nrgb_pipes);
if (rc)
- goto parse_done;
+ goto parse_fail;
- rc = mdss_mdp_pipe_addr_setup(mdata, offsets + mdata->nvig_pipes,
- ftch_id + mdata->nvig_pipes, MDSS_MDP_PIPE_TYPE_RGB,
- MDSS_MDP_SSPP_RGB0, mdata->nrgb_pipes);
+ len = min_t(int, DEFAULT_TOTAL_RGB_PIPES, (int)mdata->nrgb_pipes);
+ rc = mdss_mdp_pipe_addr_setup(mdata, mdata->rgb_pipes,
+ offsets + mdata->nvig_pipes, ftch_id + mdata->nvig_pipes,
+ MDSS_MDP_PIPE_TYPE_RGB, MDSS_MDP_SSPP_RGB0, len);
if (rc)
- goto parse_done;
+ goto parse_fail;
- off = mdata->nvig_pipes + mdata->nrgb_pipes;
+ setup_cnt += len;
+ dma_off = mdata->nvig_pipes + mdata->nrgb_pipes;
rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-dma-fetch-id",
- ftch_id + off, mdata->ndma_pipes);
+ ftch_id + dma_off, mdata->ndma_pipes);
if (rc)
- goto parse_done;
+ goto parse_fail;
rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-dma-off",
- offsets + off, mdata->ndma_pipes);
+ offsets + dma_off, mdata->ndma_pipes);
if (rc)
- goto parse_done;
+ goto parse_fail;
- rc = mdss_mdp_pipe_addr_setup(mdata, offsets + off, ftch_id + off,
- MDSS_MDP_PIPE_TYPE_DMA, MDSS_MDP_SSPP_DMA0, mdata->ndma_pipes);
+ len = mdata->ndma_pipes;
+ rc = mdss_mdp_pipe_addr_setup(mdata, mdata->dma_pipes,
+ offsets + dma_off, ftch_id + dma_off, MDSS_MDP_PIPE_TYPE_DMA,
+ MDSS_MDP_SSPP_DMA0, len);
if (rc)
- goto parse_done;
+ goto parse_fail;
+ setup_cnt += len;
+
+ if (mdata->nvig_pipes > DEFAULT_TOTAL_VIG_PIPES) {
+ rc = mdss_mdp_pipe_addr_setup(mdata,
+ mdata->vig_pipes + DEFAULT_TOTAL_VIG_PIPES,
+ offsets + DEFAULT_TOTAL_VIG_PIPES,
+ ftch_id + DEFAULT_TOTAL_VIG_PIPES,
+ MDSS_MDP_PIPE_TYPE_VIG, setup_cnt,
+ mdata->nvig_pipes - DEFAULT_TOTAL_VIG_PIPES);
+ if (rc)
+ goto parse_fail;
+
+ setup_cnt += mdata->nvig_pipes - DEFAULT_TOTAL_VIG_PIPES;
+ }
+
+ if (mdata->nrgb_pipes > DEFAULT_TOTAL_RGB_PIPES) {
+ rc = mdss_mdp_pipe_addr_setup(mdata,
+ mdata->rgb_pipes + DEFAULT_TOTAL_RGB_PIPES,
+ offsets + mdata->nvig_pipes + DEFAULT_TOTAL_RGB_PIPES,
+ ftch_id + mdata->nvig_pipes + DEFAULT_TOTAL_RGB_PIPES,
+ MDSS_MDP_PIPE_TYPE_RGB, setup_cnt,
+ mdata->nrgb_pipes - DEFAULT_TOTAL_RGB_PIPES);
+ if (rc)
+ goto parse_fail;
+
+ setup_cnt += mdata->nrgb_pipes - DEFAULT_TOTAL_RGB_PIPES;
+ }
+
+ goto parse_done;
+
+parse_fail:
+ kfree(mdata->dma_pipes);
+dma_alloc_fail:
+ kfree(mdata->rgb_pipes);
+rgb_alloc_fail:
+ kfree(mdata->vig_pipes);
parse_done:
+vig_alloc_fail:
kfree(ftch_id);
ftch_alloc_fail:
kfree(offsets);
@@ -1447,10 +1511,16 @@
rc = mdss_mdp_smp_setup(mdata, data[0], data[1]);
- if (rc)
+ if (rc) {
pr_err("unable to setup smp data\n");
+ return rc;
+ }
- return rc;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-smp-mb-per-pipe", data);
+ mdata->smp_mb_per_pipe = (!rc ? data[0] : 0);
+
+ return 0;
}
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev)
@@ -1467,6 +1537,8 @@
"qcom,mdss-has-bwc");
mdata->has_decimation = of_property_read_bool(pdev->dev.of_node,
"qcom,mdss-has-decimation");
+ mdata->has_wfd_blk = of_property_read_bool(pdev->dev.of_node,
+ "qcom,mdss-has-wfd-blk");
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index ab4ae4b..950fd27 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -118,6 +118,11 @@
struct list_head list;
};
+enum mdss_mdp_wb_ctl_type {
+ MDSS_MDP_WB_CTL_TYPE_BLOCK = 1,
+ MDSS_MDP_WB_CTL_TYPE_LINE
+};
+
struct mdss_mdp_ctl {
u32 num;
char __iomem *base;
@@ -151,6 +156,7 @@
struct mdss_mdp_mixer *mixer_left;
struct mdss_mdp_mixer *mixer_right;
struct mutex lock;
+ struct mutex *shared_lock;
struct mdss_panel_data *panel_data;
struct mdss_mdp_vsync_handler vsync_handler;
@@ -168,6 +174,7 @@
struct mdss_mdp_vsync_handler *);
void *priv_data;
+ u32 wb_type;
};
struct mdss_mdp_mixer {
@@ -294,6 +301,11 @@
struct pp_sts_type pp_sts;
};
+struct mdss_mdp_pipe_smp_map {
+ DECLARE_BITMAP(reserved, MDSS_MDP_SMP_MMB_BLOCKS);
+ DECLARE_BITMAP(allocated, MDSS_MDP_SMP_MMB_BLOCKS);
+};
+
struct mdss_mdp_pipe {
u32 num;
u32 type;
@@ -312,6 +324,8 @@
u8 vert_deci;
struct mdss_mdp_img_rect src;
struct mdss_mdp_img_rect dst;
+ u32 phase_step_x;
+ u32 phase_step_y;
struct mdss_mdp_format_params *src_fmt;
struct mdss_mdp_plane_sizes src_planes;
@@ -328,8 +342,7 @@
struct mdp_overlay req_data;
u32 params_changed;
- unsigned long smp[MAX_PLANES];
- unsigned long smp_reserved[MAX_PLANES];
+ struct mdss_mdp_pipe_smp_map smp_map[MAX_PLANES];
struct mdss_mdp_data back_buf;
struct mdss_mdp_data front_buf;
@@ -428,6 +441,7 @@
struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata,
struct msm_fb_data_type *mfd);
int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl);
int mdss_mdp_video_copy_splash_screen(struct mdss_panel_data *pdata);
void mdss_mdp_ctl_splash_start(struct mdss_panel_data *pdata);
int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl);
@@ -522,8 +536,9 @@
void mdss_mdp_smp_unreserve(struct mdss_mdp_pipe *pipe);
void mdss_mdp_smp_release(struct mdss_mdp_pipe *pipe);
-int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata, u32 *offsets,
- u32 *ftch_y_id, u32 type, u32 num_base, u32 len);
+int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata,
+ struct mdss_mdp_pipe *head, u32 *offsets, u32 *ftch_y_id, u32 type,
+ u32 num_base, u32 len);
int mdss_mdp_mixer_addr_setup(struct mdss_data_type *mdata, u32 *mixer_offsets,
u32 *dspp_offsets, u32 *pingpong_offsets, u32 type, u32 len);
int mdss_mdp_ctl_addr_setup(struct mdss_data_type *mdata, u32 *ctl_offsets,
@@ -545,6 +560,7 @@
int mdss_mdp_put_img(struct mdss_mdp_img_data *data);
int mdss_mdp_get_img(struct msmfb_data *img, struct mdss_mdp_img_data *data);
u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd);
+int mdss_mdp_calc_phase_step(u32 src, u32 dst, u32 *out_phase);
int mdss_mdp_wb_kickoff(struct msm_fb_data_type *mfd);
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
@@ -555,8 +571,13 @@
int mdss_panel_register_done(struct mdss_panel_data *pdata);
int mdss_mdp_limited_lut_igc_config(struct mdss_mdp_ctl *ctl);
int mdss_mdp_calib_config(struct mdp_calib_config_data *cfg, u32 *copyback);
+int mdss_mdp_calib_config_buffer(struct mdp_calib_config_buffer *cfg,
+ u32 *copyback);
int mdss_mdp_pipe_is_staged(struct mdss_mdp_pipe *pipe);
+int mdss_mdp_writeback_display_commit(struct mdss_mdp_ctl *ctl, void *arg);
+struct mdss_mdp_ctl *mdss_mdp_ctl_mixer_switch(struct mdss_mdp_ctl *ctl,
+ u32 return_type);
#define mfd_to_mdp5_data(mfd) (mfd->mdp.private1)
#define mfd_to_mdata(mfd) (((struct mdss_overlay_private *)\
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 9eaff61..f6225ab 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -299,9 +299,13 @@
{
struct mdss_mdp_ctl *ctl = NULL;
u32 cnum;
+ u32 nctl = mdata->nctl;
mutex_lock(&mdss_mdp_ctl_lock);
- for (cnum = off; cnum < mdata->nctl; cnum++) {
+ if (!mdata->has_wfd_blk)
+ nctl++;
+
+ for (cnum = off; cnum < nctl; cnum++) {
ctl = mdata->ctl_off + cnum;
if (ctl->ref_cnt == 0) {
ctl->ref_cnt++;
@@ -399,13 +403,18 @@
(mixer_pool[MDSS_MDP_INTF_LAYERMIXER2].ref_cnt == 0))
mixer_pool += MDSS_MDP_INTF_LAYERMIXER2;
+ /*Allocate virtual wb mixer if no dedicated wfd wb blk is present*/
+ if (!ctl->mdata->has_wfd_blk && (type == MDSS_MDP_MIXER_TYPE_WRITEBACK))
+ nmixers += 1;
+
for (i = 0; i < nmixers; i++) {
mixer = mixer_pool + i;
if (mixer->ref_cnt == 0) {
mixer->ref_cnt++;
mixer->params_changed++;
mixer->ctl = ctl;
- pr_debug("alloc mixer num%d\n", mixer->num);
+ pr_debug("alloc mixer num %d for ctl=%d\n",
+ mixer->num, ctl->num);
break;
}
mixer = NULL;
@@ -440,12 +449,16 @@
struct mdss_mdp_mixer *mixer = NULL;
ctl = mdss_mdp_ctl_alloc(mdss_res, mdss_res->nmixers_intf);
- if (!ctl)
+ if (!ctl) {
+ pr_err("unable to allocate wb ctl\n");
return NULL;
+ }
mixer = mdss_mdp_mixer_alloc(ctl, MDSS_MDP_MIXER_TYPE_WRITEBACK, false);
- if (!mixer)
+ if (!mixer) {
+ pr_err("unable to allocate wb mixer\n");
goto error;
+ }
mixer->rotator_mode = rotator;
@@ -467,6 +480,9 @@
ctl->start_fnc = mdss_mdp_writeback_start;
ctl->power_on = true;
+ ctl->wb_type = (rotator ? MDSS_MDP_WB_CTL_TYPE_BLOCK :
+ MDSS_MDP_WB_CTL_TYPE_LINE);
+ mixer->ctl = ctl;
if (ctl->start_fnc)
ctl->start_fnc(ctl);
@@ -517,6 +533,7 @@
case MIPI_VIDEO_PANEL:
return mdss_mdp_video_reconfigure_splash_done(ctl);
case MIPI_CMD_PANEL:
+ return mdss_mdp_cmd_reconfigure_splash_done(ctl);
default:
return 0;
}
@@ -704,6 +721,7 @@
mdss_mdp_mixer_free(mixer);
return -EINVAL;
}
+ ctl->wb_type = MDSS_MDP_WB_CTL_TYPE_LINE;
}
ctl->mixer_left = mixer;
@@ -1118,7 +1136,13 @@
if (pipe == NULL) {
mixercfg = MDSS_MDP_LM_BORDER_COLOR;
} else {
- mixercfg = 1 << (3 * pipe->num);
+ if (pipe->num == MDSS_MDP_SSPP_VIG3 ||
+ pipe->num == MDSS_MDP_SSPP_RGB3) {
+ /* Add 2 to account for Cursor & Border bits */
+ mixercfg = 1 << ((3 * pipe->num)+2);
+ } else {
+ mixercfg = 1 << (3 * pipe->num);
+ }
if (pipe->src_fmt->alpha_enable)
bgalpha = 1;
secure = pipe->flags & MDP_SECURE_OVERLAY_SESSION;
@@ -1186,7 +1210,10 @@
pr_debug("mixer=%d mixer_cfg=%x\n", mixer->num, mixercfg);
- ctl->flush_bits |= BIT(6) << mixer->num; /* LAYER_MIXER */
+ if (mixer->num == MDSS_MDP_INTF_LAYERMIXER3)
+ ctl->flush_bits |= BIT(20);
+ else
+ ctl->flush_bits |= BIT(6) << mixer->num;
mdp_mixer_write(mixer, MDSS_MDP_REG_LM_OP_MODE, blend_color_out);
off = __mdss_mdp_ctl_get_mixer_off(mixer);
@@ -1202,9 +1229,13 @@
struct mdss_mdp_mixer *head;
u32 i;
int rc = 0;
+ u32 size = len;
+
+ if ((type == MDSS_MDP_MIXER_TYPE_WRITEBACK) && !mdata->has_wfd_blk)
+ size++;
head = devm_kzalloc(&mdata->pdev->dev, sizeof(struct mdss_mdp_mixer) *
- len, GFP_KERNEL);
+ size, GFP_KERNEL);
if (!head) {
pr_err("unable to setup mixer type=%d :kzalloc fail\n",
@@ -1224,6 +1255,13 @@
}
}
+ /*
+ * Duplicate the last writeback mixer for concurrent line and block mode
+ * operations
+ */
+ if ((type == MDSS_MDP_MIXER_TYPE_WRITEBACK) && !mdata->has_wfd_blk)
+ head[len] = head[len - 1];
+
switch (type) {
case MDSS_MDP_MIXER_TYPE_INTF:
@@ -1247,10 +1285,24 @@
u32 *ctl_offsets, u32 *wb_offsets, u32 len)
{
struct mdss_mdp_ctl *head;
+ struct mutex *shared_lock = NULL;
u32 i;
+ u32 size = len;
+
+ if (!mdata->has_wfd_blk) {
+ size++;
+ shared_lock = devm_kzalloc(&mdata->pdev->dev,
+ sizeof(struct mutex),
+ GFP_KERNEL);
+ if (!shared_lock) {
+ pr_err("unable to allocate mem for mutex\n");
+ return -ENOMEM;
+ }
+ mutex_init(shared_lock);
+ }
head = devm_kzalloc(&mdata->pdev->dev, sizeof(struct mdss_mdp_ctl) *
- len, GFP_KERNEL);
+ size, GFP_KERNEL);
if (!head) {
pr_err("unable to setup ctl and wb: kzalloc fail\n");
@@ -1264,6 +1316,16 @@
head[i].ref_cnt = 0;
}
+ if (!mdata->has_wfd_blk) {
+ head[len - 1].shared_lock = shared_lock;
+ /*
+ * Allocate a virtual ctl to be able to perform simultaneous
+ * line mode and block mode operations on the same
+ * writeback block
+ */
+ head[len] = head[len - 1];
+ head[len].num = -1;
+ }
mdata->ctl_off = head;
return 0;
@@ -1348,7 +1410,10 @@
if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA)
ctl->flush_bits |= BIT(pipe->num) << 5;
- else /* RGB/VIG pipe */
+ else if (pipe->num == MDSS_MDP_SSPP_VIG3 ||
+ pipe->num == MDSS_MDP_SSPP_RGB3)
+ ctl->flush_bits |= BIT(pipe->num) << 10;
+ else /* RGB/VIG 0-2 pipes */
ctl->flush_bits |= BIT(pipe->num);
mutex_unlock(&ctl->lock);
@@ -1387,11 +1452,19 @@
static int mdss_mdp_mixer_update(struct mdss_mdp_mixer *mixer)
{
+ u32 off = 0;
+ if (!mixer)
+ return -EINVAL;
+
mixer->params_changed = 0;
/* skip mixer setup for rotator */
- if (!mixer->rotator_mode)
+ if (!mixer->rotator_mode) {
mdss_mdp_mixer_setup(mixer->ctl, mixer);
+ } else {
+ off = __mdss_mdp_ctl_get_mixer_off(mixer);
+ mdss_mdp_ctl_write(mixer->ctl, off, 0);
+ }
return 0;
}
@@ -1520,12 +1593,9 @@
return -ENODEV;
}
+ mutex_lock(&ctl->lock);
pr_debug("commit ctl=%d play_cnt=%d\n", ctl->num, ctl->play_cnt);
- ret = mutex_lock_interruptible(&ctl->lock);
- if (ret)
- return ret;
-
if (!ctl->power_on) {
mutex_unlock(&ctl->lock);
return 0;
@@ -1620,11 +1690,45 @@
return mixer_cnt;
}
+/**
+ * @mdss_mdp_ctl_mixer_switch() - return ctl mixer of @return_type
+ * @ctl: Pointer to ctl structure to be switched.
+ * @return_type: wb_type of the ctl to be switched to.
+ *
+ * Virtual mixer switch should be performed only when there is no
+ * dedicated wfd block and writeback block is shared.
+ */
+struct mdss_mdp_ctl *mdss_mdp_ctl_mixer_switch(struct mdss_mdp_ctl *ctl,
+ u32 return_type)
+{
+ int i;
+ struct mdss_data_type *mdata = ctl->mdata;
+
+ if (ctl->wb_type == return_type) {
+ mdata->mixer_switched = false;
+ return ctl;
+ }
+ for (i = 0; i <= mdata->nctl; i++) {
+ if (mdata->ctl_off[i].wb_type == return_type) {
+ pr_debug("switching mixer from ctl=%d to ctl=%d\n",
+ ctl->num, mdata->ctl_off[i].num);
+ mdata->mixer_switched = true;
+ return mdata->ctl_off + i;
+ }
+ }
+ pr_err("unable to switch mixer to type=%d\n", return_type);
+ return NULL;
+}
+
static inline int __mdss_mdp_ctl_get_mixer_off(struct mdss_mdp_mixer *mixer)
{
- if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF)
- return MDSS_MDP_REG_CTL_LAYER(mixer->num);
- else
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ if (mixer->num == MDSS_MDP_INTF_LAYERMIXER3)
+ return MDSS_MDP_CTL_X_LAYER_5;
+ else
+ return MDSS_MDP_REG_CTL_LAYER(mixer->num);
+ } else {
return MDSS_MDP_REG_CTL_LAYER(mixer->num +
- MDSS_MDP_INTF_MAX_LAYERMIXER);
+ MDSS_MDP_INTF_LAYERMIXER3);
+ }
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 741c7a7..ccd6e56 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -23,6 +23,7 @@
#define MDSS_MDP_HW_REV_100 0x10000000
#define MDSS_MDP_HW_REV_102 0x10020000
+#define MDSS_MDP_HW_REV_103 0x10030000
#define MDSS_REG_HW_VERSION 0x0
#define MDSS_REG_HW_INTR_STATUS 0x10
@@ -57,15 +58,19 @@
#define MDSS_MDP_INTR_PING_PONG_0_DONE BIT(8)
#define MDSS_MDP_INTR_PING_PONG_1_DONE BIT(9)
#define MDSS_MDP_INTR_PING_PONG_2_DONE BIT(10)
+#define MDSS_MDP_INTR_PING_PONG_3_DONE BIT(11)
#define MDSS_MDP_INTR_PING_PONG_0_RD_PTR BIT(12)
#define MDSS_MDP_INTR_PING_PONG_1_RD_PTR BIT(13)
#define MDSS_MDP_INTR_PING_PONG_2_RD_PTR BIT(14)
+#define MDSS_MDP_INTR_PING_PONG_3_RD_PTR BIT(15)
#define MDSS_MDP_INTR_PING_PONG_0_WR_PTR BIT(16)
#define MDSS_MDP_INTR_PING_PONG_1_WR_PTR BIT(17)
#define MDSS_MDP_INTR_PING_PONG_2_WR_PTR BIT(18)
+#define MDSS_MDP_INTR_PING_PONG_3_WR_PTR BIT(19)
#define MDSS_MDP_INTR_PING_PONG_0_AUTOREFRESH_DONE BIT(20)
#define MDSS_MDP_INTR_PING_PONG_1_AUTOREFRESH_DONE BIT(21)
#define MDSS_MDP_INTR_PING_PONG_2_AUTOREFRESH_DONE BIT(22)
+#define MDSS_MDP_INTR_PING_PONG_3_AUTOREFRESH_DONE BIT(23)
#define MDSS_MDP_INTR_INTF_0_UNDERRUN BIT(24)
#define MDSS_MDP_INTR_INTF_0_VSYNC BIT(25)
#define MDSS_MDP_INTR_INTF_1_UNDERRUN BIT(26)
@@ -97,6 +102,7 @@
MDSS_MDP_CTL2,
MDSS_MDP_CTL3,
MDSS_MDP_CTL4,
+ MDSS_MDP_CTL5,
MDSS_MDP_MAX_CTL
};
@@ -132,6 +138,8 @@
MDSS_MDP_SSPP_RGB2,
MDSS_MDP_SSPP_DMA0,
MDSS_MDP_SSPP_DMA1,
+ MDSS_MDP_SSPP_VIG3,
+ MDSS_MDP_SSPP_RGB3,
MDSS_MDP_MAX_SSPP
};
@@ -227,11 +235,13 @@
#define MDSS_MDP_NUM_REG_MIXERS 3
#define MDSS_MDP_NUM_WB_MIXERS 2
+#define MDSS_MDP_CTL_X_LAYER_5 0x24
enum mdss_mdp_mixer_intf_index {
MDSS_MDP_INTF_LAYERMIXER0,
MDSS_MDP_INTF_LAYERMIXER1,
MDSS_MDP_INTF_LAYERMIXER2,
+ MDSS_MDP_INTF_LAYERMIXER3,
MDSS_MDP_INTF_MAX_LAYERMIXER,
};
@@ -248,17 +258,10 @@
MDSS_MDP_STAGE_1,
MDSS_MDP_STAGE_2,
MDSS_MDP_STAGE_3,
+ MDSS_MDP_STAGE_4,
MDSS_MDP_MAX_STAGE
};
-enum mdss_mdp_blend_index {
- MDSS_MDP_BLEND_STAGE0,
- MDSS_MDP_BLEND_STAGE1,
- MDSS_MDP_BLEND_STAGE2,
- MDSS_MDP_BLEND_STAGE3,
- MDSS_MDP_MAX_BLEND_STAGE,
-};
-
#define MDSS_MDP_REG_LM_OFFSET(lm) (0x03200 + ((lm) * 0x400))
#define MDSS_MDP_REG_LM_OP_MODE 0x000
@@ -340,6 +343,7 @@
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW1 0x034
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW2 0x038
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW3 0x03C
+#define MDSS_MDP_REG_WB_DST_WRITE_CONFIG 0x048
#define MDSS_MDP_REG_WB_ROTATION_DNSCALER 0x050
#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C03 0x060
#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C12 0x064
@@ -390,6 +394,7 @@
MDSS_MDP_DSPP0,
MDSS_MDP_DSPP1,
MDSS_MDP_DSPP2,
+ MDSS_MDP_DSPP3,
MDSS_MDP_MAX_DSPP
};
@@ -467,6 +472,7 @@
MDSS_MDP_PINGPONG0,
MDSS_MDP_PINGPONG1,
MDSS_MDP_PINGPONG2,
+ MDSS_MDP_PINGPONG3,
MDSS_MDP_MAX_PINGPONG
};
@@ -491,29 +497,7 @@
#define MDSS_MDP_REG_SMP_ALLOC_W0 0x00180
#define MDSS_MDP_REG_SMP_ALLOC_R0 0x00230
-#define MDSS_MDP_SMP_MMB_BLOCKS 22
-
-enum mdss_mdp_smp_client_index {
- MDSS_MDP_SMP_CLIENT_UNUSED,
- MDSS_MDP_SMP_CLIENT_VIG0_FETCH_Y,
- MDSS_MDP_SMP_CLIENT_VIG0_FETCH_CR,
- MDSS_MDP_SMP_CLIENT_VIG0_FETCH_CB,
- MDSS_MDP_SMP_CLIENT_VIG1_FETCH_Y,
- MDSS_MDP_SMP_CLIENT_VIG1_FETCH_CR,
- MDSS_MDP_SMP_CLIENT_VIG1_FETCH_CB,
- MDSS_MDP_SMP_CLIENT_VIG2_FETCH_Y,
- MDSS_MDP_SMP_CLIENT_VIG2_FETCH_CR,
- MDSS_MDP_SMP_CLIENT_VIG2_FETCH_CB,
- MDSS_MDP_SMP_CLIENT_DMA0_FETCH_Y,
- MDSS_MDP_SMP_CLIENT_DMA0_FETCH_CR,
- MDSS_MDP_SMP_CLIENT_DMA0_FETCH_CB,
- MDSS_MDP_SMP_CLIENT_DMA1_FETCH_Y,
- MDSS_MDP_SMP_CLIENT_DMA1_FETCH_CR,
- MDSS_MDP_SMP_CLIENT_DMA1_FETCH_CB,
- MDSS_MDP_SMP_CLIENT_RGB0_FETCH,
- MDSS_MDP_SMP_CLIENT_RGB1_FETCH,
- MDSS_MDP_SMP_CLIENT_RGB2_FETCH,
-};
+#define MDSS_MDP_SMP_MMB_BLOCKS 44
#define MDSS_MDP_LP_MISR_SEL 0x450
#define MDSS_MDP_LP_MISR_CTRL_MDP 0x454
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
index bd4f3ea..d0c1818 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
@@ -26,20 +26,20 @@
/* wait for at most 2 vsync for lowest refresh rate (24hz) */
#define KOFF_TIMEOUT msecs_to_jiffies(84)
+#define STOP_TIMEOUT msecs_to_jiffies(16 * (VSYNC_EXPIRE_TICK + 2))
+
struct mdss_mdp_cmd_ctx {
struct mdss_mdp_ctl *ctl;
u32 pp_num;
u8 ref_cnt;
- struct completion vsync_comp;
struct completion pp_comp;
struct completion stop_comp;
mdp_vsync_handler_t send_vsync;
int panel_on;
int koff_cnt;
int clk_enabled;
- int clk_control;
int vsync_enabled;
- int expire;
+ int rdptr_enabled;
struct mutex clk_mtx;
spinlock_t clk_lock;
struct work_struct clk_work;
@@ -54,6 +54,50 @@
struct mdss_mdp_cmd_ctx mdss_mdp_cmd_ctx_list[MAX_SESSIONS];
+static inline u32 mdss_mdp_cmd_line_count(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_mixer *mixer;
+ u32 cnt = 0xffff; /* init it to an invalid value */
+ u32 init;
+ u32 height;
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
+ mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT);
+ if (!mixer) {
+ mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_RIGHT);
+ if (!mixer) {
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ goto exit;
+ }
+ }
+
+ init = mdss_mdp_pingpong_read
+ (mixer, MDSS_MDP_REG_PP_VSYNC_INIT_VAL) & 0xffff;
+
+ height = mdss_mdp_pingpong_read
+ (mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT) & 0xffff;
+
+ if (height < init) {
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ goto exit;
+ }
+
+ cnt = mdss_mdp_pingpong_read
+ (mixer, MDSS_MDP_REG_PP_INT_COUNT_VAL) & 0xffff;
+
+ if (cnt < init) /* wrap around happened at height */
+ cnt += (height - init);
+ else
+ cnt -= init;
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ pr_debug("cnt=%d init=%d height=%d\n", cnt, init, height);
+exit:
+ return cnt;
+}
+
/*
* TE configuration:
* dsi byte clock calculated base on 70 fps
@@ -146,6 +190,45 @@
return 0;
}
+static inline void mdss_mdp_cmd_clk_on(struct mdss_mdp_cmd_ctx *ctx)
+{
+ unsigned long flags;
+ mutex_lock(&ctx->clk_mtx);
+ if (!ctx->clk_enabled) {
+ ctx->clk_enabled = 1;
+ mdss_mdp_ctl_intf_event
+ (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ }
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ if (!ctx->rdptr_enabled)
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num);
+ ctx->rdptr_enabled = VSYNC_EXPIRE_TICK;
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
+ mutex_unlock(&ctx->clk_mtx);
+}
+
+static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx)
+{
+ unsigned long flags;
+ int set_clk_off = 0;
+
+ mutex_lock(&ctx->clk_mtx);
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ if (!ctx->rdptr_enabled)
+ set_clk_off = 1;
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
+
+ if (ctx->clk_enabled && set_clk_off) {
+ ctx->clk_enabled = 0;
+ mdss_mdp_ctl_intf_event
+ (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)0);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ complete(&ctx->stop_comp);
+ }
+ mutex_unlock(&ctx->clk_mtx);
+}
+
static void mdss_mdp_cmd_readptr_done(void *arg)
{
struct mdss_mdp_ctl *ctl = arg;
@@ -157,11 +240,6 @@
return;
}
- complete_all(&ctx->vsync_comp);
-
- pr_debug("%s: num=%d ctx=%d expire=%d koff=%d\n", __func__, ctl->num,
- ctx->pp_num, ctx->expire, ctx->koff_cnt);
-
vsync_time = ktime_get();
ctl->vsync_cnt++;
@@ -169,18 +247,17 @@
if (ctx->send_vsync)
ctx->send_vsync(ctl, vsync_time);
- if (ctx->expire) {
- ctx->expire--;
- if (ctx->expire == 0) {
- if (ctx->koff_cnt <= 0) {
- ctx->clk_control = 1;
- schedule_work(&ctx->clk_work);
- } else {
- /* put off one vsync */
- ctx->expire += 1;
- }
- }
+ if (!ctx->vsync_enabled) {
+ if (ctx->rdptr_enabled)
+ ctx->rdptr_enabled--;
}
+
+ if (ctx->rdptr_enabled == 0) {
+ mdss_mdp_irq_disable_nosync
+ (MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num);
+ schedule_work(&ctx->clk_work);
+ }
+
spin_unlock(&ctx->clk_lock);
}
@@ -199,8 +276,15 @@
complete_all(&ctx->pp_comp);
- if (ctx->koff_cnt)
+ if (ctx->koff_cnt) {
ctx->koff_cnt--;
+ if (ctx->koff_cnt) {
+ pr_err("%s: too many kickoffs=%d!\n", __func__,
+ ctx->koff_cnt);
+ ctx->koff_cnt = 0;
+ }
+ } else
+ pr_err("%s: should not have pingpong interrupt!\n", __func__);
pr_debug("%s: ctl_num=%d intf_num=%d ctx=%d kcnt=%d\n", __func__,
ctl->num, ctl->intf_num, ctx->pp_num, ctx->koff_cnt);
@@ -210,7 +294,6 @@
static void clk_ctrl_work(struct work_struct *work)
{
- unsigned long flags;
struct mdss_mdp_cmd_ctx *ctx =
container_of(work, typeof(*ctx), clk_work);
@@ -219,38 +302,14 @@
return;
}
- pr_debug("%s:ctx=%p num=%d\n", __func__, ctx, ctx->pp_num);
-
- mutex_lock(&ctx->clk_mtx);
- spin_lock_irqsave(&ctx->clk_lock, flags);
- if (ctx->clk_control && ctx->clk_enabled) {
- ctx->clk_enabled = 0;
- ctx->clk_control = 0;
- spin_unlock_irqrestore(&ctx->clk_lock, flags);
- /*
- * make sure dsi link is idle here
- */
- ctx->vsync_enabled = 0;
- mdss_mdp_irq_disable(MDSS_MDP_IRQ_PING_PONG_RD_PTR,
- ctx->pp_num);
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
- /* disable dsi clock */
- mdss_mdp_ctl_intf_event(ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL,
- (void *)0);
- complete(&ctx->stop_comp);
- pr_debug("%s: SET_CLK_OFF, pid=%d\n", __func__, current->pid);
- } else {
- spin_unlock_irqrestore(&ctx->clk_lock, flags);
- }
- mutex_unlock(&ctx->clk_mtx);
+ mdss_mdp_cmd_clk_off(ctx);
}
-static int mdss_mdp_cmd_vsync_ctrl(struct mdss_mdp_ctl *ctl,
- struct mdss_mdp_vsync_handler *handler)
+static int mdss_mdp_cmd_add_vsync_handler(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_vsync_handler *handle)
{
struct mdss_mdp_cmd_ctx *ctx;
unsigned long flags;
- int enable;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
@@ -258,81 +317,62 @@
return -ENODEV;
}
- enable = (handler->vsync_handler != NULL);
-
- mutex_lock(&ctx->clk_mtx);
-
- pr_debug("%s: ctx=%p ctx=%d enabled=%d %d clk_enabled=%d clk_ctrl=%d\n",
- __func__, ctx, ctx->pp_num, ctx->vsync_enabled, enable,
- ctx->clk_enabled, ctx->clk_control);
-
- if (ctx->vsync_enabled == enable) {
- mutex_unlock(&ctx->clk_mtx);
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ if (ctx->vsync_enabled) {
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
return 0;
}
+ ctx->vsync_enabled = 1;
+ ctx->send_vsync = handle->vsync_handler;
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
- if (enable) {
- spin_lock_irqsave(&ctx->clk_lock, flags);
- ctx->clk_control = 0;
- ctx->expire = 0;
- ctx->send_vsync = handler->vsync_handler;
- spin_unlock_irqrestore(&ctx->clk_lock, flags);
- if (ctx->clk_enabled == 0) {
- mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_CLK_CTRL,
- (void *)1);
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
- mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_RD_PTR,
- ctx->pp_num);
- ctx->vsync_enabled = 1;
- ctx->clk_enabled = 1;
- pr_debug("%s: SET_CLK_ON, pid=%d\n", __func__,
- current->pid);
- }
- } else {
- spin_lock_irqsave(&ctx->clk_lock, flags);
- ctx->expire = VSYNC_EXPIRE_TICK;
- spin_unlock_irqrestore(&ctx->clk_lock, flags);
- }
- mutex_unlock(&ctx->clk_mtx);
+ mdss_mdp_cmd_clk_on(ctx);
return 0;
}
-static void mdss_mdp_cmd_chk_clock(struct mdss_mdp_cmd_ctx *ctx)
+static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_vsync_handler *handle)
{
- unsigned long flags;
- int set_clk_on = 0;
+ struct mdss_mdp_cmd_ctx *ctx;
+ unsigned long flags;
+
+ ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
- pr_err("invalid ctx\n");
- return;
+ pr_err("%s: invalid ctx\n", __func__);
+ return -ENODEV;
}
- mutex_lock(&ctx->clk_mtx);
-
- pr_debug("%s: ctx=%p num=%d clk_enabled=%d\n", __func__,
- ctx, ctx->pp_num, ctx->clk_enabled);
spin_lock_irqsave(&ctx->clk_lock, flags);
- ctx->koff_cnt++;
- ctx->clk_control = 0;
- ctx->expire = VSYNC_EXPIRE_TICK;
- if (ctx->clk_enabled == 0) {
- set_clk_on++;
- ctx->clk_enabled = 1;
+ if (!ctx->vsync_enabled) {
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
+ return 0;
}
+ ctx->vsync_enabled = 0;
+ ctx->send_vsync = NULL;
+ ctx->rdptr_enabled = VSYNC_EXPIRE_TICK;
spin_unlock_irqrestore(&ctx->clk_lock, flags);
+ return 0;
+}
- if (set_clk_on) {
- mdss_mdp_ctl_intf_event(ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL,
- (void *)1);
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
- ctx->vsync_enabled = 1;
- mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num);
- pr_debug("%s: ctx=%p num=%d SET_CLK_ON\n", __func__,
- ctx, ctx->pp_num);
- }
- mutex_unlock(&ctx->clk_mtx);
+int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_panel_data *pdata;
+ int ret = 0;
+
+ pdata = ctl->panel_data;
+
+ pdata->panel_info.cont_splash_enabled = 0;
+
+ ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH,
+ NULL);
+
+ mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)0);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ return ret;
}
static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg)
@@ -374,6 +414,7 @@
int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_cmd_ctx *ctx;
+ unsigned long flags;
int rc;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
@@ -392,18 +433,19 @@
WARN(rc, "intf %d panel on error (%d)\n", ctl->intf_num, rc);
}
- mdss_mdp_cmd_chk_clock(ctx);
+ mdss_mdp_cmd_clk_on(ctx);
/*
* tx dcs command if had any
*/
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_CMDLIST_KOFF, NULL);
- INIT_COMPLETION(ctx->vsync_comp);
INIT_COMPLETION(ctx->pp_comp);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num);
-
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1);
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ ctx->koff_cnt++;
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
mb();
return 0;
@@ -412,8 +454,8 @@
int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_cmd_ctx *ctx;
+ unsigned long flags;
int need_wait = 0;
- struct mdss_mdp_vsync_handler null_handle;
int ret;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
@@ -421,25 +463,27 @@
pr_err("invalid ctx\n");
return -ENODEV;
}
-
- pr_debug("%s:+ vaync_enable=%d expire=%d\n", __func__,
- ctx->vsync_enabled, ctx->expire);
-
- mutex_lock(&ctx->clk_mtx);
- if (ctx->vsync_enabled) {
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ if (ctx->rdptr_enabled) {
INIT_COMPLETION(ctx->stop_comp);
need_wait = 1;
}
- mutex_unlock(&ctx->clk_mtx);
+ if (ctx->vsync_enabled) {
+ pr_err("%s: vsync should be disabled\n", __func__);
+ ctx->vsync_enabled = 0;
+ }
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
- if (need_wait)
- wait_for_completion_interruptible(&ctx->stop_comp);
+ if (need_wait) {
+ if (wait_for_completion_timeout(&ctx->stop_comp, STOP_TIMEOUT)
+ <= 0) {
+ WARN(1, "stop cmd time out\n");
+ mdss_mdp_cmd_clk_off(ctx);
+ }
+ }
ctx->panel_on = 0;
- null_handle.vsync_handler = NULL;
- mdss_mdp_cmd_vsync_ctrl(ctl, &null_handle);
-
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num,
NULL, NULL);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num,
@@ -499,7 +543,6 @@
ctx->ctl = ctl;
ctx->pp_num = mixer->num;
- init_completion(&ctx->vsync_comp);
init_completion(&ctx->pp_comp);
init_completion(&ctx->stop_comp);
spin_lock_init(&ctx->clk_lock);
@@ -524,9 +567,9 @@
ctl->stop_fnc = mdss_mdp_cmd_stop;
ctl->display_fnc = mdss_mdp_cmd_kickoff;
ctl->wait_pingpong = mdss_mdp_cmd_wait4pingpong;
- ctl->add_vsync_handler = mdss_mdp_cmd_vsync_ctrl;
- ctl->remove_vsync_handler = mdss_mdp_cmd_vsync_ctrl;
-
+ ctl->add_vsync_handler = mdss_mdp_cmd_add_vsync_handler;
+ ctl->remove_vsync_handler = mdss_mdp_cmd_remove_vsync_handler;
+ ctl->read_line_cnt_fnc = mdss_mdp_cmd_line_count;
pr_debug("%s:-\n", __func__);
return 0;
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index ab028e4..9b81633 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -573,7 +573,7 @@
mdss_mdp_ctl_write(ctl, 0, MDSS_MDP_LM_BORDER_COLOR);
off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
- if (mdss_mdp_rev == MDSS_MDP_HW_REV_102)
+ if (mdss_mdp_rev >= MDSS_MDP_HW_REV_102)
mdss_v2_intf_off = 0xEC00;
MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN -
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index 503d8e2..0a37573 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -205,6 +205,7 @@
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_YSTRIDE0, ystride0);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_YSTRIDE1, ystride1);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_OUT_SIZE, outsize);
+ mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_WRITE_CONFIG, 0x58);
return 0;
}
@@ -218,7 +219,7 @@
if (!ctx)
return -ENODEV;
- if (ctx->initialized) /* already set */
+ if (ctx->initialized && !ctl->shared_lock) /* already set */
return 0;
pr_debug("wfd setup ctl=%d\n", ctl->num);
@@ -346,6 +347,9 @@
rc = wait_for_completion_interruptible_timeout(&ctx->wb_comp,
KOFF_TIMEOUT);
+ mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
+ NULL, NULL);
+
if (rc <= 0) {
rc = -ENODEV;
WARN(1, "writeback kickoff timed out (%d) ctl=%d\n",
@@ -388,6 +392,9 @@
return ret;
}
+ mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
+ mdss_mdp_writeback_intr_done, ctx);
+
ctx->callback_fnc = wb_args->callback_fnc;
ctx->callback_arg = wb_args->priv_data;
@@ -433,9 +440,6 @@
ctx->initialized = false;
init_completion(&ctx->wb_comp);
- mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
- mdss_mdp_writeback_intr_done, ctx);
-
if (ctx->type == MDSS_MDP_WRITEBACK_TYPE_ROTATOR)
ctl->prepare_fnc = mdss_mdp_writeback_prepare_rot;
else /* wfd or line mode */
@@ -446,3 +450,21 @@
return ret;
}
+
+int mdss_mdp_writeback_display_commit(struct mdss_mdp_ctl *ctl, void *arg)
+{
+ if (ctl->shared_lock && !mutex_is_locked(ctl->shared_lock)) {
+ pr_err("shared mutex is not locked before commit on ctl=%d\n",
+ ctl->num);
+ return -EINVAL;
+ }
+
+ if (ctl->mdata->mixer_switched) {
+ if (ctl->mixer_left)
+ ctl->mixer_left->params_changed++;
+ if (ctl->mixer_right)
+ ctl->mixer_right->params_changed++;
+ }
+
+ return mdss_mdp_display_commit(ctl, arg);
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 43de734..24b27b4 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -180,6 +180,11 @@
pr_err("BWC: unequal src img and rect w,h\n");
return -EINVAL;
}
+
+ if (req->flags & MDP_DECIMATION_EN) {
+ pr_err("Can't enable BWC decode && decimate\n");
+ return -EINVAL;
+ }
}
if (req->flags & MDP_DEINTERLACE) {
@@ -283,7 +288,7 @@
req->id = rot->session_id;
} else {
pr_err("Unable to setup rotator session\n");
- mdss_mdp_rotator_release(rot->session_id);
+ mdss_mdp_rotator_release(rot);
}
return ret;
@@ -315,6 +320,30 @@
return 0;
}
+static int __mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe)
+{
+ u32 src;
+ int rc;
+
+ src = pipe->src.w >> pipe->horz_deci;
+ rc = mdss_mdp_calc_phase_step(src, pipe->dst.w, &pipe->phase_step_x);
+ if (rc) {
+ pr_err("Horizontal scaling calculation failed=%d! %d->%d\n",
+ rc, src, pipe->dst.w);
+ return rc;
+ }
+
+ src = pipe->src.h >> pipe->vert_deci;
+ rc = mdss_mdp_calc_phase_step(src, pipe->dst.h, &pipe->phase_step_y);
+ if (rc) {
+ pr_err("Vertical scaling calculation failed=%d! %d->%d\n",
+ rc, src, pipe->dst.h);
+ return rc;
+ }
+
+ return 0;
+}
+
static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
struct mdp_overlay *req,
struct mdss_mdp_pipe **ppipe)
@@ -465,6 +494,7 @@
pipe->overfetch_disable = fmt->is_yuv &&
!(pipe->flags & MDP_SOURCE_ROTATED_90);
+ req->id = pipe->ndx;
pipe->req_data = *req;
if (pipe->flags & MDP_OVERLAY_PP_CFG_EN) {
@@ -535,6 +565,14 @@
goto exit_fail;
}
+ ret = __mdss_mdp_overlay_setup_scaling(pipe);
+ if (ret)
+ goto exit_fail;
+
+ if ((mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) &&
+ !mdp5_data->mdata->has_wfd_blk)
+ mdss_mdp_smp_release(pipe);
+
ret = mdss_mdp_smp_reserve(pipe);
if (ret) {
pr_debug("mdss_mdp_smp_reserve failed. ret=%d\n", ret);
@@ -543,7 +581,6 @@
pipe->params_changed++;
- req->id = pipe->ndx;
req->vert_deci = pipe->vert_deci;
*ppipe = pipe;
@@ -733,8 +770,12 @@
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_mdp_pipe *pipe, *next;
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ struct mdss_mdp_ctl *tmp;
int ret;
+ if (ctl->shared_lock)
+ mutex_lock(ctl->shared_lock);
+
mutex_lock(&mdp5_data->ov_lock);
mutex_lock(&mfd->lock);
@@ -742,12 +783,44 @@
if (ret) {
mutex_unlock(&mfd->lock);
mutex_unlock(&mdp5_data->ov_lock);
+ if (ctl->shared_lock)
+ mutex_unlock(ctl->shared_lock);
return ret;
}
list_for_each_entry_safe(pipe, next, &mdp5_data->pipes_used,
used_list) {
struct mdss_mdp_data *buf;
+ /*
+ * When external is connected and no dedicated wfd is present,
+ * reprogram DMA pipe before kickoff to clear out any previous
+ * block mode configuration.
+ */
+ if ((pipe->type == MDSS_MDP_PIPE_TYPE_DMA) &&
+ (ctl->shared_lock && !ctl->mdata->has_wfd_blk)) {
+ if (ctl->mdata->mixer_switched) {
+ ret = mdss_mdp_overlay_pipe_setup(mfd,
+ &pipe->req_data, &pipe);
+ pr_debug("reseting DMA pipe for ctl=%d",
+ ctl->num);
+ }
+ if (ret) {
+ pr_err("can't reset DMA pipe ret=%d ctl=%d\n",
+ ret, ctl->num);
+ mutex_unlock(&mfd->lock);
+ goto commit_fail;
+ }
+
+ tmp = mdss_mdp_ctl_mixer_switch(ctl,
+ MDSS_MDP_WB_CTL_TYPE_LINE);
+ if (!tmp) {
+ mutex_unlock(&mfd->lock);
+ ret = -EINVAL;
+ goto commit_fail;
+ }
+ pipe->mixer = mdss_mdp_mixer_get(tmp,
+ MDSS_MDP_MIXER_MUX_DEFAULT);
+ }
if (pipe->back_buf.num_planes) {
buf = &pipe->back_buf;
} else if (ctl->play_cnt == 0) {
@@ -798,6 +871,8 @@
mdss_mdp_overlay_cleanup(mfd);
mutex_unlock(&mdp5_data->ov_lock);
+ if (ctl->shared_lock)
+ mutex_unlock(ctl->shared_lock);
return ret;
}
@@ -833,9 +908,14 @@
static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
{
int ret = 0;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_overlay_private *mdp5_data;
- if (!mfd || !mdp5_data->ctl)
+ if (!mfd)
+ return -ENODEV;
+
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl)
return -ENODEV;
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
@@ -856,10 +936,17 @@
pr_debug("unset ndx=%x\n", ndx);
- if (ndx & MDSS_MDP_ROT_SESSION_MASK)
- ret = mdss_mdp_rotator_release(ndx);
- else
+ if (ndx & MDSS_MDP_ROT_SESSION_MASK) {
+ struct mdss_mdp_rotator_session *rot;
+ rot = mdss_mdp_rotator_session_get(ndx);
+ if (rot) {
+ mdss_mdp_overlay_free_buf(&rot->src_buf);
+ mdss_mdp_overlay_free_buf(&rot->dst_buf);
+ ret = mdss_mdp_rotator_release(rot);
+ }
+ } else {
ret = mdss_mdp_overlay_release(mfd, ndx);
+ }
done:
mutex_unlock(&mdp5_data->ov_lock);
@@ -920,7 +1007,6 @@
struct msmfb_overlay_data *req)
{
struct mdss_mdp_rotator_session *rot;
- struct mdss_mdp_data src_data, dst_data;
int ret;
u32 flgs;
@@ -932,26 +1018,26 @@
flgs = rot->flags & MDP_SECURE_OVERLAY_SESSION;
- ret = mdss_mdp_overlay_get_buf(mfd, &src_data, &req->data, 1, flgs);
+ mdss_mdp_overlay_free_buf(&rot->src_buf);
+ ret = mdss_mdp_overlay_get_buf(mfd, &rot->src_buf, &req->data, 1, flgs);
if (ret) {
pr_err("src_data pmem error\n");
return ret;
}
- ret = mdss_mdp_overlay_get_buf(mfd, &dst_data, &req->dst_data, 1, flgs);
+ mdss_mdp_overlay_free_buf(&rot->dst_buf);
+ ret = mdss_mdp_overlay_get_buf(mfd, &rot->dst_buf,
+ &req->dst_data, 1, flgs);
if (ret) {
pr_err("dst_data pmem error\n");
goto dst_buf_fail;
}
- ret = mdss_mdp_rotator_queue(rot, &src_data, &dst_data);
+ ret = mdss_mdp_rotator_queue(rot, &rot->src_buf, &rot->dst_buf);
if (ret)
pr_err("rotator queue error session id=%x\n", req->id);
- mdss_mdp_overlay_free_buf(&dst_data);
dst_buf_fail:
- mdss_mdp_overlay_free_buf(&src_data);
-
return ret;
}
@@ -1155,17 +1241,21 @@
static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
{
- struct mdss_mdp_data data;
+ struct mdss_mdp_data *buf;
struct mdss_mdp_pipe *pipe;
struct fb_info *fbi;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_overlay_private *mdp5_data;
u32 offset;
int bpp, ret;
- if (!mfd || !mdp5_data->ctl)
+ if (!mfd)
return;
fbi = mfd->fbi;
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl)
+ return;
if (!fbi->fix.smem_start || fbi->fix.smem_len == 0 ||
mdp5_data->borderfill_enable) {
@@ -1181,8 +1271,6 @@
return;
}
- memset(&data, 0, sizeof(data));
-
bpp = fbi->var.bits_per_pixel / 8;
offset = fbi->var.xoffset * bpp +
fbi->var.yoffset * fbi->fix.line_length;
@@ -1199,18 +1287,6 @@
goto pan_display_error;
}
- if (is_mdss_iommu_attached()) {
- if (!mfd->iova) {
- pr_err("mfd iova is zero\n");
- goto pan_display_error;
- }
- data.p[0].addr = mfd->iova;
- } else
- data.p[0].addr = fbi->fix.smem_start;
-
- data.p[0].addr += offset;
- data.p[0].len = fbi->fix.smem_len - offset;
- data.num_planes = 1;
ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, MDSS_MDP_MIXER_MUX_LEFT);
if (ret) {
@@ -1222,13 +1298,23 @@
pr_err("unable to map base pipe\n");
goto pan_display_error;
}
- ret = mdss_mdp_pipe_queue_data(pipe, &data);
- mdss_mdp_pipe_unmap(pipe);
- if (ret) {
- pr_err("unable to queue data\n");
- goto pan_display_error;
+
+ buf = &pipe->back_buf;
+ if (is_mdss_iommu_attached()) {
+ if (!mfd->iova) {
+ pr_err("mfd iova is zero\n");
+ goto pan_display_error;
+ }
+ buf->p[0].addr = mfd->iova;
+ } else {
+ buf->p[0].addr = fbi->fix.smem_start;
}
+ buf->p[0].addr += offset;
+ buf->p[0].len = fbi->fix.smem_len - offset;
+ buf->num_planes = 1;
+ mdss_mdp_pipe_unmap(pipe);
+
if (fbi->var.xres > MAX_MIXER_WIDTH || mfd->split_display) {
ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe,
MDSS_MDP_MIXER_MUX_RIGHT);
@@ -1240,12 +1326,8 @@
pr_err("unable to map right base pipe\n");
goto pan_display_error;
}
- ret = mdss_mdp_pipe_queue_data(pipe, &data);
+ pipe->back_buf = *buf;
mdss_mdp_pipe_unmap(pipe);
- if (ret) {
- pr_err("unable to queue right data\n");
- goto pan_display_error;
- }
}
mutex_unlock(&mdp5_data->ov_lock);
@@ -1594,6 +1676,11 @@
case mdp_op_calib_mode:
ret = mdss_mdp_calib_mode(mfd, &mdp_pp.data.mdss_calib_cfg);
break;
+ case mdp_op_calib_buffer:
+ ret = mdss_mdp_calib_config_buffer(
+ (struct mdp_calib_config_buffer *)
+ &mdp_pp.data.calib_buffer, ©back);
+ break;
default:
pr_err("Unsupported request to MDP_PP IOCTL. %d = op\n",
mdp_pp.op);
@@ -1848,14 +1935,17 @@
static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
{
int rc;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
-
+ struct mdss_overlay_private *mdp5_data;
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
+ mdp5_data = mfd_to_mdp5_data(mfd);
+ if (!mdp5_data)
+ return -EINVAL;
+
if (!mdp5_data->ctl) {
struct mdss_mdp_ctl *ctl;
struct mdss_panel_data *pdata;
@@ -1910,15 +2000,16 @@
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd)
{
int rc;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
-
+ struct mdss_overlay_private *mdp5_data;
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
- if (!mdp5_data->ctl) {
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl) {
pr_err("ctl not initialized\n");
return -ENODEV;
}
@@ -1926,6 +2017,7 @@
if (!mdp5_data->ctl->power_on)
return 0;
+ mdss_mdp_overlay_free_fb_pipe(mfd);
if (!mfd->ref_cnt) {
mdss_mdp_overlay_release_all(mfd);
} else {
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index 3b91ced..eaef12b 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -30,6 +30,7 @@
static DECLARE_BITMAP(mdss_mdp_smp_mmb_pool, MDSS_MDP_SMP_MMB_BLOCKS);
static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe);
+static int __mdss_mdp_pipe_smp_mmb_is_empty(unsigned long *smp);
static inline void mdss_mdp_pipe_write(struct mdss_mdp_pipe *pipe,
u32 reg, u32 val)
@@ -87,13 +88,18 @@
{
if (!bitmap_empty(smp, SMP_MB_CNT)) {
if (write)
- mdss_mdp_smp_mmb_set(MDSS_MDP_SMP_CLIENT_UNUSED, smp);
+ mdss_mdp_smp_mmb_set(0, smp);
bitmap_andnot(mdss_mdp_smp_mmb_pool, mdss_mdp_smp_mmb_pool,
smp, SMP_MB_CNT);
bitmap_zero(smp, SMP_MB_CNT);
}
}
+static int __mdss_mdp_pipe_smp_mmb_is_empty(unsigned long *smp)
+{
+ return bitmap_weight(smp, SMP_MB_CNT) == 0;
+}
+
static void mdss_mdp_smp_set_wm_levels(struct mdss_mdp_pipe *pipe, int mb_cnt)
{
u32 fetch_size, val, wm[3];
@@ -132,8 +138,8 @@
mutex_lock(&mdss_mdp_smp_lock);
for (i = 0; i < MAX_PLANES; i++) {
- mdss_mdp_smp_mmb_free(&pipe->smp_reserved[i], false);
- mdss_mdp_smp_mmb_free(&pipe->smp[i], true);
+ mdss_mdp_smp_mmb_free(pipe->smp_map[i].reserved, false);
+ mdss_mdp_smp_mmb_free(pipe->smp_map[i].allocated, true);
}
mutex_unlock(&mdss_mdp_smp_lock);
}
@@ -144,7 +150,7 @@
mutex_lock(&mdss_mdp_smp_lock);
for (i = 0; i < MAX_PLANES; i++)
- mdss_mdp_smp_mmb_free(&pipe->smp_reserved[i], false);
+ mdss_mdp_smp_mmb_free(pipe->smp_map[i].reserved, false);
mutex_unlock(&mdss_mdp_smp_lock);
}
@@ -193,13 +199,17 @@
if (mdata->mdp_rev == MDSS_MDP_HW_REV_100)
num_blks = roundup_pow_of_two(num_blks);
+
+ if (mdata->smp_mb_per_pipe &&
+ (num_blks > mdata->smp_mb_per_pipe) &&
+ !(pipe->flags & MDP_FLIP_LR))
+ num_blks = mdata->smp_mb_per_pipe;
}
pr_debug("reserving %d mmb for pnum=%d plane=%d\n",
num_blks, pipe->num, i);
- reserved = mdss_mdp_smp_mmb_reserve(&pipe->smp[i],
- &pipe->smp_reserved[i], num_blks);
-
+ reserved = mdss_mdp_smp_mmb_reserve(pipe->smp_map[i].allocated,
+ pipe->smp_map[i].reserved, num_blks);
if (reserved < num_blks)
break;
}
@@ -207,7 +217,8 @@
if (reserved < num_blks) {
pr_debug("insufficient MMB blocks\n");
for (; i >= 0; i--)
- mdss_mdp_smp_mmb_free(&pipe->smp_reserved[i], false);
+ mdss_mdp_smp_mmb_free(pipe->smp_map[i].reserved,
+ false);
rc = -ENOMEM;
}
mutex_unlock(&mdss_mdp_smp_lock);
@@ -222,8 +233,12 @@
mutex_lock(&mdss_mdp_smp_lock);
for (i = 0; i < MAX_PLANES; i++) {
- mdss_mdp_smp_mmb_amend(&pipe->smp[i], &pipe->smp_reserved[i]);
- cnt += mdss_mdp_smp_mmb_set(pipe->ftch_id + i, &pipe->smp[i]);
+ if (__mdss_mdp_pipe_smp_mmb_is_empty(pipe->smp_map[i].reserved))
+ continue;
+ mdss_mdp_smp_mmb_amend(pipe->smp_map[i].allocated,
+ pipe->smp_map[i].reserved);
+ cnt += mdss_mdp_smp_mmb_set(pipe->ftch_id + i,
+ pipe->smp_map[i].allocated);
}
mdss_mdp_smp_set_wm_levels(pipe, cnt);
mutex_unlock(&mdss_mdp_smp_lock);
@@ -269,12 +284,13 @@
}
static struct mdss_mdp_pipe *mdss_mdp_pipe_init(struct mdss_mdp_mixer *mixer,
- u32 type)
+ u32 type, u32 off)
{
struct mdss_mdp_pipe *pipe;
struct mdss_data_type *mdata;
struct mdss_mdp_pipe *pipe_pool = NULL;
u32 npipes;
+ bool pipe_share = false;
u32 i;
if (!mixer || !mixer->ctl || !mixer->ctl->mdata)
@@ -296,6 +312,9 @@
case MDSS_MDP_PIPE_TYPE_DMA:
pipe_pool = mdata->dma_pipes;
npipes = mdata->ndma_pipes;
+ if (!mdata->has_wfd_blk &&
+ (mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK))
+ pipe_share = true;
break;
default:
@@ -304,7 +323,7 @@
break;
}
- for (i = 0; i < npipes; i++) {
+ for (i = off; i < npipes; i++) {
pipe = pipe_pool + i;
if (atomic_cmpxchg(&pipe->ref_cnt, 0, 1) == 0) {
pipe->mixer = mixer;
@@ -317,6 +336,14 @@
pr_debug("type=%x pnum=%d\n", pipe->type, pipe->num);
mutex_init(&pipe->pp_res.hist.hist_mutex);
spin_lock_init(&pipe->pp_res.hist.hist_lock);
+ } else if (pipe_share) {
+ /*
+ * when there is no dedicated wfd blk, DMA pipe can be
+ * shared as long as its attached to a writeback mixer
+ */
+ pipe = mdata->dma_pipes + mixer->num;
+ mdss_mdp_pipe_map(pipe);
+ pr_debug("pipe sharing for pipe=%d\n", pipe->num);
} else {
pr_err("no %d type pipes available\n", type);
}
@@ -328,20 +355,20 @@
{
struct mdss_mdp_pipe *pipe = NULL;
struct mdss_data_type *mdata;
- u32 pnum;
mutex_lock(&mdss_mdp_sspp_lock);
mdata = mixer->ctl->mdata;
- pnum = mixer->num;
-
- if (atomic_cmpxchg(&((mdata->dma_pipes[pnum]).ref_cnt), 0, 1) == 0) {
- pipe = &mdata->dma_pipes[pnum];
- pipe->mixer = mixer;
-
+ pipe = mdss_mdp_pipe_init(mixer, MDSS_MDP_PIPE_TYPE_DMA, mixer->num);
+ if (!pipe) {
+ pr_err("DMA pipes not available for mixer=%d\n", mixer->num);
+ } else if (pipe != &mdata->dma_pipes[mixer->num]) {
+ pr_err("Requested DMA pnum=%d not available\n",
+ mdata->dma_pipes[mixer->num].num);
+ mdss_mdp_pipe_unmap(pipe);
+ pipe = NULL;
} else {
- pr_err("DMA pnum%d\t not available\n", pnum);
+ pipe->mixer = mixer;
}
-
mutex_unlock(&mdss_mdp_sspp_lock);
return pipe;
}
@@ -351,7 +378,7 @@
{
struct mdss_mdp_pipe *pipe;
mutex_lock(&mdss_mdp_sspp_lock);
- pipe = mdss_mdp_pipe_init(mixer, type);
+ pipe = mdss_mdp_pipe_init(mixer, type, 0);
mutex_unlock(&mdss_mdp_sspp_lock);
return pipe;
}
@@ -552,20 +579,15 @@
return 0;
}
-int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata, u32 *offsets,
- u32 *ftch_id, u32 type, u32 num_base, u32 len)
+int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata,
+ struct mdss_mdp_pipe *head, u32 *offsets, u32 *ftch_id, u32 type,
+ u32 num_base, u32 len)
{
- struct mdss_mdp_pipe *head;
u32 i;
- int rc = 0;
- head = devm_kzalloc(&mdata->pdev->dev, sizeof(struct mdss_mdp_pipe) *
- len, GFP_KERNEL);
-
- if (!head) {
- pr_err("unable to setup pipe type=%d :devm_kzalloc fail\n",
- type);
- return -ENOMEM;
+ if (!head || !mdata) {
+ pr_err("unable to setup pipe type=%d: invalid input\n", type);
+ return -EINVAL;
}
for (i = 0; i < len; i++) {
@@ -576,27 +598,7 @@
head[i].base = mdata->mdp_base + offsets[i];
}
- switch (type) {
-
- case MDSS_MDP_PIPE_TYPE_VIG:
- mdata->vig_pipes = head;
- break;
-
- case MDSS_MDP_PIPE_TYPE_RGB:
- mdata->rgb_pipes = head;
- break;
-
- case MDSS_MDP_PIPE_TYPE_DMA:
- mdata->dma_pipes = head;
- break;
-
- default:
- pr_err("Invalid pipe type=%d\n", type);
- rc = -EINVAL;
- break;
- }
-
- return rc;
+ return 0;
}
static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe,
@@ -658,13 +660,14 @@
{
int ret = 0;
u32 params_changed, opmode;
+ struct mdss_mdp_ctl *ctl;
if (!pipe) {
pr_err("pipe not setup properly for queue\n");
return -ENODEV;
}
- if (!pipe->mixer) {
+ if (!pipe->mixer || !pipe->mixer->ctl) {
pr_err("pipe mixer not setup properly for queue\n");
return -ENODEV;
}
@@ -673,8 +676,16 @@
pipe->mixer->num, pipe->play_cnt);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
-
- params_changed = pipe->params_changed;
+ ctl = pipe->mixer->ctl;
+ /*
+ * Reprogram the pipe when there is no dedicated wfd blk and
+ * virtual mixer is allocated for the DMA pipe during concurrent
+ * line and block mode operations
+ */
+ params_changed = (pipe->params_changed) ||
+ ((pipe->type == MDSS_MDP_PIPE_TYPE_DMA) &&
+ (pipe->mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK)
+ && (ctl->mdata->mixer_switched));
if (src_data == NULL) {
mdss_mdp_pipe_solidfill_setup(pipe);
goto update_nobuf;
@@ -705,10 +716,9 @@
if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG)
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_VIG_OP_MODE,
opmode);
-
- mdss_mdp_smp_alloc(pipe);
}
+ mdss_mdp_smp_alloc(pipe);
ret = mdss_mdp_src_addr_setup(pipe, src_data);
if (ret) {
pr_err("addr setup error for pnum=%d\n", pipe->num);
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 963d3fb..6cedd98 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -607,39 +607,6 @@
return 0;
}
-static int mdss_mdp_leading_zero(u32 num)
-{
- u32 bit = 0x80000000;
- int i;
-
- for (i = 0; i < 32; i++) {
- if (bit & num)
- return i;
- bit >>= 1;
- }
-
- return i;
-}
-
-static u32 mdss_mdp_scale_phase_step(int f_num, u32 src, u32 dst)
-{
- u32 val, s;
- int n;
-
- n = mdss_mdp_leading_zero(src);
- if (n > f_num)
- n = f_num;
- s = src << n; /* maximum to reduce lose of resolution */
- val = s / dst;
- if (n < f_num) {
- n = f_num - n;
- val <<= n;
- val |= ((s % dst) << n) / dst;
- }
-
- return val;
-}
-
static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe)
{
u32 scale_config = 0;
@@ -705,13 +672,14 @@
}
scale_config |= MDSS_MDP_SCALEY_EN;
+ phasey_step = pipe->phase_step_y;
if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) {
- u32 chr_dst_h = pipe->dst.h;
+ u32 chroma_shift = 0;
if (!pipe->vert_deci &&
((chroma_sample == MDSS_MDP_CHROMA_420) ||
(chroma_sample == MDSS_MDP_CHROMA_H1V2)))
- chr_dst_h *= 2; /* 2x upsample chroma */
+ chroma_shift = 1; /* 2x upsample chroma */
if (src_h <= pipe->dst.h) {
scale_config |= /* G/Y, A */
@@ -722,17 +690,14 @@
(MDSS_MDP_SCALE_FILTER_PCMN << 10) |
(MDSS_MDP_SCALE_FILTER_PCMN << 18);
- if (src_h <= chr_dst_h)
+ if ((src_h >> chroma_shift) <= pipe->dst.h)
scale_config |= /* CrCb */
(MDSS_MDP_SCALE_FILTER_BIL << 14);
else
scale_config |= /* CrCb */
(MDSS_MDP_SCALE_FILTER_PCMN << 14);
- phasey_step = mdss_mdp_scale_phase_step(
- PHASE_STEP_SHIFT, src_h, chr_dst_h);
-
- writel_relaxed(phasey_step, pipe->base +
+ writel_relaxed(phasey_step >> chroma_shift, pipe->base +
MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY);
} else {
if (src_h <= pipe->dst.h)
@@ -744,9 +709,6 @@
(MDSS_MDP_SCALE_FILTER_PCMN << 10) |
(MDSS_MDP_SCALE_FILTER_PCMN << 18);
}
-
- phasey_step = mdss_mdp_scale_phase_step(
- PHASE_STEP_SHIFT, src_h, pipe->dst.h);
}
if ((src_w != pipe->dst.w) ||
@@ -762,14 +724,15 @@
}
scale_config |= MDSS_MDP_SCALEX_EN;
+ phasex_step = pipe->phase_step_x;
if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) {
- u32 chr_dst_w = pipe->dst.w;
+ u32 chroma_shift = 0;
if (!pipe->horz_deci &&
((chroma_sample == MDSS_MDP_CHROMA_420) ||
(chroma_sample == MDSS_MDP_CHROMA_H2V1)))
- chr_dst_w *= 2; /* 2x upsample chroma */
+ chroma_shift = 1; /* 2x upsample chroma */
if (src_w <= pipe->dst.w) {
scale_config |= /* G/Y, A */
@@ -780,16 +743,14 @@
(MDSS_MDP_SCALE_FILTER_PCMN << 8) |
(MDSS_MDP_SCALE_FILTER_PCMN << 16);
- if (src_w <= chr_dst_w)
+ if ((src_w >> chroma_shift) <= pipe->dst.w)
scale_config |= /* CrCb */
(MDSS_MDP_SCALE_FILTER_BIL << 12);
else
scale_config |= /* CrCb */
(MDSS_MDP_SCALE_FILTER_PCMN << 12);
- phasex_step = mdss_mdp_scale_phase_step(
- PHASE_STEP_SHIFT, src_w, chr_dst_w);
- writel_relaxed(phasex_step, pipe->base +
+ writel_relaxed(phasex_step >> chroma_shift, pipe->base +
MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX);
} else {
if (src_w <= pipe->dst.w)
@@ -801,9 +762,6 @@
(MDSS_MDP_SCALE_FILTER_PCMN << 8) |
(MDSS_MDP_SCALE_FILTER_PCMN << 16);
}
-
- phasex_step = mdss_mdp_scale_phase_step(
- PHASE_STEP_SHIFT, src_w, pipe->dst.w);
}
writel_relaxed(scale_config, pipe->base +
@@ -3371,6 +3329,14 @@
ret = 1;
else if (ptr >= 0x3200 || ptr == 0x100)
ret = 1;
+ else if (ptr == 0x104 || ptr == 0x614 || ptr == 0x714 ||
+ ptr == 0x814 || ptr == 0x914 || ptr == 0xa14)
+ ret = 1;
+ else if (ptr == 0x618 || ptr == 0x718 || ptr == 0x818 ||
+ ptr == 0x918 || ptr == 0xa18)
+ ret = 1;
+ else if (ptr == 0x2234 || ptr == 0x1e34 || ptr == 0x2634)
+ ret = 1;
}
end:
return ret;
@@ -3410,3 +3376,69 @@
mutex_unlock(&mdss_pp_mutex);
return 0;
}
+
+int mdss_mdp_calib_config_buffer(struct mdp_calib_config_buffer *cfg,
+ u32 *copyback)
+{
+ int ret = -1;
+ int counter = cfg->size / (sizeof(uint32_t) * 2);
+ uint32_t *buff = NULL, *buff_org = NULL;
+ void *ptr;
+ int i = 0;
+
+ buff_org = buff = kzalloc(cfg->size, GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("Allocation failed");
+ return ret;
+ }
+
+ if (copy_from_user(buff, cfg->buffer, cfg->size)) {
+ kfree(buff);
+ pr_err("Copy failed");
+ return ret;
+ }
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
+ if (cfg->ops & MDP_PP_OPS_READ) {
+ for (i = 0 ; i < counter ; i++) {
+ if (is_valid_calib_addr((void *) *buff)) {
+ ret = 0;
+ } else {
+ ret = -1;
+ pr_err("Address validation failed");
+ break;
+ }
+
+ ptr = (void *)(((unsigned int) *buff) +
+ (mdss_res->mdp_base));
+ buff++;
+ *buff = readl_relaxed(ptr);
+ buff++;
+ }
+ if (!ret)
+ ret = copy_to_user(cfg->buffer, buff_org, cfg->size);
+ *copyback = 1;
+ } else if (cfg->ops & MDP_PP_OPS_WRITE) {
+ for (i = 0 ; i < counter ; i++) {
+ if (is_valid_calib_addr((void *) *buff)) {
+ ret = 0;
+ } else {
+ ret = -1;
+ pr_err("Address validation failed");
+ break;
+ }
+
+ ptr = (void *)(((unsigned int) *buff) +
+ (mdss_res->mdp_base));
+ buff++;
+ writel_relaxed(*buff, ptr);
+ buff++;
+ }
+ }
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ kfree(buff_org);
+ return ret;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c
index f9894cc..fcd90e1 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.c
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.c
@@ -73,13 +73,16 @@
struct mdss_mdp_pipe *pipe = NULL;
mixer = mdss_mdp_wb_mixer_alloc(1);
- if (!mixer)
+ if (!mixer) {
+ pr_err("wb mixer alloc failed\n");
return NULL;
+ }
pipe = mdss_mdp_pipe_alloc_dma(mixer);
-
- if (!pipe)
+ if (!pipe) {
mdss_mdp_wb_mixer_destroy(mixer);
+ pr_err("dma pipe allocation failed\n");
+ }
return pipe;
}
@@ -96,12 +99,12 @@
ctl = rot_pipe->mixer->ctl;
mutex_lock(&rot->lock);
if (rot->busy) {
- pr_debug("waiting for rot=%d to complete\n", rot->pipe->num);
mdss_mdp_display_wait4comp(ctl);
rot->busy = false;
- mdss_mdp_smp_release(rot->pipe);
-
+ if (ctl->shared_lock)
+ mutex_unlock(ctl->shared_lock);
}
+ mdss_mdp_smp_release(rot->pipe);
mutex_unlock(&rot->lock);
return 0;
@@ -120,7 +123,7 @@
mutex_lock(&rot->lock);
rot->busy = true;
- ret = mdss_mdp_display_commit(ctl, &wb_args);
+ ret = mdss_mdp_writeback_display_commit(ctl, &wb_args);
if (ret) {
rot->busy = false;
pr_err("problem with kickoff rot pipe=%d", rot->pipe->num);
@@ -188,8 +191,19 @@
pr_debug("queue rotator pnum=%d\n", rot_pipe->num);
ctl = rot_pipe->mixer->ctl;
+ if (ctl->shared_lock)
+ mutex_lock(ctl->shared_lock);
- if (rot->params_changed) {
+ ctl = mdss_mdp_ctl_mixer_switch(ctl,
+ MDSS_MDP_WB_CTL_TYPE_BLOCK);
+ if (!ctl) {
+ ret = -EINVAL;
+ goto error;
+ } else {
+ rot->pipe->mixer = ctl->mixer_left;
+ }
+
+ if (rot->params_changed || ctl->mdata->mixer_switched) {
rot->params_changed = 0;
rot_pipe->flags = rot->flags;
rot_pipe->src_fmt = mdss_mdp_get_format_params(rot->format);
@@ -205,19 +219,23 @@
ret = mdss_mdp_smp_reserve(rot->pipe);
if (ret) {
pr_err("unable to mdss_mdp_smp_reserve rot data\n");
- return ret;
+ goto error;
}
ret = mdss_mdp_pipe_queue_data(rot->pipe, src_data);
if (ret) {
pr_err("unable to queue rot data\n");
mdss_mdp_smp_unreserve(rot->pipe);
- return ret;
+ goto error;
}
ret = mdss_mdp_rotator_kickoff(ctl, rot, dst_data);
return ret;
+error:
+ if (ctl->shared_lock)
+ mutex_unlock(ctl->shared_lock);
+ return ret;
}
int mdss_mdp_rotator_queue(struct mdss_mdp_rotator_session *rot,
@@ -342,6 +360,8 @@
static int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot)
{
struct mdss_mdp_pipe *rot_pipe;
+ struct mdss_mdp_ctl *tmp;
+ int ret = 0;
if (!rot)
return -ENODEV;
@@ -359,25 +379,24 @@
memset(rot, 0, sizeof(*rot));
if (rot_pipe) {
struct mdss_mdp_mixer *mixer = rot_pipe->mixer;
- mdss_mdp_pipe_destroy(rot_pipe);
+ mdss_mdp_pipe_unmap(rot_pipe);
+ tmp = mdss_mdp_ctl_mixer_switch(mixer->ctl,
+ MDSS_MDP_WB_CTL_TYPE_BLOCK);
+ if (!tmp)
+ return -EINVAL;
+ else
+ mixer = tmp->mixer_left;
mdss_mdp_wb_mixer_destroy(mixer);
}
-
- return 0;
+ return ret;
}
-int mdss_mdp_rotator_release(u32 ndx)
+int mdss_mdp_rotator_release(struct mdss_mdp_rotator_session *rot)
{
int rc = 0;
- struct mdss_mdp_rotator_session *rot;
+
mutex_lock(&rotator_lock);
- rot = mdss_mdp_rotator_session_get(ndx);
- if (rot) {
- mdss_mdp_rotator_finish(rot);
- } else {
- pr_warn("unknown session id=%x\n", ndx);
- rc = -ENOENT;
- }
+ rc = mdss_mdp_rotator_finish(rot);
mutex_unlock(&rotator_lock);
return rc;
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h
index 3401fe8..43c9e6a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.h
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.h
@@ -39,6 +39,9 @@
u8 busy;
u8 no_wait;
+ struct mdss_mdp_data src_buf;
+ struct mdss_mdp_data dst_buf;
+
struct list_head head;
struct mdss_mdp_rotator_session *next;
};
@@ -67,7 +70,7 @@
struct mdss_mdp_data *src_data,
struct mdss_mdp_data *dst_data);
-int mdss_mdp_rotator_release(u32 ndx);
+int mdss_mdp_rotator_release(struct mdss_mdp_rotator_session *rot);
int mdss_mdp_rotator_release_all(void);
#endif /* MDSS_MDP_ROTATOR_H */
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index b29b0eb..b65d894 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -41,9 +41,11 @@
MDP_INTR_PING_PONG_0,
MDP_INTR_PING_PONG_1,
MDP_INTR_PING_PONG_2,
+ MDP_INTR_PING_PONG_3,
MDP_INTR_PING_PONG_0_RD_PTR,
MDP_INTR_PING_PONG_1_RD_PTR,
MDP_INTR_PING_PONG_2_RD_PTR,
+ MDP_INTR_PING_PONG_3_RD_PTR,
MDP_INTR_WB_0,
MDP_INTR_WB_1,
MDP_INTR_WB_2,
@@ -163,6 +165,9 @@
if (isr & MDSS_MDP_INTR_PING_PONG_2_DONE)
mdss_mdp_intr_done(MDP_INTR_PING_PONG_2);
+ if (isr & MDSS_MDP_INTR_PING_PONG_3_DONE)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_3);
+
if (isr & MDSS_MDP_INTR_PING_PONG_0_RD_PTR)
mdss_mdp_intr_done(MDP_INTR_PING_PONG_0_RD_PTR);
@@ -172,6 +177,9 @@
if (isr & MDSS_MDP_INTR_PING_PONG_2_RD_PTR)
mdss_mdp_intr_done(MDP_INTR_PING_PONG_2_RD_PTR);
+ if (isr & MDSS_MDP_INTR_PING_PONG_3_RD_PTR)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_3_RD_PTR);
+
if (isr & MDSS_MDP_INTR_INTF_0_VSYNC) {
mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_0);
mdss_misr_crc_collect(mdata, DISPLAY_MISR_EDP);
@@ -561,3 +569,23 @@
return ret;
}
+
+int mdss_mdp_calc_phase_step(u32 src, u32 dst, u32 *out_phase)
+{
+ u32 unit, residue;
+
+ if (dst == 0)
+ return -EINVAL;
+
+ unit = 1 << PHASE_STEP_SHIFT;
+ *out_phase = mult_frac(src, unit, dst);
+
+ /* check if overflow is possible */
+ if (src > dst) {
+ residue = *out_phase & (unit - 1);
+ if (residue && ((residue * dst) < (unit - residue)))
+ return -EOVERFLOW;
+ }
+
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index a9deef2..ff54067 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -527,7 +527,7 @@
goto kickoff_fail;
}
- ret = mdss_mdp_display_commit(ctl, &wb_args);
+ ret = mdss_mdp_writeback_display_commit(ctl, &wb_args);
if (ret) {
pr_err("error on commit ctl=%d\n", ctl->num);
goto kickoff_fail;
diff --git a/drivers/video/msm/mdss/mhl_sii8334.c b/drivers/video/msm/mdss/mhl_sii8334.c
index 3f03725..82b56e3 100644
--- a/drivers/video/msm/mdss/mhl_sii8334.c
+++ b/drivers/video/msm/mdss/mhl_sii8334.c
@@ -373,7 +373,7 @@
/* USB_HANDSHAKING FUNCTIONS */
static int mhl_sii_device_discovery(void *data, int id,
- void (*usb_notify_cb)(int online))
+ void (*usb_notify_cb)(void *, int), void *ctx)
{
int rc;
struct mhl_tx_ctrl *mhl_ctrl = data;
@@ -398,8 +398,10 @@
return -EINVAL;
}
- if (!mhl_ctrl->notify_usb_online)
+ if (!mhl_ctrl->notify_usb_online) {
mhl_ctrl->notify_usb_online = usb_notify_cb;
+ mhl_ctrl->notify_ctx = ctx;
+ }
if (!mhl_ctrl->disc_enabled) {
spin_lock_irqsave(&mhl_ctrl->lock, flags);
@@ -904,7 +906,7 @@
mhl_ctrl->mhl_mode = 1;
power_supply_changed(&mhl_ctrl->mhl_psy);
if (mhl_ctrl->notify_usb_online)
- mhl_ctrl->notify_usb_online(1);
+ mhl_ctrl->notify_usb_online(mhl_ctrl->notify_ctx, 1);
} else {
pr_debug("%s: non-mhl sink\n", __func__);
mhl_ctrl->mhl_mode = 0;
@@ -1004,7 +1006,7 @@
mhl_msm_disconnection(mhl_ctrl);
power_supply_changed(&mhl_ctrl->mhl_psy);
if (mhl_ctrl->notify_usb_online)
- mhl_ctrl->notify_usb_online(0);
+ mhl_ctrl->notify_usb_online(mhl_ctrl->notify_ctx, 0);
return 0;
}
@@ -1019,7 +1021,7 @@
mhl_msm_disconnection(mhl_ctrl);
power_supply_changed(&mhl_ctrl->mhl_psy);
if (mhl_ctrl->notify_usb_online)
- mhl_ctrl->notify_usb_online(0);
+ mhl_ctrl->notify_usb_online(mhl_ctrl->notify_ctx, 0);
return 0;
}
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 5f52690..03efb50 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
#ifdef CONFIG_GPIOLIB
@@ -47,6 +48,26 @@
struct module;
struct device_node;
+#ifdef CONFIG_PINCTRL
+/**
+ * struct gpio_pin_range - pin range controlled by a gpio chip
+ * @head: list for maintaining set of pin ranges, used internally
+ * @pctldev: pinctrl device which handles corresponding pins
+ * @range: actual range of pins controlled by a gpio controller
+ */
+
+struct gpio_pin_range {
+ struct list_head node;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_gpio_range range;
+};
+
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins);
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
+
+#endif
+
/**
* struct gpio_chip - abstract a GPIO controller
* @label: for diagnostics
@@ -132,6 +153,15 @@
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
+#ifdef CONFIG_PINCTRL
+ /*
+ * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
+ * describe the actual pin range which they serve in an SoC. This
+ * information would be used by pinctrl subsystem to configure
+ * corresponding pins for gpio usage.
+ */
+ struct list_head pin_ranges;
+#endif
};
extern const char *gpiochip_is_requested(struct gpio_chip *chip,
diff --git a/include/linux/android_pmem.h b/include/linux/android_pmem.h
index cfca491..f338d15 100644
--- a/include/linux/android_pmem.h
+++ b/include/linux/android_pmem.h
@@ -151,10 +151,6 @@
* indicates that this region should be mapped/unmaped as needed
*/
int map_on_demand;
- /*
- * indicates this pmem may be reused via fmem
- */
- int reusable;
};
int pmem_setup(struct android_pmem_platform_data *pdata,
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 2b4542a..130d0fd 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -152,6 +152,7 @@
__REQ_MIXED_MERGE, /* merge of different types, fail separately */
__REQ_SANITIZE, /* sanitize */
__REQ_URGENT, /* urgent request */
+ __REQ_PM, /* runtime pm request */
__REQ_NR_BITS, /* stops here */
};
@@ -196,5 +197,6 @@
#define REQ_IO_STAT (1 << __REQ_IO_STAT)
#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
#define REQ_SECURE (1 << __REQ_SECURE)
+#define REQ_PM (1 << __REQ_PM)
#endif /* __LINUX_BLK_TYPES_H */
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 6502841..651a0fc 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -340,6 +340,12 @@
*/
struct kobject kobj;
+#ifdef CONFIG_PM_RUNTIME
+ struct device *dev;
+ int rpm_status;
+ unsigned int nr_pending;
+#endif
+
/*
* queue settings
*/
@@ -883,6 +889,27 @@
extern void blk_put_queue(struct request_queue *);
/*
+ * block layer runtime pm functions
+ */
+#ifdef CONFIG_PM_RUNTIME
+extern void blk_pm_runtime_init(struct request_queue *q, struct device *dev);
+extern int blk_pre_runtime_suspend(struct request_queue *q);
+extern void blk_post_runtime_suspend(struct request_queue *q, int err);
+extern void blk_pre_runtime_resume(struct request_queue *q);
+extern void blk_post_runtime_resume(struct request_queue *q, int err);
+#else
+static inline void blk_pm_runtime_init(struct request_queue *q,
+ struct device *dev) {}
+static inline int blk_pre_runtime_suspend(struct request_queue *q)
+{
+ return -ENOSYS;
+}
+static inline void blk_post_runtime_suspend(struct request_queue *q, int err) {}
+static inline void blk_pre_runtime_resume(struct request_queue *q) {}
+static inline void blk_post_runtime_resume(struct request_queue *q, int err) {}
+#endif
+
+/*
* blk_plug permits building a queue of related requests by holding the I/O
* fragments for a short period. This allows merging of sequential requests
* into single larger request. As the requests are moved from a per-task list to
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
index 51a90b7..ee1c2ff 100644
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -22,8 +22,9 @@
extern int fragmentation_index(struct zone *zone, unsigned int order);
extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *mask,
- bool sync);
+ bool sync, bool *contended);
extern int compact_pgdat(pg_data_t *pgdat, int order);
+extern void reset_isolation_suitable(pg_data_t *pgdat);
extern unsigned long compaction_suitable(struct zone *zone, int order);
/* Do not skip compaction more than 64 times */
@@ -61,10 +62,20 @@
return zone->compact_considered < (1UL << zone->compact_defer_shift);
}
+/* Returns true if restarting compaction after many failures */
+static inline bool compaction_restarting(struct zone *zone, int order)
+{
+ if (order < zone->compact_order_failed)
+ return false;
+
+ return zone->compact_defer_shift == COMPACT_MAX_DEFER_SHIFT &&
+ zone->compact_considered >= 1UL << zone->compact_defer_shift;
+}
+
#else
static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync)
+ bool sync, bool *contended)
{
return COMPACT_CONTINUE;
}
@@ -74,6 +85,10 @@
return COMPACT_CONTINUE;
}
+static inline void reset_isolation_suitable(pg_data_t *pgdat)
+{
+}
+
static inline unsigned long compaction_suitable(struct zone *zone, int order)
{
return COMPACT_SKIPPED;
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index cfc690e..5b69884 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -39,8 +39,8 @@
enum coresight_clk_rate {
CORESIGHT_CLK_RATE_OFF,
- CORESIGHT_CLK_RATE_TRACE,
- CORESIGHT_CLK_RATE_HSTRACE,
+ CORESIGHT_CLK_RATE_TRACE = 1000,
+ CORESIGHT_CLK_RATE_HSTRACE = 2000,
};
enum coresight_dev_type {
diff --git a/include/linux/device.h b/include/linux/device.h
index 810337c..35862c3 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -21,6 +21,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/mutex.h>
+#include <linux/pinctrl/devinfo.h>
#include <linux/pm.h>
#include <linux/atomic.h>
#include <asm/device.h>
@@ -595,6 +596,8 @@
* @pm_domain: Provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions
* along with subsystem-level and driver-level callbacks.
+ * @pins: For device pin management.
+ * See Documentation/pinctrl.txt for details.
* @numa_node: NUMA node this device is close to.
* @dma_mask: Dma mask (if dma'ble device).
* @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
@@ -646,6 +649,10 @@
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
+#ifdef CONFIG_PINCTRL
+ struct dev_pin_info *pins;
+#endif
+
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 73b94af..0739ece 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -27,7 +27,6 @@
#define UART_MODE 4
#define SOCKET_MODE 5
#define CALLBACK_MODE 6
-#define MEMORY_DEVICE_MODE_NRT 7
/* different values that go in for diag_data_type */
#define DATA_TYPE_EVENT 0
#define DATA_TYPE_F3 1
@@ -50,6 +49,8 @@
#define DIAG_IOCTL_DCI_CLEAR_LOGS 28
#define DIAG_IOCTL_DCI_CLEAR_EVENTS 29
#define DIAG_IOCTL_REMOTE_DEV 32
+#define DIAG_IOCTL_VOTE_REAL_TIME 33
+#define DIAG_IOCTL_GET_REAL_TIME 34
/* PC Tools IDs */
#define APQ8060_TOOLS_ID 4062
@@ -118,7 +119,7 @@
/* 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 0x09B2
+#define EVENT_LAST_ID 0x09CB
#define MSG_SSID_0 0
#define MSG_SSID_0_LAST 97
@@ -135,9 +136,9 @@
#define MSG_SSID_6 4500
#define MSG_SSID_6_LAST 4526
#define MSG_SSID_7 4600
-#define MSG_SSID_7_LAST 4613
+#define MSG_SSID_7_LAST 4614
#define MSG_SSID_8 5000
-#define MSG_SSID_8_LAST 5029
+#define MSG_SSID_8_LAST 5030
#define MSG_SSID_9 5500
#define MSG_SSID_9_LAST 5516
#define MSG_SSID_10 6000
@@ -178,7 +179,7 @@
static const uint32_t msg_bld_masks_0[] = {
MSG_LVL_LOW,
MSG_LVL_MED,
- MSG_LVL_MED,
+ MSG_LVL_LOW,
MSG_LVL_ERROR,
MSG_LVL_LOW,
MSG_LVL_MED,
@@ -186,20 +187,20 @@
MSG_LVL_HIGH,
MSG_LVL_ERROR,
MSG_LVL_LOW,
- MSG_LVL_ERROR,
+ MSG_LVL_LOW,
MSG_LVL_ERROR,
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_HIGH,
MSG_LVL_HIGH,
- MSG_LVL_HIGH,
+ MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_ERROR,
MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_MED,
- MSG_LVL_MED,
+ MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_LOW,
MSG_LVL_MED,
@@ -214,7 +215,7 @@
MSG_MASK_6|MSG_MASK_7|MSG_MASK_8|MSG_MASK_9|MSG_MASK_10| \
MSG_MASK_11|MSG_MASK_12|MSG_MASK_13|MSG_MASK_14| \
MSG_MASK_15|MSG_MASK_16|MSG_MASK_17,
- MSG_LVL_MED,
+ MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_HIGH,
MSG_LVL_HIGH,
@@ -238,7 +239,7 @@
MSG_LVL_MED|MSG_MASK_5 | \
MSG_MASK_6|MSG_MASK_7|MSG_MASK_8|MSG_MASK_9|MSG_MASK_10,
MSG_LVL_MED,
- MSG_LVL_MED,
+ MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_LOW,
@@ -270,7 +271,7 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
- MSG_LVL_HIGH,
+ MSG_LVL_HIGH | MSG_MASK_21,
MSG_LVL_HIGH,
MSG_LVL_LOW,
MSG_LVL_LOW,
@@ -290,7 +291,7 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL,
- MSG_LVL_LOW
+ MSG_LVL_MED,
};
static const uint32_t msg_bld_masks_1[] = {
@@ -403,6 +404,7 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
MSG_LVL_LOW
};
@@ -436,6 +438,7 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
+ MSG_LVL_MED,
MSG_LVL_MED
};
@@ -725,7 +728,7 @@
/* LOG CODES */
#define LOG_0 0x0
-#define LOG_1 0x17FA
+#define LOG_1 0x1808
#define LOG_2 0x0
#define LOG_3 0x0
#define LOG_4 0x4910
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index bd954ee..dd675f3 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -531,6 +531,9 @@
/* Indicates whether TS insertion is supported */
#define DMX_CAP_TS_INSERTION 0x20
+/* Indicates whether playback from secured input is supported */
+#define DMX_CAP_SECURED_INPUT_PLAYBACK 0x40
+
/* Number of decoders demux can output data to */
int num_decoders;
@@ -570,6 +573,12 @@
/* Max bitrate from single memory input. Mbit/sec */
int memory_input_max_bitrate;
+ /* Max number of supported cipher operations per PID */
+ int num_cipher_ops;
+
+ /* Max possible value of STC reported by demux, in 27MHz */
+ __u64 max_stc;
+
struct dmx_buffer_requirement section;
/* For PES not sent to decoder */
@@ -657,6 +666,15 @@
struct dmx_buffer {
unsigned int size;
int handle;
+
+ /*
+ * The following indication is relevant only when setting
+ * DVR input buffer. It indicates whether the input buffer
+ * being set is secured one or not. Secured (locked) buffers
+ * are required for playback from secured input. In such case
+ * write() syscall is not allowed.
+ */
+ int is_protected;
};
struct dmx_decoder_buffers {
@@ -681,16 +699,41 @@
struct dmx_secure_mode {
/*
- * Specifies whether secure mode should be set or not for the filter's
- * pid. Note that DMX_OUT_TSDEMUX_TAP filters can have more than 1 pid
+ * Specifies whether the filter is secure or not.
+ * Filter should be set as secured if the filter's data *may* include
+ * encrypted data that would require decryption configured through
+ * DMX_SET_CIPHER ioctl. The setting may be done while
+ * filter is in idle state only.
*/
int is_secured;
+};
- /* PID to associate with key ladder id */
+struct dmx_cipher_operation {
+ /* Indication whether the operation is encryption or decryption */
+ int encrypt;
+
+ /* The ID of the key used for decryption or encryption */
+ __u32 key_ladder_id;
+};
+
+#define DMX_MAX_CIPHER_OPERATIONS_COUNT 5
+struct dmx_cipher_operations {
+ /*
+ * The PID to perform the cipher operations on.
+ * In case of recording filter, multiple PIDs may exist in the same
+ * filter through DMX_ADD_PID ioctl, each may have different
+ * cipher operations.
+ */
__u16 pid;
- /* key ladder information to associate with the specified pid */
- __u32 key_ladder_id;
+ /* Total number of operations */
+ __u8 operations_count;
+
+ /*
+ * Cipher operation to perform on the given PID.
+ * The operations are performed in the order they are given.
+ */
+ struct dmx_cipher_operation operations[DMX_MAX_CIPHER_OPERATIONS_COUNT];
};
struct dmx_events_mask {
@@ -822,5 +865,7 @@
#define DMX_SET_TS_INSERTION _IOW('o', 70, struct dmx_set_ts_insertion)
#define DMX_ABORT_TS_INSERTION _IOW('o', 71, struct dmx_abort_ts_insertion)
#define DMX_GET_SCRAMBLING_BITS _IOWR('o', 72, struct dmx_scrambling_bits)
+#define DMX_SET_CIPHER _IOW('o', 73, struct dmx_cipher_operations)
+
#endif /*_DVBDMX_H_*/
diff --git a/include/linux/epm_adc.h b/include/linux/epm_adc.h
index 4fa41b5..6f00df7 100644
--- a/include/linux/epm_adc.h
+++ b/include/linux/epm_adc.h
@@ -97,6 +97,23 @@
uint32_t gain;
};
+struct epm_marker_level {
+ uint8_t level;
+};
+
+struct epm_gpio_buffer_request {
+ uint8_t cmd;
+ uint8_t bitmask_monitor_pin;
+ uint8_t status;
+};
+
+struct epm_get_gpio_buffer_resp {
+ uint8_t cmd;
+ uint8_t status;
+ uint8_t bitmask_monitor_pin;
+ uint32_t timestamp;
+};
+
#ifdef __KERNEL__
struct epm_adc_platform_data {
struct epm_chan_properties *channel;
@@ -121,7 +138,19 @@
#define EPM_ADC_DEINIT _IOR(EPM_ADC_IOCTL_CODE, 3, \
uint32_t)
-#define EPM_PSOC_ADC_INIT _IOR(EPM_ADC_IOCTL_CODE, 4, \
+#define EPM_MARKER1_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 90, \
+ uint32_t)
+
+#define EPM_MARKER1_RELEASE _IOWR(EPM_ADC_IOCTL_CODE, 91, \
+ uint32_t)
+
+#define EPM_MARKER2_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 95, \
+ uint32_t)
+
+#define EPM_MARKER2_RELEASE _IOWR(EPM_ADC_IOCTL_CODE, 92, \
+ uint32_t)
+
+#define EPM_PSOC_ADC_INIT _IOWR(EPM_ADC_IOCTL_CODE, 4, \
struct epm_psoc_init_resp)
#define EPM_PSOC_ADC_CHANNEL_ENABLE _IOWR(EPM_ADC_IOCTL_CODE, 5, \
@@ -157,4 +186,13 @@
#define EPM_PSOC_ADC_SET_VADC_REFERENCE _IOWR(EPM_ADC_IOCTL_CODE, 15, \
struct epm_psoc_set_vadc)
+#define EPM_PSOC_ADC_DEINIT _IOWR(EPM_ADC_IOCTL_CODE, 16, \
+ uint32_t)
+
+#define EPM_PSOC_GPIO_BUFFER_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 17, \
+ uint32_t)
+
+#define EPM_PSOC_GET_GPIO_BUFFER_DATA _IOWR(EPM_ADC_IOCTL_CODE, 18, \
+ uint32_t)
+
#endif /* __EPM_ADC_H */
diff --git a/include/linux/fmem.h b/include/linux/fmem.h
deleted file mode 100644
index cda4a0f..0000000
--- a/include/linux/fmem.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * 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 _FMEM_H_
-#define _FMEM_H_
-
-#include <linux/vmalloc.h>
-
-struct fmem_platform_data {
- unsigned long phys;
- unsigned long size;
- unsigned long reserved_size_low;
- unsigned long reserved_size_high;
- unsigned long align;
-};
-
-struct fmem_data {
- unsigned long phys;
- void *virt;
- struct vm_struct *area;
- unsigned long size;
- unsigned long reserved_size_low;
- unsigned long reserved_size_high;
-};
-
-enum fmem_state {
- FMEM_UNINITIALIZED = 0,
- FMEM_C_STATE,
- FMEM_T_STATE,
- FMEM_O_STATE,
-};
-
-#ifdef CONFIG_QCACHE
-struct fmem_data *fmem_get_info(void);
-int fmem_set_state(enum fmem_state);
-void lock_fmem_state(void);
-void unlock_fmem_state(void);
-void *fmem_map_virtual_area(int cacheability);
-void fmem_unmap_virtual_area(void);
-#else
-static inline struct fmem_data *fmem_get_info(void) { return NULL; }
-static inline int fmem_set_state(enum fmem_state f) { return -ENODEV; }
-static inline void lock_fmem_state(void) { return; }
-static inline void unlock_fmem_state(void) { return; }
-static inline void *fmem_map_virtual_area(int cacheability) { return NULL; }
-static inline void fmem_unmap_virtual_area(void) { return; }
-#endif
-
-int request_fmem_c_region(void *unused);
-int release_fmem_c_region(void *unused);
-#endif
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 6155ecf..eda50bd 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -172,6 +172,21 @@
return -EINVAL;
}
-#endif
+#ifdef CONFIG_PINCTRL
+
+static inline int
+gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins)
+{
+}
+
+static inline void
+gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+}
+
+#endif /* CONFIG_PINCTRL */
+
+#endif /* ! CONFIG_GENERIC_GPIO */
#endif /* __LINUX_GPIO_H */
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index b903dfb..e9fbf90 100644
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ b/include/linux/i2c/atmel_mxt_ts.h
@@ -77,6 +77,7 @@
int *key_codes;
bool need_calibration;
bool no_force_update;
+ bool no_lpm_support;
u8 bl_addr;
u8(*read_chg) (void);
diff --git a/include/linux/input/kxtj9.h b/include/linux/input/kxtj9.h
index d415579..65378c0 100644
--- a/include/linux/input/kxtj9.h
+++ b/include/linux/input/kxtj9.h
@@ -46,7 +46,7 @@
/* Output resolution: 8-bit valid or 12-bit valid */
#define RES_8BIT 0
#define RES_12BIT (1 << 6)
- u8 res_12bit;
+ u8 res_ctl;
/* Output g-range: +/-2g, 4g, or 8g */
#define KXTJ9_G_2G 0
#define KXTJ9_G_4G (1 << 3)
diff --git a/include/linux/input/lis3dh.h b/include/linux/input/lis3dh.h
index f081b06..7c0172f 100644
--- a/include/linux/input/lis3dh.h
+++ b/include/linux/input/lis3dh.h
@@ -37,7 +37,7 @@
#define LIS3DH_ACC_I2C_SAD_L ((LIS3DH_ACC_I2C_SADROOT<<1)|SAD0L)
#define LIS3DH_ACC_I2C_SAD_H ((LIS3DH_ACC_I2C_SADROOT<<1)|SAD0H)
#define LIS3DH_ACC_DEV_NAME "lis3dh_acc"
-
+#define ACCEL_INPUT_DEV_NAME "accelerometer"
/************************************************/
/* Accelerometer defines section */
diff --git a/include/linux/input/mpu3050.h b/include/linux/input/mpu3050.h
index 61a2920..f87dfe3 100644
--- a/include/linux/input/mpu3050.h
+++ b/include/linux/input/mpu3050.h
@@ -25,6 +25,7 @@
int (*power_off)(void);
int gpio_int;
+ int gpio_en;
int gpio_fsync;
};
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index f94efd2..14d38a5 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -66,7 +66,6 @@
struct lruvec *mem_cgroup_lru_add_list(struct zone *, struct page *,
enum lru_list);
void mem_cgroup_lru_del_list(struct page *, enum lru_list);
-void mem_cgroup_lru_del(struct page *);
struct lruvec *mem_cgroup_lru_move_lists(struct zone *, struct page *,
enum lru_list, enum lru_list);
@@ -79,6 +78,8 @@
extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
int order);
+bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
+ struct mem_cgroup *memcg);
int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg);
extern struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page);
@@ -92,10 +93,13 @@
int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup)
{
struct mem_cgroup *memcg;
+ int match;
+
rcu_read_lock();
memcg = mem_cgroup_from_task(rcu_dereference((mm)->owner));
+ match = __mem_cgroup_same_or_subtree(cgroup, memcg);
rcu_read_unlock();
- return cgroup == memcg;
+ return match;
}
extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg);
@@ -262,10 +266,6 @@
{
}
-static inline void mem_cgroup_lru_del(struct page *page)
-{
-}
-
static inline struct lruvec *mem_cgroup_lru_move_lists(struct zone *zone,
struct page *page,
enum lru_list from,
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index 412341a..bf76026 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -276,5 +276,37 @@
#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH (0x331)
#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH__POR (0x00)
+#define WCD9XXX_A_CDC_RX1_B6_CTL (0x2B5)
+#define WCD9XXX_A_CDC_RX1_B6_CTL__POR (0x80)
+#define WCD9XXX_A_CDC_RX2_B6_CTL (0x2BD)
+#define WCD9XXX_A_CDC_RX2_B6_CTL__POR (0x80)
+#define WCD9XXX_A_RX_HPH_L_GAIN (0x1AE)
+#define WCD9XXX_A_RX_HPH_L_GAIN__POR (0x00)
+#define WCD9XXX_A_RX_HPH_R_GAIN (0x1B4)
+#define WCD9XXX_A_RX_HPH_R_GAIN__POR (0x00)
+#define WCD9XXX_A_RX_HPH_CHOP_CTL (0x1A5)
+#define WCD9XXX_A_RX_HPH_CHOP_CTL__POR (0xB4)
+#define WCD9XXX_A_RX_HPH_L_TEST (0x1AF)
+#define WCD9XXX_A_RX_HPH_L_TEST__POR (0x00)
+#define WCD9XXX_A_RX_HPH_R_TEST (0x1B5)
+#define WCD9XXX_A_RX_HPH_R_TEST__POR (0x00)
+#define WCD9XXX_A_CDC_CLK_RX_B1_CTL (0x30F)
+#define WCD9XXX_A_CDC_CLK_RX_B1_CTL__POR (0x00)
+#define WCD9XXX_A_NCP_CLK (0x193)
+#define WCD9XXX_A_NCP_CLK__POR (0x94)
+#define WCD9XXX_A_RX_HPH_BIAS_WG_OCP (0x1A9)
+#define WCD9XXX_A_RX_HPH_BIAS_WG_OCP__POR (0x2A)
+#define WCD9XXX_A_RX_HPH_CNP_WG_CTL (0x1AC)
+#define WCD9XXX_A_RX_HPH_CNP_WG_CTL__POR (0xDE)
+#define WCD9XXX_A_CDC_CONN_RX2_B1_CTL (0x383)
+#define WCD9XXX_A_CDC_CONN_RX2_B1_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_PA_RAMP_B1_CTL (0x361)
+#define WCD9XXX_A_CDC_PA_RAMP_B1_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_PA_RAMP_B2_CTL (0x362)
+#define WCD9XXX_A_CDC_PA_RAMP_B2_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_PA_RAMP_B3_CTL (0x363)
+#define WCD9XXX_A_CDC_PA_RAMP_B3_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_PA_RAMP_B4_CTL (0x364)
+#define WCD9XXX_A_CDC_PA_RAMP_B4_CTL__POR (0x00)
#endif
diff --git a/include/linux/mhl_8334.h b/include/linux/mhl_8334.h
index 7297ff3..560f75b 100644
--- a/include/linux/mhl_8334.h
+++ b/include/linux/mhl_8334.h
@@ -137,7 +137,8 @@
uint8_t chip_rev_id;
int mhl_mode;
struct completion rgnd_done;
- void (*notify_usb_online)(int online);
+ void (*notify_usb_online)(void *ctx, int online);
+ void *notify_ctx;
struct usb_ext_notification *mhl_info;
bool disc_enabled;
struct power_supply mhl_psy;
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 3cc3062..b98b4b9 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -344,17 +344,6 @@
/* Architecture-specific MM context */
mm_context_t context;
- /* Swap token stuff */
- /*
- * Last value of global fault stamp as seen by this process.
- * In other words, this value gives an indication of how long
- * it has been since this task got the token.
- * Look at mm/thrash.c
- */
- unsigned int faultstamp;
- unsigned int token_priority;
- unsigned int last_interval;
-
unsigned long flags; /* Must use atomic bitops to access the bits */
struct core_state *core_state; /* coredumping support */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index f548721..e3ff5db 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -99,8 +99,8 @@
*/
unsigned int cmd_timeout_ms; /* in milliseconds */
- /* Set this flag only for blocking bkops request */
- bool bkops_busy;
+ /* Set this flag only for commands which can be HPIed */
+ bool ignore_timeout;
struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */
@@ -162,6 +162,8 @@
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool,
bool);
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
+extern int mmc_switch_ignore_timeout(struct mmc_card *, u8, u8, u8,
+ unsigned int);
extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
#define MMC_ERASE_ARG 0x00000000
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index d63232a..7539e03 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -204,16 +204,14 @@
#define LRU_ALL_EVICTABLE (LRU_ALL_FILE | LRU_ALL_ANON)
#define LRU_ALL ((1 << NR_LRU_LISTS) - 1)
-/* Isolate inactive pages */
-#define ISOLATE_INACTIVE ((__force isolate_mode_t)0x1)
-/* Isolate active pages */
-#define ISOLATE_ACTIVE ((__force isolate_mode_t)0x2)
/* Isolate clean file */
-#define ISOLATE_CLEAN ((__force isolate_mode_t)0x4)
+#define ISOLATE_CLEAN ((__force isolate_mode_t)0x1)
/* Isolate unmapped file */
-#define ISOLATE_UNMAPPED ((__force isolate_mode_t)0x8)
+#define ISOLATE_UNMAPPED ((__force isolate_mode_t)0x2)
/* Isolate for asynchronous migration */
-#define ISOLATE_ASYNC_MIGRATE ((__force isolate_mode_t)0x10)
+#define ISOLATE_ASYNC_MIGRATE ((__force isolate_mode_t)0x4)
+/* Isolate unevictable pages */
+#define ISOLATE_UNEVICTABLE ((__force isolate_mode_t)0x8)
/* LRU Isolation modes. */
typedef unsigned __bitwise__ isolate_mode_t;
@@ -378,6 +376,14 @@
*/
spinlock_t lock;
int all_unreclaimable; /* All pages pinned */
+#if defined CONFIG_COMPACTION || defined CONFIG_CMA
+ /* Set to true when the PG_migrate_skip bits should be cleared */
+ bool compact_blockskip_flush;
+
+ /* pfns where compaction scanners should start */
+ unsigned long compact_cached_free_pfn;
+ unsigned long compact_cached_migrate_pfn;
+#endif
#ifdef CONFIG_MEMORY_HOTPLUG
/* see spanned/present_pages for more description */
seqlock_t span_seqlock;
diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h
index a741107..e8ca1cd 100644
--- a/include/linux/msm_audio_acdb.h
+++ b/include/linux/msm_audio_acdb.h
@@ -54,12 +54,16 @@
#define AUDIO_SET_ASM_CUSTOM_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \
(AUDIO_MAX_COMMON_IOCTL_NUM+24), unsigned)
#define AUDIO_SET_SPEAKER_PROT _IOW(AUDIO_IOCTL_MAGIC, 25, \
- struct msm_spk_prot_cfg)
+ struct msm_spk_prot_cfg)
#define AUDIO_GET_SPEAKER_PROT _IOR(AUDIO_IOCTL_MAGIC, 26, \
- struct msm_spk_prot_status)
+ struct msm_spk_prot_status)
#define AUDIO_SET_AANC_CAL _IOW(AUDIO_IOCTL_MAGIC, \
(AUDIO_MAX_COMMON_IOCTL_NUM+27), unsigned)
-#define AUDIO_MAX_ACDB_IOCTL (AUDIO_MAX_COMMON_IOCTL_NUM+30)
+#define AUDIO_REGISTER_VOCPROC_VOL_TABLE _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+28), unsigned)
+#define AUDIO_DEREGISTER_VOCPROC_VOL_TABLE _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+29), unsigned)
+#define AUDIO_MAX_ACDB_IOCTL (AUDIO_MAX_COMMON_IOCTL_NUM+40)
/* ACDB structures */
struct cal_block {
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index 20b7317..6a8633b 100644
--- a/include/linux/msm_ion.h
+++ b/include/linux/msm_ion.h
@@ -97,7 +97,6 @@
#define ION_PIL1_HEAP_NAME "pil_1"
#define ION_PIL2_HEAP_NAME "pil_2"
#define ION_QSECOM_HEAP_NAME "qsecom"
-#define ION_FMEM_HEAP_NAME "fmem"
#define ION_SET_CACHED(__cache) (__cache | ION_FLAG_CACHED)
#define ION_SET_UNCACHED(__cache) (__cache & ~ION_FLAG_CACHED)
@@ -129,12 +128,7 @@
* @secure_size: Memory size for securing the heap.
* Note: This might be different from actual size
* of this heap in the case of a shared heap.
- * @reusable Flag indicating whether this heap is reusable of not.
- * (see FMEM)
- * @mem_is_fmem Flag indicating whether this memory is coming from fmem
- * or not.
* @fixed_position If nonzero, position in the fixed area.
- * @virt_addr: Virtual address used when using fmem.
* @iommu_map_all: Indicates whether we should map whole heap into IOMMU.
* @iommu_2x_map_domain: Indicates the domain to use for overmapping.
* @request_region: function to be called when the number of allocations
@@ -153,13 +147,10 @@
unsigned int align;
ion_phys_addr_t secure_base; /* Base addr used when heap is shared */
size_t secure_size; /* Size used for securing heap when heap is shared*/
- int reusable;
- int mem_is_fmem;
int is_cma;
enum ion_fixed_position fixed_position;
int iommu_map_all;
int iommu_2x_map_domain;
- void *virt_addr;
int (*request_region)(void *);
int (*release_region)(void *);
void *(*setup_region)(void);
@@ -171,8 +162,6 @@
* struct ion_co_heap_pdata - defines a carveout heap in the given platform
* @adjacent_mem_id: Id of heap that this heap must be adjacent to.
* @align: Alignment requirement for the memory
- * @mem_is_fmem Flag indicating whether this memory is coming from fmem
- * or not.
* @fixed_position If nonzero, position in the fixed area.
* @request_region: function to be called when the number of allocations
* goes from 0 -> 1
@@ -185,7 +174,6 @@
struct ion_co_heap_pdata {
int adjacent_mem_id;
unsigned int align;
- int mem_is_fmem;
enum ion_fixed_position fixed_position;
int (*request_region)(void *);
int (*release_region)(void *);
diff --git a/include/linux/msm_ipa.h b/include/linux/msm_ipa.h
index 5151654..b2229d3 100644
--- a/include/linux/msm_ipa.h
+++ b/include/linux/msm_ipa.h
@@ -87,6 +87,7 @@
#define IPA_FLT_NEXT_HDR (1ul << 13)
#define IPA_FLT_META_DATA (1ul << 14)
#define IPA_FLT_FRAGMENT (1ul << 15)
+#define IPA_FLT_TOS_MASKED (1ul << 16)
/**
* enum ipa_client_type - names for the various IPA "clients"
@@ -243,6 +244,8 @@
uint16_t dst_port_hi;
uint8_t type;
uint8_t code;
+ uint8_t tos_value;
+ uint8_t tos_mask;
uint32_t spi;
uint16_t src_port;
uint16_t dst_port;
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index ae88807..f8b78a4 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -227,6 +227,7 @@
#define KGSL_PERFCOUNTER_GROUP_VBIF_PWR 0xE
#define KGSL_PERFCOUNTER_NOT_USED 0xFFFFFFFF
+#define KGSL_PERFCOUNTER_BROKEN 0xFFFFFFFE
/* structure holds list of ibs */
struct kgsl_ibdesc {
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 18921a0..c3ff9de 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -606,6 +606,12 @@
uint32_t data;
};
+struct mdp_calib_config_buffer {
+ uint32_t ops;
+ uint32_t size;
+ uint32_t *buffer;
+};
+
#define MDSS_MAX_BL_BRIGHTNESS 255
#define AD_BL_LIN_LEN (MDSS_MAX_BL_BRIGHTNESS + 1)
@@ -697,6 +703,7 @@
mdp_op_ad_cfg,
mdp_op_ad_input,
mdp_op_calib_mode,
+ mdp_op_calib_buffer,
mdp_op_max,
};
@@ -724,6 +731,7 @@
struct mdss_ad_init_cfg ad_init_cfg;
struct mdss_calib_cfg mdss_calib_cfg;
struct mdss_ad_input ad_input;
+ struct mdp_calib_config_buffer calib_buffer;
} data;
};
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index c88d2a9..86e4c91 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -108,6 +108,7 @@
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
PG_compound_lock,
#endif
+ PG_readahead, /* page in a readahead window */
__NR_PAGEFLAGS,
/* Filesystems */
diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h
index 19ef95d..be655e4 100644
--- a/include/linux/pageblock-flags.h
+++ b/include/linux/pageblock-flags.h
@@ -30,6 +30,9 @@
PB_migrate,
PB_migrate_end = PB_migrate + 3 - 1,
/* 3 bits required for migrate types */
+#ifdef CONFIG_COMPACTION
+ PB_migrate_skip,/* If set the block is skipped by compaction */
+#endif /* CONFIG_COMPACTION */
NR_PAGEBLOCK_BITS
};
@@ -65,10 +68,22 @@
void set_pageblock_flags_group(struct page *page, unsigned long flags,
int start_bitidx, int end_bitidx);
+#ifdef CONFIG_COMPACTION
+#define get_pageblock_skip(page) \
+ get_pageblock_flags_group(page, PB_migrate_skip, \
+ PB_migrate_skip)
+#define clear_pageblock_skip(page) \
+ set_pageblock_flags_group(page, 0, PB_migrate_skip, \
+ PB_migrate_skip)
+#define set_pageblock_skip(page) \
+ set_pageblock_flags_group(page, 1, PB_migrate_skip, \
+ PB_migrate_skip)
+#endif /* CONFIG_COMPACTION */
+
#define get_pageblock_flags(page) \
- get_pageblock_flags_group(page, 0, NR_PAGEBLOCK_BITS-1)
+ get_pageblock_flags_group(page, 0, PB_migrate_end)
#define set_pageblock_flags(page, flags) \
set_pageblock_flags_group(page, flags, \
- 0, NR_PAGEBLOCK_BITS-1)
+ 0, PB_migrate_end)
#endif /* PAGEBLOCK_FLAGS_H */
diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h
index 191e726..d281645 100644
--- a/include/linux/pinctrl/consumer.h
+++ b/include/linux/pinctrl/consumer.h
@@ -20,6 +20,7 @@
/* This struct is private to the core and should be regarded as a cookie */
struct pinctrl;
struct pinctrl_state;
+struct device;
#ifdef CONFIG_PINCTRL
@@ -36,6 +37,9 @@
const char *name);
extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);
+extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev);
+extern void devm_pinctrl_put(struct pinctrl *p);
+
#else /* !CONFIG_PINCTRL */
static inline int pinctrl_request_gpio(unsigned gpio)
@@ -79,6 +83,15 @@
return 0;
}
+static inline struct pinctrl * __must_check devm_pinctrl_get(struct device *dev)
+{
+ return NULL;
+}
+
+static inline void devm_pinctrl_put(struct pinctrl *p)
+{
+}
+
#endif /* CONFIG_PINCTRL */
static inline struct pinctrl * __must_check pinctrl_get_select(
@@ -113,6 +126,38 @@
return pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT);
}
+static inline struct pinctrl * __must_check devm_pinctrl_get_select(
+ struct device *dev, const char *name)
+{
+ struct pinctrl *p;
+ struct pinctrl_state *s;
+ int ret;
+
+ p = devm_pinctrl_get(dev);
+ if (IS_ERR(p))
+ return p;
+
+ s = pinctrl_lookup_state(p, name);
+ if (IS_ERR(s)) {
+ devm_pinctrl_put(p);
+ return ERR_CAST(s);
+ }
+
+ ret = pinctrl_select_state(p, s);
+ if (ret < 0) {
+ devm_pinctrl_put(p);
+ return ERR_PTR(ret);
+ }
+
+ return p;
+}
+
+static inline struct pinctrl * __must_check devm_pinctrl_get_select_default(
+ struct device *dev)
+{
+ return devm_pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT);
+}
+
#ifdef CONFIG_PINCONF
extern int pin_config_get(const char *dev_name, const char *name,
diff --git a/include/linux/pinctrl/devinfo.h b/include/linux/pinctrl/devinfo.h
new file mode 100644
index 0000000..6e5f8a9
--- /dev/null
+++ b/include/linux/pinctrl/devinfo.h
@@ -0,0 +1,45 @@
+/*
+ * Per-device information from the pin control system.
+ * This is the stuff that get included into the device
+ * core.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ * This interface is used in the core to keep track of pins.
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef PINCTRL_DEVINFO_H
+#define PINCTRL_DEVINFO_H
+
+#ifdef CONFIG_PINCTRL
+
+/* The device core acts as a consumer toward pinctrl */
+#include <linux/pinctrl/consumer.h>
+
+/**
+ * struct dev_pin_info - pin state container for devices
+ * @p: pinctrl handle for the containing device
+ * @default_state: the default state for the handle, if found
+ */
+struct dev_pin_info {
+ struct pinctrl *p;
+ struct pinctrl_state *default_state;
+};
+
+extern int pinctrl_bind_pins(struct device *dev);
+
+#else
+
+/* Stubs if we're not using pinctrl */
+
+static inline int pinctrl_bind_pins(struct device *dev)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PINCTRL */
+#endif /* PINCTRL_DEVINFO_H */
diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h
index e4d1de7..7d22ab0 100644
--- a/include/linux/pinctrl/machine.h
+++ b/include/linux/pinctrl/machine.h
@@ -154,7 +154,7 @@
extern int pinctrl_register_mappings(struct pinctrl_map const *map,
unsigned num_maps);
-
+extern void pinctrl_provide_dummies(void);
#else
static inline int pinctrl_register_mappings(struct pinctrl_map const *map,
@@ -163,5 +163,8 @@
return 0;
}
-#endif /* !CONFIG_PINMUX */
+static inline void pinctrl_provide_dummies(void)
+{
+}
+#endif /* !CONFIG_PINCTRL */
#endif
diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h
index 4f0abb9..9fd5ec5 100644
--- a/include/linux/pinctrl/pinconf-generic.h
+++ b/include/linux/pinctrl/pinconf-generic.h
@@ -29,12 +29,21 @@
* if for example some other pin is going to drive the signal connected
* to it for a while. Pins used for input are usually always high
* impedance.
+ * @PIN_CONFIG_BIAS_BUS_HOLD: the pin will be set to weakly latch so that it
+ * weakly drives the last value on a tristate bus, also known as a "bus
+ * holder", "bus keeper" or "repeater". This allows another device on the
+ * bus to change the value by driving the bus high or low and switching to
+ * tristate. The argument is ignored.
* @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
* impedance to VDD). If the argument is != 0 pull-up is enabled,
* if it is 0, pull-up is disabled.
* @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high
* impedance to GROUND). If the argument is != 0 pull-down is enabled,
* if it is 0, pull-down is disabled.
+ * @PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: the pin will be pulled up or down based
+ * on embedded knowledge of the controller, like current mux function.
+ * If the argument is != 0 pull up/down is enabled, if it is 0,
+ * the pull is disabled.
* @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and
* low, this is the most typical case and is typically achieved with two
* active transistors on the output. Sending this config will enabale
@@ -46,11 +55,15 @@
* @PIN_CONFIG_DRIVE_OPEN_SOURCE: the pin will be driven with open source
* (open emitter). Sending this config will enabale open drain mode, the
* argument is ignored.
+ * @PIN_CONFIG_DRIVE_STRENGTH: the pin will output the current passed as
+ * argument. The argument is in mA.
+ * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
+ * If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
+ * schmitt-trigger mode is disabled.
* @PIN_CONFIG_INPUT_SCHMITT: this will configure an input pin to run in
* schmitt-trigger mode. If the schmitt-trigger has adjustable hysteresis,
* the threshold value is given on a custom format as argument when
- * setting pins to this mode. The argument zero turns the schmitt trigger
- * off.
+ * setting pins to this mode.
* @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode,
* which means it will wait for signals to settle when reading inputs. The
* argument gives the debounce time on a custom format. Setting the
@@ -58,10 +71,15 @@
* @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power
* supplies, the argument to this parameter (on a custom format) tells
* the driver which alternative power source to use.
+ * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
+ * this parameter (on a custom format) tells the driver which alternative
+ * slew rate to use.
* @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power
* operation, if several modes of operation are supported these can be
* passed in the argument on a custom form, else just use argument 1
* to indicate low power mode, argument 0 turns low power mode off.
+ * @PIN_CONFIG_OUTPUT: this will configure the pin in output, use argument
+ * 1 to indicate high level, argument 0 to indicate low level.
* @PIN_CONFIG_END: this is the last enumerator for pin configurations, if
* you need to pass in custom configurations to the pin controller, use
* PIN_CONFIG_END+1 as the base offset.
@@ -69,15 +87,21 @@
enum pin_config_param {
PIN_CONFIG_BIAS_DISABLE,
PIN_CONFIG_BIAS_HIGH_IMPEDANCE,
+ PIN_CONFIG_BIAS_BUS_HOLD,
PIN_CONFIG_BIAS_PULL_UP,
PIN_CONFIG_BIAS_PULL_DOWN,
+ PIN_CONFIG_BIAS_PULL_PIN_DEFAULT,
PIN_CONFIG_DRIVE_PUSH_PULL,
PIN_CONFIG_DRIVE_OPEN_DRAIN,
PIN_CONFIG_DRIVE_OPEN_SOURCE,
+ PIN_CONFIG_DRIVE_STRENGTH,
+ PIN_CONFIG_INPUT_SCHMITT_ENABLE,
PIN_CONFIG_INPUT_SCHMITT,
PIN_CONFIG_INPUT_DEBOUNCE,
PIN_CONFIG_POWER_SOURCE,
+ PIN_CONFIG_SLEW_RATE,
PIN_CONFIG_LOW_POWER_MODE,
+ PIN_CONFIG_OUTPUT,
PIN_CONFIG_END = 0x7FFF,
};
diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h
index ec431f0..e7a7201 100644
--- a/include/linux/pinctrl/pinconf.h
+++ b/include/linux/pinctrl/pinconf.h
@@ -25,7 +25,6 @@
* @pin_config_get: get the config of a certain pin, if the requested config
* is not available on this controller this should return -ENOTSUPP
* and if it is available but disabled it should return -EINVAL
- * @pin_config_get: get the config of a certain pin
* @pin_config_set: configure an individual pin
* @pin_config_group_get: get configurations for an entire pin group
* @pin_config_group_set: configure all pins in a group
@@ -33,6 +32,8 @@
* per-device info for a certain pin in debugfs
* @pin_config_group_dbg_show: optional debugfs display hook that will provide
* per-device info for a certain group in debugfs
+ * @pin_config_config_dbg_show: optional debugfs display hook that will decode
+ * and display a driver's pin configuration parameter
*/
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
@@ -56,6 +57,9 @@
void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned selector);
+ void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned long config);
};
#endif
diff --git a/include/linux/pinctrl/pinctrl-state.h b/include/linux/pinctrl/pinctrl-state.h
index 3920e28..b5919f8 100644
--- a/include/linux/pinctrl/pinctrl-state.h
+++ b/include/linux/pinctrl/pinctrl-state.h
@@ -2,5 +2,23 @@
* Standard pin control state definitions
*/
+/**
+ * @PINCTRL_STATE_DEFAULT: the state the pinctrl handle shall be put
+ * into as default, usually this means the pins are up and ready to
+ * be used by the device driver. This state is commonly used by
+ * hogs to configure muxing and pins at boot, and also as a state
+ * to go into when returning from sleep and idle in
+ * .pm_runtime_resume() or ordinary .resume() for example.
+ * @PINCTRL_STATE_IDLE: the state the pinctrl handle shall be put into
+ * when the pins are idle. This is a state where the system is relaxed
+ * but not fully sleeping - some power may be on but clocks gated for
+ * example. Could typically be set from a pm_runtime_suspend() or
+ * pm_runtime_idle() operation.
+ * @PINCTRL_STATE_SLEEP: the state the pinctrl handle shall be put into
+ * when the pins are sleeping. This is a state where the system is in
+ * its lowest sleep state. Could typically be set from an
+ * ordinary .suspend() function.
+ */
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
+#define PINCTRL_STATE_SLEEP "sleep"
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 4e9f078..d19ad40 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -21,9 +21,11 @@
struct device;
struct pinctrl_dev;
+struct pinctrl_map;
struct pinmux_ops;
struct pinconf_ops;
struct gpio_chip;
+struct device_node;
/**
* struct pinctrl_pin_desc - boards/machines provide information on their
@@ -64,17 +66,24 @@
/**
* struct pinctrl_ops - global pin control operations, to be implemented by
* pin controller drivers.
- * @list_groups: list the number of selectable named groups available
- * in this pinmux driver, the core will begin on 0 and call this
- * repeatedly as long as it returns >= 0 to enumerate the groups
+ * @get_groups_count: Returns the count of total number of groups registered.
* @get_group_name: return the group name of the pin group
* @get_group_pins: return an array of pins corresponding to a certain
* group selector @pins, and the size of the array in @num_pins
* @pin_dbg_show: optional debugfs display hook that will provide per-device
* info for a certain pin in debugfs
+ * @dt_node_to_map: parse a device tree "pin configuration node", and create
+ * mapping table entries for it. These are returned through the @map and
+ * @num_maps output parameters. This function is optional, and may be
+ * omitted for pinctrl drivers that do not support device tree.
+ * @dt_free_map: free mapping table entries created via @dt_node_to_map. The
+ * top-level @map pointer must be freed, along with any dynamically
+ * allocated members of the mapping table entries themselves. This
+ * function is optional, and may be omitted for pinctrl drivers that do
+ * not support device tree.
*/
struct pinctrl_ops {
- int (*list_groups) (struct pinctrl_dev *pctldev, unsigned selector);
+ int (*get_groups_count) (struct pinctrl_dev *pctldev);
const char *(*get_group_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_group_pins) (struct pinctrl_dev *pctldev,
@@ -83,6 +92,11 @@
unsigned *num_pins);
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned offset);
+ int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps);
+ void (*dt_free_map) (struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps);
};
/**
@@ -117,9 +131,30 @@
extern bool pin_is_valid(struct pinctrl_dev *pctldev, int pin);
extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range);
+extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *ranges,
+ unsigned nranges);
extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range);
+
+extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
+ struct pinctrl_gpio_range *range);
+extern struct pinctrl_gpio_range *
+pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
+ unsigned int pin);
+
+#ifdef CONFIG_OF
+extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np);
+#else
+static inline
+struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+
extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev);
+extern const char *pinctrl_dev_get_devname(struct pinctrl_dev *pctldev);
extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev);
#else
diff --git a/include/linux/pinctrl/pinmux.h b/include/linux/pinctrl/pinmux.h
index 47e9237..1818dcb 100644
--- a/include/linux/pinctrl/pinmux.h
+++ b/include/linux/pinctrl/pinmux.h
@@ -23,15 +23,14 @@
/**
* struct pinmux_ops - pinmux operations, to be implemented by pin controller
* drivers that support pinmuxing
- * @request: called by the core to see if a certain pin can be made available
+ * @request: called by the core to see if a certain pin can be made
* available for muxing. This is called by the core to acquire the pins
* before selecting any actual mux setting across a function. The driver
* is allowed to answer "no" by returning a negative error code
* @free: the reverse function of the request() callback, frees a pin after
* being requested
- * @list_functions: list the number of selectable named functions available
- * in this pinmux driver, the core will begin on 0 and call this
- * repeatedly as long as it returns >= 0 to enumerate mux settings
+ * @get_functions_count: returns number of selectable named functions available
+ * in this pinmux driver
* @get_function_name: return the function name of the muxing selector,
* called by the core to figure out which mux setting it shall map a
* certain device to
@@ -62,7 +61,7 @@
struct pinmux_ops {
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
- int (*list_functions) (struct pinctrl_dev *pctldev, unsigned selector);
+ int (*get_functions_count) (struct pinctrl_dev *pctldev);
const char *(*get_function_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_function_groups) (struct pinctrl_dev *pctldev,
diff --git a/include/linux/regulator/krait-regulator.h b/include/linux/regulator/krait-regulator.h
index 2683fd7..bd2ca82 100644
--- a/include/linux/regulator/krait-regulator.h
+++ b/include/linux/regulator/krait-regulator.h
@@ -26,14 +26,14 @@
#ifdef CONFIG_ARCH_MSM8974
int __init krait_power_init(void);
-void secondary_cpu_hs_init(void *base_ptr);
+void secondary_cpu_hs_init(void *base_ptr, int cpu);
#else
static inline int __init krait_power_init(void)
{
return -ENOSYS;
}
-static inline void secondary_cpu_hs_init(void *base_ptr) {}
+static inline void secondary_cpu_hs_init(void *base_ptr, int cpu) {}
#endif
#endif
diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index fd07c45..a5ddc60 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -67,6 +67,17 @@
struct list_head same_anon_vma; /* locked by anon_vma->mutex */
};
+enum ttu_flags {
+ TTU_UNMAP = 0, /* unmap mode */
+ TTU_MIGRATION = 1, /* migration mode */
+ TTU_MUNLOCK = 2, /* munlock mode */
+ TTU_ACTION_MASK = 0xff,
+
+ TTU_IGNORE_MLOCK = (1 << 8), /* ignore mlock */
+ TTU_IGNORE_ACCESS = (1 << 9), /* don't age */
+ TTU_IGNORE_HWPOISON = (1 << 10),/* corrupted page is recoverable */
+};
+
#ifdef CONFIG_MMU
static inline void get_anon_vma(struct anon_vma *anon_vma)
{
@@ -161,16 +172,6 @@
int page_referenced_one(struct page *, struct vm_area_struct *,
unsigned long address, unsigned int *mapcount, unsigned long *vm_flags);
-enum ttu_flags {
- TTU_UNMAP = 0, /* unmap mode */
- TTU_MIGRATION = 1, /* migration mode */
- TTU_MUNLOCK = 2, /* munlock mode */
- TTU_ACTION_MASK = 0xff,
-
- TTU_IGNORE_MLOCK = (1 << 8), /* ignore mlock */
- TTU_IGNORE_ACCESS = (1 << 9), /* don't age */
- TTU_IGNORE_HWPOISON = (1 << 10),/* corrupted page is recoverable */
-};
#define TTU_ACTION(x) ((x) & TTU_ACTION_MASK)
bool is_vma_temporary_stack(struct vm_area_struct *vma);
diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h
index 5c5b777..132135e 100644
--- a/include/linux/slimbus/slimbus.h
+++ b/include/linux/slimbus/slimbus.h
@@ -511,8 +511,11 @@
* @wakeup: This function pointer implements controller-specific procedure
* to wake it up from clock-pause. Framework will call this to bring
* the controller out of clock pause.
- * @config_port: Configure a port and make it ready for data transfer. This is
- * called by framework after connect_port message is sent successfully.
+ * @alloc_port: Allocate a port and make it ready for data transfer. This is
+ * called by framework to make sure controller can take necessary steps
+ * to initialize its port
+ * @dealloc_port: Counter-part of alloc_port. This is called by framework so
+ * that controller can free resources associated with this port
* @framer_handover: If this controller has multiple framers, this API will
* be called to switch between framers if controller desires to change
* the active framer.
@@ -557,7 +560,9 @@
int (*get_laddr)(struct slim_controller *ctrl,
const u8 *ea, u8 elen, u8 *laddr);
int (*wakeup)(struct slim_controller *ctrl);
- int (*config_port)(struct slim_controller *ctrl,
+ int (*alloc_port)(struct slim_controller *ctrl,
+ u8 port);
+ void (*dealloc_port)(struct slim_controller *ctrl,
u8 port);
int (*framer_handover)(struct slim_controller *ctrl,
struct slim_framer *new_framer);
@@ -795,6 +800,8 @@
* Channel specified in chanh needs to be allocated first.
* Returns -EALREADY if source is already configured for this channel.
* Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid direction is specified for non-manager port,
+ * or if the manager side port number is out of bounds, or in incorrect state
*/
extern int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh);
@@ -808,6 +815,9 @@
* Channel specified in chanh needs to be allocated first.
* Returns -EALREADY if sink is already configured for this channel.
* Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid parameters are passed, or invalid direction is
+ * specified for non-manager port, or if the manager side port number is out of
+ * bounds, or in incorrect state
*/
extern int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink,
u16 chanh);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index b1fd5c7..d5bd6ee 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -251,7 +251,7 @@
/* linux/mm/vmscan.c */
extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
gfp_t gfp_mask, nodemask_t *mask);
-extern int __isolate_lru_page(struct page *page, isolate_mode_t mode, int file);
+extern int __isolate_lru_page(struct page *page, isolate_mode_t mode);
extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem,
gfp_t gfp_mask, bool noswap);
extern unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
@@ -355,23 +355,6 @@
extern int try_to_free_swap(struct page *);
struct backing_dev_info;
-/* linux/mm/thrash.c */
-extern struct mm_struct *swap_token_mm;
-extern void grab_swap_token(struct mm_struct *);
-extern void __put_swap_token(struct mm_struct *);
-extern void disable_swap_token(struct mem_cgroup *memcg);
-
-static inline int has_swap_token(struct mm_struct *mm)
-{
- return (mm == swap_token_mm);
-}
-
-static inline void put_swap_token(struct mm_struct *mm)
-{
- if (has_swap_token(mm))
- __put_swap_token(mm);
-}
-
#ifdef CONFIG_CGROUP_MEM_RES_CTLR
extern void
mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout);
@@ -476,24 +459,6 @@
return entry;
}
-/* linux/mm/thrash.c */
-static inline void put_swap_token(struct mm_struct *mm)
-{
-}
-
-static inline void grab_swap_token(struct mm_struct *mm)
-{
-}
-
-static inline int has_swap_token(struct mm_struct *mm)
-{
- return 0;
-}
-
-static inline void disable_swap_token(struct mem_cgroup *memcg)
-{
-}
-
static inline void
mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent)
{
diff --git a/include/linux/test-iosched.h b/include/linux/test-iosched.h
index 89d3b30..5b05b7a 100644
--- a/include/linux/test-iosched.h
+++ b/include/linux/test-iosched.h
@@ -130,7 +130,7 @@
* @check_test_result_fn: Test specific test result checking
* callback
* @get_test_case_str_fn: Test specific function to get the test name
- * @test_duration: A jiffies value saved for timing
+ * @test_duration: A ktime value saved for timing
* calculations
* @data: Test specific private data
* @test_byte_count: Total number of bytes dispatched in
@@ -144,7 +144,7 @@
check_test_result_fn *check_test_result_fn;
post_test_fn *post_test_fn;
get_test_case_str_fn *get_test_case_str_fn;
- unsigned long test_duration;
+ ktime_t test_duration;
get_rq_disk_fn *get_rq_disk_fn;
void *data;
unsigned long test_byte_count;
@@ -263,4 +263,6 @@
void test_iosched_add_urgent_req(struct test_request *test_rq);
int test_is_req_urgent(struct request *rq);
+
+void check_test_completion(void);
#endif /* _LINUX_TEST_IOSCHED_H */
diff --git a/include/linux/usb/Kbuild b/include/linux/usb/Kbuild
index b607f35..087d163 100644
--- a/include/linux/usb/Kbuild
+++ b/include/linux/usb/Kbuild
@@ -5,6 +5,7 @@
header-y += functionfs.h
header-y += gadgetfs.h
header-y += midi.h
+header-y += msm_ext_chg.h
header-y += g_printer.h
header-y += tmc.h
header-y += video.h
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 82044f7..fea832e 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -545,6 +545,7 @@
const char *name;
struct device dev;
u8 usb_core_id;
+ bool l1_supported;
};
static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
diff --git a/include/linux/usb/msm_ext_chg.h b/include/linux/usb/msm_ext_chg.h
new file mode 100644
index 0000000..dcc786d
--- /dev/null
+++ b/include/linux/usb/msm_ext_chg.h
@@ -0,0 +1,31 @@
+#ifndef __LINUX_USB_MSM_EXT_CHG_H
+#define __LINUX_USB_MSM_EXT_CHG_H
+
+#include <linux/ioctl.h>
+
+#define USB_CHG_BLOCK_ULPI 1
+#define USB_CHG_BLOCK_QSCRATCH 2
+
+/**
+ * struct msm_usb_chg_info - MSM USB charger block details.
+ * @chg_block_type: The type of charger block. QSCRATCH/ULPI.
+ * @page_offset: USB charger register base may not be aligned to
+ * PAGE_SIZE. The kernel driver aligns the base
+ * address and use it for memory mapping. This
+ * page_offset is used by user space to calaculate
+ * the corret charger register base address.
+ * @length: The length of the charger register address space.
+ */
+struct msm_usb_chg_info {
+ uint32_t chg_block_type;
+ off_t page_offset;
+ size_t length;
+};
+
+/* Get the MSM USB charger block information */
+#define MSM_USB_EXT_CHG_INFO _IOW('M', 0, struct msm_usb_chg_info)
+
+/* Vote against USB hardware low power mode */
+#define MSM_USB_EXT_CHG_BLOCK_LPM _IOW('M', 1, int)
+
+#endif /* __LINUX_USB_MSM_EXT_CHG_H */
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 04b99b7..f04d2bf 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -26,6 +26,7 @@
#include <linux/pm_qos.h>
#include <linux/hrtimer.h>
#include <linux/power_supply.h>
+#include <linux/cdev.h>
/*
* The following are bit fields describing the usb_request.udc_priv word.
* These bit fields are set by function drivers that wish to queue
@@ -42,6 +43,20 @@
#define MSM_VENDOR_ID BIT(16)
/**
+ * Requested USB votes for BUS bandwidth
+ *
+ * USB_NO_PERF_VOTE BUS Vote for inactive USB session or disconnect
+ * USB_MAX_PERF_VOTE Maximum BUS bandwidth vote
+ * USB_MIN_PERF_VOTE Minimum BUS bandwidth vote (for some hw same as NO_PERF)
+ *
+ */
+enum usb_bus_vote {
+ USB_NO_PERF_VOTE = 0,
+ USB_MAX_PERF_VOTE,
+ USB_MIN_PERF_VOTE,
+};
+
+/**
* Supported USB modes
*
* USB_PERIPHERAL Only peripheral mode is supported.
@@ -151,6 +166,7 @@
USB_ACA_C_CHARGER,
USB_ACA_DOCK_CHARGER,
USB_PROPRIETARY_CHARGER,
+ USB_FLOATED_CHARGER,
};
/**
@@ -189,6 +205,10 @@
* @pmic_id_irq: IRQ number assigned for PMIC USB ID line.
* @mpm_otgsessvld_int: MPM wakeup pin assigned for OTG SESSVLD
* interrupt. Used when .otg_control == OTG_PHY_CONTROL.
+ * @mpm_dpshv_int: MPM wakeup pin assigned for DP SHV interrupt.
+ * Used during host bus suspend.
+ * @mpm_dmshv_int: MPM wakeup pin assigned for DM SHV interrupt.
+ * Used during host bus suspend.
* @mhl_enable: indicates MHL connector or not.
* @disable_reset_on_disconnect: perform USB PHY and LINK reset
* on USB cable disconnection.
@@ -207,6 +227,7 @@
* @log2_itc: value of 2^(log2_itc-1) will be used as the
* interrupt threshold (ITC), when log2_itc is
* between 1 to 7.
+ * @l1_supported: enable link power management support.
*/
struct msm_otg_platform_data {
int *phy_init_seq;
@@ -219,6 +240,8 @@
void (*setup_gpio)(enum usb_otg_state state);
int pmic_id_irq;
unsigned int mpm_otgsessvld_int;
+ unsigned int mpm_dpshv_int;
+ unsigned int mpm_dmshv_int;
bool mhl_enable;
bool disable_reset_on_disconnect;
bool pnoc_errata_fix;
@@ -231,6 +254,7 @@
struct msm_bus_scale_pdata *bus_scale_table;
const char *mhl_dev_name;
int log2_itc;
+ bool l1_supported;
};
/* phy related flags */
@@ -309,6 +333,7 @@
* @xo_handle: TCXO buffer handle
* @bus_perf_client: Bus performance client handle to request BUS bandwidth
* @mhl_enabled: MHL driver registration successful and MHL enabled.
+ * @host_bus_suspend: indicates host bus suspend or not.
* @chg_check_timer: The timer used to implement the workaround to detect
* very slow plug in of wall charger.
*/
@@ -323,6 +348,7 @@
struct clk *phy_reset_clk;
struct clk *core_clk;
long core_clk_rate;
+ struct resource *io_res;
void __iomem *regs;
#define ID 0
#define B_SESS_VLD 1
@@ -364,6 +390,7 @@
struct msm_xo_voter *xo_handle;
uint32_t bus_perf_client;
bool mhl_enabled;
+ bool host_bus_suspend;
struct timer_list chg_check_timer;
/*
* Allowing PHY power collpase turns off the HSUSB 3.3v and 1.8v
@@ -388,6 +415,11 @@
* analog regulators into LPM while going to USB low power mode.
*/
#define ALLOW_PHY_REGULATORS_LPM BIT(3)
+ /*
+ * Allow PHY RETENTION mode before turning off the digital
+ * voltage regulator(VDDCX) during host mode.
+ */
+#define ALLOW_HOST_PHY_RETENTION BIT(4)
unsigned long lpm_flags;
#define PHY_PWR_COLLAPSED BIT(0)
#define PHY_RETENTIONED BIT(1)
@@ -404,6 +436,14 @@
unsigned int online;
unsigned int host_mode;
unsigned int current_max;
+
+ dev_t ext_chg_dev;
+ struct cdev ext_chg_cdev;
+ struct class *ext_chg_class;
+ struct device *ext_chg_device;
+ bool ext_chg_opened;
+ bool ext_chg_active;
+ struct completion ext_chg_wait;
};
struct ci13xxx_platform_data {
@@ -414,6 +454,7 @@
*/
int log2_itc;
void *prv_data;
+ bool l1_supported;
};
struct msm_hsic_host_platform_data {
@@ -440,6 +481,7 @@
bool disable_park_mode;
bool consider_ipa_handshake;
bool ahb_async_bridge_bypass;
+ bool disable_cerr;
};
struct msm_usb_host_platform_data {
@@ -447,6 +489,8 @@
int pmic_gpio_dp_irq;
unsigned int dock_connect_irq;
bool use_sec_phy;
+ bool no_selective_suspend;
+ int resume_gpio;
};
/**
@@ -462,7 +506,7 @@
/**
* struct usb_ext_notification: event notification structure
* @notify: pointer to client function to call when ID event is detected.
- * The last parameter is provided by driver to be called back when
+ * The function parameter is provided by driver to be called back when
* external client indicates it is done using the USB. This function
* should return 0 if handled successfully, otherise an error code.
* @ctxt: client-specific context pointer
@@ -476,7 +520,7 @@
* called with the online parameter set to false.
*/
struct usb_ext_notification {
- int (*notify)(void *, int, void (*)(int online));
+ int (*notify)(void *, int, void (*)(void *, int online), void *);
void *ctxt;
};
#ifdef CONFIG_USB_BAM
@@ -506,7 +550,7 @@
int msm_data_fifo_config(struct usb_ep *ep, u32 addr, u32 size,
u8 dst_pipe_idx);
-void msm_dwc3_restart_usb_session(void);
+void msm_dwc3_restart_usb_session(struct usb_gadget *gadget);
int msm_register_usb_ext_notification(struct usb_ext_notification *info);
#else
@@ -526,7 +570,7 @@
return -ENODEV;
}
-static inline void msm_dwc3_restart_usb_session(void)
+static inline void msm_dwc3_restart_usb_session(struct usb_gadget *gadget)
{
return;
}
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index 0d57d93..064d210 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -24,6 +24,7 @@
#define USB_HS_GPTIMER_BASE (MSM_USB_BASE + 0x80)
#define GENCFG2_SESS_VLD_CTRL_EN BIT(7)
+#define GENCFG2_LINESTATE_DIFF_WAKEUP_EN BIT(12)
#define USB_USBCMD (MSM_USB_BASE + 0x0140)
#define USB_USBSTS (MSM_USB_BASE + 0x0144)
@@ -39,6 +40,15 @@
#define USB_USBINTR (MSM_USB_BASE + 0x0148)
#define USB_FRINDEX (MSM_USB_BASE + 0x014C)
+#define USB_L1_EP_CTRL (MSM_USB_BASE + 0x0250)
+#define USB_L1_CONFIG (MSM_USB_BASE + 0x0254)
+
+#define L1_CONFIG_LPM_EN BIT(4)
+#define L1_CONFIG_REMOTE_WAKEUP BIT(5)
+#define L1_CONFIG_GATE_SYS_CLK BIT(7)
+#define L1_CONFIG_PHY_LPM BIT(10)
+#define L1_CONFIG_PLL BIT(11)
+
#define PORTSC_PHCD (1 << 23) /* phy suspend mode */
#define PORTSC_PTS_MASK (3 << 30)
#define PORTSC_PTS_ULPI (3 << 30)
@@ -73,6 +83,7 @@
#define PHY_RETEN (1 << 1) /* PHY retention enable/disable */
#define PHY_IDHV_INTEN (1 << 8) /* PHY ID HV interrupt */
#define PHY_OTGSESSVLDHV_INTEN (1 << 9) /* PHY Session Valid HV int. */
+#define PHY_CLAMP_DPDMSE_EN (1 << 21) /* PHY mpm DP DM clamp enable */
#define STS_PCI (1 << 2) /* R/WC - Port Change Detect */
#define STS_URI (1 << 6) /* R/WC - RESET recv'd */
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 72f5c96..642c4fe 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -131,6 +131,9 @@
/* link reset handling, called from defer_kevent */
int (*link_reset)(struct usbnet *);
+ /*in case if usbnet wrapper wants to override rx_complete()*/
+ void (*rx_complete) (struct urb *);
+
/* fixup rx packet (strip framing) */
int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
@@ -228,5 +231,7 @@
extern void usbnet_set_msglevel(struct net_device *, u32);
extern void usbnet_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
extern int usbnet_nway_reset(struct net_device *net);
+extern void usbnet_terminate_urbs(struct usbnet *dev);
+extern void rx_complete(struct urb *urb);
#endif /* __LINUX_USB_USBNET_H */
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 5c7ae02..7ad0c16 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1836,12 +1836,6 @@
V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO,
V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP
};
-#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO \
- (V4L2_CID_MPEG_MSM_VIDC_BASE + 23)
-enum v4l2_mpeg_vidc_video_h264_vui_timing_info {
- V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED = 0,
- V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED = 1
-};
#define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 26)
enum v4l2_mpeg_vidc_perf_level {
@@ -1851,9 +1845,32 @@
};
#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB \
- (V4L2_CID_MPEG_MSM_VIDC_BASE+27)
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 27)
+
#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE \
- (V4L2_CID_MPEG_MSM_VIDC_BASE+28)
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 28)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 29)
+enum v4l2_mpeg_vidc_video_h264_vui_timing_info {
+ V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED = 0,
+ V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED = 1
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE+30)
+enum v4l2_mpeg_vidc_video_alloc_mode_type {
+ V4L2_MPEG_VIDC_VIDEO_STATIC = 0,
+ V4L2_MPEG_VIDC_VIDEO_RING = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_ASSEMBLY \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE+31)
+enum v4l2_mpeg_vidc_video_assembly {
+ V4L2_MPEG_VIDC_FRAME_ASSEMBLY_DISABLE = 0,
+ V4L2_MPEG_VIDC_FRAME_ASSEMBLY_ENABLE = 1,
+};
+
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
#define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1)
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
index 06f8e38..8bbb324 100644
--- a/include/linux/vm_event_item.h
+++ b/include/linux/vm_event_item.h
@@ -37,8 +37,12 @@
KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY,
KSWAPD_SKIP_CONGESTION_WAIT,
PAGEOUTRUN, ALLOCSTALL, PGROTATED,
+#ifdef CONFIG_MIGRATION
+ PGMIGRATE_SUCCESS, PGMIGRATE_FAIL,
+#endif
#ifdef CONFIG_COMPACTION
- COMPACTBLOCKS, COMPACTPAGES, COMPACTPAGEFAILED,
+ COMPACTMIGRATE_SCANNED, COMPACTFREE_SCANNED,
+ COMPACTISOLATED,
COMPACTSTALL, COMPACTFAIL, COMPACTSUCCESS,
#endif
#ifdef CONFIG_HUGETLB_PAGE
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index 31798d6..2805401 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -46,6 +46,8 @@
#define MAX_EEPROM_NAME 32
+#define MAX_AF_ITERATIONS 3
+
enum msm_camera_i2c_reg_addr_type {
MSM_CAMERA_I2C_BYTE_ADDR = 1,
MSM_CAMERA_I2C_WORD_ADDR,
@@ -78,6 +80,11 @@
enum msm_sensor_power_seq_gpio_t {
SENSOR_GPIO_RESET,
SENSOR_GPIO_STANDBY,
+ SENSOR_GPIO_AF_PWDM,
+ SENSOR_GPIO_VIO,
+ SENSOR_GPIO_VANA,
+ SENSOR_GPIO_VDIG,
+ SENSOR_GPIO_VAF,
SENSOR_GPIO_MAX,
};
@@ -133,6 +140,11 @@
REG_GPIO,
};
+enum sensor_af_t {
+ SENSOR_AF_FOCUSSED,
+ SENSOR_AF_NOT_FOCUSSED,
+};
+
struct msm_sensor_power_setting {
enum msm_sensor_power_seq_type_t seq_type;
uint16_t seq_val;
@@ -305,18 +317,19 @@
CFG_EEPROM_READ_CAL_DATA,
CFG_EEPROM_WRITE_DATA,
};
+
struct eeprom_get_t {
- uint16_t num_bytes;
+ uint32_t num_bytes;
};
struct eeprom_read_t {
uint8_t *dbuffer;
- uint16_t num_bytes;
+ uint32_t num_bytes;
};
struct eeprom_write_t {
uint8_t *dbuffer;
- uint16_t num_bytes;
+ uint32_t num_bytes;
};
struct msm_eeprom_cfg_data {
@@ -345,6 +358,11 @@
CFG_SET_RESOLUTION,
CFG_SET_STOP_STREAM,
CFG_SET_START_STREAM,
+ CFG_SET_SATURATION,
+ CFG_SET_CONTRAST,
+ CFG_SET_SHARPNESS,
+ CFG_SET_AUTOFOCUS,
+ CFG_CANCEL_AUTOFOCUS,
};
enum msm_actuator_cfg_type_t {
@@ -508,6 +526,9 @@
#define VIDIOC_MSM_EEPROM_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct msm_eeprom_cfg_data)
+#define VIDIOC_MSM_SENSOR_GET_AF_STATUS \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, uint32_t)
+
#define MSM_V4L2_PIX_FMT_META v4l2_fourcc('M', 'E', 'T', 'A') /* META */
#endif /* __LINUX_MSM_CAM_SENSOR_H */
diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h
index 4d36688..5ae852a 100644
--- a/include/media/msmb_isp.h
+++ b/include/media/msmb_isp.h
@@ -11,6 +11,7 @@
#define ISP_NATIVE_BUF_BIT 0x10000
#define ISP0_BIT 0x20000
#define ISP1_BIT 0x40000
+#define ISP_META_CHANNEL_BIT 0x80000
#define ISP_STATS_STREAM_BIT 0x80000000
enum ISP_START_PIXEL_PATTERN {
diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h
index d6151c0..887b291 100644
--- a/include/media/radio-iris.h
+++ b/include/media/radio-iris.h
@@ -615,6 +615,7 @@
#define PI_CODE_OFFSET 4
#define AF_SIZE_OFFSET 6
#define AF_LIST_OFFSET 7
+#define RT_A_B_FLAG_OFFSET 4
/*FM states*/
enum radio_state_t {
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index c68d427..60845de 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1,6 +1,7 @@
/*
BlueZ - Bluetooth protocol stack for Linux
- Copyright (c) 2000-2001, 2010-2012, The Linux Foundation. All rights reserved.
+ Copyright (c) 2000-2001, The Linux Foundation. All rights reserved.
+ Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
@@ -354,6 +355,7 @@
void *smp_conn;
struct timer_list smp_timer;
__u8 conn_valid;
+ __u8 hidp_session_valid;
void (*connect_cfm_cb) (struct hci_conn *conn, u8 status);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 6efb2e1..7c6a558 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -383,10 +383,18 @@
int data_direction, void *buffer, unsigned bufflen,
unsigned char *sense, int timeout, int retries,
int flag, int *resid);
-extern int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
- int data_direction, void *buffer, unsigned bufflen,
- struct scsi_sense_hdr *, int timeout, int retries,
- int *resid);
+extern int scsi_execute_req_flags(struct scsi_device *sdev,
+ const unsigned char *cmd, int data_direction, void *buffer,
+ unsigned bufflen, struct scsi_sense_hdr *sshdr, int timeout,
+ int retries, int *resid, int flags);
+static inline int scsi_execute_req(struct scsi_device *sdev,
+ const unsigned char *cmd, int data_direction, void *buffer,
+ unsigned bufflen, struct scsi_sense_hdr *sshdr, int timeout,
+ int retries, int *resid)
+{
+ return scsi_execute_req_flags(sdev, cmd, data_direction, buffer,
+ bufflen, sshdr, timeout, retries, resid, 0);
+}
#ifdef CONFIG_PM_RUNTIME
extern int scsi_autopm_get_device(struct scsi_device *);
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 0d3b5a0..364de9a 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -632,43 +632,44 @@
/* Port ID. Update afe_get_port_index
* when a new port is added here. */
-#define PRIMARY_I2S_RX 0 /* index = 0 */
-#define PRIMARY_I2S_TX 1 /* index = 1 */
-#define SECONDARY_I2S_RX 4 /* index = 4 */
-#define SECONDARY_I2S_TX 5 /* index = 5 */
-#define MI2S_RX 6 /* index = 6 */
-#define MI2S_TX 7 /* index = 7 */
-#define HDMI_RX 8 /* index = 8 */
-#define RSVD_2 9 /* index = 9 */
-#define RSVD_3 10 /* index = 10 */
-#define DIGI_MIC_TX 11 /* index = 11 */
-#define VOICE_RECORD_RX 0x8003 /* index = 12 */
-#define VOICE_RECORD_TX 0x8004 /* index = 13 */
-#define VOICE_PLAYBACK_TX 0x8005 /* index = 14 */
+#define PRIMARY_I2S_RX 0
+#define PRIMARY_I2S_TX 1
+#define SECONDARY_I2S_RX 4
+#define SECONDARY_I2S_TX 5
+#define MI2S_RX 6
+#define MI2S_TX 7
+#define HDMI_RX 8
+#define RSVD_2 9
+#define RSVD_3 10
+#define DIGI_MIC_TX 11
+#define VOICE2_PLAYBACK_TX 0x8002
+#define VOICE_RECORD_RX 0x8003
+#define VOICE_RECORD_TX 0x8004
+#define VOICE_PLAYBACK_TX 0x8005
/* Slimbus Multi channel port id pool */
-#define SLIMBUS_0_RX 0x4000 /* index = 15 */
-#define SLIMBUS_0_TX 0x4001 /* index = 16 */
-#define SLIMBUS_1_RX 0x4002 /* index = 17 */
-#define SLIMBUS_1_TX 0x4003 /* index = 18 */
+#define SLIMBUS_0_RX 0x4000
+#define SLIMBUS_0_TX 0x4001
+#define SLIMBUS_1_RX 0x4002
+#define SLIMBUS_1_TX 0x4003
#define SLIMBUS_2_RX 0x4004
#define SLIMBUS_2_TX 0x4005
#define SLIMBUS_3_RX 0x4006
#define SLIMBUS_3_TX 0x4007
#define SLIMBUS_4_RX 0x4008
-#define SLIMBUS_4_TX 0x4009 /* index = 24 */
+#define SLIMBUS_4_TX 0x4009
#define SLIMBUS_5_RX 0x400a
#define SLIMBUS_5_TX 0x400b
#define SLIMBUS_6_RX 0x400c
#define SLIMBUS_6_TX 0x400d
#define SLIMBUS_PORT_LAST SLIMBUS_6_TX
-#define INT_BT_SCO_RX 0x3000 /* index = 25 */
-#define INT_BT_SCO_TX 0x3001 /* index = 26 */
-#define INT_BT_A2DP_RX 0x3002 /* index = 27 */
-#define INT_FM_RX 0x3004 /* index = 28 */
-#define INT_FM_TX 0x3005 /* index = 29 */
-#define RT_PROXY_PORT_001_RX 0x2000 /* index = 30 */
-#define RT_PROXY_PORT_001_TX 0x2001 /* index = 31 */
+#define INT_BT_SCO_RX 0x3000
+#define INT_BT_SCO_TX 0x3001
+#define INT_BT_A2DP_RX 0x3002
+#define INT_FM_RX 0x3004
+#define INT_FM_TX 0x3005
+#define RT_PROXY_PORT_001_RX 0x2000
+#define RT_PROXY_PORT_001_TX 0x2001
#define AFE_PORT_INVALID 0xFFFF
#define SLIMBUS_INVALID AFE_PORT_INVALID
@@ -818,6 +819,7 @@
* to this port from where the voice path delivers them on the
* Rx path.
*/
+#define AFE_PORT_ID_VOICE2_PLAYBACK_TX 0x8002
#define AFE_PORT_ID_VOICE_PLAYBACK_TX 0x8005
#define AFE_PORT_ID_INVALID 0xFFFF
@@ -6930,8 +6932,8 @@
} __packed;
/* Ultrasound supported formats */
-#define US_POINT_EPOS_FORMAT 0x00012310
-#define US_RAW_FORMAT 0x0001127C
-#define US_PROX_FORMAT 0x0001272B
+#define US_POINT_EPOS_FORMAT_V2 0x0001272D
+#define US_RAW_FORMAT_V2 0x0001272C
+#define US_PROX_FORMAT_V2 0x0001272E
#endif /*_APR_AUDIO_V2_H_ */
diff --git a/include/sound/msm-dai-q6-v2.h b/include/sound/msm-dai-q6-v2.h
index c34a397..e2f9399 100644
--- a/include/sound/msm-dai-q6-v2.h
+++ b/include/sound/msm-dai-q6-v2.h
@@ -40,7 +40,6 @@
};
struct msm_dai_auxpcm_pdata {
- void *clk_cfg;
struct msm_dai_auxpcm_config mode_8k;
struct msm_dai_auxpcm_config mode_16k;
};
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
index 2e4c7c1..12934aa 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -83,6 +83,7 @@
IDX_AFE_PORT_ID_PRIMARY_MI2S_TX = 41,
IDX_AFE_PORT_ID_SECONDARY_PCM_RX = 42,
IDX_AFE_PORT_ID_SECONDARY_PCM_TX = 43,
+ IDX_VOICE2_PLAYBACK_TX = 44,
IDX_GLOBAL_CFG,
AFE_MAX_PORTS
};
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 2138689..4ad1ca9 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -191,6 +191,9 @@
int q6asm_open_read(struct audio_client *ac, uint32_t format
/*, uint16_t bits_per_sample*/);
+int q6asm_open_read_v2(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample);
+
int q6asm_open_write(struct audio_client *ac, uint32_t format
/*, uint16_t bits_per_sample*/);
@@ -252,6 +255,10 @@
int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
uint32_t rate, uint32_t channels);
+int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample);
+
int q6asm_set_encdec_chan_map(struct audio_client *ac,
uint32_t num_channels);
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index ea7a203..41f8607 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -51,6 +51,44 @@
);
/*
+ * Tracepoint for task enqueue/dequeue:
+ */
+TRACE_EVENT(sched_enq_deq_task,
+
+ TP_PROTO(struct task_struct *p, int enqueue),
+
+ TP_ARGS(p, enqueue),
+
+ TP_STRUCT__entry(
+ __array( char, comm, TASK_COMM_LEN )
+ __field( pid_t, pid )
+ __field( int, prio )
+ __field( int, cpu )
+ __field( int, enqueue )
+ __field(unsigned int, nr_running )
+ __field(unsigned long, cpu_load )
+ __field(unsigned int, rt_nr_running )
+ ),
+
+ TP_fast_assign(
+ memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
+ __entry->pid = p->pid;
+ __entry->prio = p->prio;
+ __entry->cpu = task_cpu(p);
+ __entry->enqueue = enqueue;
+ __entry->nr_running = task_rq(p)->nr_running;
+ __entry->cpu_load = task_rq(p)->cpu_load[0];
+ __entry->rt_nr_running = task_rq(p)->rt.rt_nr_running;
+ ),
+
+ TP_printk("cpu=%d %s comm=%s pid=%d prio=%d nr_running=%u cpu_load=%lu rt_nr_running=%u",
+ __entry->cpu, __entry->enqueue ? "enqueue" : "dequeue",
+ __entry->comm, __entry->pid,
+ __entry->prio, __entry->nr_running,
+ __entry->cpu_load, __entry->rt_nr_running)
+);
+
+/*
* Tracepoint for waking up a task:
*/
DECLARE_EVENT_CLASS(sched_wakeup_template,
@@ -179,6 +217,31 @@
__entry->orig_cpu, __entry->dest_cpu)
);
+/*
+ * Tracepoint for a CPU going offline/online:
+ */
+TRACE_EVENT(sched_cpu_hotplug,
+
+ TP_PROTO(int affected_cpu, int error, int status),
+
+ TP_ARGS(affected_cpu, error, status),
+
+ TP_STRUCT__entry(
+ __field( int, affected_cpu )
+ __field( int, error )
+ __field( int, status )
+ ),
+
+ TP_fast_assign(
+ __entry->affected_cpu = affected_cpu;
+ __entry->error = error;
+ __entry->status = status;
+ ),
+
+ TP_printk("cpu %d %s error=%d", __entry->affected_cpu,
+ __entry->status ? "online" : "offline", __entry->error)
+);
+
DECLARE_EVENT_CLASS(sched_process_template,
TP_PROTO(struct task_struct *p),
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index f64560e..bab3b87 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -13,7 +13,7 @@
#define RECLAIM_WB_ANON 0x0001u
#define RECLAIM_WB_FILE 0x0002u
#define RECLAIM_WB_MIXED 0x0010u
-#define RECLAIM_WB_SYNC 0x0004u
+#define RECLAIM_WB_SYNC 0x0004u /* Unused, all reclaim async */
#define RECLAIM_WB_ASYNC 0x0008u
#define show_reclaim_flags(flags) \
@@ -25,15 +25,15 @@
{RECLAIM_WB_ASYNC, "RECLAIM_WB_ASYNC"} \
) : "RECLAIM_WB_NONE"
-#define trace_reclaim_flags(page, sync) ( \
+#define trace_reclaim_flags(page) ( \
(page_is_file_cache(page) ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \
- (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_SYNC : RECLAIM_WB_ASYNC) \
+ (RECLAIM_WB_ASYNC) \
)
-#define trace_shrink_flags(file, sync) ( \
- (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_MIXED : \
- (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON)) | \
- (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_SYNC : RECLAIM_WB_ASYNC) \
+#define trace_shrink_flags(file) \
+ ( \
+ (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \
+ (RECLAIM_WB_ASYNC) \
)
TRACE_EVENT(mm_vmscan_kswapd_sleep,
@@ -263,22 +263,16 @@
unsigned long nr_requested,
unsigned long nr_scanned,
unsigned long nr_taken,
- unsigned long nr_lumpy_taken,
- unsigned long nr_lumpy_dirty,
- unsigned long nr_lumpy_failed,
isolate_mode_t isolate_mode,
int file),
- TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file),
+ TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file),
TP_STRUCT__entry(
__field(int, order)
__field(unsigned long, nr_requested)
__field(unsigned long, nr_scanned)
__field(unsigned long, nr_taken)
- __field(unsigned long, nr_lumpy_taken)
- __field(unsigned long, nr_lumpy_dirty)
- __field(unsigned long, nr_lumpy_failed)
__field(isolate_mode_t, isolate_mode)
__field(int, file)
),
@@ -288,22 +282,16 @@
__entry->nr_requested = nr_requested;
__entry->nr_scanned = nr_scanned;
__entry->nr_taken = nr_taken;
- __entry->nr_lumpy_taken = nr_lumpy_taken;
- __entry->nr_lumpy_dirty = nr_lumpy_dirty;
- __entry->nr_lumpy_failed = nr_lumpy_failed;
__entry->isolate_mode = isolate_mode;
__entry->file = file;
),
- TP_printk("isolate_mode=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu contig_taken=%lu contig_dirty=%lu contig_failed=%lu file=%d",
+ TP_printk("isolate_mode=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu file=%d",
__entry->isolate_mode,
__entry->order,
__entry->nr_requested,
__entry->nr_scanned,
__entry->nr_taken,
- __entry->nr_lumpy_taken,
- __entry->nr_lumpy_dirty,
- __entry->nr_lumpy_failed,
__entry->file)
);
@@ -313,13 +301,10 @@
unsigned long nr_requested,
unsigned long nr_scanned,
unsigned long nr_taken,
- unsigned long nr_lumpy_taken,
- unsigned long nr_lumpy_dirty,
- unsigned long nr_lumpy_failed,
isolate_mode_t isolate_mode,
int file),
- TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file)
+ TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
);
@@ -329,13 +314,10 @@
unsigned long nr_requested,
unsigned long nr_scanned,
unsigned long nr_taken,
- unsigned long nr_lumpy_taken,
- unsigned long nr_lumpy_dirty,
- unsigned long nr_lumpy_failed,
isolate_mode_t isolate_mode,
int file),
- TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file)
+ TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
);
@@ -395,88 +377,6 @@
show_reclaim_flags(__entry->reclaim_flags))
);
-TRACE_EVENT(replace_swap_token,
- TP_PROTO(struct mm_struct *old_mm,
- struct mm_struct *new_mm),
-
- TP_ARGS(old_mm, new_mm),
-
- TP_STRUCT__entry(
- __field(struct mm_struct*, old_mm)
- __field(unsigned int, old_prio)
- __field(struct mm_struct*, new_mm)
- __field(unsigned int, new_prio)
- ),
-
- TP_fast_assign(
- __entry->old_mm = old_mm;
- __entry->old_prio = old_mm ? old_mm->token_priority : 0;
- __entry->new_mm = new_mm;
- __entry->new_prio = new_mm->token_priority;
- ),
-
- TP_printk("old_token_mm=%p old_prio=%u new_token_mm=%p new_prio=%u",
- __entry->old_mm, __entry->old_prio,
- __entry->new_mm, __entry->new_prio)
-);
-
-DECLARE_EVENT_CLASS(put_swap_token_template,
- TP_PROTO(struct mm_struct *swap_token_mm),
-
- TP_ARGS(swap_token_mm),
-
- TP_STRUCT__entry(
- __field(struct mm_struct*, swap_token_mm)
- ),
-
- TP_fast_assign(
- __entry->swap_token_mm = swap_token_mm;
- ),
-
- TP_printk("token_mm=%p", __entry->swap_token_mm)
-);
-
-DEFINE_EVENT(put_swap_token_template, put_swap_token,
- TP_PROTO(struct mm_struct *swap_token_mm),
- TP_ARGS(swap_token_mm)
-);
-
-DEFINE_EVENT_CONDITION(put_swap_token_template, disable_swap_token,
- TP_PROTO(struct mm_struct *swap_token_mm),
- TP_ARGS(swap_token_mm),
- TP_CONDITION(swap_token_mm != NULL)
-);
-
-TRACE_EVENT_CONDITION(update_swap_token_priority,
- TP_PROTO(struct mm_struct *mm,
- unsigned int old_prio,
- struct mm_struct *swap_token_mm),
-
- TP_ARGS(mm, old_prio, swap_token_mm),
-
- TP_CONDITION(mm->token_priority != old_prio),
-
- TP_STRUCT__entry(
- __field(struct mm_struct*, mm)
- __field(unsigned int, old_prio)
- __field(unsigned int, new_prio)
- __field(struct mm_struct*, swap_token_mm)
- __field(unsigned int, swap_token_prio)
- ),
-
- TP_fast_assign(
- __entry->mm = mm;
- __entry->old_prio = old_prio;
- __entry->new_prio = mm->token_priority;
- __entry->swap_token_mm = swap_token_mm;
- __entry->swap_token_prio = swap_token_mm ? swap_token_mm->token_priority : 0;
- ),
-
- TP_printk("mm=%p old_prio=%u new_prio=%u swap_token_mm=%p token_prio=%u",
- __entry->mm, __entry->old_prio, __entry->new_prio,
- __entry->swap_token_mm, __entry->swap_token_prio)
-);
-
#endif /* _TRACE_VMSCAN_H */
/* This part must be outside protection */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 2f0d7542..01f68c4 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -4902,7 +4902,7 @@
* @root: the css supporsed to be an ancestor of the child.
*
* Returns true if "root" is an ancestor of "child" in its hierarchy. Because
- * this function reads css->id, this use rcu_dereference() and rcu_read_lock().
+ * this function reads css->id, the caller must hold rcu_read_lock().
* But, considering usual usage, the csses should be valid objects after test.
* Assuming that the caller will do some action to the child if this returns
* returns true, the caller must take "child";s reference count.
@@ -4914,18 +4914,18 @@
{
struct css_id *child_id;
struct css_id *root_id;
- bool ret = true;
- rcu_read_lock();
child_id = rcu_dereference(child->id);
+ if (!child_id)
+ return false;
root_id = rcu_dereference(root->id);
- if (!child_id
- || !root_id
- || (child_id->depth < root_id->depth)
- || (child_id->stack[root_id->depth] != root_id->id))
- ret = false;
- rcu_read_unlock();
- return ret;
+ if (!root_id)
+ return false;
+ if (child_id->depth < root_id->depth)
+ return false;
+ if (child_id->stack[root_id->depth] != root_id->id)
+ return false;
+ return true;
}
void free_css_id(struct cgroup_subsys *ss, struct cgroup_subsys_state *css)
diff --git a/kernel/cpu.c b/kernel/cpu.c
index fb4a5ac..d5ab2e6 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -17,6 +17,8 @@
#include <linux/gfp.h>
#include <linux/suspend.h>
+#include <trace/events/sched.h>
+
#ifdef CONFIG_SMP
/* Serializes the updates to cpu_online_mask, cpu_present_mask */
static DEFINE_MUTEX(cpu_add_remove_lock);
@@ -264,6 +266,7 @@
out_release:
cpu_hotplug_done();
+ trace_sched_cpu_hotplug(cpu, err, 0);
if (!err)
cpu_notify_nofail(CPU_POST_DEAD | mod, hcpu);
return err;
@@ -321,6 +324,7 @@
if (ret != 0)
__cpu_notify(CPU_UP_CANCELED | mod, hcpu, nr_calls, NULL);
cpu_hotplug_done();
+ trace_sched_cpu_hotplug(cpu, ret, 1);
return ret;
}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index fd126f8..aafa4c1 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5118,7 +5118,7 @@
static int perf_swevent_init(struct perf_event *event)
{
- int event_id = event->attr.config;
+ u64 event_id = event->attr.config;
if (event->attr.type != PERF_TYPE_SOFTWARE)
return -ENOENT;
diff --git a/kernel/fork.c b/kernel/fork.c
index 0de735c..a8bf721 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -597,7 +597,6 @@
list_del(&mm->mmlist);
spin_unlock(&mmlist_lock);
}
- put_swap_token(mm);
if (mm->binfmt)
module_put(mm->binfmt->module);
mmdrop(mm);
@@ -815,10 +814,6 @@
memcpy(mm, oldmm, sizeof(*mm));
mm_init_cpumask(mm);
- /* Initializing for Swap token stuff */
- mm->token_priority = 0;
- mm->last_interval = 0;
-
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
mm->pmd_huge_pte = NULL;
#endif
@@ -896,10 +891,6 @@
goto fail_nomem;
good_mm:
- /* Initializing for Swap token stuff */
- mm->token_priority = 0;
- mm->last_interval = 0;
-
tsk->mm = mm;
tsk->active_mm = mm;
return 0;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 3513fef..d9c4b64 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -727,6 +727,7 @@
update_rq_clock(rq);
sched_info_queued(p);
p->sched_class->enqueue_task(rq, p, flags);
+ trace_sched_enq_deq_task(p, 1);
}
static void dequeue_task(struct rq *rq, struct task_struct *p, int flags)
@@ -734,6 +735,7 @@
update_rq_clock(rq);
sched_info_dequeued(p);
p->sched_class->dequeue_task(rq, p, flags);
+ trace_sched_enq_deq_task(p, 0);
}
void activate_task(struct rq *rq, struct task_struct *p, int flags)
diff --git a/mm/Makefile b/mm/Makefile
index 8aada89..ccecbf9 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -25,7 +25,7 @@
obj-$(CONFIG_HAVE_MEMBLOCK) += memblock.o
obj-$(CONFIG_BOUNCE) += bounce.o
-obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o
+obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o
obj-$(CONFIG_HAS_DMA) += dmapool.o
obj-$(CONFIG_HUGETLBFS) += hugetlb.o
obj-$(CONFIG_NUMA) += mempolicy.o
diff --git a/mm/compaction.c b/mm/compaction.c
index 353f1c5..673142d 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -16,6 +16,21 @@
#include <linux/sysfs.h>
#include "internal.h"
+#ifdef CONFIG_COMPACTION
+static inline void count_compact_event(enum vm_event_item item)
+{
+ count_vm_event(item);
+}
+
+static inline void count_compact_events(enum vm_event_item item, long delta)
+{
+ count_vm_events(item, delta);
+}
+#else
+#define count_compact_event(item) do { } while (0)
+#define count_compact_events(item, delta) do { } while (0)
+#endif
+
#if defined CONFIG_COMPACTION || defined CONFIG_CMA
#define CREATE_TRACE_POINTS
@@ -50,44 +65,228 @@
return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE;
}
+#ifdef CONFIG_COMPACTION
+/* Returns true if the pageblock should be scanned for pages to isolate. */
+static inline bool isolation_suitable(struct compact_control *cc,
+ struct page *page)
+{
+ if (cc->ignore_skip_hint)
+ return true;
+
+ return !get_pageblock_skip(page);
+}
+
+/*
+ * This function is called to clear all cached information on pageblocks that
+ * should be skipped for page isolation when the migrate and free page scanner
+ * meet.
+ */
+static void __reset_isolation_suitable(struct zone *zone)
+{
+ unsigned long start_pfn = zone->zone_start_pfn;
+ unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ unsigned long pfn;
+
+ zone->compact_cached_migrate_pfn = start_pfn;
+ zone->compact_cached_free_pfn = end_pfn;
+ zone->compact_blockskip_flush = false;
+
+ /* Walk the zone and mark every pageblock as suitable for isolation */
+ for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
+ struct page *page;
+
+ cond_resched();
+
+ if (!pfn_valid(pfn))
+ continue;
+
+ page = pfn_to_page(pfn);
+ if (zone != page_zone(page))
+ continue;
+
+ clear_pageblock_skip(page);
+ }
+}
+
+void reset_isolation_suitable(pg_data_t *pgdat)
+{
+ int zoneid;
+
+ for (zoneid = 0; zoneid < MAX_NR_ZONES; zoneid++) {
+ struct zone *zone = &pgdat->node_zones[zoneid];
+ if (!populated_zone(zone))
+ continue;
+
+ /* Only flush if a full compaction finished recently */
+ if (zone->compact_blockskip_flush)
+ __reset_isolation_suitable(zone);
+ }
+}
+
+/*
+ * If no pages were isolated then mark this pageblock to be skipped in the
+ * future. The information is later cleared by __reset_isolation_suitable().
+ */
+static void update_pageblock_skip(struct compact_control *cc,
+ struct page *page, unsigned long nr_isolated,
+ bool migrate_scanner)
+{
+ struct zone *zone = cc->zone;
+ if (!page)
+ return;
+
+ if (!nr_isolated) {
+ unsigned long pfn = page_to_pfn(page);
+ set_pageblock_skip(page);
+
+ /* Update where compaction should restart */
+ if (migrate_scanner) {
+ if (!cc->finished_update_migrate &&
+ pfn > zone->compact_cached_migrate_pfn)
+ zone->compact_cached_migrate_pfn = pfn;
+ } else {
+ if (!cc->finished_update_free &&
+ pfn < zone->compact_cached_free_pfn)
+ zone->compact_cached_free_pfn = pfn;
+ }
+ }
+}
+#else
+static inline bool isolation_suitable(struct compact_control *cc,
+ struct page *page)
+{
+ return true;
+}
+
+static void update_pageblock_skip(struct compact_control *cc,
+ struct page *page, unsigned long nr_isolated,
+ bool migrate_scanner)
+{
+}
+#endif /* CONFIG_COMPACTION */
+
+static inline bool should_release_lock(spinlock_t *lock)
+{
+ return need_resched() || spin_is_contended(lock);
+}
+
+/*
+ * Compaction requires the taking of some coarse locks that are potentially
+ * very heavily contended. Check if the process needs to be scheduled or
+ * if the lock is contended. For async compaction, back out in the event
+ * if contention is severe. For sync compaction, schedule.
+ *
+ * Returns true if the lock is held.
+ * Returns false if the lock is released and compaction should abort
+ */
+static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
+ bool locked, struct compact_control *cc)
+{
+ if (should_release_lock(lock)) {
+ if (locked) {
+ spin_unlock_irqrestore(lock, *flags);
+ locked = false;
+ }
+
+ /* async aborts if taking too long or contended */
+ if (!cc->sync) {
+ cc->contended = true;
+ return false;
+ }
+
+ cond_resched();
+ }
+
+ if (!locked)
+ spin_lock_irqsave(lock, *flags);
+ return true;
+}
+
+static inline bool compact_trylock_irqsave(spinlock_t *lock,
+ unsigned long *flags, struct compact_control *cc)
+{
+ return compact_checklock_irqsave(lock, flags, false, cc);
+}
+
+/* Returns true if the page is within a block suitable for migration to */
+static bool suitable_migration_target(struct page *page)
+{
+ int migratetype = get_pageblock_migratetype(page);
+
+ /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
+ if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
+ return false;
+
+ /* If the page is a large free page, then allow migration */
+ if (PageBuddy(page) && page_order(page) >= pageblock_order)
+ return true;
+
+ /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
+ if (migrate_async_suitable(migratetype))
+ return true;
+
+ /* Otherwise skip the block */
+ return false;
+}
+
/*
* Isolate free pages onto a private freelist. Caller must hold zone->lock.
* If @strict is true, will abort returning 0 on any invalid PFNs or non-free
* pages inside of the pageblock (even though it may still end up isolating
* some pages).
*/
-static unsigned long isolate_freepages_block(unsigned long blockpfn,
+static unsigned long isolate_freepages_block(struct compact_control *cc,
+ unsigned long blockpfn,
unsigned long end_pfn,
struct list_head *freelist,
bool strict)
{
int nr_scanned = 0, total_isolated = 0;
- struct page *cursor;
+ struct page *cursor, *valid_page = NULL;
+ unsigned long nr_strict_required = end_pfn - blockpfn;
+ unsigned long flags;
+ bool locked = false;
cursor = pfn_to_page(blockpfn);
- /* Isolate free pages. This assumes the block is valid */
+ /* Isolate free pages. */
for (; blockpfn < end_pfn; blockpfn++, cursor++) {
int isolated, i;
struct page *page = cursor;
- if (!pfn_valid_within(blockpfn)) {
- if (strict)
- return 0;
- continue;
- }
nr_scanned++;
-
- if (!PageBuddy(page)) {
- if (strict)
- return 0;
+ if (!pfn_valid_within(blockpfn))
continue;
- }
+ if (!valid_page)
+ valid_page = page;
+ if (!PageBuddy(page))
+ continue;
+
+ /*
+ * The zone lock must be held to isolate freepages.
+ * Unfortunately this is a very coarse lock and can be
+ * heavily contended if there are parallel allocations
+ * or parallel compactions. For async compaction do not
+ * spin on the lock and we acquire the lock as late as
+ * possible.
+ */
+ locked = compact_checklock_irqsave(&cc->zone->lock, &flags,
+ locked, cc);
+ if (!locked)
+ break;
+
+ /* Recheck this is a suitable migration target under lock */
+ if (!strict && !suitable_migration_target(page))
+ break;
+
+ /* Recheck this is a buddy page under lock */
+ if (!PageBuddy(page))
+ continue;
/* Found a free page, break it into order-0 pages */
isolated = split_free_page(page);
if (!isolated && strict)
- return 0;
+ break;
total_isolated += isolated;
for (i = 0; i < isolated; i++) {
list_add(&page->lru, freelist);
@@ -102,6 +301,25 @@
}
trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
+
+ /*
+ * If strict isolation is requested by CMA then check that all the
+ * pages requested were isolated. If there were any failures, 0 is
+ * returned and CMA will fail.
+ */
+ if (strict && nr_strict_required > total_isolated)
+ total_isolated = 0;
+
+ if (locked)
+ spin_unlock_irqrestore(&cc->zone->lock, flags);
+
+ /* Update the pageblock-skip if the whole pageblock was scanned */
+ if (blockpfn == end_pfn)
+ update_pageblock_skip(cc, valid_page, total_isolated, false);
+
+ count_compact_events(COMPACTFREE_SCANNED, nr_scanned);
+ if (total_isolated)
+ count_compact_events(COMPACTISOLATED, total_isolated);
return total_isolated;
}
@@ -119,17 +337,14 @@
* a free page).
*/
unsigned long
-isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
+isolate_freepages_range(struct compact_control *cc,
+ unsigned long start_pfn, unsigned long end_pfn)
{
- unsigned long isolated, pfn, block_end_pfn, flags;
- struct zone *zone = NULL;
+ unsigned long isolated, pfn, block_end_pfn;
LIST_HEAD(freelist);
- if (pfn_valid(start_pfn))
- zone = page_zone(pfn_to_page(start_pfn));
-
for (pfn = start_pfn; pfn < end_pfn; pfn += isolated) {
- if (!pfn_valid(pfn) || zone != page_zone(pfn_to_page(pfn)))
+ if (!pfn_valid(pfn) || cc->zone != page_zone(pfn_to_page(pfn)))
break;
/*
@@ -139,10 +354,8 @@
block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
block_end_pfn = min(block_end_pfn, end_pfn);
- spin_lock_irqsave(&zone->lock, flags);
- isolated = isolate_freepages_block(pfn, block_end_pfn,
+ isolated = isolate_freepages_block(cc, pfn, block_end_pfn,
&freelist, true);
- spin_unlock_irqrestore(&zone->lock, flags);
/*
* In strict mode, isolate_freepages_block() returns 0 if
@@ -173,7 +386,7 @@
}
/* Update the number of anon and file isolated pages in the zone */
-static void acct_isolated(struct zone *zone, struct compact_control *cc)
+static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc)
{
struct page *page;
unsigned int count[2] = { 0, };
@@ -181,8 +394,14 @@
list_for_each_entry(page, &cc->migratepages, lru)
count[!!page_is_file_cache(page)]++;
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ /* If locked we can use the interrupt unsafe versions */
+ if (locked) {
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+ __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ } else {
+ mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+ mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ }
}
/* Similar to reclaim, but different enough that they don't share logic */
@@ -206,6 +425,7 @@
* @cc: Compaction control structure.
* @low_pfn: The first PFN of the range.
* @end_pfn: The one-past-the-last PFN of the range.
+ * @unevictable: true if it allows to isolate unevictable pages
*
* Isolate all pages that can be migrated from the range specified by
* [low_pfn, end_pfn). Returns zero if there is a fatal signal
@@ -221,12 +441,15 @@
*/
unsigned long
isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
- unsigned long low_pfn, unsigned long end_pfn)
+ unsigned long low_pfn, unsigned long end_pfn, bool unevictable)
{
unsigned long last_pageblock_nr = 0, pageblock_nr;
unsigned long nr_scanned = 0, nr_isolated = 0;
struct list_head *migratelist = &cc->migratepages;
- isolate_mode_t mode = ISOLATE_ACTIVE|ISOLATE_INACTIVE;
+ isolate_mode_t mode = 0;
+ unsigned long flags;
+ bool locked = false;
+ struct page *page = NULL, *valid_page = NULL;
/*
* Ensure that there are not too many pages isolated from the LRU
@@ -246,25 +469,14 @@
/* Time to isolate some pages for migration */
cond_resched();
- spin_lock_irq(&zone->lru_lock);
for (; low_pfn < end_pfn; low_pfn++) {
- struct page *page;
- bool locked = true;
-
/* give a chance to irqs before checking need_resched() */
- if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) {
- spin_unlock_irq(&zone->lru_lock);
- locked = false;
+ if (locked && !((low_pfn+1) % SWAP_CLUSTER_MAX)) {
+ if (should_release_lock(&zone->lru_lock)) {
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
+ locked = false;
+ }
}
- if (need_resched() || spin_is_contended(&zone->lru_lock)) {
- if (locked)
- spin_unlock_irq(&zone->lru_lock);
- cond_resched();
- spin_lock_irq(&zone->lru_lock);
- if (fatal_signal_pending(current))
- break;
- } else if (!locked)
- spin_lock_irq(&zone->lru_lock);
/*
* migrate_pfn does not necessarily start aligned to a
@@ -293,6 +505,14 @@
if (page_zone(page) != zone)
continue;
+ if (!valid_page)
+ valid_page = page;
+
+ /* If isolation recently failed, do not retry */
+ pageblock_nr = low_pfn >> pageblock_order;
+ if (!isolation_suitable(cc, page))
+ goto next_pageblock;
+
/* Skip if free */
if (PageBuddy(page))
continue;
@@ -302,24 +522,43 @@
* migration is optimistic to see if the minimum amount of work
* satisfies the allocation
*/
- pageblock_nr = low_pfn >> pageblock_order;
if (!cc->sync && last_pageblock_nr != pageblock_nr &&
!migrate_async_suitable(get_pageblock_migratetype(page))) {
- low_pfn += pageblock_nr_pages;
- low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
- last_pageblock_nr = pageblock_nr;
- continue;
+ cc->finished_update_migrate = true;
+ goto next_pageblock;
}
+ /* Check may be lockless but that's ok as we recheck later */
if (!PageLRU(page))
continue;
/*
- * PageLRU is set, and lru_lock excludes isolation,
- * splitting and collapsing (collapsing has already
- * happened if PageLRU is set).
+ * PageLRU is set. lru_lock normally excludes isolation
+ * splitting and collapsing (collapsing has already happened
+ * if PageLRU is set) but the lock is not necessarily taken
+ * here and it is wasteful to take it just to check transhuge.
+ * Check TransHuge without lock and skip the whole pageblock if
+ * it's either a transhuge or hugetlbfs page, as calling
+ * compound_order() without preventing THP from splitting the
+ * page underneath us may return surprising results.
*/
if (PageTransHuge(page)) {
+ if (!locked)
+ goto next_pageblock;
+ low_pfn += (1 << compound_order(page)) - 1;
+ continue;
+ }
+
+ /* Check if it is ok to still hold the lock */
+ locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
+ locked, cc);
+ if (!locked || fatal_signal_pending(current))
+ break;
+
+ /* Recheck PageLRU and PageTransHuge under lock */
+ if (!PageLRU(page))
+ continue;
+ if (PageTransHuge(page)) {
low_pfn += (1 << compound_order(page)) - 1;
continue;
}
@@ -327,13 +566,17 @@
if (!cc->sync)
mode |= ISOLATE_ASYNC_MIGRATE;
+ if (unevictable)
+ mode |= ISOLATE_UNEVICTABLE;
+
/* Try isolate the page */
- if (__isolate_lru_page(page, mode, 0) != 0)
+ if (__isolate_lru_page(page, mode) != 0)
continue;
VM_BUG_ON(PageTransCompound(page));
/* Successfully isolated */
+ cc->finished_update_migrate = true;
del_page_from_lru_list(zone, page, page_lru(page));
list_add(&page->lru, migratelist);
cc->nr_migratepages++;
@@ -344,42 +587,35 @@
++low_pfn;
break;
}
+
+ continue;
+
+next_pageblock:
+ low_pfn += pageblock_nr_pages;
+ low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
+ last_pageblock_nr = pageblock_nr;
}
- acct_isolated(zone, cc);
+ acct_isolated(zone, locked, cc);
- spin_unlock_irq(&zone->lru_lock);
+ if (locked)
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
+
+ /* Update the pageblock-skip if the whole pageblock was scanned */
+ if (low_pfn == end_pfn)
+ update_pageblock_skip(cc, valid_page, nr_isolated, true);
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
+ count_compact_events(COMPACTMIGRATE_SCANNED, nr_scanned);
+ if (nr_isolated)
+ count_compact_events(COMPACTISOLATED, nr_isolated);
+
return low_pfn;
}
#endif /* CONFIG_COMPACTION || CONFIG_CMA */
#ifdef CONFIG_COMPACTION
-
-/* Returns true if the page is within a block suitable for migration to */
-static bool suitable_migration_target(struct page *page)
-{
-
- int migratetype = get_pageblock_migratetype(page);
-
- /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
- if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
- return false;
-
- /* If the page is a large free page, then allow migration */
- if (PageBuddy(page) && page_order(page) >= pageblock_order)
- return true;
-
- /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
- if (migrate_async_suitable(migratetype))
- return true;
-
- /* Otherwise skip the block */
- return false;
-}
-
/*
* Based on information in the current compact_control, find blocks
* suitable for isolating free pages from and then isolate them.
@@ -389,7 +625,6 @@
{
struct page *page;
unsigned long high_pfn, low_pfn, pfn, zone_end_pfn, end_pfn;
- unsigned long flags;
int nr_freepages = cc->nr_freepages;
struct list_head *freelist = &cc->freepages;
@@ -437,29 +672,34 @@
if (!suitable_migration_target(page))
continue;
- /*
- * Found a block suitable for isolating free pages from. Now
- * we disabled interrupts, double check things are ok and
- * isolate the pages. This is to minimise the time IRQs
- * are disabled
- */
+ /* If isolation recently failed, do not retry */
+ if (!isolation_suitable(cc, page))
+ continue;
+
+ /* Found a block suitable for isolating free pages from */
isolated = 0;
- spin_lock_irqsave(&zone->lock, flags);
- if (suitable_migration_target(page)) {
- end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
- isolated = isolate_freepages_block(pfn, end_pfn,
- freelist, false);
- nr_freepages += isolated;
- }
- spin_unlock_irqrestore(&zone->lock, flags);
+
+ /*
+ * As pfn may not start aligned, pfn+pageblock_nr_page
+ * may cross a MAX_ORDER_NR_PAGES boundary and miss
+ * a pfn_valid check. Ensure isolate_freepages_block()
+ * only scans within a pageblock
+ */
+ end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
+ end_pfn = min(end_pfn, zone_end_pfn);
+ isolated = isolate_freepages_block(cc, pfn, end_pfn,
+ freelist, false);
+ nr_freepages += isolated;
/*
* Record the highest PFN we isolated pages from. When next
* looking for free pages, the search will restart here as
* page migration may have returned some pages to the allocator
*/
- if (isolated)
+ if (isolated) {
+ cc->finished_update_free = true;
high_pfn = max(high_pfn, pfn);
+ }
}
/* split_free_page does not map the pages */
@@ -544,8 +784,8 @@
}
/* Perform the isolation */
- low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn);
- if (!low_pfn)
+ low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn, false);
+ if (!low_pfn || cc->contended)
return ISOLATE_ABORT;
cc->migrate_pfn = low_pfn;
@@ -563,8 +803,18 @@
return COMPACT_PARTIAL;
/* Compaction run completes if the migrate and free scanner meet */
- if (cc->free_pfn <= cc->migrate_pfn)
+ if (cc->free_pfn <= cc->migrate_pfn) {
+ /*
+ * Mark that the PG_migrate_skip information should be cleared
+ * by kswapd when it goes to sleep. kswapd does not set the
+ * flag itself as the decision to be clear should be directly
+ * based on an allocation request.
+ */
+ if (!current_is_kswapd())
+ zone->compact_blockskip_flush = true;
+
return COMPACT_COMPLETE;
+ }
/*
* order == -1 is expected when compacting via
@@ -582,12 +832,14 @@
/* Direct compactor: Is a suitable page free? */
for (order = cc->order; order < MAX_ORDER; order++) {
+ struct free_area *area = &zone->free_area[order];
+
/* Job done if page is free of the right migratetype */
- if (!list_empty(&zone->free_area[order].free_list[cc->migratetype]))
+ if (!list_empty(&area->free_list[cc->migratetype]))
return COMPACT_PARTIAL;
/* Job done if allocation would set block type */
- if (order >= pageblock_order && zone->free_area[order].nr_free)
+ if (cc->order >= pageblock_order && area->nr_free)
return COMPACT_PARTIAL;
}
@@ -647,6 +899,8 @@
static int compact_zone(struct zone *zone, struct compact_control *cc)
{
int ret;
+ unsigned long start_pfn = zone->zone_start_pfn;
+ unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
ret = compaction_suitable(zone, cc->order);
switch (ret) {
@@ -659,10 +913,29 @@
;
}
- /* Setup to move all movable pages to the end of the zone */
- cc->migrate_pfn = zone->zone_start_pfn;
- cc->free_pfn = cc->migrate_pfn + zone->spanned_pages;
- cc->free_pfn &= ~(pageblock_nr_pages-1);
+ /*
+ * Setup to move all movable pages to the end of the zone. Used cached
+ * information on where the scanners should start but check that it
+ * is initialised by ensuring the values are within zone boundaries.
+ */
+ cc->migrate_pfn = zone->compact_cached_migrate_pfn;
+ cc->free_pfn = zone->compact_cached_free_pfn;
+ if (cc->free_pfn < start_pfn || cc->free_pfn > end_pfn) {
+ cc->free_pfn = end_pfn & ~(pageblock_nr_pages-1);
+ zone->compact_cached_free_pfn = cc->free_pfn;
+ }
+ if (cc->migrate_pfn < start_pfn || cc->migrate_pfn > end_pfn) {
+ cc->migrate_pfn = start_pfn;
+ zone->compact_cached_migrate_pfn = cc->migrate_pfn;
+ }
+
+ /*
+ * Clear pageblock skip if there were failures recently and compaction
+ * is about to be retried after being deferred. kswapd does not do
+ * this reset as it'll reset the cached information when going to sleep.
+ */
+ if (compaction_restarting(zone, cc->order) && !current_is_kswapd())
+ __reset_isolation_suitable(zone);
migrate_prep_local();
@@ -673,6 +946,8 @@
switch (isolate_migratepages(zone, cc)) {
case ISOLATE_ABORT:
ret = COMPACT_PARTIAL;
+ putback_lru_pages(&cc->migratepages);
+ cc->nr_migratepages = 0;
goto out;
case ISOLATE_NONE:
continue;
@@ -687,10 +962,6 @@
update_nr_listpages(cc);
nr_remaining = cc->nr_migratepages;
- count_vm_event(COMPACTBLOCKS);
- count_vm_events(COMPACTPAGES, nr_migrate - nr_remaining);
- if (nr_remaining)
- count_vm_events(COMPACTPAGEFAILED, nr_remaining);
trace_mm_compaction_migratepages(nr_migrate - nr_remaining,
nr_remaining);
@@ -698,8 +969,11 @@
if (err) {
putback_lru_pages(&cc->migratepages);
cc->nr_migratepages = 0;
+ if (err == -ENOMEM) {
+ ret = COMPACT_PARTIAL;
+ goto out;
+ }
}
-
}
out:
@@ -712,8 +986,9 @@
static unsigned long compact_zone_order(struct zone *zone,
int order, gfp_t gfp_mask,
- bool sync)
+ bool sync, bool *contended)
{
+ unsigned long ret;
struct compact_control cc = {
.nr_freepages = 0,
.nr_migratepages = 0,
@@ -725,7 +1000,13 @@
INIT_LIST_HEAD(&cc.freepages);
INIT_LIST_HEAD(&cc.migratepages);
- return compact_zone(zone, &cc);
+ ret = compact_zone(zone, &cc);
+
+ VM_BUG_ON(!list_empty(&cc.freepages));
+ VM_BUG_ON(!list_empty(&cc.migratepages));
+
+ *contended = cc.contended;
+ return ret;
}
int sysctl_extfrag_threshold = 500;
@@ -737,12 +1018,14 @@
* @gfp_mask: The GFP mask of the current allocation
* @nodemask: The allowed nodes to allocate from
* @sync: Whether migration is synchronous or not
+ * @contended: Return value that is true if compaction was aborted due to lock contention
+ * @page: Optionally capture a free page of the requested order during compaction
*
* This is the main entry point for direct page compaction.
*/
unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync)
+ bool sync, bool *contended)
{
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
int may_enter_fs = gfp_mask & __GFP_FS;
@@ -752,15 +1035,11 @@
int rc = COMPACT_SKIPPED;
int alloc_flags = 0;
- /*
- * Check whether it is worth even starting compaction. The order check is
- * made because an assumption is made that the page allocator can satisfy
- * the "cheaper" orders without taking special steps
- */
+ /* Check if the GFP flags allow compaction */
if (!order || !may_enter_fs || !may_perform_io)
return rc;
- count_vm_event(COMPACTSTALL);
+ count_compact_event(COMPACTSTALL);
#ifdef CONFIG_CMA
if (allocflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
@@ -771,7 +1050,8 @@
nodemask) {
int status;
- status = compact_zone_order(zone, order, gfp_mask, sync);
+ status = compact_zone_order(zone, order, gfp_mask, sync,
+ contended);
rc = max(status, rc);
/* If a normal allocation would succeed, stop compacting */
@@ -808,7 +1088,7 @@
if (cc->order > 0) {
int ok = zone_watermark_ok(zone, cc->order,
low_wmark_pages(zone), 0, 0);
- if (ok && cc->order > zone->compact_order_failed)
+ if (ok && cc->order >= zone->compact_order_failed)
zone->compact_order_failed = cc->order + 1;
/* Currently async compaction is never deferred. */
else if (!ok && cc->sync)
@@ -843,7 +1123,7 @@
}
/* Compact all nodes in the system */
-static int compact_nodes(void)
+static void compact_nodes(void)
{
int nid;
@@ -852,8 +1132,6 @@
for_each_online_node(nid)
compact_node(nid);
-
- return COMPACT_COMPLETE;
}
/* The written value is actually unused, all memory is compacted */
@@ -864,7 +1142,7 @@
void __user *buffer, size_t *length, loff_t *ppos)
{
if (write)
- return compact_nodes();
+ compact_nodes();
return 0;
}
diff --git a/mm/internal.h b/mm/internal.h
index 8c6fd44..3439ef4 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -120,17 +120,24 @@
unsigned long free_pfn; /* isolate_freepages search base */
unsigned long migrate_pfn; /* isolate_migratepages search base */
bool sync; /* Synchronous migration */
+ bool ignore_skip_hint; /* Scan blocks even if marked skip */
+ bool finished_update_free; /* True when the zone cached pfns are
+ * no longer being updated
+ */
+ bool finished_update_migrate;
int order; /* order a direct compactor needs */
int migratetype; /* MOVABLE, RECLAIMABLE etc */
struct zone *zone;
+ bool contended; /* True if a lock was contended */
};
unsigned long
-isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn);
+isolate_freepages_range(struct compact_control *cc,
+ unsigned long start_pfn, unsigned long end_pfn);
unsigned long
isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
- unsigned long low_pfn, unsigned long end_pfn);
+ unsigned long low_pfn, unsigned long end_pfn, bool unevictable);
#endif
@@ -356,3 +363,6 @@
#define ALLOC_HIGH 0x20 /* __GFP_HIGH set */
#define ALLOC_CPUSET 0x40 /* check for correct cpuset */
#define ALLOC_CMA 0x80 /* allow allocations from CMA areas */
+
+unsigned long reclaim_clean_pages_from_list(struct zone *zone,
+ struct list_head *page_list);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 7685d4a..d436634 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1116,11 +1116,6 @@
mz->lru_size[lru] -= 1 << compound_order(page);
}
-void mem_cgroup_lru_del(struct page *page)
-{
- mem_cgroup_lru_del_list(page, page_lru(page));
-}
-
/**
* mem_cgroup_lru_move_lists - account for moving a page between lrus
* @zone: zone of the page
@@ -1149,15 +1144,25 @@
* Checks whether given mem is same or in the root_mem_cgroup's
* hierarchy subtree
*/
-static bool mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
- struct mem_cgroup *memcg)
+bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
+ struct mem_cgroup *memcg)
{
- if (root_memcg != memcg) {
- return (root_memcg->use_hierarchy &&
- css_is_ancestor(&memcg->css, &root_memcg->css));
- }
+ if (root_memcg == memcg)
+ return true;
+ if (!root_memcg->use_hierarchy)
+ return false;
+ return css_is_ancestor(&memcg->css, &root_memcg->css);
+}
- return true;
+static bool mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
+ struct mem_cgroup *memcg)
+{
+ bool ret;
+
+ rcu_read_lock();
+ ret = __mem_cgroup_same_or_subtree(root_memcg, memcg);
+ rcu_read_unlock();
+ return ret;
}
int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg)
@@ -5610,7 +5615,6 @@
if (mm) {
if (mc.to)
mem_cgroup_move_charge(mm);
- put_swap_token(mm);
mmput(mm);
}
if (mc.to)
diff --git a/mm/memory.c b/mm/memory.c
index 2111354..b6ab040 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2935,7 +2935,6 @@
delayacct_set_flag(DELAYACCT_PF_SWAPIN);
page = lookup_swap_cache(entry);
if (!page) {
- grab_swap_token(mm); /* Contend for token _before_ read-in */
page = swapin_readahead(entry,
GFP_HIGHUSER_MOVABLE, vma, address);
if (!page) {
@@ -2965,6 +2964,7 @@
}
locked = lock_page_or_retry(page, mm, flags);
+
delayacct_clear_flag(DELAYACCT_PF_SWAPIN);
if (!locked) {
ret |= VM_FAULT_RETRY;
diff --git a/mm/migrate.c b/mm/migrate.c
index 79a791f..4f5c02e 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -969,6 +969,7 @@
{
int retry = 1;
int nr_failed = 0;
+ int nr_succeeded = 0;
int pass = 0;
struct page *page;
struct page *page2;
@@ -997,6 +998,7 @@
trace_migrate_retry(retry);
break;
case 0:
+ nr_succeeded++;
break;
default:
/* Permanent failure */
@@ -1007,6 +1009,10 @@
}
rc = 0;
out:
+ if (nr_succeeded)
+ count_vm_events(PGMIGRATE_SUCCESS, nr_succeeded);
+ if (nr_failed)
+ count_vm_events(PGMIGRATE_FAIL, nr_failed);
if (!swapwrite)
current->flags &= ~PF_SWAPWRITE;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 8a93508..9cc2f45 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1441,6 +1441,45 @@
set_page_refcounted(page + i);
}
+static int __isolate_free_page(struct page *page, unsigned int order)
+{
+ unsigned long watermark;
+ struct zone *zone;
+ int mt;
+
+ BUG_ON(!PageBuddy(page));
+
+ zone = page_zone(page);
+ mt = get_pageblock_migratetype(page);
+
+ if (mt != MIGRATE_ISOLATE && !is_migrate_cma(mt)) {
+ /* Obey watermarks as if the page was being allocated */
+ watermark = low_wmark_pages(zone) + (1 << order);
+ if (!zone_watermark_ok(zone, 0, watermark, 0, 0))
+ return 0;
+
+ __mod_zone_freepage_state(zone, -(1UL << order), mt);
+ }
+
+ /* Remove page from free list */
+ list_del(&page->lru);
+ zone->free_area[order].nr_free--;
+ rmv_page_order(page);
+
+ /* Set the pageblock if the isolated page is at least a pageblock */
+ if (order >= pageblock_order - 1) {
+ struct page *endpage = page + (1 << order) - 1;
+ for (; page < endpage; page += pageblock_nr_pages) {
+ mt = get_pageblock_migratetype(page);
+ if (mt != MIGRATE_ISOLATE && !is_migrate_cma(mt))
+ set_pageblock_migratetype(page,
+ MIGRATE_MOVABLE);
+ }
+ }
+
+ return 1UL << order;
+}
+
/*
* Similar to split_page except the page is already free. As this is only
* being used for migration, the migratetype of the block also changes.
@@ -1454,45 +1493,18 @@
int split_free_page(struct page *page)
{
unsigned int order;
- unsigned long watermark;
- struct zone *zone;
- int mt;
+ int nr_pages;
- BUG_ON(!PageBuddy(page));
-
- zone = page_zone(page);
order = page_order(page);
- mt = get_pageblock_migratetype(page);
- /* Obey watermarks as if the page was being allocated */
- watermark = low_wmark_pages(zone) + (1 << order);
- if (!is_migrate_cma(mt) && mt != MIGRATE_ISOLATE &&
- !zone_watermark_ok(zone, 0, watermark, 0, 0))
+ nr_pages = __isolate_free_page(page, order);
+ if (!nr_pages)
return 0;
- /* Remove page from free list */
- list_del(&page->lru);
- zone->free_area[order].nr_free--;
- rmv_page_order(page);
-
- if (unlikely(mt != MIGRATE_ISOLATE))
- __mod_zone_freepage_state(zone, -(1UL << order), mt);
-
/* Split into individual pages */
set_page_refcounted(page);
split_page(page, order);
-
- if (order >= pageblock_order - 1) {
- struct page *endpage = page + (1 << order) - 1;
- for (; page < endpage; page += pageblock_nr_pages) {
- mt = get_pageblock_migratetype(page);
- if (mt != MIGRATE_ISOLATE && !is_migrate_cma(mt))
- set_pageblock_migratetype(page,
- MIGRATE_MOVABLE);
- }
- }
-
- return 1 << order;
+ return nr_pages;
}
/*
@@ -2134,11 +2146,9 @@
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
int migratetype, bool sync_migration,
- bool *deferred_compaction,
+ bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
- struct page *page;
-
if (!order)
return NULL;
@@ -2149,9 +2159,12 @@
current->flags |= PF_MEMALLOC;
*did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
- nodemask, sync_migration);
+ nodemask, sync_migration,
+ contended_compaction);
current->flags &= ~PF_MEMALLOC;
+
if (*did_some_progress != COMPACT_SKIPPED) {
+ struct page *page;
/* Page migration frees to the PCP lists but we want merging */
drain_pages(get_cpu());
@@ -2162,6 +2175,7 @@
alloc_flags, preferred_zone,
migratetype);
if (page) {
+ preferred_zone->compact_blockskip_flush = false;
preferred_zone->compact_considered = 0;
preferred_zone->compact_defer_shift = 0;
if (order >= preferred_zone->compact_order_failed)
@@ -2195,7 +2209,7 @@
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
int migratetype, bool sync_migration,
- bool *deferred_compaction,
+ bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
return NULL;
@@ -2362,6 +2376,7 @@
unsigned long did_some_progress;
bool sync_migration = false;
bool deferred_compaction = false;
+ bool contended_compaction = false;
/*
* In the slowpath, we sanity check order to avoid ever trying to
@@ -2443,6 +2458,7 @@
nodemask,
alloc_flags, preferred_zone,
migratetype, sync_migration,
+ &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -2452,10 +2468,11 @@
/*
* If compaction is deferred for high-order allocations, it is because
* sync compaction recently failed. In this is the case and the caller
- * has requested the system not be heavily disrupted, fail the
- * allocation now instead of entering direct reclaim
+ * requested a movable allocation that does not heavily disrupt the
+ * system then fail the allocation instead of entering direct reclaim.
*/
- if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD))
+ if ((deferred_compaction || contended_compaction) &&
+ (gfp_mask & __GFP_NO_KSWAPD))
goto nopage;
/* Try direct reclaim and then allocating */
@@ -2526,6 +2543,7 @@
nodemask,
alloc_flags, preferred_zone,
migratetype, sync_migration,
+ &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -5592,26 +5610,28 @@
}
/*
- * This is designed as sub function...plz see page_isolation.c also.
- * set/clear page block's type to be ISOLATE.
- * page allocater never alloc memory from ISOLATE block.
+ * This function checks whether pageblock includes unmovable pages or not.
+ * If @count is not zero, it is okay to include less @count unmovable pages
+ *
+ * PageLRU check wihtout isolation or lru_lock could race so that
+ * MIGRATE_MOVABLE block might include unmovable pages. It means you can't
+ * expect this function should be exact.
*/
-
-static int
-__count_immobile_pages(struct zone *zone, struct page *page, int count)
+static bool
+__has_unmovable_pages(struct zone *zone, struct page *page, int count)
{
unsigned long pfn, iter, found;
int mt;
/*
* For avoiding noise data, lru_add_drain_all() should be called
- * If ZONE_MOVABLE, the zone never contains immobile pages
+ * If ZONE_MOVABLE, the zone never contains unmovable pages
*/
if (zone_idx(zone) == ZONE_MOVABLE)
- return true;
+ return false;
mt = get_pageblock_migratetype(page);
if (mt == MIGRATE_MOVABLE || is_migrate_cma(mt))
- return true;
+ return false;
pfn = page_to_pfn(page);
for (found = 0, iter = 0; iter < pageblock_nr_pages; iter++) {
@@ -5621,11 +5641,18 @@
continue;
page = pfn_to_page(check);
- if (!page_count(page)) {
+ /*
+ * We can't use page_count without pin a page
+ * because another CPU can free compound page.
+ * This check already skips compound tails of THP
+ * because their page->_count is zero at all time.
+ */
+ if (!atomic_read(&page->_count)) {
if (PageBuddy(page))
iter += (1 << page_order(page)) - 1;
continue;
}
+
if (!PageLRU(page))
found++;
/*
@@ -5642,9 +5669,9 @@
* page at boot.
*/
if (found > count)
- return false;
+ return true;
}
- return true;
+ return false;
}
bool is_pageblock_removable_nolock(struct page *page)
@@ -5668,7 +5695,7 @@
zone->zone_start_pfn + zone->spanned_pages <= pfn)
return false;
- return __count_immobile_pages(zone, page, 0);
+ return !__has_unmovable_pages(zone, page, 0);
}
int set_migratetype_isolate(struct page *page)
@@ -5707,12 +5734,12 @@
* FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
* We just check MOVABLE pages.
*/
- if (__count_immobile_pages(zone, page, arg.pages_found))
+ if (!__has_unmovable_pages(zone, page, arg.pages_found))
ret = 0;
-
/*
- * immobile means "not-on-lru" paes. If immobile is larger than
- * removable-by-driver pages reported by notifier, we'll fail.
+ * Unmovable means "not-on-lru" pages. If Unmovable pages are
+ * larger than removable-by-driver pages reported by notifier,
+ * we'll fail.
*/
out:
@@ -5775,34 +5802,27 @@
}
/* [start, end) must belong to a single zone. */
-static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
+static int __alloc_contig_migrate_range(struct compact_control *cc,
+ unsigned long start, unsigned long end)
{
/* This function is based on compact_zone() from compaction.c. */
-
+ unsigned long nr_reclaimed;
unsigned long pfn = start;
unsigned int tries = 0;
int ret = 0;
- struct compact_control cc = {
- .nr_migratepages = 0,
- .order = -1,
- .zone = page_zone(pfn_to_page(start)),
- .sync = true,
- };
- INIT_LIST_HEAD(&cc.migratepages);
-
migrate_prep();
- while (pfn < end || !list_empty(&cc.migratepages)) {
+ while (pfn < end || !list_empty(&cc->migratepages)) {
if (fatal_signal_pending(current)) {
ret = -EINTR;
break;
}
- if (list_empty(&cc.migratepages)) {
- cc.nr_migratepages = 0;
- pfn = isolate_migratepages_range(cc.zone, &cc,
- pfn, end);
+ if (list_empty(&cc->migratepages)) {
+ cc->nr_migratepages = 0;
+ pfn = isolate_migratepages_range(cc->zone, cc,
+ pfn, end, true);
if (!pfn) {
ret = -EINTR;
break;
@@ -5813,12 +5833,16 @@
break;
}
- ret = migrate_pages(&cc.migratepages,
+ nr_reclaimed = reclaim_clean_pages_from_list(cc->zone, &cc->migratepages);
+
+ cc->nr_migratepages -= nr_reclaimed;
+
+ ret = migrate_pages(&cc->migratepages,
__alloc_contig_migrate_alloc,
0, false, MIGRATE_SYNC);
}
- putback_lru_pages(&cc.migratepages);
+ putback_lru_pages(&cc->migratepages);
return ret > 0 ? 0 : ret;
}
@@ -5849,6 +5873,15 @@
unsigned long outer_start, outer_end;
int ret = 0, order;
+ struct compact_control cc = {
+ .nr_migratepages = 0,
+ .order = -1,
+ .zone = page_zone(pfn_to_page(start)),
+ .sync = true,
+ .ignore_skip_hint = true,
+ };
+ INIT_LIST_HEAD(&cc.migratepages);
+
/*
* What we do here is we mark all pageblocks in range as
* MIGRATE_ISOLATE. Because pageblock and max order pages may
@@ -5880,7 +5913,7 @@
zone->cma_alloc = 1;
- ret = __alloc_contig_migrate_range(start, end);
+ ret = __alloc_contig_migrate_range(&cc, start, end);
if (ret)
goto done;
@@ -5924,7 +5957,7 @@
/* Grab isolated pages from freelists. */
- outer_end = isolate_freepages_range(outer_start, end);
+ outer_end = isolate_freepages_range(&cc, outer_start, end);
if (!outer_end) {
ret = -EBUSY;
goto done;
@@ -5945,8 +5978,15 @@
void free_contig_range(unsigned long pfn, unsigned nr_pages)
{
- for (; nr_pages--; ++pfn)
- __free_page(pfn_to_page(pfn));
+ unsigned int count = 0;
+
+ for (; nr_pages--; pfn++) {
+ struct page *page = pfn_to_page(pfn);
+
+ count += page_count(page) != 1;
+ __free_page(page);
+ }
+ WARN(count != 0, "%d pages are still in use!\n", count);
}
#endif
@@ -6053,6 +6093,7 @@
#ifdef CONFIG_MEMORY_FAILURE
{1UL << PG_hwpoison, "hwpoison" },
#endif
+ {1UL << PG_readahead, "PG_readahead" },
{-1UL, NULL },
};
diff --git a/mm/readahead.c b/mm/readahead.c
index 728a7a3..56f8a24 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -184,6 +184,9 @@
if (!page)
break;
page->index = page_offset;
+
+ page->flags |= (1L << PG_readahead);
+
list_add(&page->lru, &page_pool);
if (page_idx == nr_to_read - lookahead_size)
SetPageReadahead(page);
diff --git a/mm/rmap.c b/mm/rmap.c
index 5b5ad58..0f3b7cd 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -755,12 +755,6 @@
pte_unmap_unlock(pte, ptl);
}
- /* Pretend the page is referenced if the task has the
- swap token and is in the middle of a page fault. */
- if (mm != current->mm && has_swap_token(mm) &&
- rwsem_is_locked(&mm->mmap_sem))
- referenced++;
-
(*mapcount)--;
if (referenced)
diff --git a/mm/thrash.c b/mm/thrash.c
deleted file mode 100644
index 57ad495..0000000
--- a/mm/thrash.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * mm/thrash.c
- *
- * Copyright (C) 2004, Red Hat, Inc.
- * Copyright (C) 2004, Rik van Riel <riel@redhat.com>
- * Released under the GPL, see the file COPYING for details.
- *
- * Simple token based thrashing protection, using the algorithm
- * described in: http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/abs05-1.html
- *
- * Sep 2006, Ashwin Chaugule <ashwin.chaugule@celunite.com>
- * Improved algorithm to pass token:
- * Each task has a priority which is incremented if it contended
- * for the token in an interval less than its previous attempt.
- * If the token is acquired, that task's priority is boosted to prevent
- * the token from bouncing around too often and to let the task make
- * some progress in its execution.
- */
-
-#include <linux/jiffies.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/swap.h>
-#include <linux/memcontrol.h>
-
-#include <trace/events/vmscan.h>
-
-#define TOKEN_AGING_INTERVAL (0xFF)
-
-static DEFINE_SPINLOCK(swap_token_lock);
-struct mm_struct *swap_token_mm;
-static struct mem_cgroup *swap_token_memcg;
-
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm)
-{
- struct mem_cgroup *memcg;
-
- memcg = try_get_mem_cgroup_from_mm(mm);
- if (memcg)
- css_put(mem_cgroup_css(memcg));
-
- return memcg;
-}
-#else
-static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm)
-{
- return NULL;
-}
-#endif
-
-void grab_swap_token(struct mm_struct *mm)
-{
- int current_interval;
- unsigned int old_prio = mm->token_priority;
- static unsigned int global_faults;
- static unsigned int last_aging;
-
- global_faults++;
-
- current_interval = global_faults - mm->faultstamp;
-
- if (!spin_trylock(&swap_token_lock))
- return;
-
- /* First come first served */
- if (!swap_token_mm)
- goto replace_token;
-
- /*
- * Usually, we don't need priority aging because long interval faults
- * makes priority decrease quickly. But there is one exception. If the
- * token owner task is sleeping, it never make long interval faults.
- * Thus, we need a priority aging mechanism instead. The requirements
- * of priority aging are
- * 1) An aging interval is reasonable enough long. Too short aging
- * interval makes quick swap token lost and decrease performance.
- * 2) The swap token owner task have to get priority aging even if
- * it's under sleep.
- */
- if ((global_faults - last_aging) > TOKEN_AGING_INTERVAL) {
- swap_token_mm->token_priority /= 2;
- last_aging = global_faults;
- }
-
- if (mm == swap_token_mm) {
- mm->token_priority += 2;
- goto update_priority;
- }
-
- if (current_interval < mm->last_interval)
- mm->token_priority++;
- else {
- if (likely(mm->token_priority > 0))
- mm->token_priority--;
- }
-
- /* Check if we deserve the token */
- if (mm->token_priority > swap_token_mm->token_priority)
- goto replace_token;
-
-update_priority:
- trace_update_swap_token_priority(mm, old_prio, swap_token_mm);
-
-out:
- mm->faultstamp = global_faults;
- mm->last_interval = current_interval;
- spin_unlock(&swap_token_lock);
- return;
-
-replace_token:
- mm->token_priority += 2;
- trace_replace_swap_token(swap_token_mm, mm);
- swap_token_mm = mm;
- swap_token_memcg = swap_token_memcg_from_mm(mm);
- last_aging = global_faults;
- goto out;
-}
-
-/* Called on process exit. */
-void __put_swap_token(struct mm_struct *mm)
-{
- spin_lock(&swap_token_lock);
- if (likely(mm == swap_token_mm)) {
- trace_put_swap_token(swap_token_mm);
- swap_token_mm = NULL;
- swap_token_memcg = NULL;
- }
- spin_unlock(&swap_token_lock);
-}
-
-static bool match_memcg(struct mem_cgroup *a, struct mem_cgroup *b)
-{
- if (!a)
- return true;
- if (!b)
- return true;
- if (a == b)
- return true;
- return false;
-}
-
-void disable_swap_token(struct mem_cgroup *memcg)
-{
- /* memcg reclaim don't disable unrelated mm token. */
- if (match_memcg(memcg, swap_token_memcg)) {
- spin_lock(&swap_token_lock);
- if (match_memcg(memcg, swap_token_memcg)) {
- trace_disable_swap_token(swap_token_mm);
- swap_token_mm = NULL;
- swap_token_memcg = NULL;
- }
- spin_unlock(&swap_token_lock);
- }
-}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 33dc256..c69f5e2 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -53,24 +53,6 @@
#define CREATE_TRACE_POINTS
#include <trace/events/vmscan.h>
-/*
- * reclaim_mode determines how the inactive list is shrunk
- * RECLAIM_MODE_SINGLE: Reclaim only order-0 pages
- * RECLAIM_MODE_ASYNC: Do not block
- * RECLAIM_MODE_SYNC: Allow blocking e.g. call wait_on_page_writeback
- * RECLAIM_MODE_LUMPYRECLAIM: For high-order allocations, take a reference
- * page from the LRU and reclaim all pages within a
- * naturally aligned range
- * RECLAIM_MODE_COMPACTION: For high-order allocations, reclaim a number of
- * order-0 pages and then compact the zone
- */
-typedef unsigned __bitwise__ reclaim_mode_t;
-#define RECLAIM_MODE_SINGLE ((__force reclaim_mode_t)0x01u)
-#define RECLAIM_MODE_ASYNC ((__force reclaim_mode_t)0x02u)
-#define RECLAIM_MODE_SYNC ((__force reclaim_mode_t)0x04u)
-#define RECLAIM_MODE_LUMPYRECLAIM ((__force reclaim_mode_t)0x08u)
-#define RECLAIM_MODE_COMPACTION ((__force reclaim_mode_t)0x10u)
-
struct scan_control {
/* Incremented by the number of inactive pages that were scanned */
unsigned long nr_scanned;
@@ -96,11 +78,8 @@
int order;
- /*
- * Intend to reclaim enough continuous memory rather than reclaim
- * enough amount of memory. i.e, mode for high order allocation.
- */
- reclaim_mode_t reclaim_mode;
+ /* Scan (total_size >> priority) pages at once */
+ int priority;
/*
* The memory cgroup that hit its limit and as a result is the
@@ -164,26 +143,16 @@
{
return !sc->target_mem_cgroup;
}
-
-static bool scanning_global_lru(struct mem_cgroup_zone *mz)
-{
- return !mz->mem_cgroup;
-}
#else
static bool global_reclaim(struct scan_control *sc)
{
return true;
}
-
-static bool scanning_global_lru(struct mem_cgroup_zone *mz)
-{
- return true;
-}
#endif
static struct zone_reclaim_stat *get_reclaim_stat(struct mem_cgroup_zone *mz)
{
- if (!scanning_global_lru(mz))
+ if (!mem_cgroup_disabled())
return mem_cgroup_get_reclaim_stat(mz->mem_cgroup, mz->zone);
return &mz->zone->reclaim_stat;
@@ -192,7 +161,7 @@
static unsigned long zone_nr_lru_pages(struct mem_cgroup_zone *mz,
enum lru_list lru)
{
- if (!scanning_global_lru(mz))
+ if (!mem_cgroup_disabled())
return mem_cgroup_zone_nr_lru_pages(mz->mem_cgroup,
zone_to_nid(mz->zone),
zone_idx(mz->zone),
@@ -364,39 +333,6 @@
return ret;
}
-static void set_reclaim_mode(int priority, struct scan_control *sc,
- bool sync)
-{
- reclaim_mode_t syncmode = sync ? RECLAIM_MODE_SYNC : RECLAIM_MODE_ASYNC;
-
- /*
- * Initially assume we are entering either lumpy reclaim or
- * reclaim/compaction.Depending on the order, we will either set the
- * sync mode or just reclaim order-0 pages later.
- */
- if (COMPACTION_BUILD)
- sc->reclaim_mode = RECLAIM_MODE_COMPACTION;
- else
- sc->reclaim_mode = RECLAIM_MODE_LUMPYRECLAIM;
-
- /*
- * Avoid using lumpy reclaim or reclaim/compaction if possible by
- * restricting when its set to either costly allocations or when
- * under memory pressure
- */
- if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
- sc->reclaim_mode |= syncmode;
- else if (sc->order && priority < DEF_PRIORITY - 2)
- sc->reclaim_mode |= syncmode;
- else
- sc->reclaim_mode = RECLAIM_MODE_SINGLE | RECLAIM_MODE_ASYNC;
-}
-
-static void reset_reclaim_mode(struct scan_control *sc)
-{
- sc->reclaim_mode = RECLAIM_MODE_SINGLE | RECLAIM_MODE_ASYNC;
-}
-
static inline int is_page_cache_freeable(struct page *page)
{
/*
@@ -416,10 +352,6 @@
return 1;
if (bdi == current->backing_dev_info)
return 1;
-
- /* lumpy reclaim for hugepage often need a lot of write */
- if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
- return 1;
return 0;
}
@@ -523,8 +455,7 @@
/* synchronous write or broken a_ops? */
ClearPageReclaim(page);
}
- trace_mm_vmscan_writepage(page,
- trace_reclaim_flags(page, sc->reclaim_mode));
+ trace_mm_vmscan_writepage(page, trace_reclaim_flags(page));
inc_zone_page_state(page, NR_VMSCAN_WRITE);
return PAGE_SUCCESS;
}
@@ -701,19 +632,15 @@
};
static enum page_references page_check_references(struct page *page,
- struct mem_cgroup_zone *mz,
struct scan_control *sc)
{
int referenced_ptes, referenced_page;
unsigned long vm_flags;
- referenced_ptes = page_referenced(page, 1, mz->mem_cgroup, &vm_flags);
+ referenced_ptes = page_referenced(page, 1, sc->target_mem_cgroup,
+ &vm_flags);
referenced_page = TestClearPageReferenced(page);
- /* Lumpy reclaim - ignore references */
- if (sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM)
- return PAGEREF_RECLAIM;
-
/*
* Mlock lost the isolation race with us. Let try_to_unmap()
* move the page to the unevictable list.
@@ -763,11 +690,12 @@
* shrink_page_list() returns the number of reclaimed pages
*/
static unsigned long shrink_page_list(struct list_head *page_list,
- struct mem_cgroup_zone *mz,
+ struct zone *zone,
struct scan_control *sc,
- int priority,
+ enum ttu_flags ttu_flags,
unsigned long *ret_nr_dirty,
- unsigned long *ret_nr_writeback)
+ unsigned long *ret_nr_writeback,
+ bool force_reclaim)
{
LIST_HEAD(ret_pages);
LIST_HEAD(free_pages);
@@ -780,10 +708,10 @@
cond_resched();
while (!list_empty(page_list)) {
- enum page_references references;
struct address_space *mapping;
struct page *page;
int may_enter_fs;
+ enum page_references references = PAGEREF_RECLAIM_CLEAN;
cond_resched();
@@ -794,7 +722,7 @@
goto keep;
VM_BUG_ON(PageActive(page));
- VM_BUG_ON(page_zone(page) != mz->zone);
+ VM_BUG_ON(page_zone(page) != zone);
sc->nr_scanned++;
@@ -813,22 +741,13 @@
if (PageWriteback(page)) {
nr_writeback++;
- /*
- * Synchronous reclaim cannot queue pages for
- * writeback due to the possibility of stack overflow
- * but if it encounters a page under writeback, wait
- * for the IO to complete.
- */
- if ((sc->reclaim_mode & RECLAIM_MODE_SYNC) &&
- may_enter_fs)
- wait_on_page_writeback(page);
- else {
- unlock_page(page);
- goto keep_lumpy;
- }
+ unlock_page(page);
+ goto keep;
}
- references = page_check_references(page, mz, sc);
+ if (!force_reclaim)
+ references = page_check_references(page, sc);
+
switch (references) {
case PAGEREF_ACTIVATE:
goto activate_locked;
@@ -858,7 +777,7 @@
* processes. Try to unmap it here.
*/
if (page_mapped(page) && mapping) {
- switch (try_to_unmap(page, TTU_UNMAP)) {
+ switch (try_to_unmap(page, ttu_flags)) {
case SWAP_FAIL:
goto activate_locked;
case SWAP_AGAIN:
@@ -879,7 +798,8 @@
* unless under significant pressure.
*/
if (page_is_file_cache(page) &&
- (!current_is_kswapd() || priority >= DEF_PRIORITY - 2)) {
+ (!current_is_kswapd() ||
+ sc->priority >= DEF_PRIORITY - 2)) {
/*
* Immediately reclaim when written back.
* Similar in principal to deactivate_page()
@@ -908,7 +828,7 @@
goto activate_locked;
case PAGE_SUCCESS:
if (PageWriteback(page))
- goto keep_lumpy;
+ goto keep;
if (PageDirty(page))
goto keep;
@@ -994,7 +914,6 @@
try_to_free_swap(page);
unlock_page(page);
putback_lru_page(page);
- reset_reclaim_mode(sc);
continue;
activate_locked:
@@ -1007,8 +926,6 @@
keep_locked:
unlock_page(page);
keep:
- reset_reclaim_mode(sc);
-keep_lumpy:
list_add(&page->lru, &ret_pages);
VM_BUG_ON(PageLRU(page) || PageUnevictable(page));
}
@@ -1020,7 +937,7 @@
* will encounter the same problem
*/
if (nr_dirty && nr_dirty == nr_congested && global_reclaim(sc))
- zone_set_flag(mz->zone, ZONE_CONGESTED);
+ zone_set_flag(zone, ZONE_CONGESTED);
free_hot_cold_page_list(&free_pages, 1);
@@ -1031,6 +948,33 @@
return nr_reclaimed;
}
+unsigned long reclaim_clean_pages_from_list(struct zone *zone,
+ struct list_head *page_list)
+{
+ struct scan_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ .priority = DEF_PRIORITY,
+ .may_unmap = 1,
+ };
+ unsigned long ret, dummy1, dummy2;
+ struct page *page, *next;
+ LIST_HEAD(clean_pages);
+
+ list_for_each_entry_safe(page, next, page_list, lru) {
+ if (page_is_file_cache(page) && !PageDirty(page)) {
+ ClearPageActive(page);
+ list_move(&page->lru, &clean_pages);
+ }
+ }
+
+ ret = shrink_page_list(&clean_pages, zone, &sc,
+ TTU_UNMAP|TTU_IGNORE_ACCESS,
+ &dummy1, &dummy2, true);
+ list_splice(&clean_pages, page_list);
+ __mod_zone_page_state(zone, NR_ISOLATED_FILE, -ret);
+ return ret;
+}
+
/*
* Attempt to remove the specified page from its LRU. Only take this page
* if it is of the appropriate PageActive status. Pages which are being
@@ -1041,35 +985,16 @@
*
* returns 0 on success, -ve errno on failure.
*/
-int __isolate_lru_page(struct page *page, isolate_mode_t mode, int file)
+int __isolate_lru_page(struct page *page, isolate_mode_t mode)
{
- bool all_lru_mode;
int ret = -EINVAL;
/* Only take pages on the LRU. */
if (!PageLRU(page))
return ret;
- all_lru_mode = (mode & (ISOLATE_ACTIVE|ISOLATE_INACTIVE)) ==
- (ISOLATE_ACTIVE|ISOLATE_INACTIVE);
-
- /*
- * When checking the active state, we need to be sure we are
- * dealing with comparible boolean values. Take the logical not
- * of each.
- */
- if (!all_lru_mode && !PageActive(page) != !(mode & ISOLATE_ACTIVE))
- return ret;
-
- if (!all_lru_mode && !!page_is_file_cache(page) != file)
- return ret;
-
- /*
- * When this function is being called for lumpy reclaim, we
- * initially look into all LRU pages, active, inactive and
- * unevictable; only give shrink_page_list evictable pages.
- */
- if (PageUnevictable(page))
+ /* Compaction should not handle unevictable pages but CMA can do so */
+ if (PageUnevictable(page) && !(mode & ISOLATE_UNEVICTABLE))
return ret;
ret = -EBUSY;
@@ -1135,52 +1060,38 @@
* Appropriate locks must be held before calling this function.
*
* @nr_to_scan: The number of pages to look through on the list.
- * @mz: The mem_cgroup_zone to pull pages from.
+ * @lruvec: The LRU vector to pull pages from.
* @dst: The temp list to put pages on to.
* @nr_scanned: The number of pages that were scanned.
* @sc: The scan_control struct for this reclaim session
* @mode: One of the LRU isolation modes
- * @active: True [1] if isolating active pages
- * @file: True [1] if isolating file [!anon] pages
+ * @lru: LRU list id for isolating
*
* returns how many pages were moved onto *@dst.
*/
static unsigned long isolate_lru_pages(unsigned long nr_to_scan,
- struct mem_cgroup_zone *mz, struct list_head *dst,
+ struct lruvec *lruvec, struct list_head *dst,
unsigned long *nr_scanned, struct scan_control *sc,
- isolate_mode_t mode, int active, int file)
+ isolate_mode_t mode, enum lru_list lru)
{
- struct lruvec *lruvec;
struct list_head *src;
unsigned long nr_taken = 0;
- unsigned long nr_lumpy_taken = 0;
- unsigned long nr_lumpy_dirty = 0;
- unsigned long nr_lumpy_failed = 0;
unsigned long scan;
- int lru = LRU_BASE;
+ int file = is_file_lru(lru);
- lruvec = mem_cgroup_zone_lruvec(mz->zone, mz->mem_cgroup);
- if (active)
- lru += LRU_ACTIVE;
- if (file)
- lru += LRU_FILE;
src = &lruvec->lists[lru];
for (scan = 0; scan < nr_to_scan && !list_empty(src); scan++) {
struct page *page;
- unsigned long pfn;
- unsigned long end_pfn;
- unsigned long page_pfn;
- int zone_id;
page = lru_to_page(src);
prefetchw_prev_lru_page(page, src, flags);
VM_BUG_ON(!PageLRU(page));
- switch (__isolate_lru_page(page, mode, file)) {
+ switch (__isolate_lru_page(page, mode)) {
case 0:
- mem_cgroup_lru_del(page);
+ mem_cgroup_lru_del_list(page, lru);
list_move(&page->lru, dst);
nr_taken += hpage_nr_pages(page);
break;
@@ -1193,84 +1104,6 @@
default:
BUG();
}
-
- if (!sc->order || !(sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM))
- continue;
-
- /*
- * Attempt to take all pages in the order aligned region
- * surrounding the tag page. Only take those pages of
- * the same active state as that tag page. We may safely
- * round the target page pfn down to the requested order
- * as the mem_map is guaranteed valid out to MAX_ORDER,
- * where that page is in a different zone we will detect
- * it from its zone id and abort this block scan.
- */
- zone_id = page_zone_id(page);
- page_pfn = page_to_pfn(page);
- pfn = page_pfn & ~((1 << sc->order) - 1);
- end_pfn = pfn + (1 << sc->order);
- for (; pfn < end_pfn; pfn++) {
- struct page *cursor_page;
-
- /* The target page is in the block, ignore it. */
- if (unlikely(pfn == page_pfn))
- continue;
-
- /* Avoid holes within the zone. */
- if (unlikely(!pfn_valid_within(pfn)))
- break;
-
- cursor_page = pfn_to_page(pfn);
-
- /* Check that we have not crossed a zone boundary. */
- if (unlikely(page_zone_id(cursor_page) != zone_id))
- break;
-
- /*
- * If we don't have enough swap space, reclaiming of
- * anon page which don't already have a swap slot is
- * pointless.
- */
- if (nr_swap_pages <= 0 && PageSwapBacked(cursor_page) &&
- !PageSwapCache(cursor_page))
- break;
-
- if (__isolate_lru_page(cursor_page, mode, file) == 0) {
- unsigned int isolated_pages;
-
- mem_cgroup_lru_del(cursor_page);
- list_move(&cursor_page->lru, dst);
- isolated_pages = hpage_nr_pages(cursor_page);
- nr_taken += isolated_pages;
- nr_lumpy_taken += isolated_pages;
- if (PageDirty(cursor_page))
- nr_lumpy_dirty += isolated_pages;
- scan++;
- pfn += isolated_pages - 1;
- } else {
- /*
- * Check if the page is freed already.
- *
- * We can't use page_count() as that
- * requires compound_head and we don't
- * have a pin on the page here. If a
- * page is tail, we may or may not
- * have isolated the head, so assume
- * it's not free, it'd be tricky to
- * track the head status without a
- * page pin.
- */
- if (!PageTail(cursor_page) &&
- !atomic_read(&cursor_page->_count))
- continue;
- break;
- }
- }
-
- /* If we break out of the loop above, lumpy reclaim failed */
- if (pfn < end_pfn)
- nr_lumpy_failed++;
}
*nr_scanned = scan;
@@ -1278,7 +1111,6 @@
trace_mm_vmscan_lru_isolate(sc->order,
nr_to_scan, scan,
nr_taken,
- nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed,
mode, file);
return nr_taken;
}
@@ -1407,112 +1239,25 @@
list_splice(&pages_to_free, page_list);
}
-static noinline_for_stack void
-update_isolated_counts(struct mem_cgroup_zone *mz,
- struct list_head *page_list,
- unsigned long *nr_anon,
- unsigned long *nr_file)
-{
- struct zone *zone = mz->zone;
- unsigned int count[NR_LRU_LISTS] = { 0, };
- unsigned long nr_active = 0;
- struct page *page;
- int lru;
-
- /*
- * Count pages and clear active flags
- */
- list_for_each_entry(page, page_list, lru) {
- int numpages = hpage_nr_pages(page);
- lru = page_lru_base_type(page);
- if (PageActive(page)) {
- lru += LRU_ACTIVE;
- ClearPageActive(page);
- nr_active += numpages;
- }
- count[lru] += numpages;
- }
-
- preempt_disable();
- __count_vm_events(PGDEACTIVATE, nr_active);
-
- __mod_zone_page_state(zone, NR_ACTIVE_FILE,
- -count[LRU_ACTIVE_FILE]);
- __mod_zone_page_state(zone, NR_INACTIVE_FILE,
- -count[LRU_INACTIVE_FILE]);
- __mod_zone_page_state(zone, NR_ACTIVE_ANON,
- -count[LRU_ACTIVE_ANON]);
- __mod_zone_page_state(zone, NR_INACTIVE_ANON,
- -count[LRU_INACTIVE_ANON]);
-
- *nr_anon = count[LRU_ACTIVE_ANON] + count[LRU_INACTIVE_ANON];
- *nr_file = count[LRU_ACTIVE_FILE] + count[LRU_INACTIVE_FILE];
-
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, *nr_anon);
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, *nr_file);
- preempt_enable();
-}
-
-/*
- * Returns true if a direct reclaim should wait on pages under writeback.
- *
- * If we are direct reclaiming for contiguous pages and we do not reclaim
- * everything in the list, try again and wait for writeback IO to complete.
- * This will stall high-order allocations noticeably. Only do that when really
- * need to free the pages under high memory pressure.
- */
-static inline bool should_reclaim_stall(unsigned long nr_taken,
- unsigned long nr_freed,
- int priority,
- struct scan_control *sc)
-{
- int lumpy_stall_priority;
-
- /* kswapd should not stall on sync IO */
- if (current_is_kswapd())
- return false;
-
- /* Only stall on lumpy reclaim */
- if (sc->reclaim_mode & RECLAIM_MODE_SINGLE)
- return false;
-
- /* If we have reclaimed everything on the isolated list, no stall */
- if (nr_freed == nr_taken)
- return false;
-
- /*
- * For high-order allocations, there are two stall thresholds.
- * High-cost allocations stall immediately where as lower
- * order allocations such as stacks require the scanning
- * priority to be much higher before stalling.
- */
- if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
- lumpy_stall_priority = DEF_PRIORITY;
- else
- lumpy_stall_priority = DEF_PRIORITY / 3;
-
- return priority <= lumpy_stall_priority;
-}
-
/*
* shrink_inactive_list() is a helper for shrink_zone(). It returns the number
* of reclaimed pages
*/
static noinline_for_stack unsigned long
shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz,
- struct scan_control *sc, int priority, int file)
+ struct scan_control *sc, enum lru_list lru)
{
LIST_HEAD(page_list);
unsigned long nr_scanned;
unsigned long nr_reclaimed = 0;
unsigned long nr_taken;
- unsigned long nr_anon;
- unsigned long nr_file;
unsigned long nr_dirty = 0;
unsigned long nr_writeback = 0;
- isolate_mode_t isolate_mode = ISOLATE_INACTIVE;
+ isolate_mode_t isolate_mode = 0;
+ int file = is_file_lru(lru);
struct zone *zone = mz->zone;
struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(mz);
+ struct lruvec *lruvec = mem_cgroup_zone_lruvec(zone, mz->mem_cgroup);
while (unlikely(too_many_isolated(zone, file, sc))) {
congestion_wait(BLK_RW_ASYNC, HZ/10);
@@ -1522,10 +1267,6 @@
return SWAP_CLUSTER_MAX;
}
- set_reclaim_mode(priority, sc, false);
- if (sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM)
- isolate_mode |= ISOLATE_ACTIVE;
-
lru_add_drain();
if (!sc->may_unmap)
@@ -1535,8 +1276,12 @@
spin_lock_irq(&zone->lru_lock);
- nr_taken = isolate_lru_pages(nr_to_scan, mz, &page_list, &nr_scanned,
- sc, isolate_mode, 0, file);
+ nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &page_list,
+ &nr_scanned, sc, isolate_mode, lru);
+
+ __mod_zone_page_state(zone, NR_LRU_BASE + lru, -nr_taken);
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, nr_taken);
+
if (global_reclaim(sc)) {
zone->pages_scanned += nr_scanned;
if (current_is_kswapd())
@@ -1551,22 +1296,12 @@
if (nr_taken == 0)
return 0;
- update_isolated_counts(mz, &page_list, &nr_anon, &nr_file);
-
- nr_reclaimed = shrink_page_list(&page_list, mz, sc, priority,
- &nr_dirty, &nr_writeback);
-
- /* Check if we should syncronously wait for writeback */
- if (should_reclaim_stall(nr_taken, nr_reclaimed, priority, sc)) {
- set_reclaim_mode(priority, sc, true);
- nr_reclaimed += shrink_page_list(&page_list, mz, sc,
- priority, &nr_dirty, &nr_writeback);
- }
+ nr_reclaimed = shrink_page_list(&page_list, zone, sc, TTU_UNMAP,
+ &nr_dirty, &nr_writeback, false);
spin_lock_irq(&zone->lru_lock);
- reclaim_stat->recent_scanned[0] += nr_anon;
- reclaim_stat->recent_scanned[1] += nr_file;
+ reclaim_stat->recent_scanned[file] += nr_taken;
if (global_reclaim(sc)) {
if (current_is_kswapd())
@@ -1579,8 +1314,7 @@
putback_inactive_pages(mz, &page_list);
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, -nr_anon);
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, -nr_file);
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, -nr_taken);
spin_unlock_irq(&zone->lru_lock);
@@ -1609,14 +1343,15 @@
* DEF_PRIORITY-6 For SWAP_CLUSTER_MAX isolated pages, throttle if any
* isolated page is PageWriteback
*/
- if (nr_writeback && nr_writeback >= (nr_taken >> (DEF_PRIORITY-priority)))
+ if (nr_writeback && nr_writeback >=
+ (nr_taken >> (DEF_PRIORITY - sc->priority)))
wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10);
trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id,
zone_idx(zone),
nr_scanned, nr_reclaimed,
- priority,
- trace_shrink_flags(file, sc->reclaim_mode));
+ sc->priority,
+ trace_shrink_flags(file));
return nr_reclaimed;
}
@@ -1679,7 +1414,7 @@
static void shrink_active_list(unsigned long nr_to_scan,
struct mem_cgroup_zone *mz,
struct scan_control *sc,
- int priority, int file)
+ enum lru_list lru)
{
unsigned long nr_taken;
unsigned long nr_scanned;
@@ -1690,13 +1425,13 @@
struct page *page;
struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(mz);
unsigned long nr_rotated = 0;
- isolate_mode_t isolate_mode = ISOLATE_ACTIVE;
+ isolate_mode_t isolate_mode = 0;
+ int file = is_file_lru(lru);
struct zone *zone = mz->zone;
+ struct lruvec *lruvec = mem_cgroup_zone_lruvec(zone, mz->mem_cgroup);
lru_add_drain();
- reset_reclaim_mode(sc);
-
if (!sc->may_unmap)
isolate_mode |= ISOLATE_UNMAPPED;
if (!sc->may_writepage)
@@ -1704,18 +1439,15 @@
spin_lock_irq(&zone->lru_lock);
- nr_taken = isolate_lru_pages(nr_to_scan, mz, &l_hold, &nr_scanned, sc,
- isolate_mode, 1, file);
+ nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &l_hold,
+ &nr_scanned, sc, isolate_mode, lru);
if (global_reclaim(sc))
zone->pages_scanned += nr_scanned;
reclaim_stat->recent_scanned[file] += nr_taken;
__count_zone_vm_events(PGREFILL, zone, nr_scanned);
- if (file)
- __mod_zone_page_state(zone, NR_ACTIVE_FILE, -nr_taken);
- else
- __mod_zone_page_state(zone, NR_ACTIVE_ANON, -nr_taken);
+ __mod_zone_page_state(zone, NR_LRU_BASE + lru, -nr_taken);
__mod_zone_page_state(zone, NR_ISOLATED_ANON + file, nr_taken);
spin_unlock_irq(&zone->lru_lock);
@@ -1737,7 +1469,8 @@
}
}
- if (page_referenced(page, 0, mz->mem_cgroup, &vm_flags)) {
+ if (page_referenced(page, 0, sc->target_mem_cgroup,
+ &vm_flags)) {
nr_rotated += hpage_nr_pages(page);
/*
* Identify referenced, file-backed active pages and
@@ -1770,10 +1503,8 @@
*/
reclaim_stat->recent_rotated[file] += nr_rotated;
- move_active_pages_to_lru(zone, &l_active, &l_hold,
- LRU_ACTIVE + file * LRU_FILE);
- move_active_pages_to_lru(zone, &l_inactive, &l_hold,
- LRU_BASE + file * LRU_FILE);
+ move_active_pages_to_lru(zone, &l_active, &l_hold, lru);
+ move_active_pages_to_lru(zone, &l_inactive, &l_hold, lru - LRU_ACTIVE);
__mod_zone_page_state(zone, NR_ISOLATED_ANON + file, -nr_taken);
spin_unlock_irq(&zone->lru_lock);
@@ -1811,7 +1542,7 @@
if (!total_swap_pages)
return 0;
- if (!scanning_global_lru(mz))
+ if (!mem_cgroup_disabled())
return mem_cgroup_inactive_anon_is_low(mz->mem_cgroup,
mz->zone);
@@ -1850,7 +1581,7 @@
*/
static int inactive_file_is_low(struct mem_cgroup_zone *mz)
{
- if (!scanning_global_lru(mz))
+ if (!mem_cgroup_disabled())
return mem_cgroup_inactive_file_is_low(mz->mem_cgroup,
mz->zone);
@@ -1867,25 +1598,24 @@
static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,
struct mem_cgroup_zone *mz,
- struct scan_control *sc, int priority)
+ struct scan_control *sc)
{
int file = is_file_lru(lru);
if (is_active_lru(lru)) {
if (inactive_list_is_low(mz, file))
- shrink_active_list(nr_to_scan, mz, sc, priority, file);
+ shrink_active_list(nr_to_scan, mz, sc, lru);
return 0;
}
- return shrink_inactive_list(nr_to_scan, mz, sc, priority, file);
+ return shrink_inactive_list(nr_to_scan, mz, sc, lru);
}
-static int vmscan_swappiness(struct mem_cgroup_zone *mz,
- struct scan_control *sc)
+static int vmscan_swappiness(struct scan_control *sc)
{
if (global_reclaim(sc))
return vm_swappiness;
- return mem_cgroup_swappiness(mz->mem_cgroup);
+ return mem_cgroup_swappiness(sc->target_mem_cgroup);
}
/*
@@ -1897,7 +1627,7 @@
* nr[0] = anon pages to scan; nr[1] = file pages to scan
*/
static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
- unsigned long *nr, int priority)
+ unsigned long *nr)
{
unsigned long anon, file, free;
unsigned long anon_prio, file_prio;
@@ -1953,8 +1683,8 @@
* With swappiness at 100, anonymous and file have the same priority.
* This scanning priority is essentially the inverse of IO cost.
*/
- anon_prio = vmscan_swappiness(mz, sc);
- file_prio = 200 - vmscan_swappiness(mz, sc);
+ anon_prio = vmscan_swappiness(sc);
+ file_prio = 200 - vmscan_swappiness(sc);
/*
* OK, so we have swap space and a fair amount of page cache
@@ -1983,10 +1713,10 @@
* proportional to the fraction of recently scanned pages on
* each list that were recently referenced and in active use.
*/
- ap = (anon_prio + 1) * (reclaim_stat->recent_scanned[0] + 1);
+ ap = anon_prio * (reclaim_stat->recent_scanned[0] + 1);
ap /= reclaim_stat->recent_rotated[0] + 1;
- fp = (file_prio + 1) * (reclaim_stat->recent_scanned[1] + 1);
+ fp = file_prio * (reclaim_stat->recent_scanned[1] + 1);
fp /= reclaim_stat->recent_rotated[1] + 1;
spin_unlock_irq(&mz->zone->lru_lock);
@@ -1999,8 +1729,8 @@
unsigned long scan;
scan = zone_nr_lru_pages(mz, lru);
- if (priority || noswap) {
- scan >>= priority;
+ if (sc->priority || noswap || !vmscan_swappiness(sc)) {
+ scan >>= sc->priority;
if (!scan && force_scan)
scan = SWAP_CLUSTER_MAX;
scan = div64_u64(scan * fraction[file], denominator);
@@ -2009,12 +1739,23 @@
}
}
+/* Use reclaim/compaction for costly allocs or under memory pressure */
+static bool in_reclaim_compaction(struct scan_control *sc)
+{
+ if (COMPACTION_BUILD && sc->order &&
+ (sc->order > PAGE_ALLOC_COSTLY_ORDER ||
+ sc->priority < DEF_PRIORITY - 2))
+ return true;
+
+ return false;
+}
+
/*
- * Reclaim/compaction depends on a number of pages being freed. To avoid
- * disruption to the system, a small number of order-0 pages continue to be
- * rotated and reclaimed in the normal fashion. However, by the time we get
- * back to the allocator and call try_to_compact_zone(), we ensure that
- * there are enough free pages for it to be likely successful
+ * Reclaim/compaction is used for high-order allocation requests. It reclaims
+ * order-0 pages before compacting the zone. should_continue_reclaim() returns
+ * true if more pages should be reclaimed such that when the page allocator
+ * calls try_to_compact_zone() that it will have enough free pages to succeed.
+ * It will give up earlier than that if there is difficulty reclaiming pages.
*/
static inline bool should_continue_reclaim(struct mem_cgroup_zone *mz,
unsigned long nr_reclaimed,
@@ -2025,7 +1766,7 @@
unsigned long inactive_lru_pages;
/* If not in reclaim/compaction mode, stop */
- if (!(sc->reclaim_mode & RECLAIM_MODE_COMPACTION))
+ if (!in_reclaim_compaction(sc))
return false;
/* Consider stopping depending on scan and reclaim activity */
@@ -2076,7 +1817,7 @@
/*
* This is a basic per-zone page freer. Used by both kswapd and direct reclaim.
*/
-static void shrink_mem_cgroup_zone(int priority, struct mem_cgroup_zone *mz,
+static void shrink_mem_cgroup_zone(struct mem_cgroup_zone *mz,
struct scan_control *sc)
{
unsigned long nr[NR_LRU_LISTS];
@@ -2089,7 +1830,7 @@
restart:
nr_reclaimed = 0;
nr_scanned = sc->nr_scanned;
- get_scan_count(mz, sc, nr, priority);
+ get_scan_count(mz, sc, nr);
blk_start_plug(&plug);
while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
@@ -2101,7 +1842,7 @@
nr[lru] -= nr_to_scan;
nr_reclaimed += shrink_list(lru, nr_to_scan,
- mz, sc, priority);
+ mz, sc);
}
}
/*
@@ -2112,7 +1853,8 @@
* with multiple processes reclaiming pages, the total
* freeing target can get unreasonably large.
*/
- if (nr_reclaimed >= nr_to_reclaim && priority < DEF_PRIORITY)
+ if (nr_reclaimed >= nr_to_reclaim &&
+ sc->priority < DEF_PRIORITY)
break;
}
blk_finish_plug(&plug);
@@ -2123,23 +1865,23 @@
* rebalance the anon lru active/inactive ratio.
*/
if (inactive_anon_is_low(mz))
- shrink_active_list(SWAP_CLUSTER_MAX, mz, sc, priority, 0);
+ shrink_active_list(SWAP_CLUSTER_MAX, mz,
+ sc, LRU_ACTIVE_ANON);
/* reclaim/compaction might need reclaim to continue */
if (should_continue_reclaim(mz, nr_reclaimed,
- sc->nr_scanned - nr_scanned, sc))
+ sc->nr_scanned - nr_scanned, sc))
goto restart;
throttle_vm_writeout(sc->gfp_mask);
}
-static void shrink_zone(int priority, struct zone *zone,
- struct scan_control *sc)
+static void shrink_zone(struct zone *zone, struct scan_control *sc)
{
struct mem_cgroup *root = sc->target_mem_cgroup;
struct mem_cgroup_reclaim_cookie reclaim = {
.zone = zone,
- .priority = priority,
+ .priority = sc->priority,
};
struct mem_cgroup *memcg;
@@ -2150,7 +1892,7 @@
.zone = zone,
};
- shrink_mem_cgroup_zone(priority, &mz, sc);
+ shrink_mem_cgroup_zone(&mz, sc);
/*
* Limit reclaim has historically picked one memcg and
* scanned it with decreasing priority levels until
@@ -2226,8 +1968,7 @@
* the caller that it should consider retrying the allocation instead of
* further reclaim.
*/
-static bool shrink_zones(int priority, struct zonelist *zonelist,
- struct scan_control *sc)
+static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
{
struct zoneref *z;
struct zone *zone;
@@ -2254,7 +1995,8 @@
if (global_reclaim(sc)) {
if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
continue;
- if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+ if (zone->all_unreclaimable &&
+ sc->priority != DEF_PRIORITY)
continue; /* Let kswapd poll it */
if (COMPACTION_BUILD) {
/*
@@ -2286,7 +2028,7 @@
/* need some check for avoid more shrink_zone() */
}
- shrink_zone(priority, zone, sc);
+ shrink_zone(zone, sc);
}
return aborted_reclaim;
@@ -2337,7 +2079,6 @@
struct scan_control *sc,
struct shrink_control *shrink)
{
- int priority;
unsigned long total_scanned = 0;
struct reclaim_state *reclaim_state = current->reclaim_state;
struct zoneref *z;
@@ -2350,11 +2091,9 @@
if (global_reclaim(sc))
count_vm_event(ALLOCSTALL);
- for (priority = DEF_PRIORITY; priority >= 0; priority--) {
+ do {
sc->nr_scanned = 0;
- if (!priority)
- disable_swap_token(sc->target_mem_cgroup);
- aborted_reclaim = shrink_zones(priority, zonelist, sc);
+ aborted_reclaim = shrink_zones(zonelist, sc);
/*
* Don't shrink slabs when reclaiming memory from
@@ -2396,7 +2135,7 @@
/* Take a nap, wait for some writeback to complete */
if (!sc->hibernation_mode && sc->nr_scanned &&
- priority < DEF_PRIORITY - 2) {
+ sc->priority < DEF_PRIORITY - 2) {
struct zone *preferred_zone;
first_zones_zonelist(zonelist, gfp_zone(sc->gfp_mask),
@@ -2404,7 +2143,7 @@
&preferred_zone);
wait_iff_congested(preferred_zone, BLK_RW_ASYNC, HZ/10);
}
- }
+ } while (--sc->priority >= 0);
out:
delayacct_freepages_end();
@@ -2442,6 +2181,7 @@
.may_unmap = 1,
.may_swap = 1,
.order = order,
+ .priority = DEF_PRIORITY,
.target_mem_cgroup = NULL,
.nodemask = nodemask,
};
@@ -2474,6 +2214,7 @@
.may_unmap = 1,
.may_swap = !noswap,
.order = 0,
+ .priority = 0,
.target_mem_cgroup = memcg,
};
struct mem_cgroup_zone mz = {
@@ -2484,7 +2225,7 @@
sc.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
(GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK);
- trace_mm_vmscan_memcg_softlimit_reclaim_begin(0,
+ trace_mm_vmscan_memcg_softlimit_reclaim_begin(sc.order,
sc.may_writepage,
sc.gfp_mask);
@@ -2495,7 +2236,7 @@
* will pick up pages from other mem cgroup's as well. We hack
* the priority and make it zero.
*/
- shrink_mem_cgroup_zone(0, &mz, &sc);
+ shrink_mem_cgroup_zone(&mz, &sc);
trace_mm_vmscan_memcg_softlimit_reclaim_end(sc.nr_reclaimed);
@@ -2516,6 +2257,7 @@
.may_swap = !noswap,
.nr_to_reclaim = SWAP_CLUSTER_MAX,
.order = 0,
+ .priority = DEF_PRIORITY,
.target_mem_cgroup = memcg,
.nodemask = NULL, /* we don't care the placement */
.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
@@ -2546,8 +2288,7 @@
}
#endif
-static void age_active_anon(struct zone *zone, struct scan_control *sc,
- int priority)
+static void age_active_anon(struct zone *zone, struct scan_control *sc)
{
struct mem_cgroup *memcg;
@@ -2563,7 +2304,7 @@
if (inactive_anon_is_low(&mz))
shrink_active_list(SWAP_CLUSTER_MAX, &mz,
- sc, priority, 0);
+ sc, LRU_ACTIVE_ANON);
memcg = mem_cgroup_iter(NULL, memcg, NULL);
} while (memcg);
@@ -2672,7 +2413,6 @@
{
int all_zones_ok;
unsigned long balanced;
- int priority;
int i;
int end_zone = 0; /* Inclusive. 0 = ZONE_DMA */
unsigned long total_scanned;
@@ -2696,18 +2436,15 @@
};
loop_again:
total_scanned = 0;
+ sc.priority = DEF_PRIORITY;
sc.nr_reclaimed = 0;
sc.may_writepage = !laptop_mode;
count_vm_event(PAGEOUTRUN);
- for (priority = DEF_PRIORITY; priority >= 0; priority--) {
+ do {
unsigned long lru_pages = 0;
int has_under_min_watermark_zone = 0;
- /* The swap token gets in the way of swapout... */
- if (!priority)
- disable_swap_token(NULL);
-
all_zones_ok = 1;
balanced = 0;
@@ -2721,14 +2458,15 @@
if (!populated_zone(zone))
continue;
- if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+ if (zone->all_unreclaimable &&
+ sc.priority != DEF_PRIORITY)
continue;
/*
* Do some background aging of the anon list, to give
* pages a chance to be referenced before reclaiming.
*/
- age_active_anon(zone, &sc, priority);
+ age_active_anon(zone, &sc);
/*
* If the number of buffer_heads in the machine
@@ -2776,7 +2514,8 @@
if (!populated_zone(zone))
continue;
- if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+ if (zone->all_unreclaimable &&
+ sc.priority != DEF_PRIORITY)
continue;
sc.nr_scanned = 0;
@@ -2820,7 +2559,7 @@
!zone_watermark_ok_safe(zone, testorder,
high_wmark_pages(zone) + balance_gap,
end_zone, 0)) {
- shrink_zone(priority, zone, &sc);
+ shrink_zone(zone, &sc);
reclaim_state->reclaimed_slab = 0;
nr_slab = shrink_slab(&shrink, sc.nr_scanned, lru_pages);
@@ -2877,7 +2616,7 @@
* OK, kswapd is getting into trouble. Take a nap, then take
* another pass across the zones.
*/
- if (total_scanned && (priority < DEF_PRIORITY - 2)) {
+ if (total_scanned && (sc.priority < DEF_PRIORITY - 2)) {
if (has_under_min_watermark_zone)
count_vm_event(KSWAPD_SKIP_CONGESTION_WAIT);
else
@@ -2892,7 +2631,7 @@
*/
if (sc.nr_reclaimed >= SWAP_CLUSTER_MAX)
break;
- }
+ } while (--sc.priority >= 0);
out:
/*
@@ -2942,7 +2681,8 @@
if (!populated_zone(zone))
continue;
- if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+ if (zone->all_unreclaimable &&
+ sc.priority != DEF_PRIORITY)
continue;
/* Would compaction fail due to lack of free memory? */
@@ -3013,7 +2753,18 @@
* them before going back to sleep.
*/
set_pgdat_percpu_threshold(pgdat, calculate_normal_threshold);
- schedule();
+
+ /*
+ * Compaction records what page blocks it recently failed to
+ * isolate pages from and skips them in the future scanning.
+ * When kswapd is going to sleep, it is reasonable to assume
+ * that pages and compaction may succeed so reset the cache.
+ */
+ reset_isolation_suitable(pgdat);
+
+ if (!kthread_should_stop())
+ schedule();
+
set_pgdat_percpu_threshold(pgdat, calculate_pressure_threshold);
} else {
if (remaining)
@@ -3209,6 +2960,7 @@
.nr_to_reclaim = nr_to_reclaim,
.hibernation_mode = 1,
.order = 0,
+ .priority = DEF_PRIORITY,
};
struct shrink_control shrink = {
.gfp_mask = sc.gfp_mask,
@@ -3386,7 +3138,6 @@
const unsigned long nr_pages = 1 << order;
struct task_struct *p = current;
struct reclaim_state reclaim_state;
- int priority;
struct scan_control sc = {
.may_writepage = !!(zone_reclaim_mode & RECLAIM_WRITE),
.may_unmap = !!(zone_reclaim_mode & RECLAIM_SWAP),
@@ -3395,6 +3146,7 @@
SWAP_CLUSTER_MAX),
.gfp_mask = gfp_mask,
.order = order,
+ .priority = ZONE_RECLAIM_PRIORITY,
};
struct shrink_control shrink = {
.gfp_mask = sc.gfp_mask,
@@ -3417,11 +3169,9 @@
* Free memory by calling shrink zone with increasing
* priorities until we have enough memory freed.
*/
- priority = ZONE_RECLAIM_PRIORITY;
do {
- shrink_zone(priority, zone, &sc);
- priority--;
- } while (priority >= 0 && sc.nr_reclaimed < nr_pages);
+ shrink_zone(zone, &sc);
+ } while (sc.nr_reclaimed < nr_pages && --sc.priority >= 0);
}
nr_slab_pages0 = zone_page_state(zone, NR_SLAB_RECLAIMABLE);
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 8e18d6b..959a558 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -761,10 +761,14 @@
"pgrotated",
+#ifdef CONFIG_MIGRATION
+ "pgmigrate_success",
+ "pgmigrate_fail",
+#endif
#ifdef CONFIG_COMPACTION
- "compact_blocks_moved",
- "compact_pages_moved",
- "compact_pagemigrate_failed",
+ "compact_migrate_scanned",
+ "compact_free_scanned",
+ "compact_isolated",
"compact_stall",
"compact_fail",
"compact_success",
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 7ffcc1b..9ffd0f1 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1,6 +1,7 @@
/*
BlueZ - Bluetooth protocol stack for Linux
- Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved.
+ Copyright (c) 2000-2001, The Linux Foundation. All rights reserved.
+ Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
@@ -659,6 +660,9 @@
hci_conn_put_device(conn);
+ if (conn->hidp_session_valid)
+ hci_conn_put_device(conn);
+
hci_dev_put(hdev);
return 0;
@@ -1267,8 +1271,10 @@
void hci_conn_put_device(struct hci_conn *conn)
{
- if (atomic_dec_and_test(&conn->devref))
+ if (atomic_dec_and_test(&conn->devref)) {
+ conn->hidp_session_valid = false;
hci_conn_del_sysfs(conn);
+ }
}
EXPORT_SYMBOL(hci_conn_put_device);
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 2353e91..e214cb2 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -1,7 +1,7 @@
/*
HIDP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
- 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 as
@@ -108,7 +108,7 @@
hdev = hci_get_route(dst, BDADDR_ANY);
if (hdev) {
session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
- if (session->conn)
+ if (session->conn && session->conn->hidp_session_valid)
dev = &session->conn->dev;
hci_dev_put(hdev);
@@ -661,8 +661,10 @@
hci_dev_lock_bh(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
- if (conn)
+ if (conn) {
+ conn->hidp_session_valid = true;
hci_conn_hold_device(conn);
+ }
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
diff --git a/net/ipv4/netfilter/ipt_NATTYPE.c b/net/ipv4/netfilter/ipt_NATTYPE.c
index f181be4..be405f7 100644
--- a/net/ipv4/netfilter/ipt_NATTYPE.c
+++ b/net/ipv4/netfilter/ipt_NATTYPE.c
@@ -59,7 +59,7 @@
struct list_head list;
struct timer_list timeout;
unsigned long timeout_value;
- unsigned char is_valid;
+ unsigned int nattype_cookie;
unsigned short proto; /* Protocol: TCP or UDP */
struct nf_nat_ipv4_range range; /* LAN side source information */
unsigned short nat_port; /* Routed NAT port */
@@ -67,6 +67,8 @@
unsigned short dest_port;/* Original egress packets destination port */
};
+#define NATTYPE_COOKIE 0x11abcdef
+
/*
* TODO: It might be better to use a hash table for performance in
* heavy traffic.
@@ -109,7 +111,7 @@
if (!nte)
return false;
spin_lock_bh(&nattype_lock);
- if (!nte->is_valid) {
+ if (nte->nattype_cookie != NATTYPE_COOKIE) {
spin_unlock_bh(&nattype_lock);
return false;
}
@@ -483,7 +485,7 @@
add_timer(&nte->timeout);
list_add(&nte->list, &nattype_list);
ct->nattype_entry = (unsigned long)nte;
- nte->is_valid = 1;
+ nte->nattype_cookie = NATTYPE_COOKIE;
spin_unlock_bh(&nattype_lock);
nattype_nte_debug_print(nte, "ADD");
return XT_CONTINUE;
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index d7eafd5..eb535cc 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -716,6 +716,9 @@
/* Don't set timer yet: wait for confirmation */
setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct);
write_pnet(&ct->ct_net, net);
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+ ct->nattype_entry = 0;
+#endif
#ifdef CONFIG_NF_CONNTRACK_ZONES
if (zone) {
struct nf_conntrack_zone *nf_ct_zone;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 2356791..d85cf09 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -648,6 +648,9 @@
struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr;
int err;
+ if (addr_len < sizeof(struct sockaddr_nl))
+ return -EINVAL;
+
if (nladdr->nl_family != AF_NETLINK)
return -EINVAL;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 68fd9d7..1dee449 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3031,10 +3031,10 @@
params.listen_interval =
nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
- if (info->attrs[NL80211_ATTR_STA_AID])
- params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
- else
+ if (info->attrs[NL80211_ATTR_PEER_AID])
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+ else
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
if (!params.aid || params.aid > IEEE80211_MAX_AID)
return -EINVAL;
@@ -3104,7 +3104,8 @@
params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
}
/* TDLS peers cannot be added */
- if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+ if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+ info->attrs[NL80211_ATTR_PEER_AID])
return -EINVAL;
/* but don't bother the driver with it */
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
@@ -3116,7 +3117,8 @@
break;
case NL80211_IFTYPE_MESH_POINT:
/* TDLS peers cannot be added */
- if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+ if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+ info->attrs[NL80211_ATTR_PEER_AID])
return -EINVAL;
break;
case NL80211_IFTYPE_STATION:
diff --git a/sound/soc/codecs/msm8x10-wcd.c b/sound/soc/codecs/msm8x10-wcd.c
index f3c6ef8..de0b4da 100644
--- a/sound/soc/codecs/msm8x10-wcd.c
+++ b/sound/soc/codecs/msm8x10-wcd.c
@@ -35,9 +35,11 @@
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include <mach/qdsp6v2/apr.h>
+#include <mach/subsystem_notif.h>
#include "msm8x10-wcd.h"
#include "wcd9xxx-resmgr.h"
#include "msm8x10_wcd_registers.h"
+#include "../msm/qdsp6v2/q6core.h"
#define MSM8X10_WCD_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
@@ -58,7 +60,7 @@
#define MAX_MSM8X10_WCD_DEVICE 4
#define CODEC_DT_MAX_PROP_SIZE 40
-#define MSM8X10_WCD_I2C_GSBI_SLAVE_ID "5-000d"
+#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64
enum {
MSM8X10_WCD_I2C_TOP_LEVEL = 0,
@@ -117,6 +119,12 @@
MSM8X10_WCD_BANDGAP_MBHC_MODE,
};
+enum {
+ ON_DEMAND_MICBIAS = 0,
+ ON_DEMAND_CP,
+ ON_DEMAND_SUPPLIES_MAX,
+};
+
struct hpf_work {
struct msm8x10_wcd_priv *msm8x10_wcd;
u32 decimator;
@@ -126,6 +134,16 @@
static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+struct on_demand_supply {
+ struct regulator *supply;
+ atomic_t ref;
+};
+
+static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = {
+ "cdc-vdd-mic-bias",
+ "cdc-vdda-cp",
+};
+
struct msm8x10_wcd_priv {
struct snd_soc_codec *codec;
u32 adc_count;
@@ -136,6 +154,7 @@
bool clock_active;
bool config_mode_active;
bool mbhc_polling_active;
+ struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
struct mutex codec_resource_lock;
/* resmgr module */
struct wcd9xxx_resmgr resmgr;
@@ -159,13 +178,9 @@
int mod_id;
};
-static char *msm8x10_wcd_supplies[] = {
- "cdc-vdda-cp", "cdc-vdda-h", "cdc-vdd-px", "cdc-vdd-1p2v",
- "cdc-vdd-mic-bias",
-};
-
static int msm8x10_wcd_dt_parse_vreg_info(struct device *dev,
- struct msm8x10_wcd_regulator *vreg, const char *vreg_name);
+ struct msm8x10_wcd_regulator *vreg,
+ const char *vreg_name, bool ondemand);
static int msm8x10_wcd_dt_parse_micbias_info(struct device *dev,
struct msm8x10_wcd_micbias_setting *micbias);
static struct msm8x10_wcd_pdata *msm8x10_wcd_populate_dt_pdata(
@@ -173,6 +188,11 @@
struct msm8x10_wcd_i2c msm8x10_wcd_modules[MAX_MSM8X10_WCD_DEVICE];
+static void *adsp_state_notifier;
+
+static struct snd_soc_codec *registered_codec;
+#define ADSP_STATE_READY_TIMEOUT_MS 2000
+
static int get_i2c_msm8x10_wcd_device_info(u16 reg,
struct msm8x10_wcd_i2c **msm8x10_wcd)
@@ -449,7 +469,8 @@
static int msm8x10_wcd_dt_parse_vreg_info(struct device *dev,
- struct msm8x10_wcd_regulator *vreg, const char *vreg_name)
+ struct msm8x10_wcd_regulator *vreg, const char *vreg_name,
+ bool ondemand)
{
int len, ret = 0;
const __be32 *prop;
@@ -471,6 +492,7 @@
prop_name, dev->of_node->full_name);
vreg->name = vreg_name;
+ vreg->ondemand = ondemand;
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
"qcom,%s-voltage", vreg_name);
@@ -479,7 +501,7 @@
if (!prop || (len != (2 * sizeof(__be32)))) {
dev_err(dev, "%s %s property\n",
prop ? "invalid format" : "no", prop_name);
- return -ENODEV;
+ return -EINVAL;
} else {
vreg->min_uV = be32_to_cpup(&prop[0]);
vreg->max_uV = be32_to_cpup(&prop[1]);
@@ -492,12 +514,12 @@
if (ret) {
dev_err(dev, "Looking up %s property in node %s failed",
prop_name, dev->of_node->full_name);
- return -ENODEV;
+ return -EFAULT;
}
vreg->optimum_uA = prop_val;
- dev_info(dev, "%s: vol=[%d %d]uV, curr=[%d]uA\n", vreg->name,
- vreg->min_uV, vreg->max_uV, vreg->optimum_uA);
+ dev_info(dev, "%s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n\n", vreg->name,
+ vreg->min_uV, vreg->max_uV, vreg->optimum_uA, vreg->ondemand);
return 0;
}
@@ -545,38 +567,72 @@
struct device *dev)
{
struct msm8x10_wcd_pdata *pdata;
- int ret = 0, i;
- char **codec_supplies;
- u32 num_of_supplies = 0;
+ int ret, static_cnt, ond_cnt, idx, i;
+ const char *name = NULL;
+ const char *static_prop_name = "qcom,cdc-static-supplies";
+ const char *ond_prop_name = "qcom,cdc-on-demand-supplies";
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "could not allocate memory for platform data\n");
return NULL;
}
- if ((!strcmp(dev_name(dev), MSM8X10_WCD_I2C_GSBI_SLAVE_ID))) {
- codec_supplies = msm8x10_wcd_supplies;
- num_of_supplies = ARRAY_SIZE(msm8x10_wcd_supplies);
- } else {
- dev_err(dev, "%s unsupported device %s\n",
- __func__, dev_name(dev));
+
+ static_cnt = of_property_count_strings(dev->of_node, static_prop_name);
+ if (IS_ERR_VALUE(static_cnt)) {
+ dev_err(dev, "%s: Failed to get static supplies %d\n", __func__,
+ static_cnt);
+ ret = -EINVAL;
goto err;
}
- if (num_of_supplies > ARRAY_SIZE(pdata->regulator)) {
+ /* On-demand supply list is an optional property */
+ ond_cnt = of_property_count_strings(dev->of_node, ond_prop_name);
+ if (IS_ERR_VALUE(ond_cnt))
+ ond_cnt = 0;
+
+ BUG_ON(static_cnt <= 0 || ond_cnt < 0);
+ if ((static_cnt + ond_cnt) > ARRAY_SIZE(pdata->regulator)) {
dev_err(dev, "%s: Num of supplies %u > max supported %u\n",
- __func__, num_of_supplies,
- ARRAY_SIZE(pdata->regulator));
-
+ __func__, static_cnt, ARRAY_SIZE(pdata->regulator));
+ ret = -EINVAL;
goto err;
}
- for (i = 0; i < num_of_supplies; i++) {
- ret = msm8x10_wcd_dt_parse_vreg_info(dev, &pdata->regulator[i],
- codec_supplies[i]);
+ for (idx = 0; idx < static_cnt; idx++) {
+ ret = of_property_read_string_index(dev->of_node,
+ static_prop_name, idx,
+ &name);
+ if (ret) {
+ dev_err(dev, "%s: of read string %s idx %d error %d\n",
+ __func__, static_prop_name, idx, ret);
+ goto err;
+ }
+
+ dev_dbg(dev, "%s: Found static cdc supply %s\n", __func__,
+ name);
+ ret = msm8x10_wcd_dt_parse_vreg_info(dev,
+ &pdata->regulator[idx],
+ name, false);
if (ret)
goto err;
}
+
+ for (i = 0; i < ond_cnt; i++, idx++) {
+ ret = of_property_read_string_index(dev->of_node, ond_prop_name,
+ i, &name);
+ if (ret)
+ goto err;
+
+ dev_dbg(dev, "%s: Found on-demand cdc supply %s\n", __func__,
+ name);
+ ret = msm8x10_wcd_dt_parse_vreg_info(dev,
+ &pdata->regulator[idx],
+ name, true);
+ if (ret)
+ goto err;
+ }
+
ret = msm8x10_wcd_dt_parse_micbias_info(dev, &pdata->micbias);
if (ret)
goto err;
@@ -588,12 +644,64 @@
return NULL;
}
+static int msm8x10_wcd_codec_enable_on_demand_supply(
+ struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = w->codec;
+ struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
+ struct on_demand_supply *supply;
+
+ if (w->shift >= ON_DEMAND_SUPPLIES_MAX) {
+ ret = -EINVAL;
+ goto out;
+ }
+ dev_dbg(codec->dev, "%s: supply: %s event: %d ref: %d\n",
+ __func__, on_demand_supply_name[w->shift], event,
+ atomic_read(&msm8x10_wcd->on_demand_list[w->shift].ref));
+
+ supply = &msm8x10_wcd->on_demand_list[w->shift];
+ WARN_ONCE(!supply->supply, "%s isn't defined\n",
+ on_demand_supply_name[w->shift]);
+ if (!supply->supply)
+ goto out;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (atomic_inc_return(&supply->ref) == 1)
+ ret = regulator_enable(supply->supply);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to enable %s\n",
+ __func__,
+ on_demand_supply_name[w->shift]);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (atomic_read(&supply->ref) == 0) {
+ dev_dbg(codec->dev, "%s: %s supply has been disabled.\n",
+ __func__, on_demand_supply_name[w->shift]);
+ goto out;
+ }
+ if (atomic_dec_return(&supply->ref) == 0)
+ ret = regulator_disable(supply->supply);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to disable %s\n",
+ __func__,
+ on_demand_supply_name[w->shift]);
+ break;
+ default:
+ break;
+ }
+out:
+ return ret;
+}
+
static int msm8x10_wcd_codec_enable_charge_pump(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- dev_dbg(codec->dev, "%s: event = %d\n", __func__, event);
+ dev_dbg(codec->dev, "%s: event = %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* Enable charge pump clock*/
@@ -625,6 +733,8 @@
snd_soc_update_bits(codec,
MSM8X10_WCD_A_CP_STATIC, 0x08, 0x00);
break;
+ default:
+ break;
}
return 0;
}
@@ -1382,6 +1492,7 @@
snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10);
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2);
+ snd_soc_update_bits(codec, w->reg, 0x1, 0x0);
break;
case SND_SOC_DAPM_POST_PMU:
usleep_range(20000, 20100);
@@ -1394,6 +1505,7 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
+ snd_soc_update_bits(codec, w->reg, 0x1, 0x1);
break;
}
return 0;
@@ -1695,9 +1807,6 @@
{"I2S TX1", NULL, "TX_I2S_CLK"},
{"I2S TX2", NULL, "TX_I2S_CLK"},
- {"DEC1 MUX", NULL, "TX CLK"},
- {"DEC2 MUX", NULL, "TX CLK"},
-
{"I2S TX1", NULL, "DEC1 MUX"},
{"I2S TX2", NULL, "DEC2 MUX"},
@@ -1729,6 +1838,7 @@
{"LINEOUT PA", NULL, "CP"},
{"LINEOUT PA", NULL, "LINEOUT DAC"},
+ {"CP", NULL, "CP_REGULATOR"},
{"CP", NULL, "RX_BIAS"},
{"SPK PA", NULL, "SPK DAC"},
{"SPK DAC", NULL, "RX3 CHAIN"},
@@ -1808,6 +1918,9 @@
{"MIC BIAS Internal1", NULL, "INT_LDO_H"},
{"MIC BIAS Internal2", NULL, "INT_LDO_H"},
{"MIC BIAS External", NULL, "INT_LDO_H"},
+ {"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"},
+ {"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"},
+ {"MIC BIAS External", NULL, "MICBIAS_REGULATOR"},
};
static int msm8x10_wcd_startup(struct snd_pcm_substream *substream,
@@ -2136,8 +2249,6 @@
SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_SUPPLY("TX CLK", MSM8X10_WCD_A_CDC_DIG_CLK_CTL,
- 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -2230,9 +2341,21 @@
SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0,
&rx2_mix2_inp1_mux),
+ SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM,
+ ON_DEMAND_MICBIAS, 0,
+ msm8x10_wcd_codec_enable_on_demand_supply,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("CP_REGULATOR", SND_SOC_NOPM,
+ ON_DEMAND_CP, 0,
+ msm8x10_wcd_codec_enable_on_demand_supply,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
SND_SOC_DAPM_SUPPLY("CP", MSM8X10_WCD_A_CP_EN, 0, 0,
- msm8x10_wcd_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ msm8x10_wcd_codec_enable_charge_pump, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
msm8x10_wcd_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
@@ -2336,10 +2459,11 @@
/* Reduce LINE DAC bias to 70% */
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_RX_LINE_BIAS_PA, 0x78),
- /* Disable TX7 internal biasing path which can cause leakage */
+ /* Disable internal biasing path which can cause leakage */
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_BIAS_CURR_CTL_2, 0x04),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_CFILT_1_VAL, 0x60),
- MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_1_CTL, 0x82),
+ /* Enable pulldown to reduce leakage */
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_1_CTL, 0x83),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_COM_BIAS, 0xE0),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_1_EN, 0x32),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_2_EN, 0x32),
@@ -2350,6 +2474,9 @@
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_FREQ_THRESH_B3_CTL, 0x1A),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_FREQ_THRESH_B4_CTL, 0x47),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_GAIN_THRESH_CTL, 0x23),
+
+ /* Always set TXD_CLK_EN bit to reduce the leakage */
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_DIG_CLK_CTL, 0x10),
};
static void msm8x10_wcd_update_reg_defaults(struct snd_soc_codec *codec)
@@ -2414,6 +2541,68 @@
return 0;
}
+static struct regulator *wcd8x10_wcd_codec_find_regulator(
+ const struct msm8x10_wcd *msm8x10,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; i < msm8x10->num_of_supplies; i++) {
+ if (msm8x10->supplies[i].supply &&
+ !strncmp(msm8x10->supplies[i].supply, name, strlen(name)))
+ return msm8x10->supplies[i].consumer;
+ }
+
+ return NULL;
+}
+
+static int msm8x10_wcd_device_up(struct snd_soc_codec *codec)
+{
+ pr_debug("%s: device up!\n", __func__);
+
+ mutex_lock(&codec->mutex);
+
+ msm8x10_wcd_bringup(codec);
+ msm8x10_wcd_codec_init_reg(codec);
+ msm8x10_wcd_update_reg_defaults(codec);
+
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+
+static int adsp_state_callback(struct notifier_block *nb, unsigned long value,
+ void *priv)
+{
+ bool timedout;
+ unsigned long timeout;
+
+ if (value == SUBSYS_AFTER_POWERUP) {
+ pr_debug("%s: ADSP is about to power up. bring up codec\n",
+ __func__);
+
+ timeout = jiffies +
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+ while (!(timedout = time_after(jiffies, timeout))) {
+ if (!q6core_is_adsp_ready()) {
+ pr_debug("%s: ADSP isn't ready\n", __func__);
+ } else {
+ pr_debug("%s: ADSP is ready\n", __func__);
+ msm8x10_wcd_device_up(registered_codec);
+ break;
+ }
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block adsp_state_notifier_block = {
+ .notifier_call = adsp_state_callback,
+ .priority = -INT_MAX,
+};
+
+
static int msm8x10_wcd_codec_probe(struct snd_soc_codec *codec)
{
struct msm8x10_wcd_priv *msm8x10_wcd;
@@ -2439,20 +2628,43 @@
msm8x10_wcd_bringup(codec);
msm8x10_wcd_codec_init_reg(codec);
msm8x10_wcd_update_reg_defaults(codec);
-
+ msm8x10_wcd->on_demand_list[ON_DEMAND_CP].supply =
+ wcd8x10_wcd_codec_find_regulator(
+ codec->control_data,
+ on_demand_supply_name[ON_DEMAND_CP]);
+ atomic_set(&msm8x10_wcd->on_demand_list[ON_DEMAND_CP].ref, 0);
+ msm8x10_wcd->on_demand_list[ON_DEMAND_MICBIAS].supply =
+ wcd8x10_wcd_codec_find_regulator(
+ codec->control_data,
+ on_demand_supply_name[ON_DEMAND_MICBIAS]);
+ atomic_set(&msm8x10_wcd->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);
msm8x10_wcd->mclk_enabled = false;
msm8x10_wcd->bandgap_type = MSM8X10_WCD_BANDGAP_OFF;
msm8x10_wcd->clock_active = false;
msm8x10_wcd->config_mode_active = false;
msm8x10_wcd->mbhc_polling_active = false;
mutex_init(&msm8x10_wcd->codec_resource_lock);
- msm8x10_wcd->codec = codec;
+ registered_codec = codec;
+ adsp_state_notifier =
+ subsys_notif_register_notifier("adsp",
+ &adsp_state_notifier_block);
+ if (!adsp_state_notifier) {
+ pr_err("%s: Failed to register adsp state notifier\n",
+ __func__);
+ registered_codec = NULL;
+ return -ENOMEM;
+ }
return 0;
}
static int msm8x10_wcd_codec_remove(struct snd_soc_codec *codec)
{
+ struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
+ msm8x10_wcd->on_demand_list[ON_DEMAND_CP].supply = NULL;
+ atomic_set(&msm8x10_wcd->on_demand_list[ON_DEMAND_CP].ref, 0);
+ msm8x10_wcd->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL;
+ atomic_set(&msm8x10_wcd->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);
return 0;
}
@@ -2478,7 +2690,7 @@
.num_dapm_routes = ARRAY_SIZE(audio_map),
};
-static int msm8x10_wcd_enable_supplies(struct msm8x10_wcd *msm8x10,
+static int msm8x10_wcd_init_supplies(struct msm8x10_wcd *msm8x10,
struct msm8x10_wcd_pdata *pdata)
{
int ret;
@@ -2516,8 +2728,13 @@
}
for (i = 0; i < msm8x10->num_of_supplies; i++) {
+ if (regulator_count_voltages(msm8x10->supplies[i].consumer) <=
+ 0)
+ continue;
+
ret = regulator_set_voltage(msm8x10->supplies[i].consumer,
- pdata->regulator[i].min_uV, pdata->regulator[i].max_uV);
+ pdata->regulator[i].min_uV,
+ pdata->regulator[i].max_uV);
if (ret) {
dev_err(msm8x10->dev, "%s: Setting regulator voltage failed for regulator %s err = %d\n",
__func__, msm8x10->supplies[i].supply, ret);
@@ -2530,24 +2747,13 @@
dev_err(msm8x10->dev, "%s: Setting regulator optimum mode failed for regulator %s err = %d\n",
__func__, msm8x10->supplies[i].supply, ret);
goto err_get;
+ } else {
+ ret = 0;
}
}
- ret = regulator_bulk_enable(msm8x10->num_of_supplies,
- msm8x10->supplies);
- if (ret != 0) {
- dev_err(msm8x10->dev, "Failed to enable supplies: err = %d\n",
- ret);
- goto err_configure;
- }
return ret;
-err_configure:
- for (i = 0; i < msm8x10->num_of_supplies; i++) {
- regulator_set_voltage(msm8x10->supplies[i].consumer, 0,
- pdata->regulator[i].max_uV);
- regulator_set_optimum_mode(msm8x10->supplies[i].consumer, 0);
- }
err_get:
regulator_bulk_free(msm8x10->num_of_supplies, msm8x10->supplies);
err_supplies:
@@ -2556,6 +2762,35 @@
return ret;
}
+static int msm8x10_wcd_enable_static_supplies(struct msm8x10_wcd *msm8x10,
+ struct msm8x10_wcd_pdata *pdata)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < msm8x10->num_of_supplies; i++) {
+ if (pdata->regulator[i].ondemand)
+ continue;
+ ret = regulator_enable(msm8x10->supplies[i].consumer);
+ if (ret) {
+ pr_err("%s: Failed to enable %s\n", __func__,
+ msm8x10->supplies[i].supply);
+ break;
+ } else {
+ pr_debug("%s: Enabled regulator %s\n", __func__,
+ msm8x10->supplies[i].supply);
+ }
+ }
+
+ while (ret && --i)
+ if (!pdata->regulator[i].ondemand)
+ regulator_disable(msm8x10->supplies[i].consumer);
+
+ return ret;
+}
+
+
+
static void msm8x10_wcd_disable_supplies(struct msm8x10_wcd *msm8x10,
struct msm8x10_wcd_pdata *pdata)
{
@@ -2564,6 +2799,9 @@
regulator_bulk_disable(msm8x10->num_of_supplies,
msm8x10->supplies);
for (i = 0; i < msm8x10->num_of_supplies; i++) {
+ if (regulator_count_voltages(msm8x10->supplies[i].consumer) <=
+ 0)
+ continue;
regulator_set_voltage(msm8x10->supplies[i].consumer, 0,
pdata->regulator[i].max_uV);
regulator_set_optimum_mode(msm8x10->supplies[i].consumer, 0);
@@ -2676,12 +2914,21 @@
msm8x10->dev = &client->dev;
msm8x10->read_dev = msm8x10_wcd_reg_read;
msm8x10->write_dev = msm8x10_wcd_reg_write;
- ret = msm8x10_wcd_enable_supplies(msm8x10, pdata);
+ ret = msm8x10_wcd_init_supplies(msm8x10, pdata);
if (ret) {
dev_err(&client->dev, "%s: Fail to enable Codec supplies\n",
__func__);
goto err_codec;
}
+
+ ret = msm8x10_wcd_enable_static_supplies(msm8x10, pdata);
+ if (ret) {
+ pr_err("%s: Fail to enable Codec pre-reset supplies\n",
+ __func__);
+ goto err_codec;
+ }
+ usleep_range(5, 5);
+
ret = msm8x10_wcd_device_init(msm8x10);
if (ret) {
dev_err(&client->dev,
diff --git a/sound/soc/codecs/msm8x10-wcd.h b/sound/soc/codecs/msm8x10-wcd.h
index 08a2725..d8f6ace 100644
--- a/sound/soc/codecs/msm8x10-wcd.h
+++ b/sound/soc/codecs/msm8x10-wcd.h
@@ -147,6 +147,7 @@
int min_uV;
int max_uV;
int optimum_uA;
+ bool ondemand;
struct regulator *regulator;
};
diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c
index 46bce9e..e5d5c32 100644
--- a/sound/soc/codecs/msm_hdmi_codec_rx.c
+++ b/sound/soc/codecs/msm_hdmi_codec_rx.c
@@ -113,7 +113,7 @@
channel_allocation);
codec_data->hdmi_ops.audio_info_setup(codec_data->hdmi_core_pdev,
- num_channels, channel_allocation,
+ params_rate(params), num_channels, channel_allocation,
level_shift, down_mix);
return 0;
diff --git a/sound/soc/codecs/msm_stub.c b/sound/soc/codecs/msm_stub.c
index 0cbcaf3..bdf1eb4 100644
--- a/sound/soc/codecs/msm_stub.c
+++ b/sound/soc/codecs/msm_stub.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,7 +36,8 @@
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
},
},
};
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index 8949e03..7b896c2 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -73,6 +73,9 @@
#define TAPAN_SLIM_IRQ_OVERFLOW (1 << 0)
#define TAPAN_SLIM_IRQ_UNDERFLOW (1 << 1)
#define TAPAN_SLIM_IRQ_PORT_CLOSED (1 << 2)
+
+#define TAPAN_IRQ_MBHC_JACK_SWITCH 21
+
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -303,18 +306,18 @@
return 0;
}
- WCD9XXX_BCL_LOCK(&priv->resmgr);
+ codec = priv->codec;
+ mutex_lock(&codec->mutex);
old = spkr_drv_wrnd;
ret = param_set_int(val, kp);
if (ret) {
- WCD9XXX_BCL_UNLOCK(&priv->resmgr);
+ mutex_unlock(&codec->mutex);
return ret;
}
- codec = priv->codec;
dev_dbg(codec->dev, "%s: spkr_drv_wrnd %d -> %d\n",
__func__, old, spkr_drv_wrnd);
- if (old == 0 && spkr_drv_wrnd == 1) {
+ if ((old == -1 || old == 0) && spkr_drv_wrnd == 1) {
WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
wcd9xxx_resmgr_get_bandgap(&priv->resmgr,
WCD9XXX_BANDGAP_AUDIO_MODE);
@@ -329,8 +332,8 @@
snd_soc_update_bits(codec, TAPAN_A_SPKR_DRV_EN, 0x80,
0x00);
}
+ mutex_unlock(&codec->mutex);
- WCD9XXX_BCL_UNLOCK(&priv->resmgr);
return 0;
}
@@ -1698,7 +1701,6 @@
struct tapan_priv *tapan = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event);
- WCD9XXX_BCL_LOCK(&tapan->resmgr);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
tapan->spkr_pa_widget_on = true;
@@ -1709,7 +1711,6 @@
snd_soc_update_bits(codec, TAPAN_A_SPKR_DRV_EN, 0x80, 0x00);
break;
}
- WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
return 0;
}
@@ -4272,6 +4273,12 @@
*/
TAPAN_REG_VAL(TAPAN_A_MICB_2_MBHC, 0x41),
+ /*
+ * Default register settings to support dynamic change of
+ * vdd_buck between 1.8 volts and 2.15 volts.
+ */
+ TAPAN_REG_VAL(TAPAN_A_BUCK_MODE_2, 0xAA),
+
};
static const struct tapan_reg_mask_val tapan_2_x_reg_reset_values[] = {
@@ -4450,6 +4457,72 @@
wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tapan);
}
+
+static void tapan_enable_mux_bias_block(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x00);
+}
+
+static void tapan_put_cfilt_fast_mode(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x30, 0x30);
+}
+
+static void tapan_codec_specific_cal_setup(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x0C, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0xE0, 0xE0);
+}
+
+static int tapan_get_jack_detect_irq(struct snd_soc_codec *codec)
+{
+ return TAPAN_IRQ_MBHC_JACK_SWITCH;
+}
+
+static struct wcd9xxx_cfilt_mode tapan_codec_switch_cfilt_mode(
+ struct wcd9xxx_mbhc *mbhc,
+ bool fast)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_cfilt_mode cfilt_mode;
+
+ if (fast)
+ cfilt_mode.reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_EN;
+ else
+ cfilt_mode.reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_DSBL;
+
+ cfilt_mode.cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x30;
+ return cfilt_mode;
+}
+
+static void tapan_select_cfilt(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60, 0x00);
+}
+
+static void tapan_free_irq(struct wcd9xxx_mbhc *mbhc)
+{
+ void *cdata = mbhc->codec->control_data;
+ wcd9xxx_free_irq(cdata, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
+}
+
+static const struct wcd9xxx_mbhc_cb mbhc_cb = {
+ .enable_mux_bias_block = tapan_enable_mux_bias_block,
+ .cfilt_fast_mode = tapan_put_cfilt_fast_mode,
+ .codec_specific_cal = tapan_codec_specific_cal_setup,
+ .jack_detect_irq = tapan_get_jack_detect_irq,
+ .switch_cfilt_mode = tapan_codec_switch_cfilt_mode,
+ .select_cfilt = tapan_select_cfilt,
+ .free_irq = tapan_free_irq,
+};
+
int tapan_hs_detect(struct snd_soc_codec *codec,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
@@ -4468,7 +4541,6 @@
codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
tapan = snd_soc_codec_get_drvdata(codec);
mutex_lock(&codec->mutex);
- WCD9XXX_BCL_LOCK(&tapan->resmgr);
if (codec->reg_def_copy) {
pr_debug("%s: Update ASOC cache", __func__);
@@ -4477,7 +4549,6 @@
codec->reg_size, GFP_KERNEL);
if (!codec->reg_cache) {
pr_err("%s: Cache update failed!\n", __func__);
- WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
mutex_unlock(&codec->mutex);
return -ENOMEM;
}
@@ -4486,7 +4557,6 @@
wcd9xxx_resmgr_post_ssr(&tapan->resmgr);
if (spkr_drv_wrnd == 1)
snd_soc_update_bits(codec, TAPAN_A_SPKR_DRV_EN, 0x80, 0x80);
- WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
tapan_update_reg_defaults(codec);
tapan_update_reg_mclk_rate(wcd9xxx);
@@ -4505,8 +4575,7 @@
rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
- WCD9XXX_MBHC_VERSION_TAPAN,
- rco_clk_rate);
+ &mbhc_cb, rco_clk_rate);
if (ret)
pr_err("%s: mbhc init failed %d\n", __func__, ret);
else
@@ -4526,6 +4595,30 @@
return 0;
}
+static enum wcd9xxx_buck_volt tapan_codec_get_buck_mv(
+ struct snd_soc_codec *codec)
+{
+ int buck_volt = WCD9XXX_CDC_BUCK_UNSUPPORTED;
+ struct tapan_priv *tapan = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_pdata *pdata = tapan->resmgr.pdata;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ if (!strncmp(pdata->regulator[i].name,
+ WCD9XXX_SUPPLY_BUCK_NAME,
+ sizeof(WCD9XXX_SUPPLY_BUCK_NAME))) {
+ if ((pdata->regulator[i].min_uV ==
+ WCD9XXX_CDC_BUCK_MV_1P8) ||
+ (pdata->regulator[i].min_uV ==
+ WCD9XXX_CDC_BUCK_MV_2P15))
+ buck_volt = pdata->regulator[i].min_uV;
+ break;
+ }
+ }
+ pr_debug("%s: S4 voltage requested is %d\n", __func__, buck_volt);
+ return buck_volt;
+}
+
static int tapan_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
@@ -4558,10 +4651,6 @@
snd_soc_codec_set_drvdata(codec, tapan);
- /* TODO: Read buck voltage from DT property */
- tapan->clsh_d.buck_mv = WCD9XXX_CDC_BUCK_MV_1P8;
- wcd9xxx_clsh_init(&tapan->clsh_d, &tapan->resmgr);
-
/* codec resmgr module init */
wcd9xxx = codec->control_data;
pdata = dev_get_platdata(codec->dev->parent);
@@ -4572,14 +4661,24 @@
return ret;
}
+ tapan->clsh_d.buck_mv = tapan_codec_get_buck_mv(codec);
+ /*
+ * If 1.8 volts is requested on the vdd_cp line, then
+ * assume that S4 is in a dynamically switchable state
+ * and can switch between 1.8 volts and 2.15 volts
+ */
+ if (tapan->clsh_d.buck_mv == WCD9XXX_CDC_BUCK_MV_1P8)
+ tapan->clsh_d.is_dynamic_vdd_cp = true;
+ wcd9xxx_clsh_init(&tapan->clsh_d, &tapan->resmgr);
+
if (TAPAN_IS_1_0(control->version))
rco_clk_rate = TAPAN_MCLK_CLK_12P288MHZ;
else
rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
- WCD9XXX_MBHC_VERSION_TAPAN,
- rco_clk_rate);
+ &mbhc_cb, rco_clk_rate);
+
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
return ret;
diff --git a/sound/soc/codecs/wcd9320-tables.c b/sound/soc/codecs/wcd9320-tables.c
index e834b80..af9725c 100644
--- a/sound/soc/codecs/wcd9320-tables.c
+++ b/sound/soc/codecs/wcd9320-tables.c
@@ -680,7 +680,10 @@
[TAIKO_A_CDC_SPKR_CLIPDET_VAL5] = 1,
[TAIKO_A_CDC_SPKR_CLIPDET_VAL6] = 1,
[TAIKO_A_CDC_SPKR_CLIPDET_VAL7] = 1,
-
+ [TAIKO_A_CDC_PA_RAMP_B1_CTL] = 1,
+ [TAIKO_A_CDC_PA_RAMP_B2_CTL] = 1,
+ [TAIKO_A_CDC_PA_RAMP_B3_CTL] = 1,
+ [TAIKO_A_CDC_PA_RAMP_B4_CTL] = 1,
};
const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE] = {
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 49894ca..59354a3 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -604,18 +604,17 @@
return 0;
}
- WCD9XXX_BCL_LOCK(&priv->resmgr);
+ codec = priv->codec;
+ mutex_lock(&codec->mutex);
old = spkr_drv_wrnd;
ret = param_set_int(val, kp);
if (ret) {
- WCD9XXX_BCL_UNLOCK(&priv->resmgr);
+ mutex_unlock(&codec->mutex);
return ret;
}
- WCD9XXX_BCL_UNLOCK(&priv->resmgr);
pr_debug("%s: spkr_drv_wrnd %d -> %d\n", __func__, old, spkr_drv_wrnd);
- codec = priv->codec;
- if (old == 0 && spkr_drv_wrnd == 1) {
+ if ((old == -1 || old == 0) && spkr_drv_wrnd == 1) {
WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
wcd9xxx_resmgr_get_bandgap(&priv->resmgr,
WCD9XXX_BANDGAP_AUDIO_MODE);
@@ -630,6 +629,7 @@
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80,
0x00);
}
+ mutex_unlock(&codec->mutex);
return 0;
}
@@ -1050,7 +1050,7 @@
0x07, rate);
/* Set the static gain offset */
if (comp == COMPANDER_1
- && buck_mv == WCD9XXX_CDC_BUCK_MV_2P15) {
+ && buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
snd_soc_update_bits(codec,
TAIKO_A_CDC_COMP0_B4_CTL + (comp * 8),
0x80, 0x80);
@@ -1456,6 +1456,32 @@
analog_gain),
};
+static int taiko_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t zl, zr;
+ bool hphr;
+ struct soc_multi_mixer_control *mc;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct taiko_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+
+ hphr = mc->shift;
+ wcd9xxx_mbhc_get_impedance(&priv->mbhc, &zl, &zr);
+ pr_debug("%s: zl %u, zr %u\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+ taiko_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+ taiko_hph_impedance_get, NULL),
+};
+
static const char * const rx_mix1_text[] = {
"ZERO", "SRC1", "SRC2", "IIR1", "IIR2", "RX1", "RX2", "RX3", "RX4",
"RX5", "RX6", "RX7"
@@ -2420,7 +2446,6 @@
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
pr_debug("%s: %d %s\n", __func__, event, w->name);
- WCD9XXX_BCL_LOCK(&taiko->resmgr);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
taiko->spkr_pa_widget_on = true;
@@ -2431,7 +2456,6 @@
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x00);
break;
}
- WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
return 0;
}
@@ -3072,6 +3096,8 @@
{
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+ uint32_t impedl, impedr;
+ int ret = 0;
pr_debug("%s %s %d\n", __func__, w->name, event);
@@ -3083,6 +3109,13 @@
WCD9XXX_CLSH_STATE_HPHL,
WCD9XXX_CLSH_REQ_ENABLE,
WCD9XXX_CLSH_EVENT_PRE_DAC);
+ ret = wcd9xxx_mbhc_get_impedance(&taiko_p->mbhc,
+ &impedl, &impedr);
+ if (!ret)
+ wcd9xxx_clsh_imped_config(codec, impedl);
+ else
+ dev_err(codec->dev, "Failed to get mbhc impedance %d\n",
+ ret);
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL,
@@ -4639,7 +4672,7 @@
.rate_max = 192000,
.rate_min = 8000,
.channels_min = 1,
- .channels_max = 5,
+ .channels_max = 8,
},
.ops = &taiko_dai_ops,
},
@@ -6189,7 +6222,6 @@
codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
taiko = snd_soc_codec_get_drvdata(codec);
mutex_lock(&codec->mutex);
- WCD9XXX_BCL_LOCK(&taiko->resmgr);
if (codec->reg_def_copy) {
pr_debug("%s: Update ASOC cache", __func__);
@@ -6201,7 +6233,6 @@
wcd9xxx_resmgr_post_ssr(&taiko->resmgr);
if (spkr_drv_wrnd == 1)
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x80);
- WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
taiko_update_reg_defaults(codec);
taiko_codec_init_reg(codec);
@@ -6223,8 +6254,7 @@
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
- WCD9XXX_MBHC_VERSION_TAIKO,
- rco_clk_rate);
+ NULL, rco_clk_rate);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
} else {
@@ -6394,6 +6424,8 @@
}
taiko->clsh_d.buck_mv = taiko_codec_get_buck_mv(codec);
+ /* Taiko does not support dynamic switching of vdd_cp */
+ taiko->clsh_d.is_dynamic_vdd_cp = false;
wcd9xxx_clsh_init(&taiko->clsh_d, &taiko->resmgr);
if (TAIKO_IS_1_0(core->version))
@@ -6404,8 +6436,7 @@
/* init and start mbhc */
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
- WCD9XXX_MBHC_VERSION_TAIKO,
- rco_clk_rate);
+ NULL, rco_clk_rate);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
goto err_init;
@@ -6495,6 +6526,9 @@
ARRAY_SIZE(taiko_2_x_analog_gain_controls));
}
+ snd_soc_add_codec_controls(codec, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+
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 916ff1a..d00b843 100644
--- a/sound/soc/codecs/wcd9xxx-common.c
+++ b/sound/soc/codecs/wcd9xxx-common.c
@@ -30,6 +30,387 @@
#define BUCK_SETTLE_TIME_US 50
#define NCP_SETTLE_TIME_US 50
+#define MAX_IMPED_PARAMS 13
+
+struct wcd9xxx_imped_val {
+ u32 imped_val;
+ u8 index;
+};
+
+static const struct wcd9xxx_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = {
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x46},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0C},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x47},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0C},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x49},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x12},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x35},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x4E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0E},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x49},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x16},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x17},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x5F},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xCF},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0F},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x59},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xCE},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xBD},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x10},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x66},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9A},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xBD},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA6},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x11},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x79},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA6},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAD},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x08},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x12},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x76},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x4E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAD},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x09},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x12},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x78},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x12},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xD0},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0A},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x13},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x7A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xB7},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0B},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x14},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x60},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x09},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA4},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1F},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0C},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x14},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x79},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x17},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1F},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0D},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x15},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x78},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x16},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0E},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x89},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x40},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x10},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xD0},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x12},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x17},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xB7},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x10},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x17},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA4},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x18},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x9A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x08},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x27},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x18},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8B},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x18},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x20},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1A},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x9A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x17},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x1A},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xA9},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1F},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xB9},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x10},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x23},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x18},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xA9},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x27},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x35},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x26},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+ },
+};
+
+static const struct wcd9xxx_imped_val imped_index[] = {
+ {4000, 0},
+ {4500, 1},
+ {5000, 2},
+ {5500, 3},
+ {6000, 4},
+ {6500, 5},
+ {7000, 6},
+ {7700, 7},
+ {8470, 8},
+ {9317, 9},
+ {10248, 10},
+ {11273, 11},
+ {12400, 12},
+ {13641, 13},
+ {15005, 14},
+ {16505, 15},
+ {18156, 16},
+ {19971, 17},
+ {21969, 18},
+ {24165, 19},
+ {26582, 20},
+ {29240, 21},
+ {32164, 22},
+};
+
static inline void wcd9xxx_enable_clsh_block(
struct snd_soc_codec *codec,
bool on)
@@ -165,6 +546,40 @@
}
}
+static int get_impedance_index(u32 imped)
+{
+ int i = 0;
+ if (imped < imped_index[i].imped_val) {
+ pr_debug("%s, detected impedance is less than 4 Ohm\n",
+ __func__);
+ goto ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(imped_index); i++) {
+ if (imped >= imped_index[i].imped_val &&
+ imped < imped_index[i + 1].imped_val)
+ break;
+ }
+ret:
+ pr_debug("%s: selected impedance index = %d\n",
+ __func__, imped_index[i].index);
+ return imped_index[i].index;
+}
+
+void wcd9xxx_clsh_imped_config(struct snd_soc_codec *codec,
+ int imped)
+{
+ int i = 0;
+ int index = 0;
+ index = get_impedance_index(imped);
+ if (index > ARRAY_SIZE(imped_index)) {
+ pr_err("%s, invalid imped = %d\n", __func__, imped);
+ return;
+ }
+ for (i = 0; i < MAX_IMPED_PARAMS; i++)
+ snd_soc_write(codec, imped_table[index][i].reg,
+ imped_table[index][i].val);
+}
+
static void wcd9xxx_clsh_comp_req(struct snd_soc_codec *codec,
struct wcd9xxx_clsh_cdc_data *clsh_d,
int compute_pa, bool on)
@@ -221,20 +636,26 @@
usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US);
}
-static void wcd9xxx_clsh_enable_post_pa(struct snd_soc_codec *codec)
+
+/* This will be called for all states except Lineout */
+static void wcd9xxx_clsh_enable_post_pa(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *cdc_clsh_d)
{
int i;
const struct wcd9xxx_reg_mask_val reg_set[] = {
{WCD9XXX_A_BUCK_MODE_5, 0x02, 0x00},
{WCD9XXX_A_NCP_STATIC, 0x20, 0x00},
{WCD9XXX_A_BUCK_MODE_3, 0x04, 0x04},
- {WCD9XXX_A_BUCK_MODE_3, 0x08, 0x08},
};
for (i = 0; i < ARRAY_SIZE(reg_set); i++)
snd_soc_update_bits(codec, reg_set[i].reg,
reg_set[i].mask, reg_set[i].val);
+ if (!cdc_clsh_d->is_dynamic_vdd_cp)
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_3,
+ 0x08, 0x08);
+
dev_dbg(codec->dev, "%s: completed clsh mode settings after PA enable\n",
__func__);
@@ -478,13 +899,17 @@
snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
0x20, 0x01);
wcd9xxx_enable_ncp(codec, true);
- msleep(NCP_SETTLE_TIME_US);
+ /* NCP settle time recommended by codec specification */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
} else {
snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN,
0x40, 0x00);
wcd9xxx_enable_ncp(codec, true);
- msleep(NCP_SETTLE_TIME_US);
+ /* NCP settle time recommended by codec specification */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
0x01, 0x01);
snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
@@ -557,7 +982,7 @@
} else if (req_state != WCD9XXX_CLSH_STATE_LO) {
- wcd9xxx_clsh_enable_post_pa(codec);
+ wcd9xxx_clsh_enable_post_pa(codec, cdc_clsh_d);
}
break;
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
index 316742d..654964e 100644
--- a/sound/soc/codecs/wcd9xxx-common.h
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -52,6 +52,7 @@
struct wcd9xxx_clsh_cdc_data {
u8 state;
int buck_mv;
+ bool is_dynamic_vdd_cp;
struct wcd9xxx_resmgr *resmgr;
};
@@ -73,6 +74,9 @@
extern void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh,
struct wcd9xxx_resmgr *resmgr);
+extern void wcd9xxx_clsh_imped_config(struct snd_soc_codec *codec,
+ int imped);
+
enum wcd9xxx_codec_event {
WCD9XXX_CODEC_EVENT_CODEC_UP = 0,
};
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 618070b..d0c00a7 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -18,6 +18,7 @@
#include <linux/printk.h>
#include <linux/ratelimit.h>
#include <linux/debugfs.h>
+#include <linux/list.h>
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
@@ -36,6 +37,7 @@
#include "wcd9306.h"
#include "wcd9xxx-mbhc.h"
#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
@@ -92,7 +94,12 @@
#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
-#define WCD9XXX_USLEEP_RANGE_MARGIN_US 1000
+#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
+
+/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
+#define WCD9XXX_WG_TIME_FACTOR_US 240
+
+#define WCD9XXX_IRQ_MBHC_JACK_SWITCH_DEFAULT 28
static bool detect_use_vddio_switch = true;
@@ -110,6 +117,12 @@
enum wcd9xxx_mbhc_plug_type _type;
};
+struct wcd9xxx_register_save_node {
+ struct list_head lh;
+ u16 reg;
+ u16 value;
+};
+
enum meas_type {
STA = 0,
DCE,
@@ -140,6 +153,9 @@
WCD9XXX_CURRENT_V_BR_H,
};
+static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr);
+
static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
{
return mbhc->polling_active;
@@ -150,64 +166,6 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
}
-static int wcd9xxx_enable_mux_bias_block(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
- 0x80, 0x80);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
- 0x80, 0x00);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int wcd9xxx_put_cfilt_fast_mode(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, 0x00);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, 0x70);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int wcd9xxx_codec_specific_cal_setup(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
- 0x04, 0x04);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
- 0x0C, 0x04);
- snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0xE0, 0xE0);
- /* Make sure the calibration is ON */
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_val,
- 0x02, 0x02);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
/* called under codec_resource_lock acquisition */
static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
{
@@ -229,20 +187,18 @@
{
struct snd_soc_codec *codec = mbhc->codec;
int mbhc_state = mbhc->mbhc_state;
- int ret;
pr_debug("%s: enter\n", __func__);
if (!mbhc->polling_active) {
pr_debug("Polling is not active, do not start polling\n");
return;
}
-
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret) {
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
- return;
- }
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
if (!mbhc->no_mic_headset_override &&
mbhc_state == MBHC_STATE_POTENTIAL) {
@@ -264,11 +220,15 @@
pr_debug("%s: leave\n", __func__);
}
-/* called under codec_resource_lock acquisition */
-static void __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
+/*
+ * called under codec_resource_lock acquisition
+ * return old status
+ */
+static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
int vddio_switch, bool restartpolling,
bool checkpolling)
{
+ bool ret;
int cfilt_k_val;
bool override;
struct snd_soc_codec *codec;
@@ -278,9 +238,11 @@
if (mbhc->micbias_enable) {
pr_debug("%s: micbias is already on\n", __func__);
- return;
+ ret = mbhc->mbhc_micbias_switched;
+ return ret;
}
+ ret = mbhc->mbhc_micbias_switched;
if (vddio_switch && !mbhc->mbhc_micbias_switched &&
(!checkpolling || mbhc->polling_active)) {
if (restartpolling)
@@ -387,11 +349,13 @@
mbhc->mbhc_micbias_switched = false;
pr_debug("%s: VDDIO switch disabled\n", __func__);
}
+
+ return ret;
}
static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
{
- return __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
+ __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
}
static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
@@ -481,14 +445,18 @@
(mbhc->mbhc_data.v_brl >> 8) & 0xFF);
}
-static int wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
bool fast)
{
struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_cfilt_mode cfilt_mode;
u8 reg_mode_val, cur_mode_val;
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
+ cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
+ reg_mode_val = cfilt_mode.reg_mode_val;
+ cur_mode_val = cfilt_mode.cur_mode_val;
+ } else {
if (fast)
reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
else
@@ -496,50 +464,21 @@
cur_mode_val =
snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
-
- if (cur_mode_val != reg_mode_val) {
- if (mbhc->polling_active)
- wcd9xxx_pause_hs_polling(mbhc);
- snd_soc_update_bits(codec,
- mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x40, reg_mode_val);
- if (mbhc->polling_active)
- wcd9xxx_start_hs_polling(mbhc);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- if (fast)
- reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_EN;
- else
- reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_DSBL;
-
- cur_mode_val =
- snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x70;
-
- if (cur_mode_val != reg_mode_val) {
- if (mbhc->polling_active)
- wcd9xxx_pause_hs_polling(mbhc);
- snd_soc_update_bits(codec,
- mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, reg_mode_val);
- if (mbhc->polling_active)
- wcd9xxx_start_hs_polling(mbhc);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
- break;
- default:
- return -EINVAL;
}
- return 0;
+ if (cur_mode_val != reg_mode_val) {
+ if (mbhc->polling_active)
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, reg_mode_val);
+ if (mbhc->polling_active)
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cur_mode_val, reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cur_mode_val);
+ }
}
static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
@@ -811,6 +750,7 @@
mbhc->micbias_enable_cb(mbhc->codec, false);
mbhc->micbias_enable = false;
}
+ mbhc->zl = mbhc->zr = 0;
pr_debug("%s: Reporting removal %d(%x)\n", __func__,
jack_type, mbhc->hph_status);
wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
@@ -835,6 +775,7 @@
}
pr_debug("%s: Reporting removal (%x)\n",
__func__, mbhc->hph_status);
+ mbhc->zl = mbhc->zr = 0;
wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
0, WCD9XXX_JACK_MASK);
mbhc->hph_status = 0;
@@ -858,6 +799,8 @@
pr_debug("%s: Enabling micbias\n", __func__);
mbhc->micbias_enable_cb(mbhc->codec, true);
}
+
+ wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
jack_type, mbhc->hph_status);
wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
@@ -1043,7 +986,6 @@
struct snd_soc_codec *codec = mbhc->codec;
short bias_value;
u8 cfilt_mode;
- int ret;
WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
@@ -1066,14 +1008,19 @@
/* Make sure CFILT is in fast mode, save current mode */
cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
- ret = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x70, 0x00);
+
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
@@ -1094,10 +1041,6 @@
snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
return bias_value;
-
-gen_err:
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
- return ret;
}
static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
@@ -1189,10 +1132,15 @@
/*
* wcd9xxx_find_plug_type : Find out and return the best plug type with given
* list of wcd9xxx_mbhc_detect structure.
+ * param mbhc wcd9xxx_mbhc structure
+ * param dt collected measurements
+ * param size array size of dt
+ * param event_state mbhc->event_state when dt is collected
*/
static enum wcd9xxx_mbhc_plug_type
wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
- struct wcd9xxx_mbhc_detect *dt, const int size)
+ struct wcd9xxx_mbhc_detect *dt, const int size,
+ unsigned long event_state)
{
int i;
int ch;
@@ -1205,6 +1153,8 @@
const s16 hs_max = plug_type->v_hs_max;
const s16 no_mic = plug_type->v_no_mic;
+ pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
+
for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
if (d->vddio)
@@ -1246,7 +1196,10 @@
goto exit;
}
}
- if (ch != size && ch > 0) {
+
+ if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
+ pr_debug("%s: HPHL PA was ON\n", __func__);
+ } else if (ch != size && ch > 0) {
pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
type = PLUG_TYPE_INVALID;
goto exit;
@@ -1286,12 +1239,18 @@
maxv))
type = PLUG_TYPE_GND_MIC_SWAP;
}
- if (((type == PLUG_TYPE_HEADSET || type == PLUG_TYPE_HEADPHONE) &&
- ch != size) || (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
- pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
- __func__, type);
- type = PLUG_TYPE_INVALID;
+
+ /* if HPHL PA was on, we cannot use hphl status */
+ if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
+ if (((type == PLUG_TYPE_HEADSET ||
+ type == PLUG_TYPE_HEADPHONE) && ch != size) ||
+ (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
+ pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
+ __func__, type);
+ type = PLUG_TYPE_INVALID;
+ }
}
+
if (type == PLUG_TYPE_HEADSET && dvddio) {
if ((dvddio->_vdces > hs_max) ||
(dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
@@ -1359,6 +1318,7 @@
wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
{
int i;
+ bool vddioon;
struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
@@ -1373,6 +1333,13 @@
/* GND and MIC swap detection requires at least 2 rounds of DCE */
BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
+ /*
+ * There are chances vddio switch is on and cfilt voltage is adjusted
+ * to vddio voltage even after plug type removal reported.
+ */
+ vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
+ pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
+
plug_type_ptr =
WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
@@ -1415,7 +1382,11 @@
wcd9xxx_codec_hphr_gnd_switch(codec, false);
}
- type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt));
+ if (vddioon)
+ __wcd9xxx_switch_micbias(mbhc, 1, false, false);
+
+ type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
+ mbhc->event_state);
pr_debug("%s: leave\n", __func__);
return type;
@@ -2199,6 +2170,7 @@
unsigned long timeout;
int retry = 0, pt_gnd_mic_swap_cnt = 0;
bool correction = false;
+ bool wrk_complete = true;
pr_debug("%s: enter\n", __func__);
@@ -2224,12 +2196,14 @@
++retry;
rmb();
if (mbhc->hs_detect_work_stop) {
+ wrk_complete = false;
pr_debug("%s: stop requested\n", __func__);
break;
}
msleep(HS_DETECT_PLUG_INERVAL_MS);
if (wcd9xxx_swch_level_remove(mbhc)) {
+ wrk_complete = false;
pr_debug("%s: Switch level is low\n", __func__);
break;
}
@@ -2303,7 +2277,9 @@
if (plug_type == PLUG_TYPE_HIGH_HPH) {
pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
__func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
}
/* Turn off override */
if (!correction)
@@ -2313,10 +2289,11 @@
if (mbhc->mbhc_cfg->detect_extn_cable) {
WCD9XXX_BCL_LOCK(mbhc->resmgr);
- if (mbhc->current_plug == PLUG_TYPE_HEADPHONE ||
+ if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
+ wrk_complete) ||
mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
mbhc->current_plug == PLUG_TYPE_INVALID ||
- plug_type == PLUG_TYPE_INVALID) {
+ (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
/* Enable removal detection */
wcd9xxx_cleanup_hs_polling(mbhc);
wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
@@ -2903,7 +2880,8 @@
dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
mbhc->mbhc_data.t_dce = dce_wait;
- mbhc->mbhc_data.t_sta = sta_wait;
+ /* give extra margin to sta for safety */
+ mbhc->mbhc_data.t_sta = sta_wait + 250;
mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
n_ready[idx]) + 10;
@@ -2927,7 +2905,6 @@
{
u8 cfilt_mode;
u16 reg0, reg1;
- int ret;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
@@ -2944,21 +2921,30 @@
* Need to restore defaults once calculation is done.
*/
cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
- ret = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, 0x00);
+
/*
* Micbias, CFILT, LDOH, MBHC MUX mode settings
* to perform ADC calibration
*/
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
+ mbhc->mbhc_cb->select_cfilt(codec, mbhc);
+ else
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
mbhc->mbhc_cfg->micbias << 5);
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
- ret = wcd9xxx_codec_specific_cal_setup(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
+ mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x04, 0x04);
+
/* Pull down micbias to ground */
reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
@@ -2967,9 +2953,11 @@
snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
/* Connect the MUX to micbias */
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -2991,9 +2979,11 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -3008,9 +2998,11 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -3024,19 +3016,17 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
usleep_range(100, 100);
wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
wcd9xxx_turn_onoff_rel_detection(codec, true);
pr_debug("%s: leave\n", __func__);
- return;
-
-gen_err:
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
}
static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
@@ -3095,8 +3085,14 @@
{
int ret = 0;
void *core = mbhc->resmgr->core;
+ struct snd_soc_codec *codec = mbhc->codec;
int jack_irq;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->jack_detect_irq)
+ jack_irq = mbhc->mbhc_cb->jack_detect_irq(codec);
+ else
+ jack_irq = WCD9XXX_IRQ_MBHC_JACK_SWITCH_DEFAULT;
+
if (mbhc->mbhc_cfg->gpio) {
ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
wcd9xxx_mech_plug_detect_irq,
@@ -3118,17 +3114,6 @@
snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
1 << 1, 1 << 1);
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- jack_irq = WCD9320_IRQ_MBHC_JACK_SWITCH;
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- jack_irq = WCD9306_IRQ_MBHC_JACK_SWITCH;
- break;
- default:
- return -EINVAL;
- }
-
ret = wcd9xxx_request_irq(core, jack_irq,
wcd9xxx_mech_plug_detect_irq,
"Jack Detect",
@@ -3357,7 +3342,7 @@
int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
- int rc;
+ int rc = 0;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
@@ -3381,10 +3366,13 @@
wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
/* Put CFILT in fast mode by default */
- rc = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (rc)
- pr_err("%s: Error returned, ret: %d\n", __func__, rc);
- else if (!mbhc->mbhc_cfg->read_fw_bin)
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, WCD9XXX_CFILT_FAST_MODE);
+
+ if (!mbhc->mbhc_cfg->read_fw_bin)
rc = wcd9xxx_init_and_calibrate(mbhc);
else
schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
@@ -3612,7 +3600,7 @@
* Switch CFILT to slow mode if MBHC CFILT is being
* used.
*/
- ret = wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
break;
case WCD9XXX_EVENT_POST_CFILT_1_OFF:
case WCD9XXX_EVENT_POST_CFILT_2_OFF:
@@ -3623,7 +3611,7 @@
* Switch CFILT to fast mode if MBHC CFILT is not
* used anymore.
*/
- ret = wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
break;
/* System resume */
case WCD9XXX_EVENT_POST_RESUME:
@@ -3648,6 +3636,300 @@
return ret;
}
+static int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
+ struct list_head *list,
+ uint16_t reg, uint8_t mask,
+ uint8_t value)
+{
+ int rc;
+ struct wcd9xxx_register_save_node *node;
+
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ if (unlikely(!node)) {
+ pr_err("%s: Not enough memory\n", __func__);
+ return -ENOMEM;
+ }
+ node->reg = reg;
+ node->value = snd_soc_read(codec, reg);
+ list_add(&node->lh, list);
+ if (mask == 0xFF)
+ rc = snd_soc_write(codec, reg, value);
+ else
+ rc = snd_soc_update_bits(codec, reg, mask, value);
+ return rc;
+}
+
+static int wcd9xxx_prepare_static_pa(struct wcd9xxx_mbhc *mbhc,
+ struct list_head *lh)
+{
+ int i;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ const struct wcd9xxx_reg_mask_val reg_set_paon[] = {
+ {WCD9XXX_A_RX_HPH_OCP_CTL, 0x18, 0x00},
+ {WCD9XXX_A_RX_HPH_L_TEST, 0x1, 0x0},
+ {WCD9XXX_A_RX_HPH_R_TEST, 0x1, 0x0},
+ {WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xff, 0x1A},
+ {WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xff, 0xDB},
+ {WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xff, 0x15},
+ {WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x81},
+ {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x01, 0x01},
+ {WCD9XXX_A_RX_HPH_CHOP_CTL, 0xff, 0xA4},
+ {WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x81},
+ {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x02, 0x02},
+ {WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x2C},
+ {WCD9XXX_A_NCP_CLK, 0xff, 0xFC},
+ {WCD9XXX_A_BUCK_CTRL_CCL_3, 0xff, 0x60},
+ {WCD9XXX_A_RX_COM_BIAS, 0xff, 0x80},
+ {WCD9XXX_A_BUCK_MODE_3, 0xff, 0xC6},
+ {WCD9XXX_A_BUCK_MODE_4, 0xff, 0xE6},
+ {WCD9XXX_A_BUCK_MODE_5, 0xff, 0x02},
+ {WCD9XXX_A_BUCK_MODE_1, 0xff, 0xA1},
+ {WCD9XXX_A_NCP_EN, 0xff, 0xFF},
+ {WCD9XXX_A_BUCK_MODE_5, 0xff, 0x7B},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, 0xff, 0xE6},
+ {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0xC0},
+ {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0xC0},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++)
+ wcd9xxx_soc_update_bits_push(codec, lh,
+ reg_set_paon[i].reg,
+ reg_set_paon[i].mask,
+ reg_set_paon[i].val);
+ pr_debug("%s: PAs are prepared\n", __func__);
+
+ return 0;
+}
+
+static void wcd9xxx_restore_registers(struct wcd9xxx_mbhc *mbhc,
+ struct list_head *lh)
+{
+ struct wcd9xxx_register_save_node *node, *nodetmp;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ list_for_each_entry_safe(node, nodetmp, lh, lh) {
+ snd_soc_write(codec, node->reg, node->value);
+ list_del(&node->lh);
+ kfree(node);
+ }
+}
+
+static void wcd9xxx_unprepare_static_pa(struct wcd9xxx_mbhc *mbhc,
+ struct list_head *lh)
+{
+ wcd9xxx_restore_registers(mbhc, lh);
+}
+
+static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) *
+ WCD9XXX_WG_TIME_FACTOR_US;
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30,
+ enable ? 0x30 : 0x0);
+ /* Wait for wave gen time to avoid pop noise */
+ usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__,
+ enable ? "enabled" : "disabled", wg_time);
+ return 0;
+}
+
+static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr)
+{
+ int i;
+ int ret = 0;
+ s16 l[3], r[3];
+ s16 *z[] = {
+ &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
+ };
+ LIST_HEAD(lh);
+ LIST_HEAD(lhpa);
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int ramp_wait_us = 18 * 1000;
+ const int mux_wait_us = 25;
+ const int alphal = 364; /* 0.005555 * 65536 = 364.05 */
+ const int alphar = 364; /* 0.005555 * 65536 = 364.05 */
+ const int beta = 3855; /* 0.011765 * 5 * 65536 = 3855.15 */
+ const int rref = 11333; /* not scaled up */
+ const int shift = 16;
+ int64_t rl, rr = 0; /* milliohm */
+ const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
+ /* Phase 1 */
+ /* Set MBHC_MUX for HPHL without ical */
+ {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
+ /* Set MBHC_MUX for HPHR without ical */
+ {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
+ /* Set MBHC_MUX for HPHR with ical */
+ {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
+ /* Set MBHC_MUX for HPHL with ical */
+ {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
+
+ /* Phase 2 */
+ {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
+ /* Set MBHC_MUX for HPHR without ical and wait for 25us */
+ {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
+ };
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ /*
+ * Impedance detection is an intrusive function as it mutes RX paths,
+ * enable PAs and etc. Therefore codec drvier including ALSA
+ * shouldn't read and write hardware registers during detection.
+ */
+ mutex_lock(&codec->mutex);
+
+ WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+ wcd9xxx_resmgr_get_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+ WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+
+ pr_debug("%s: Setting impedance detection\n", __func__);
+ wcd9xxx_prepare_static_pa(mbhc, &lhpa);
+ wcd9xxx_enable_static_pa(mbhc, true);
+
+ /*
+ * save old value of registers and write the new value to restore old
+ * value back, WCD9XXX_A_CDC_PA_RAMP_B{1,2,3,4}_CTL registers don't
+ * need to be restored as those are solely used by impedance detection.
+ */
+#define __w(reg, mask, value) \
+ do { \
+ ret = wcd9xxx_soc_update_bits_push(codec, &lh, reg, mask, \
+ value); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+ /* Reset the PA Ramp */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1C);
+ /*
+ * Connect the PA Ramp to PA chain and release reset with keep it
+ * connected.
+ */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+ /* Program the PA Ramp to FS_48K, L shift 1 and sample num to 24 */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, 0x3 << 4 | 0x6);
+ /* 0x56 for 10mv. 0xC0 is for 50mv */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0xC0);
+ /* Enable MBHC MUX, Set MUX current to 37.5uA and ADC7 */
+ __w(WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0);
+ __w(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0);
+ __w(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xFF, 0x78);
+ __w(WCD9XXX_A_TX_7_MBHC_EN, 0xFF, 0x8C);
+ /* Change NSA and NAVG */
+ __w(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x4 << 4, 0x4 << 4);
+ __w(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xFF, 0x10);
+ /* Reset MBHC and set it up for STA */
+ __w(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x0A);
+ __w(WCD9XXX_A_CDC_MBHC_EN_CTL, 0xFF, 0x02);
+ __w(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x02);
+
+ /* Set HPH_MBHC for zdet */
+ __w(WCD9XXX_A_MBHC_HPH, 0xB3, 0x80);
+
+ pr_debug("%s: Performing impedance detection\n", __func__);
+ /* Phase 1 */
+ for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
+ __w(reg_set_mux[i].reg, reg_set_mux[i].mask,
+ reg_set_mux[i].val);
+ /* 25us is required after mux change to settle down */
+ usleep_range(mux_wait_us,
+ mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, false, false);
+ }
+
+ /* Phase 2 */
+ /* Start the PA ramp on HPH L and R */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x05);
+ /* Ramp generator takes ~17ms */
+ usleep_range(ramp_wait_us,
+ ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ /* Disable Ical */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+ /* Ramp generator takes ~17ms */
+ usleep_range(ramp_wait_us,
+ ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ for (; i < ARRAY_SIZE(reg_set_mux); i++) {
+ __w(reg_set_mux[i].reg, reg_set_mux[i].mask,
+ reg_set_mux[i].val);
+ /* 25us is required after mux change to settle down */
+ usleep_range(mux_wait_us,
+ mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, false, false);
+ }
+
+ /* Ramp HPH L & R back to Zero */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x0A);
+ /* Ramp generator takes ~17ms */
+ usleep_range(ramp_wait_us,
+ ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+#undef __w
+
+ /* Clean up starts */
+ /* Turn off PA ramp generator */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x0);
+ wcd9xxx_enable_static_pa(mbhc, false);
+ wcd9xxx_restore_registers(mbhc, &lh);
+ wcd9xxx_unprepare_static_pa(mbhc, &lhpa);
+
+ mutex_unlock(&codec->mutex);
+
+ WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+ wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+ WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+
+ rl = (int)(l[0] - l[1]) * 1000 / (l[0] - l[2]);
+ rl = rl * rref * alphal;
+ rl = rl >> shift;
+ rl = rl * beta;
+ rl = rl >> shift;
+ *zl = rl;
+
+ rr = (int)(r[0] - r[1]) * 1000 / (r[0] - r[2]);
+ rr = rr * rref * alphar;
+ rr = rr >> shift;
+ rr = rr * beta;
+ rr = rr >> shift;
+ *zr = rr;
+
+ pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d), rl: %lld\n",
+ __func__,
+ l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2],
+ rl);
+ pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d), rr: %lld\n",
+ __func__,
+ r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2],
+ rr);
+ pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
+ pr_debug("%s: Impedance detection completed\n", __func__);
+
+ return ret;
+}
+
+int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr)
+{
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ *zl = mbhc->zl;
+ *zr = mbhc->zr;
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ if (*zl && *zr)
+ return 0;
+ else
+ return -EINVAL;
+}
+
/*
* wcd9xxx_mbhc_init : initialize MBHC internal structures.
*
@@ -3656,8 +3938,7 @@
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
struct snd_soc_codec *codec,
int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
- int version,
- int rco_clk_rate)
+ const struct wcd9xxx_mbhc_cb *mbhc_cb, int rco_clk_rate)
{
int ret;
void *core;
@@ -3681,8 +3962,8 @@
mbhc->resmgr = resmgr;
mbhc->resmgr->mbhc = mbhc;
mbhc->micbias_enable_cb = micbias_enable_cb;
- mbhc->mbhc_version = version;
mbhc->rco_clk_rate = rco_clk_rate;
+ mbhc->mbhc_cb = mbhc_cb;
if (mbhc->headset_jack.jack == NULL) {
ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
@@ -3805,17 +4086,11 @@
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- wcd9xxx_free_irq(cdata, WCD9320_IRQ_MBHC_JACK_SWITCH, mbhc);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- wcd9xxx_free_irq(cdata, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
- break;
- default:
- pr_err("%s: irq free failed! Invalid MBHC version %d\n",
- __func__, mbhc->mbhc_version);
- }
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->free_irq)
+ mbhc->mbhc_cb->free_irq(mbhc);
+ else
+ wcd9xxx_free_irq(cdata, WCD9320_IRQ_MBHC_JACK_SWITCH,
+ mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, mbhc);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 71a62b2..1f6502f 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -16,8 +16,8 @@
#define WCD9XXX_CFILT_FAST_MODE 0x00
#define WCD9XXX_CFILT_SLOW_MODE 0x40
-#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x70
-#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x40
+#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x30
+#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x00
struct mbhc_micbias_regs {
u16 cfilt_val;
@@ -61,12 +61,6 @@
s16 v_inval_ins_high;
};
-enum wcd9xxx_mbhc_version {
- WCD9XXX_MBHC_VERSION_UNKNOWN = 0,
- WCD9XXX_MBHC_VERSION_TAIKO,
- WCD9XXX_MBHC_VERSION_TAPAN,
-};
-
enum wcd9xxx_mbhc_plug_type {
PLUG_TYPE_INVALID = -1,
PLUG_TYPE_NONE,
@@ -218,6 +212,23 @@
bool (*swap_gnd_mic) (struct snd_soc_codec *);
};
+struct wcd9xxx_cfilt_mode {
+ u8 reg_mode_val;
+ u8 cur_mode_val;
+};
+
+struct wcd9xxx_mbhc_cb {
+ void (*enable_mux_bias_block) (struct snd_soc_codec *);
+ void (*cfilt_fast_mode) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+ void (*codec_specific_cal) (struct snd_soc_codec *,
+ struct wcd9xxx_mbhc *);
+ int (*jack_detect_irq) (struct snd_soc_codec *);
+ struct wcd9xxx_cfilt_mode (*switch_cfilt_mode) (struct wcd9xxx_mbhc *,
+ bool);
+ void (*select_cfilt) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+ void (*free_irq) (struct wcd9xxx_mbhc *);
+};
+
struct wcd9xxx_mbhc {
bool polling_active;
/* Delayed work to report long button press */
@@ -225,6 +236,7 @@
int buttons_pressed;
enum wcd9xxx_mbhc_state mbhc_state;
struct wcd9xxx_mbhc_config *mbhc_cfg;
+ const struct wcd9xxx_mbhc_cb *mbhc_cb;
struct mbhc_internal_cal_data mbhc_data;
@@ -279,7 +291,8 @@
bool micbias_enable;
int (*micbias_enable_cb) (struct snd_soc_codec*, bool);
- enum wcd9xxx_mbhc_version mbhc_version;
+ /* impedance of hphl and hphr */
+ uint32_t zl, zr;
u32 rco_clk_rate;
@@ -349,10 +362,12 @@
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
struct snd_soc_codec *codec,
int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
- int version,
+ const struct wcd9xxx_mbhc_cb *mbhc_cb,
int rco_clk_rate);
void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
void *wcd9xxx_mbhc_cal_btn_det_mp(
const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
const enum wcd9xxx_mbhc_btn_det_mem mem);
+int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr);
#endif /* __WCD9XXX_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index 84f236e..be11e53 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -209,8 +209,8 @@
int old_clk_rco_users, old_clk_mclk_users;
pr_debug("%s: enter\n", __func__);
- WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ WCD9XXX_BG_CLK_LOCK(resmgr);
old_bg_audio_users = resmgr->bg_audio_users;
old_bg_mbhc_users = resmgr->bg_mbhc_users;
old_clk_rco_users = resmgr->clk_rco_users;
@@ -243,6 +243,7 @@
while (old_clk_rco_users--)
wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_RCO);
}
+ WCD9XXX_BG_CLK_UNLOCK(resmgr);
pr_debug("%s: leave\n", __func__);
}
diff --git a/sound/soc/msm/apq8074.c b/sound/soc/msm/apq8074.c
index 9a2f83b..cb101bd 100644
--- a/sound/soc/msm/apq8074.c
+++ b/sound/soc/msm/apq8074.c
@@ -1922,7 +1922,7 @@
{
.name = LPASS_BE_AUXPCM_RX,
.stream_name = "AUX PCM Playback",
- .cpu_dai_name = "msm-dai-q6.4106",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
@@ -1937,7 +1937,7 @@
{
.name = LPASS_BE_AUXPCM_TX,
.stream_name = "AUX PCM Capture",
- .cpu_dai_name = "msm-dai-q6.4107",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
diff --git a/sound/soc/msm/mdm9625.c b/sound/soc/msm/mdm9625.c
index fd21c38..ad8d85a 100644
--- a/sound/soc/msm/mdm9625.c
+++ b/sound/soc/msm/mdm9625.c
@@ -928,7 +928,7 @@
{
.name = LPASS_BE_AUXPCM_RX,
.stream_name = "AUX PCM Playback",
- .cpu_dai_name = "msm-dai-q6.4106",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
@@ -942,7 +942,7 @@
{
.name = LPASS_BE_AUXPCM_TX,
.stream_name = "AUX PCM Capture",
- .cpu_dai_name = "msm-dai-q6.4107",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 1b51595..a0ed887 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -67,7 +67,8 @@
.aif_name = "MM_UL1",
.rates = (SNDRV_PCM_RATE_8000_48000|
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 4,
.rate_min = 8000,
@@ -713,6 +714,33 @@
.ops = &msm_fe_dai_ops,
.name = "VOICE2_STUB",
},
+ {
+ .playback = {
+ .stream_name = "Multimedia9 Playback",
+ .aif_name = "MM_DL9",
+ .rates = (SNDRV_PCM_RATE_8000_192000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "Multimedia9 Capture",
+ .aif_name = "MM_UL9",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia9",
+ },
};
static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index d973c17..171db0a 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -1805,7 +1805,7 @@
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 5,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 192000,
},
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 9782b2d..d3ba3d5 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -36,12 +36,20 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
+/* It takes about 13ms for Class-D PAs to ramp-up */
+#define EXT_CLASS_D_EN_DELAY 13000
+#define EXT_CLASS_D_DIS_DELAY 3000
+#define EXT_CLASS_D_DELAY_DELTA 2000
+
#define WCD9XXX_MBHC_DEF_BUTTONS 8
#define WCD9XXX_MBHC_DEF_RLOADS 5
#define TAPAN_EXT_CLK_RATE 9600000
#define NUM_OF_AUXPCM_GPIOS 4
+#define LO_1_SPK_AMP 0x1
+#define LO_2_SPK_AMP 0x2
+
static int msm8226_auxpcm_rate = 8000;
static atomic_t auxpcm_rsc_ref;
static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"};
@@ -72,7 +80,7 @@
.gpio_irq = 0,
.gpio_level_insert = 0,
.detect_extn_cable = true,
- .micbias_enable_flags = 0,
+ .micbias_enable_flags = 1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET,
.insert_detect = true,
.swap_gnd_mic = NULL,
};
@@ -116,6 +124,7 @@
SLIM_4_TX_1 = 150, /* In-call musid delivery TX */
};
+static int msm8226_ext_spk_pamp;
static int msm_slim_0_rx_ch = 1;
static int msm_slim_0_tx_ch = 1;
@@ -125,6 +134,7 @@
static struct mutex cdc_mclk_mutex;
static struct clk *codec_clk;
static int clk_users;
+static int ext_spk_amp_gpio = -1;
static int vdd_spkr_gpio = -1;
static int msm_proxy_rx_ch = 2;
@@ -189,6 +199,99 @@
return 0;
}
+static void msm8226_ext_spk_power_amp_enable(u32 enable)
+{
+ if (enable) {
+ gpio_direction_output(ext_spk_amp_gpio, enable);
+ /* time takes enable the external power amplifier */
+ usleep_range(EXT_CLASS_D_EN_DELAY,
+ EXT_CLASS_D_EN_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ } else {
+ gpio_direction_output(ext_spk_amp_gpio, enable);
+ /* time takes disable the external power amplifier */
+ usleep_range(EXT_CLASS_D_DIS_DELAY,
+ EXT_CLASS_D_DIS_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ }
+
+ pr_debug("%s: %s external speaker PAs.\n", __func__,
+ enable ? "Enable" : "Disable");
+}
+
+static void msm8226_ext_spk_power_amp_on(u32 spk)
+{
+ if (gpio_is_valid(ext_spk_amp_gpio)) {
+ if (spk & (LO_1_SPK_AMP | LO_2_SPK_AMP)) {
+ pr_debug("%s:Enable left and right speakers case spk = 0x%x\n",
+ __func__, spk);
+
+ msm8226_ext_spk_pamp |= spk;
+
+ if ((msm8226_ext_spk_pamp & LO_1_SPK_AMP) &&
+ (msm8226_ext_spk_pamp & LO_2_SPK_AMP))
+ if (ext_spk_amp_gpio >= 0) {
+ pr_debug("%s enable power", __func__);
+ msm8226_ext_spk_power_amp_enable(1);
+ }
+ } else {
+ pr_err("%s: Invalid external speaker ampl. spk = 0x%x\n",
+ __func__, spk);
+ }
+ }
+}
+
+static void msm8226_ext_spk_power_amp_off(u32 spk)
+{
+ if (gpio_is_valid(ext_spk_amp_gpio)) {
+ if (spk & (LO_1_SPK_AMP | LO_2_SPK_AMP)) {
+ pr_debug("%s Disable left and right speakers case spk = 0x%08x",
+ __func__, spk);
+
+ msm8226_ext_spk_pamp &= ~spk;
+
+ if (!msm8226_ext_spk_pamp) {
+ if (ext_spk_amp_gpio >= 0) {
+ pr_debug("%s disable power", __func__);
+ msm8226_ext_spk_power_amp_enable(0);
+ }
+ msm8226_ext_spk_pamp = 0;
+ }
+ } else {
+ pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ }
+ }
+}
+
+static int msm8226_ext_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ pr_debug("%s()\n", __func__);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (!strncmp(w->name, "Lineout_1 amp", 14))
+ msm8226_ext_spk_power_amp_on(LO_1_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_2 amp", 14))
+ msm8226_ext_spk_power_amp_on(LO_2_SPK_AMP);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ } else {
+ if (!strncmp(w->name, "Lineout_1 amp", 14))
+ msm8226_ext_spk_power_amp_off(LO_1_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_2 amp", 14))
+ msm8226_ext_spk_power_amp_off(LO_2_SPK_AMP);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int msm8226_vdd_spkr_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -230,6 +333,9 @@
SND_SOC_DAPM_MIC("Digital Mic5", NULL),
SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+ SND_SOC_DAPM_SPK("Lineout_1 amp", msm8226_ext_spkramp_event),
+ SND_SOC_DAPM_SPK("Lineout_2 amp", msm8226_ext_spkramp_event),
+
SND_SOC_DAPM_SUPPLY("EXT_VDD_SPKR", SND_SOC_NOPM, 0, 0,
msm8226_vdd_spkr_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
@@ -1025,6 +1131,21 @@
.ignore_pmdown_time = 1,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
},
+ {
+ .name = "MSM8226 Media9",
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* This dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
@@ -1124,7 +1245,7 @@
{
.name = LPASS_BE_AUXPCM_RX,
.stream_name = "AUX PCM Playback",
- .cpu_dai_name = "msm-dai-q6.4106",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
@@ -1139,7 +1260,7 @@
{
.name = LPASS_BE_AUXPCM_TX,
.stream_name = "AUX PCM Capture",
- .cpu_dai_name = "msm-dai-q6.4107",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
@@ -1301,6 +1422,19 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
},
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
};
struct snd_soc_card snd_soc_card_msm8226 = {
@@ -1409,7 +1543,7 @@
pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,cdc-us-euro-gpios", 0);
if (pdata->us_euro_gpio < 0) {
- dev_info(&pdev->dev,
+ dev_dbg(&pdev->dev,
"property %s in node %s not found %d\n",
"qcom,cdc-us-euro-gpios", pdev->dev.of_node->full_name,
pdata->us_euro_gpio);
@@ -1499,7 +1633,7 @@
vdd_spkr_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,cdc-vdd-spkr-gpios", 0);
if (vdd_spkr_gpio < 0) {
- dev_err(&pdev->dev,
+ dev_dbg(&pdev->dev,
"Looking up %s property in node %s failed %d\n",
"qcom, cdc-vdd-spkr-gpios",
pdev->dev.of_node->full_name, vdd_spkr_gpio);
@@ -1514,19 +1648,38 @@
}
}
+ ext_spk_amp_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,cdc-lineout-spkr-gpios", 0);
+ if (ext_spk_amp_gpio < 0) {
+ dev_err(&pdev->dev,
+ "Looking up %s property in node %s failed %d\n",
+ "qcom, cdc-lineout-spkr-gpios",
+ pdev->dev.of_node->full_name, ext_spk_amp_gpio);
+ } else {
+ ret = gpio_request(ext_spk_amp_gpio,
+ "TAPAN_CODEC_LINEOUT_SPKR");
+ if (ret) {
+ /* GPIO to enable EXT AMP exists, but failed request */
+ dev_err(card->dev,
+ "%s: Failed to request tapan amp spkr gpio %d\n",
+ __func__, ext_spk_amp_gpio);
+ goto err_vdd_spkr;
+ }
+ }
+
mbhc_cfg.gpio_level_insert = of_property_read_bool(pdev->dev.of_node,
"qcom,headset-jack-type-NO");
msm8226_setup_hs_jack(pdev, pdata);
ret = msm8226_prepare_codec_mclk(card);
if (ret)
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
}
mutex_init(&cdc_mclk_mutex);
@@ -1536,7 +1689,7 @@
dev_err(&pdev->dev, "Looking up %s property in node %s failed",
"qcom,prim-auxpcm-gpio-set",
pdev->dev.of_node->full_name);
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
}
if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-prim")) {
lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4);
@@ -1546,16 +1699,22 @@
dev_err(&pdev->dev, "Invalid value %s for AUXPCM GPIO set\n",
auxpcm_pri_gpio_set);
ret = -EINVAL;
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
}
if (lpaif_pri_muxsel_virt_addr == NULL) {
pr_err("%s Pri muxsel virt addr is null\n", __func__);
ret = -EINVAL;
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
}
return 0;
+err_lineout_spkr:
+ if (ext_spk_amp_gpio >= 0) {
+ gpio_free(ext_spk_amp_gpio);
+ ext_spk_amp_gpio = -1;
+ }
+
err_vdd_spkr:
if (vdd_spkr_gpio >= 0) {
gpio_free(vdd_spkr_gpio);
@@ -1579,11 +1738,15 @@
struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
gpio_free(pdata->mclk_gpio);
- gpio_free(vdd_spkr_gpio);
+ if (vdd_spkr_gpio >= 0)
+ gpio_free(vdd_spkr_gpio);
+ if (ext_spk_amp_gpio >= 0)
+ gpio_free(ext_spk_amp_gpio);
if (pdata->us_euro_gpio > 0)
gpio_free(pdata->us_euro_gpio);
vdd_spkr_gpio = -1;
+ ext_spk_amp_gpio = -1;
snd_soc_unregister_card(card);
return 0;
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index a39a18b..0b7e7f2 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -208,6 +208,7 @@
static int msm_hdmi_rx_ch = 2;
static int slim0_rx_sample_rate = SAMPLING_RATE_48KHZ;
static int msm_proxy_rx_ch = 2;
+static int hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
static struct mutex cdc_mclk_mutex;
static struct clk *codec_clk;
@@ -481,13 +482,12 @@
pr_debug("%s Left and right speakers case spk = 0x%08x",
__func__, spk);
-
+ msm8974_ext_spk_pamp &= ~spk;
if (!msm8974_ext_spk_pamp) {
if (ext_spk_amp_gpio >= 0 &&
msm8974_liquid_dock_dev != NULL &&
msm8974_liquid_dock_dev->dock_plug_det == 0)
msm8974_liquid_ext_spk_power_amp_enable(0);
- msm8974_ext_spk_pamp = 0;
}
} else {
@@ -711,7 +711,8 @@
static const char *const spk_function[] = {"Off", "On"};
static const char *const slim0_rx_ch_text[] = {"One", "Two"};
static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four",
- "Five"};
+ "Five", "Six", "Seven",
+ "Eight"};
static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
"Six", "Seven", "Eight"};
static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE"};
@@ -720,6 +721,8 @@
static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight"};
+static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192"};
static const char *const btsco_rate_text[] = {"8000", "16000"};
static const struct soc_enum msm_btsco_enum[] = {
SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
@@ -940,6 +943,57 @@
return 1;
}
+static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (hdmi_rx_sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+ hdmi_rx_sample_rate);
+
+ return 0;
+}
+
+static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ucontrol value = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ hdmi_rx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ hdmi_rx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+ }
+
+ pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+ hdmi_rx_sample_rate);
+
+ return 0;
+}
+
static int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -1055,7 +1109,7 @@
hdmi_rx_bit_format);
if (channels->max < 2)
channels->min = channels->max = 2;
- rate->min = rate->max = 48000;
+ rate->min = rate->max = hdmi_rx_sample_rate;
channels->min = channels->max = msm_hdmi_rx_ch;
return 0;
@@ -1319,11 +1373,12 @@
static const struct soc_enum msm_snd_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
- SOC_ENUM_SINGLE_EXT(5, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(8, slim0_tx_ch_text),
SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
SOC_ENUM_SINGLE_EXT(2, rx_bit_format_text),
SOC_ENUM_SINGLE_EXT(3, slim0_rx_sample_rate_text),
SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(3, hdmi_rx_sample_rate_text),
};
static const struct snd_kcontrol_new msm_snd_controls[] = {
@@ -1347,6 +1402,8 @@
msm_proxy_rx_ch_get, msm_proxy_rx_ch_put),
SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
msm_btsco_rate_get, msm_btsco_rate_put),
+ SOC_ENUM_EXT("HDMI_RX SampleRate", msm_snd_enum[7],
+ hdmi_rx_sample_rate_get, hdmi_rx_sample_rate_put),
};
static bool msm8974_swap_gnd_mic(struct snd_soc_codec *codec)
@@ -2229,7 +2286,7 @@
{
.name = LPASS_BE_AUXPCM_RX,
.stream_name = "AUX PCM Playback",
- .cpu_dai_name = "msm-dai-q6.4106",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
@@ -2244,7 +2301,7 @@
{
.name = LPASS_BE_AUXPCM_TX,
.stream_name = "AUX PCM Capture",
- .cpu_dai_name = "msm-dai-q6.4107",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
@@ -2258,7 +2315,7 @@
{
.name = LPASS_BE_SEC_AUXPCM_RX,
.stream_name = "Sec AUX PCM Playback",
- .cpu_dai_name = "msm-dai-q6.4108",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
@@ -2273,7 +2330,7 @@
{
.name = LPASS_BE_SEC_AUXPCM_TX,
.stream_name = "Sec AUX PCM Capture",
- .cpu_dai_name = "msm-dai-q6.4109",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
diff --git a/sound/soc/msm/msm8x10.c b/sound/soc/msm/msm8x10.c
index a076246..4d9632c 100644
--- a/sound/soc/msm/msm8x10.c
+++ b/sound/soc/msm/msm8x10.c
@@ -34,11 +34,19 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
+/* It takes about 13ms for Class-D PAs to ramp-up */
+#define EXT_CLASS_D_EN_DELAY 13000
+#define EXT_CLASS_D_DIS_DELAY 3000
+#define EXT_CLASS_D_DELAY_DELTA 2000
+
+
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
static int msm_btsco_ch = 1;
static int msm_proxy_rx_ch = 2;
static struct snd_soc_jack hs_jack;
+static struct platform_device *spdev;
+static int ext_spk_amp_gpio = -1;
/*
@@ -86,15 +94,69 @@
static int msm8x10_mclk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
+static int msm_ext_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+static void msm8x10_enable_ext_spk_power_amp(u32 on);
static const struct snd_soc_dapm_widget msm8x10_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
msm8x10_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SPK("Lineout amp", msm_ext_spkramp_event),
SND_SOC_DAPM_MIC("Handset Mic", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
+static int msm8x10_ext_spk_power_amp_init(void)
+{
+ int ret = 0;
+
+ ext_spk_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,ext-spk-amp-gpio", 0);
+ if (ext_spk_amp_gpio >= 0) {
+ ret = gpio_request(ext_spk_amp_gpio, "ext_spk_amp_gpio");
+ if (ret) {
+ pr_err("%s: gpio_request failed for ext_spk_amp_gpio.\n",
+ __func__);
+ return -EINVAL;
+ }
+ gpio_direction_output(ext_spk_amp_gpio, 0);
+ }
+ return 0;
+}
+
+static int msm_ext_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ pr_debug("%s()\n", __func__);
+
+ if (ext_spk_amp_gpio >= 0) {
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ msm8x10_enable_ext_spk_power_amp(1);
+ else
+ msm8x10_enable_ext_spk_power_amp(0);
+ }
+ return 0;
+
+}
+
+static void msm8x10_enable_ext_spk_power_amp(u32 on)
+{
+ if (on) {
+ gpio_direction_output(ext_spk_amp_gpio, on);
+ /*time takes enable the external power amplifier*/
+ usleep_range(EXT_CLASS_D_EN_DELAY,
+ EXT_CLASS_D_EN_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ } else {
+ gpio_direction_output(ext_spk_amp_gpio, on);
+ /*time takes disable the external power amplifier*/
+ usleep_range(EXT_CLASS_D_DIS_DELAY,
+ EXT_CLASS_D_DIS_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ }
+
+ pr_debug("%s: %s external speaker PAs.\n", __func__,
+ on ? "Enable" : "Disable");
+}
static int msm_config_mclk(u16 port_id, struct afe_digital_clk_cfg *cfg)
{
@@ -265,10 +327,11 @@
int ret = 0;
pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev));
-
+ msm8x10_ext_spk_power_amp_init();
snd_soc_dapm_new_controls(dapm, msm8x10_dapm_widgets,
ARRAY_SIZE(msm8x10_dapm_widgets));
+ snd_soc_dapm_enable_pin(dapm, "Lineout amp");
snd_soc_dapm_sync(dapm);
ret = snd_soc_jack_new(codec, "Headset Jack",
@@ -686,11 +749,16 @@
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret)
+ goto err;
+
ret = snd_soc_of_parse_audio_routing(card,
"qcom,audio-routing");
if (ret)
goto err;
+ spdev = pdev;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
@@ -708,6 +776,8 @@
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
+ if (gpio_is_valid(ext_spk_amp_gpio))
+ gpio_free(ext_spk_amp_gpio);
snd_soc_unregister_card(card);
mutex_destroy(&cdc_mclk_mutex);
return 0;
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
index 60f4669..094c58b 100644
--- a/sound/soc/msm/qdsp6/q6voice.c
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -2356,10 +2356,11 @@
}
if (common.ec_ref_ext == true) {
ret = voice_send_set_device_cmd_v2(v);
- if (ret < 0)
+ if (ret < 0) {
pr_err("%s: set device V2 failed rc =%x\n",
__func__, ret);
goto fail;
+ }
}
/* send cvs cal */
ret = voice_send_cvs_map_memory_cmd(v);
@@ -3354,10 +3355,11 @@
if (v->voc_state == VOC_CHANGE) {
if (common.ec_ref_ext == true) {
ret = voice_send_set_device_cmd_v2(v);
- if (ret < 0)
+ if (ret < 0) {
pr_err("%s: set device V2 failed\n"
"rc =%x\n", __func__, ret);
goto fail;
+ }
} else {
ret = voice_send_set_device_cmd(v);
if (ret < 0) {
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.c b/sound/soc/msm/qdsp6v2/audio_acdb.c
index 3b6a415..2836b87 100644
--- a/sound/soc/msm/qdsp6v2/audio_acdb.c
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.c
@@ -20,6 +20,7 @@
#include <linux/mm.h>
#include <linux/msm_audio_ion.h>
#include "audio_acdb.h"
+#include "q6voice.h"
#define MAX_NETWORKS 15
@@ -153,15 +154,18 @@
atomic_set(&acdb_data.valid_asm_custom_top, 1);
}
-void get_adm_custom_topology(struct acdb_cal_block *cal_block)
+int get_adm_custom_topology(struct acdb_cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s\n", __func__);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
+ /* Only return allow one access after memory registered */
if (atomic_read(&acdb_data.valid_adm_custom_top) == 0) {
cal_block->cal_size = 0;
goto done;
@@ -175,17 +179,19 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.adm_custom_topology.cal_kvaddr);
done:
- return;
+ return result;
}
-void store_adm_custom_topology(struct cal_block *cal_block)
+int store_adm_custom_topology(struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s,\n", __func__);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
+ result = -EINVAL;
goto done;
}
@@ -197,18 +203,21 @@
cal_block->cal_offset +
atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_asm_custom_topology(struct acdb_cal_block *cal_block)
+int get_asm_custom_topology(struct acdb_cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
+ /* Only return allow one access after memory registered */
if (atomic_read(&acdb_data.valid_asm_custom_top) == 0) {
cal_block->cal_size = 0;
goto done;
@@ -222,17 +231,19 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.asm_custom_topology.cal_kvaddr);
done:
- return;
+ return result;
}
-void store_asm_custom_topology(struct cal_block *cal_block)
+int store_asm_custom_topology(struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s,\n", __func__);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
+ result = -EINVAL;
goto done;
}
@@ -244,24 +255,37 @@
cal_block->cal_offset +
atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_voice_cal_allocation(struct acdb_cal_block *cal_block)
+int get_voice_cal_allocation(struct acdb_cal_block *cal_block)
{
+ int result = 0;
+ pr_debug("%s,\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
cal_block->cal_size = ACDB_TOTAL_VOICE_ALLOCATION;
cal_block->cal_paddr =
atomic_read(&acdb_data.vocproc_cal.cal_paddr);
cal_block->cal_kvaddr =
atomic_read(&acdb_data.vocproc_cal.cal_kvaddr);
+done:
+ return result;
}
-void get_aanc_cal(struct acdb_cal_block *cal_block)
+int get_aanc_cal(struct acdb_cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
@@ -272,18 +296,20 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.aanc_cal.cal_kvaddr);
done:
- return;
+ return result;
}
-void store_aanc_cal(struct cal_block *cal_block)
+int store_aanc_cal(struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s,\n", __func__);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
- goto done;
+ result = -EINVAL;
+ goto done;
}
atomic_set(&acdb_data.aanc_cal.cal_size,
@@ -293,15 +319,17 @@
atomic_set(&acdb_data.aanc_cal.cal_kvaddr,
cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_lsm_cal(struct acdb_cal_block *cal_block)
+int get_lsm_cal(struct acdb_cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
@@ -312,17 +340,19 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.lsm_cal.cal_kvaddr);
done:
- return;
+ return result;
}
-void store_lsm_cal(struct cal_block *cal_block)
+int store_lsm_cal(struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s,\n", __func__);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
+ result = -EINVAL;
goto done;
}
@@ -333,15 +363,17 @@
atomic_set(&acdb_data.lsm_cal.cal_kvaddr,
cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_anc_cal(struct acdb_cal_block *cal_block)
+int get_anc_cal(struct acdb_cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
@@ -352,17 +384,19 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.anc_cal.cal_kvaddr);
done:
- return;
+ return result;
}
-void store_anc_cal(struct cal_block *cal_block)
+int store_anc_cal(struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s,\n", __func__);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
+ result = -EINVAL;
goto done;
}
@@ -373,22 +407,25 @@
atomic_set(&acdb_data.anc_cal.cal_kvaddr,
cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void store_afe_cal(int32_t path, struct cal_block *cal_block)
+int store_afe_cal(int32_t path, struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s, path = %d\n", __func__, path);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
+ result = -EINVAL;
goto done;
}
if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) {
pr_err("ACDB=> Bad path sent to %s, path: %d\n",
__func__, path);
+ result = -EINVAL;
goto done;
}
@@ -399,20 +436,23 @@
atomic_set(&acdb_data.afe_cal[path].cal_kvaddr,
cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block)
+int get_afe_cal(int32_t path, struct acdb_cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s, path = %d\n", __func__, path);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) {
pr_err("ACDB=> Bad path sent to %s, path: %d\n",
__func__, path);
+ result = -EINVAL;
goto done;
}
@@ -423,22 +463,25 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.afe_cal[path].cal_kvaddr);
done:
- return;
+ return result;
}
-void store_audproc_cal(int32_t path, struct cal_block *cal_block)
+int store_audproc_cal(int32_t path, struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s, path = %d\n", __func__, path);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
+ result = -EINVAL;
goto done;
}
if (path >= MAX_AUDPROC_TYPES) {
pr_err("ACDB=> Bad path sent to %s, path: %d\n",
__func__, path);
+ result = -EINVAL;
goto done;
}
@@ -449,20 +492,23 @@
atomic_set(&acdb_data.audproc_cal[path].cal_kvaddr,
cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block)
+int get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s, path = %d\n", __func__, path);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
if (path >= MAX_AUDPROC_TYPES) {
pr_err("ACDB=> Bad path sent to %s, path: %d\n",
__func__, path);
+ result = -EINVAL;
goto done;
}
@@ -473,22 +519,25 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.audproc_cal[path].cal_kvaddr);
done:
- return;
+ return result;
}
-void store_audstrm_cal(int32_t path, struct cal_block *cal_block)
+int store_audstrm_cal(int32_t path, struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s, path = %d\n", __func__, path);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
+ result = -EINVAL;
goto done;
}
if (path >= MAX_AUDPROC_TYPES) {
pr_err("ACDB=> Bad path sent to %s, path: %d\n",
__func__, path);
+ result = -EINVAL;
goto done;
}
@@ -499,20 +548,23 @@
atomic_set(&acdb_data.audstrm_cal[path].cal_kvaddr,
cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block)
+int get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s, path = %d\n", __func__, path);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
if (path >= MAX_AUDPROC_TYPES) {
pr_err("ACDB=> Bad path sent to %s, path: %d\n",
__func__, path);
+ result = -EINVAL;
goto done;
}
@@ -523,22 +575,25 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.audstrm_cal[path].cal_kvaddr);
done:
- return;
+ return result;
}
-void store_audvol_cal(int32_t path, struct cal_block *cal_block)
+int store_audvol_cal(int32_t path, struct cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s, path = %d\n", __func__, path);
if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
pr_err("%s: offset %d is > mem_len %ld\n",
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
+ result = -EINVAL;
goto done;
}
if (path >= MAX_AUDPROC_TYPES) {
pr_err("ACDB=> Bad path sent to %s, path: %d\n",
__func__, path);
+ result = -EINVAL;
goto done;
}
@@ -549,20 +604,23 @@
atomic_set(&acdb_data.audvol_cal[path].cal_kvaddr,
cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block)
+int get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block)
{
+ int result = 0;
pr_debug("%s, path = %d\n", __func__, path);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
if (path >= MAX_AUDPROC_TYPES || path < 0) {
pr_err("ACDB=> Bad path sent to %s, path: %d\n",
__func__, path);
+ result = -EINVAL;
goto done;
}
@@ -573,15 +631,19 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.audvol_cal[path].cal_kvaddr);
done:
- return;
+ return result;
}
-void store_voice_col_data(uint32_t vocproc_type, uint32_t cal_size,
+int store_voice_col_data(uint32_t vocproc_type, uint32_t cal_size,
uint32_t *cal_block)
{
+ int result = 0;
+ pr_debug("%s,\n", __func__);
+
if (cal_size > MAX_COL_SIZE) {
pr_err("%s: col size is to big %d\n", __func__,
cal_size);
+ result = -EINVAL;
goto done;
}
if (copy_from_user(acdb_data.col_data[vocproc_type],
@@ -589,19 +651,24 @@
cal_size)) {
pr_err("%s: fail to copy col size %d\n",
__func__, cal_size);
+ result = -EINVAL;
goto done;
}
atomic_set(&acdb_data.vocproc_col_cal[vocproc_type].cal_size,
cal_size);
done:
- return;
+ return result;
}
-void get_voice_col_data(uint32_t vocproc_type,
+int get_voice_col_data(uint32_t vocproc_type,
struct acdb_cal_block *cal_block)
{
+ int result = 0;
+ pr_debug("%s,\n", __func__);
+
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
@@ -612,12 +679,13 @@
cal_block->cal_kvaddr = atomic_read(&acdb_data.
vocproc_col_cal[vocproc_type].cal_kvaddr);
done:
- return;
+ return result;
}
-void store_vocproc_dev_cfg_cal(struct cal_block *cal_block)
+int store_vocproc_dev_cfg_cal(struct cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block->cal_offset >
@@ -626,6 +694,7 @@
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
atomic_set(&acdb_data.vocproc_dev_cal.cal_size, 0);
+ result = -EINVAL;
goto done;
}
@@ -639,12 +708,19 @@
atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block)
+int get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
+ goto done;
+ }
cal_block->cal_size =
atomic_read(&acdb_data.vocproc_dev_cal.cal_size);
@@ -652,13 +728,16 @@
atomic_read(&acdb_data.vocproc_dev_cal.cal_paddr);
cal_block->cal_kvaddr =
atomic_read(&acdb_data.vocproc_dev_cal.cal_kvaddr);
+done:
+ return result;
}
-void store_vocproc_cal(struct cal_block *cal_block)
+int store_vocproc_cal(struct cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block->cal_offset >
atomic64_read(&acdb_data.mem_len)) {
@@ -666,6 +745,7 @@
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
atomic_set(&acdb_data.vocproc_cal.cal_size, 0);
+ result = -EINVAL;
goto done;
}
@@ -679,15 +759,17 @@
atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_vocproc_cal(struct acdb_cal_block *cal_block)
+int get_vocproc_cal(struct acdb_cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
@@ -698,12 +780,13 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.vocproc_cal.cal_kvaddr);
done:
- return;
+ return result;
}
-void store_vocstrm_cal(struct cal_block *cal_block)
+int store_vocstrm_cal(struct cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block->cal_offset >
atomic64_read(&acdb_data.mem_len)) {
@@ -711,6 +794,7 @@
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
atomic_set(&acdb_data.vocstrm_cal.cal_size, 0);
+ result = -EINVAL;
goto done;
}
@@ -724,15 +808,17 @@
atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_vocstrm_cal(struct acdb_cal_block *cal_block)
+int get_vocstrm_cal(struct acdb_cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
@@ -743,12 +829,13 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.vocstrm_cal.cal_kvaddr);
done:
- return;
+ return result;
}
-void store_vocvol_cal(struct cal_block *cal_block)
+int store_vocvol_cal(struct cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block->cal_offset >
atomic64_read(&acdb_data.mem_len)) {
@@ -756,6 +843,7 @@
__func__, cal_block->cal_offset,
(long)atomic64_read(&acdb_data.mem_len));
atomic_set(&acdb_data.vocvol_cal.cal_size, 0);
+ result = -EINVAL;
goto done;
}
@@ -769,15 +857,17 @@
atomic64_read(&acdb_data.kvaddr));
done:
- return;
+ return result;
}
-void get_vocvol_cal(struct acdb_cal_block *cal_block)
+int get_vocvol_cal(struct acdb_cal_block *cal_block)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_block == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
@@ -788,47 +878,60 @@
cal_block->cal_kvaddr =
atomic_read(&acdb_data.vocvol_cal.cal_kvaddr);
done:
- return;
+ return result;
}
void store_sidetone_cal(struct sidetone_cal *cal_data)
{
- pr_debug("%s\n", __func__);
+ pr_debug("%s,\n", __func__);
atomic_set(&acdb_data.sidetone_cal.enable, cal_data->enable);
atomic_set(&acdb_data.sidetone_cal.gain, cal_data->gain);
}
-
-void get_sidetone_cal(struct sidetone_cal *cal_data)
+int get_sidetone_cal(struct sidetone_cal *cal_data)
{
- pr_debug("%s\n", __func__);
+ int result = 0;
+ pr_debug("%s,\n", __func__);
if (cal_data == NULL) {
pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
goto done;
}
cal_data->enable = atomic_read(&acdb_data.sidetone_cal.enable);
cal_data->gain = atomic_read(&acdb_data.sidetone_cal.gain);
done:
- return;
+ return result;
}
-void get_spk_protection_cfg(struct msm_spk_prot_cfg *prot_cfg)
+
+int get_spk_protection_cfg(struct msm_spk_prot_cfg *prot_cfg)
{
+ int result = 0;
+ pr_debug("%s,\n", __func__);
+
mutex_lock(&acdb_data.acdb_mutex);
if (prot_cfg) {
prot_cfg->mode = acdb_data.spk_prot_cfg.mode;
prot_cfg->r0 = acdb_data.spk_prot_cfg.r0;
prot_cfg->t0 = acdb_data.spk_prot_cfg.t0;
- } else
+ } else {
pr_err("%s prot_cfg is NULL\n", __func__);
+ result = -EINVAL;
+ }
mutex_unlock(&acdb_data.acdb_mutex);
+
+ return result;
}
-static void get_spk_protection_status(struct msm_spk_prot_status *status)
+
+static int get_spk_protection_status(struct msm_spk_prot_status *status)
{
+ int result = 0;
+ struct afe_spkr_prot_get_vi_calib calib_resp;
+ pr_debug("%s,\n", __func__);
+
/*Call AFE function here to query the status*/
- struct afe_spkr_prot_get_vi_calib calib_resp;
if (status) {
status->status = -EINVAL;
if (!afe_spk_prot_get_calib_data(&calib_resp)) {
@@ -839,8 +942,42 @@
status->r0 = calib_resp.res_cfg.r0_cali_q24;
}
}
- } else
+ } else {
pr_err("%s invalid params\n", __func__);
+ result = -EINVAL;
+ }
+
+ return result;
+}
+
+static int register_vocvol_table(void)
+{
+ int result = 0;
+ pr_debug("%s\n", __func__);
+
+ result = voc_register_vocproc_vol_table();
+ if (result < 0) {
+ pr_err("%s: Register vocproc vol failed!\n", __func__);
+ goto done;
+ }
+
+done:
+ return result;
+}
+
+static int deregister_vocvol_table(void)
+{
+ int result = 0;
+ pr_debug("%s\n", __func__);
+
+ result = voc_deregister_vocproc_vol_table();
+ if (result < 0) {
+ pr_err("%s: Deregister vocproc vol failed!\n", __func__);
+ goto done;
+ }
+
+done:
+ return result;
}
static int acdb_open(struct inode *inode, struct file *f)
@@ -1033,10 +1170,16 @@
}
if (copy_to_user((void *)arg, &prot_status,
sizeof(prot_status))) {
- pr_err("%s Failed to update prot_status\n", __func__);
+ pr_err("%s: Failed to update prot_status\n", __func__);
}
mutex_unlock(&acdb_data.acdb_mutex);
goto done;
+ case AUDIO_REGISTER_VOCPROC_VOL_TABLE:
+ result = register_vocvol_table();
+ goto done;
+ case AUDIO_DEREGISTER_VOCPROC_VOL_TABLE:
+ result = deregister_vocvol_table();
+ goto done;
}
if (copy_from_user(&size, (void *) arg, sizeof(size))) {
@@ -1054,13 +1197,16 @@
switch (cmd) {
case AUDIO_SET_VOCPROC_COL_CAL:
- store_voice_col_data(VOCPROC_CAL, size, (uint32_t *)arg);
+ result = store_voice_col_data(VOCPROC_CAL,
+ size, (uint32_t *)arg);
goto done;
case AUDIO_SET_VOCSTRM_COL_CAL:
- store_voice_col_data(VOCSTRM_CAL, size, (uint32_t *)arg);
+ result = store_voice_col_data(VOCSTRM_CAL,
+ size, (uint32_t *)arg);
goto done;
case AUDIO_SET_VOCVOL_COL_CAL:
- store_voice_col_data(VOCVOL_CAL, size, (uint32_t *)arg);
+ result = store_voice_col_data(VOCVOL_CAL,
+ size, (uint32_t *)arg);
goto done;
}
@@ -1083,61 +1229,63 @@
switch (cmd) {
case AUDIO_SET_AUDPROC_TX_CAL:
- store_audproc_cal(TX_CAL, (struct cal_block *)data);
+ result = store_audproc_cal(TX_CAL, (struct cal_block *)data);
goto done;
case AUDIO_SET_AUDPROC_RX_CAL:
- store_audproc_cal(RX_CAL, (struct cal_block *)data);
+ result = store_audproc_cal(RX_CAL, (struct cal_block *)data);
goto done;
case AUDIO_SET_AUDPROC_TX_STREAM_CAL:
- store_audstrm_cal(TX_CAL, (struct cal_block *)data);
+ result = store_audstrm_cal(TX_CAL, (struct cal_block *)data);
goto done;
case AUDIO_SET_AUDPROC_RX_STREAM_CAL:
- store_audstrm_cal(RX_CAL, (struct cal_block *)data);
+ result = store_audstrm_cal(RX_CAL, (struct cal_block *)data);
goto done;
case AUDIO_SET_AUDPROC_TX_VOL_CAL:
- store_audvol_cal(TX_CAL, (struct cal_block *)data);
+ result = store_audvol_cal(TX_CAL, (struct cal_block *)data);
goto done;
case AUDIO_SET_AUDPROC_RX_VOL_CAL:
- store_audvol_cal(RX_CAL, (struct cal_block *)data);
+ result = store_audvol_cal(RX_CAL, (struct cal_block *)data);
goto done;
case AUDIO_SET_AFE_TX_CAL:
- store_afe_cal(TX_CAL, (struct cal_block *)data);
+ result = store_afe_cal(TX_CAL, (struct cal_block *)data);
goto done;
case AUDIO_SET_AFE_RX_CAL:
- store_afe_cal(RX_CAL, (struct cal_block *)data);
+ result = store_afe_cal(RX_CAL, (struct cal_block *)data);
goto done;
case AUDIO_SET_VOCPROC_CAL:
- store_vocproc_cal((struct cal_block *)data);
+ result = store_vocproc_cal((struct cal_block *)data);
goto done;
case AUDIO_SET_VOCPROC_STREAM_CAL:
- store_vocstrm_cal((struct cal_block *)data);
+ result = store_vocstrm_cal((struct cal_block *)data);
goto done;
case AUDIO_SET_VOCPROC_VOL_CAL:
- store_vocvol_cal((struct cal_block *)data);
+ result = store_vocvol_cal((struct cal_block *)data);
goto done;
case AUDIO_SET_VOCPROC_DEV_CFG_CAL:
- store_vocproc_dev_cfg_cal((struct cal_block *)data);
+ result = store_vocproc_dev_cfg_cal((struct cal_block *)data);
goto done;
case AUDIO_SET_SIDETONE_CAL:
store_sidetone_cal((struct sidetone_cal *)data);
goto done;
case AUDIO_SET_ANC_CAL:
- store_anc_cal((struct cal_block *)data);
+ result = store_anc_cal((struct cal_block *)data);
goto done;
case AUDIO_SET_LSM_CAL:
- store_lsm_cal((struct cal_block *)data);
+ result = store_lsm_cal((struct cal_block *)data);
goto done;
case AUDIO_SET_ADM_CUSTOM_TOPOLOGY:
- store_adm_custom_topology((struct cal_block *)data);
+ result = store_adm_custom_topology((struct cal_block *)data);
goto done;
case AUDIO_SET_ASM_CUSTOM_TOPOLOGY:
- store_asm_custom_topology((struct cal_block *)data);
+ result = store_asm_custom_topology((struct cal_block *)data);
goto done;
case AUDIO_SET_AANC_CAL:
- store_aanc_cal((struct cal_block *)data);
+ result = store_aanc_cal((struct cal_block *)data);
goto done;
default:
pr_err("ACDB=> ACDB ioctl not found!\n");
+ result = -EFAULT;
+ goto done;
}
done:
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.h b/sound/soc/msm/qdsp6v2/audio_acdb.h
index 3c644ed..1685894 100644
--- a/sound/soc/msm/qdsp6v2/audio_acdb.h
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.h
@@ -48,23 +48,23 @@
uint32_t get_adm_tx_topology(void);
uint32_t get_asm_topology(void);
void reset_custom_topology_flags(void);
-void get_adm_custom_topology(struct acdb_cal_block *cal_block);
-void get_asm_custom_topology(struct acdb_cal_block *cal_block);
-void get_voice_cal_allocation(struct acdb_cal_block *cal_block);
-void get_lsm_cal(struct acdb_cal_block *cal_block);
-void get_anc_cal(struct acdb_cal_block *cal_block);
-void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block);
-void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block);
-void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block);
-void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block);
-void get_voice_col_data(uint32_t vocproc_type,
+int get_adm_custom_topology(struct acdb_cal_block *cal_block);
+int get_asm_custom_topology(struct acdb_cal_block *cal_block);
+int get_voice_cal_allocation(struct acdb_cal_block *cal_block);
+int get_lsm_cal(struct acdb_cal_block *cal_block);
+int get_anc_cal(struct acdb_cal_block *cal_block);
+int get_afe_cal(int32_t path, struct acdb_cal_block *cal_block);
+int get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block);
+int get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block);
+int get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block);
+int get_voice_col_data(uint32_t vocproc_type,
struct acdb_cal_block *cal_block);
-void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block);
-void get_vocproc_cal(struct acdb_cal_block *cal_block);
-void get_vocstrm_cal(struct acdb_cal_block *cal_block);
-void get_vocvol_cal(struct acdb_cal_block *cal_block);
-void get_sidetone_cal(struct sidetone_cal *cal_data);
-void get_spk_protection_cfg(struct msm_spk_prot_cfg *prot_cfg);
-void get_aanc_cal(struct acdb_cal_block *cal_block);
+int get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block);
+int get_vocproc_cal(struct acdb_cal_block *cal_block);
+int get_vocstrm_cal(struct acdb_cal_block *cal_block);
+int get_vocvol_cal(struct acdb_cal_block *cal_block);
+int get_sidetone_cal(struct sidetone_cal *cal_data);
+int get_spk_protection_cfg(struct msm_spk_prot_cfg *prot_cfg);
+int get_aanc_cal(struct acdb_cal_block *cal_block);
#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 2509859..aa6ef6b 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -36,6 +36,7 @@
#include "msm-compr-q6-v2.h"
#include "msm-pcm-routing-v2.h"
#include "audio_ocmem.h"
+#include <sound/tlv.h>
#define COMPRE_CAPTURE_NUM_PERIODS 16
/* Allocate the worst case frame size for compressed audio */
@@ -48,13 +49,14 @@
COMPRE_CAPTURE_HEADER_SIZE) * \
MAX_NUM_FRAMES_PER_BUFFER)
#define COMPRE_OUTPUT_METADATA_SIZE (sizeof(struct output_meta_data_st))
+#define COMPRESSED_LR_VOL_MAX_STEPS 0x20002000
+const DECLARE_TLV_DB_LINEAR(compr_rx_vol_gain, 0,
+ COMPRESSED_LR_VOL_MAX_STEPS);
struct snd_msm {
- struct msm_audio *prtd;
- unsigned volume;
atomic_t audio_ocmem_req;
};
-static struct snd_msm compressed_audio = {NULL, 0x20002000} ;
+static struct snd_msm compressed_audio;
static struct audio_locks the_locks;
@@ -616,7 +618,6 @@
populate_codec_list(compr, runtime);
runtime->private_data = compr;
atomic_set(&prtd->eos, 0);
- compressed_audio.prtd = &compr->prtd;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (!atomic_cmpxchg(&compressed_audio.audio_ocmem_req, 0, 1))
audio_ocmem_process_req(AUDIO, true);
@@ -628,27 +629,29 @@
return 0;
}
-int compressed_set_volume(unsigned volume)
+static int compressed_set_volume(struct msm_audio *prtd, uint32_t volume)
{
int rc = 0;
int avg_vol = 0;
- if (compressed_audio.prtd && compressed_audio.prtd->audio_client) {
- if (compressed_audio.prtd->channel_mode > 2) {
- avg_vol = (((volume >> 16) & 0xFFFF) +
- (volume & 0xFFFF)) / 2;
- rc = q6asm_set_volume(
- compressed_audio.prtd->audio_client, avg_vol);
+ int lgain = (volume >> 16) & 0xFFFF;
+ int rgain = volume & 0xFFFF;
+ if (prtd && prtd->audio_client) {
+ pr_debug("%s: channels %d volume 0x%x\n", __func__,
+ prtd->channel_mode, volume);
+ if ((prtd->channel_mode == 2) &&
+ (lgain != rgain)) {
+ pr_debug("%s: call q6asm_set_lrgain\n", __func__);
+ rc = q6asm_set_lrgain(prtd->audio_client, lgain, rgain);
} else {
- rc = q6asm_set_lrgain(
- compressed_audio.prtd->audio_client,
- (volume >> 16) & 0xFFFF, volume & 0xFFFF);
+ avg_vol = (lgain + rgain)/2;
+ pr_debug("%s: call q6asm_set_volume\n", __func__);
+ rc = q6asm_set_volume(prtd->audio_client, avg_vol);
}
if (rc < 0) {
pr_err("%s: Send Volume command failed rc=%d\n",
- __func__, rc);
+ __func__, rc);
}
}
- compressed_audio.volume = volume;
return rc;
}
@@ -674,7 +677,6 @@
atomic_read(&compressed_audio.audio_ocmem_req));
prtd->pcm_irq_pos = 0;
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
- compressed_audio.prtd = NULL;
q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
msm_pcm_routing_dereg_phy_stream(
@@ -816,17 +818,15 @@
(params_periods(params) <= runtime->hw.channels_max))
prtd->channel_mode = params_channels(params);
- ret = compressed_set_volume(0);
+ ret = compressed_set_volume(prtd, 0);
if (ret < 0)
pr_err("%s : Set Volume failed : %d", __func__, ret);
- ret = q6asm_set_softpause(compressed_audio.prtd->audio_client,
- &softpause);
+ ret = q6asm_set_softpause(prtd->audio_client, &softpause);
if (ret < 0)
pr_err("%s: Send SoftPause Param failed ret=%d\n",
__func__, ret);
- ret = q6asm_set_softvolume(compressed_audio.prtd->audio_client,
- &softvol);
+ ret = q6asm_set_softvolume(prtd->audio_client, &softvol);
if (ret < 0)
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
__func__, ret);
@@ -1166,6 +1166,66 @@
return 0;
}
+static int msm_compr_volume_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_substream *substream =
+ vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct msm_audio *prtd;
+ int volume = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: volume : %x\n", __func__, volume);
+ if (!substream)
+ return -ENODEV;
+ if (!substream->runtime)
+ return 0;
+ prtd = substream->runtime->private_data;
+ if (prtd)
+ rc = compressed_set_volume(prtd, volume);
+
+ return rc;
+}
+
+static int msm_compr_volume_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_substream *substream =
+ vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct msm_audio *prtd;
+
+ pr_debug("%s\n", __func__);
+ if (!substream)
+ return -ENODEV;
+ if (!substream->runtime)
+ return 0;
+ prtd = substream->runtime->private_data;
+ if (prtd)
+ ucontrol->value.integer.value[0] = prtd->volume;
+ return 0;
+}
+
+static int msm_compr_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_volume *volume_info;
+ struct snd_kcontrol *kctl;
+
+ dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
+ ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1, rtd->dai_link->be_id,
+ &volume_info);
+ if (ret < 0)
+ return ret;
+ kctl = volume_info->kctl;
+ kctl->put = msm_compr_volume_ctl_put;
+ kctl->get = msm_compr_volume_ctl_get;
+ kctl->tlv.p = compr_rx_vol_gain;
+ return 0;
+}
static struct snd_pcm_ops msm_compr_ops = {
.open = msm_compr_open,
@@ -1186,6 +1246,10 @@
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_compr_add_controls(rtd);
+ if (ret)
+ pr_err("%s, kctl add failed\n", __func__);
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
index 16df886..0cf044c 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -264,11 +264,12 @@
static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
.playback = {
- .rates = SNDRV_PCM_RATE_48000,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 2,
.channels_max = 8,
- .rate_max = 48000,
+ .rate_max = 192000,
.rate_min = 48000,
},
.ops = &msm_dai_q6_hdmi_ops,
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index b07e91e..1434970 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -28,6 +28,9 @@
#include <sound/pcm_params.h>
#include <mach/clk.h>
+#define MSM_DAI_PRI_AUXPCM_DT_DEV_ID 1
+#define MSM_DAI_SEC_AUXPCM_DT_DEV_ID 2
+
static const struct afe_clk_cfg lpass_clk_cfg_default = {
AFE_API_VERSION_I2S_CONFIG,
Q6AFE_LPASS_OSR_CLK_2_P048_MHZ,
@@ -39,6 +42,10 @@
};
enum {
STATUS_PORT_STARTED, /* track if AFE port has started */
+ /* track AFE Tx port status for bi-directional transfers */
+ STATUS_TX_PORT,
+ /* track AFE Rx port status for bi-directional transfers */
+ STATUS_RX_PORT,
STATUS_MAX
};
@@ -66,6 +73,16 @@
struct msm_dai_q6_mi2s_dai_config rx_dai;
};
+struct msm_dai_q6_auxpcm_dai_data {
+ /* BITMAP to track Rx and Tx port usage count */
+ DECLARE_BITMAP(auxpcm_port_status, STATUS_MAX);
+ struct mutex rlock; /* auxpcm dev resource lock */
+ u16 rx_pid; /* AUXPCM RX AFE port ID */
+ u16 tx_pid; /* AUXPCM TX AFE port ID */
+ struct afe_clk_cfg clk_cfg; /* hold LPASS clock configuration */
+ struct msm_dai_q6_dai_data bdai_data; /* incoporate base DAI data */
+};
+
/* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command
* 0: linear PCM
* 1: non-linear PCM
@@ -83,27 +100,43 @@
SOC_ENUM_SINGLE_EXT(4, mi2s_format),
};
-static DEFINE_MUTEX(aux_pcm_mutex);
-static int aux_pcm_count;
-
static int msm_dai_q6_auxpcm_hw_params(
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data;
struct msm_dai_auxpcm_pdata *auxpcm_pdata =
(struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+ int rc = 0;
- if (params_channels(params) != 1) {
- dev_err(dai->dev, "AUX PCM supports only mono stream\n");
+ if (params_channels(params) != 1 || (params_rate(params) != 8000 &&
+ params_rate(params) != 16000)) {
+ dev_err(dai->dev, "%s: invalid param chan %d rate %d\n",
+ __func__, params_channels(params), params_rate(params));
return -EINVAL;
}
+
+ mutex_lock(&aux_dai_data->rlock);
+
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+ /* AUXPCM DAI in use */
+ if (dai_data->rate != params_rate(params)) {
+ dev_err(dai->dev, "%s: rate mismatch of running DAI\n",
+ __func__);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&aux_dai_data->rlock);
+ return rc;
+ }
+
dai_data->channels = params_channels(params);
dai_data->rate = params_rate(params);
- switch (dai_data->rate) {
- case 8000:
+ if (dai_data->rate == 8000) {
dai_data->port_config.pcm.pcm_cfg_minor_version =
AFE_API_VERSION_PCM_CONFIG;
dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode_8k.mode;
@@ -119,8 +152,7 @@
dai_data->port_config.pcm.bit_width = 16;
dai_data->port_config.pcm.slot_number_mapping[0] =
auxpcm_pdata->mode_8k.slot;
- break;
- case 16000:
+ } else {
dai_data->port_config.pcm.pcm_cfg_minor_version =
AFE_API_VERSION_PCM_CONFIG;
dai_data->port_config.pcm.aux_mode =
@@ -138,12 +170,19 @@
dai_data->port_config.pcm.bit_width = 16;
dai_data->port_config.pcm.slot_number_mapping[0] =
auxpcm_pdata->mode_16k.slot;
- break;
- default:
- dev_err(dai->dev, "AUX PCM supports only 8kHz and 16kHz sampling rate\n");
- return -EINVAL;
}
- return 0;
+
+ dev_dbg(dai->dev, "%s: aux_mode %x sync_src %x frame_setting %x\n",
+ __func__, dai_data->port_config.pcm.aux_mode,
+ dai_data->port_config.pcm.sync_src,
+ dai_data->port_config.pcm.frame_setting);
+ dev_dbg(dai->dev, "%s: qtype %x dout %x num_map %x\n",
+ __func__, dai_data->port_config.pcm.quantype,
+ dai_data->port_config.pcm.ctrl_data_out_enable,
+ dai_data->port_config.pcm.slot_number_mapping[0]);
+
+ mutex_unlock(&aux_dai_data->rlock);
+ return rc;
}
static void msm_dai_q6_auxpcm_shutdown(struct snd_pcm_substream *substream,
@@ -151,114 +190,115 @@
{
int rc = 0;
struct afe_clk_cfg *lpass_pcm_src_clk = NULL;
- struct afe_clk_cfg lpass_pcm_oe_clk;
- struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
- unsigned int rx_port = 0;
- unsigned int tx_port = 0;
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+ dev_get_drvdata(dai->dev);
- mutex_lock(&aux_pcm_mutex);
+ mutex_lock(&aux_dai_data->rlock);
- if (aux_pcm_count == 0) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. Just return\n",
+ if (!(test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status))) {
+ dev_dbg(dai->dev, "%s(): dai->id %d PCM ports already closed\n",
__func__, dai->id);
- mutex_unlock(&aux_pcm_mutex);
- return;
+ goto exit;
}
- aux_pcm_count--;
-
- if (aux_pcm_count > 0) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d\n",
- __func__, dai->id, aux_pcm_count);
- mutex_unlock(&aux_pcm_mutex);
- return;
- } else if (aux_pcm_count < 0) {
- dev_err(dai->dev, "%s(): ERROR: dai->id %d aux_pcm_count = %d < 0\n",
- __func__, dai->id, aux_pcm_count);
- aux_pcm_count = 0;
- mutex_unlock(&aux_pcm_mutex);
- return;
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status))
+ clear_bit(STATUS_TX_PORT,
+ aux_dai_data->auxpcm_port_status);
+ else {
+ dev_dbg(dai->dev, "%s(): PCM_TX port already closed\n",
+ __func__);
+ goto exit;
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status))
+ clear_bit(STATUS_RX_PORT,
+ aux_dai_data->auxpcm_port_status);
+ else {
+ dev_dbg(dai->dev, "%s(): PCM_RX port already closed\n",
+ __func__);
+ goto exit;
+ }
+ }
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+ dev_dbg(dai->dev, "%s(): cannot shutdown PCM ports\n",
+ __func__);
+ goto exit;
}
- pr_debug("%s: dai->id = %d aux_pcm_count = %d\n", __func__,
- dai->id, aux_pcm_count);
+ dev_dbg(dai->dev, "%s: dai->id = %d closing PCM AFE ports\n",
+ __func__, dai->id);
- auxpcm_pdata = (struct msm_dai_auxpcm_pdata *)dai->dev->platform_data;
- lpass_pcm_src_clk = (struct afe_clk_cfg *)auxpcm_pdata->clk_cfg;
+ lpass_pcm_src_clk = (struct afe_clk_cfg *) &aux_dai_data->clk_cfg;
- if (dai->id == AFE_PORT_ID_PRIMARY_PCM_RX
- || dai->id == AFE_PORT_ID_PRIMARY_PCM_TX) {
- rx_port = AFE_PORT_ID_PRIMARY_PCM_RX;
- tx_port = AFE_PORT_ID_PRIMARY_PCM_TX;
- } else if (dai->id == AFE_PORT_ID_SECONDARY_PCM_RX
- || dai->id == AFE_PORT_ID_SECONDARY_PCM_TX) {
- rx_port = AFE_PORT_ID_SECONDARY_PCM_RX;
- tx_port = AFE_PORT_ID_SECONDARY_PCM_TX;
- }
-
- rc = afe_close(rx_port); /* can block */
+ rc = afe_close(aux_dai_data->rx_pid); /* can block */
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to close PCM_RX AFE port\n");
- rc = afe_close(tx_port);
+ rc = afe_close(aux_dai_data->tx_pid);
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to close AUX PCM TX port\n");
lpass_pcm_src_clk->clk_val1 = 0;
- afe_set_lpass_clock(tx_port, lpass_pcm_src_clk);
- afe_set_lpass_clock(rx_port, lpass_pcm_src_clk);
+ afe_set_lpass_clock(aux_dai_data->rx_pid, lpass_pcm_src_clk);
+ afe_set_lpass_clock(aux_dai_data->tx_pid, lpass_pcm_src_clk);
- memcpy(&lpass_pcm_oe_clk, &lpass_clk_cfg_default,
- sizeof(struct afe_clk_cfg));
- lpass_pcm_oe_clk.clk_val1 = 0;
- afe_set_lpass_clock(rx_port, &lpass_pcm_oe_clk);
-
- mutex_unlock(&aux_pcm_mutex);
+exit:
+ mutex_unlock(&aux_dai_data->rlock);
+ return;
}
static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data;
struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
int rc = 0;
unsigned long pcm_clk_rate;
- struct afe_clk_cfg lpass_pcm_oe_clk;
struct afe_clk_cfg *lpass_pcm_src_clk = NULL;
- unsigned int rx_port = 0;
- unsigned int tx_port = 0;
auxpcm_pdata = dai->dev->platform_data;
- lpass_pcm_src_clk = (struct afe_clk_cfg *)auxpcm_pdata->clk_cfg;
+ lpass_pcm_src_clk = (struct afe_clk_cfg *) &aux_dai_data->clk_cfg;
- mutex_lock(&aux_pcm_mutex);
+ mutex_lock(&aux_dai_data->rlock);
- if (aux_pcm_count == 2) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 2. Just return.\n",
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (test_bit(STATUS_TX_PORT,
+ aux_dai_data->auxpcm_port_status)) {
+ dev_dbg(dai->dev, "%s(): PCM_TX port already ON\n",
+ __func__);
+ goto exit;
+ } else
+ set_bit(STATUS_TX_PORT,
+ aux_dai_data->auxpcm_port_status);
+ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (test_bit(STATUS_RX_PORT,
+ aux_dai_data->auxpcm_port_status)) {
+ dev_dbg(dai->dev, "%s(): PCM_RX port already ON\n",
+ __func__);
+ goto exit;
+ } else
+ set_bit(STATUS_RX_PORT,
+ aux_dai_data->auxpcm_port_status);
+ }
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) &&
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+ dev_dbg(dai->dev, "%s(): PCM ports already set\n", __func__);
+ goto exit;
+ }
+
+ dev_dbg(dai->dev, "%s: dai->id:%d opening afe ports\n",
__func__, dai->id);
- mutex_unlock(&aux_pcm_mutex);
- return 0;
- } else if (aux_pcm_count > 2) {
- dev_err(dai->dev, "%s(): ERROR: dai->id %d aux_pcm_count = %d > 2\n",
- __func__, dai->id, aux_pcm_count);
- mutex_unlock(&aux_pcm_mutex);
- return 0;
- }
-
- aux_pcm_count++;
- if (aux_pcm_count == 2) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d after increment\n",
- __func__, dai->id, aux_pcm_count);
- mutex_unlock(&aux_pcm_mutex);
- return 0;
- }
-
- pr_debug("%s:dai->id:%d aux_pcm_count = %d. opening afe\n",
- __func__, dai->id, aux_pcm_count);
rc = afe_q6_interface_prepare();
- if (IS_ERR_VALUE(rc))
+ if (IS_ERR_VALUE(rc)) {
dev_err(dai->dev, "fail to open AFE APR\n");
+ goto fail;
+ }
/*
* For AUX PCM Interface the below sequence of clk
@@ -279,54 +319,42 @@
} else {
dev_err(dai->dev, "%s: Invalid AUX PCM rate %d\n", __func__,
dai_data->rate);
- mutex_unlock(&aux_pcm_mutex);
- return -EINVAL;
+ rc = -EINVAL;
+ goto fail;
}
memcpy(lpass_pcm_src_clk, &lpass_clk_cfg_default,
sizeof(struct afe_clk_cfg));
lpass_pcm_src_clk->clk_val1 = pcm_clk_rate;
- memcpy(&lpass_pcm_oe_clk, &lpass_clk_cfg_default,
- sizeof(struct afe_clk_cfg));
- lpass_pcm_oe_clk.clk_val1 = Q6AFE_LPASS_OSR_CLK_12_P288_MHZ;
-
- if (dai->id == AFE_PORT_ID_PRIMARY_PCM_RX ||
- dai->id == AFE_PORT_ID_PRIMARY_PCM_TX) {
- rx_port = AFE_PORT_ID_PRIMARY_PCM_RX;
- tx_port = AFE_PORT_ID_PRIMARY_PCM_TX;
- } else if (dai->id == AFE_PORT_ID_SECONDARY_PCM_RX ||
- dai->id == AFE_PORT_ID_SECONDARY_PCM_TX) {
- rx_port = AFE_PORT_ID_SECONDARY_PCM_RX;
- tx_port = AFE_PORT_ID_SECONDARY_PCM_TX;
- }
-
- rc = afe_set_lpass_clock(rx_port, lpass_pcm_src_clk);
+ rc = afe_set_lpass_clock(aux_dai_data->rx_pid, lpass_pcm_src_clk);
if (rc < 0) {
- pr_err("%s:afe_set_lpass_clock on RX pcm_src_clk failed\n",
- __func__);
+ dev_err(dai->dev,
+ "%s:afe_set_lpass_clock on RX pcm_src_clk failed\n",
+ __func__);
goto fail;
}
- rc = afe_set_lpass_clock(tx_port, lpass_pcm_src_clk);
+ rc = afe_set_lpass_clock(aux_dai_data->tx_pid, lpass_pcm_src_clk);
if (rc < 0) {
- pr_err("%s:afe_set_lpass_clock on TX pcm_src_clk failed\n",
- __func__);
+ dev_err(dai->dev,
+ "%s:afe_set_lpass_clock on TX pcm_src_clk failed\n",
+ __func__);
goto fail;
}
- rc = afe_set_lpass_clock(rx_port, &lpass_pcm_oe_clk);
- if (rc < 0) {
- pr_err("%s:afe_set_lpass_clock on pcm_oe_clk failed\n",
- __func__);
- goto fail;
- }
-
- afe_open(rx_port, &dai_data->port_config, dai_data->rate);
- afe_open(tx_port, &dai_data->port_config, dai_data->rate);
+ afe_open(aux_dai_data->rx_pid, &dai_data->port_config, dai_data->rate);
+ afe_open(aux_dai_data->tx_pid, &dai_data->port_config, dai_data->rate);
+ goto exit;
fail:
- mutex_unlock(&aux_pcm_mutex);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status);
+ else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status);
+
+exit:
+ mutex_unlock(&aux_dai_data->rlock);
return rc;
}
@@ -335,8 +363,8 @@
{
int rc = 0;
- pr_debug("%s:port:%d cmd:%d aux_pcm_count= %d\n",
- __func__, dai->id, cmd, aux_pcm_count);
+ pr_debug("%s:port:%d cmd:%d\n",
+ __func__, dai->id, cmd);
switch (cmd) {
@@ -359,84 +387,33 @@
}
-static int msm_dai_q6_dai_auxpcm_probe(struct snd_soc_dai *dai)
-{
- struct msm_dai_q6_dai_data *dai_data;
- int rc = 0;
- struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
-
- auxpcm_pdata = (struct msm_dai_auxpcm_pdata *)
- dev_get_drvdata(dai->dev);
- dai->dev->platform_data = auxpcm_pdata;
- dai->id = dai->dev->id;
-
-
- dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL);
-
- if (!dai_data) {
- dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
- dai->id);
- rc = -ENOMEM;
- } else
- dev_set_drvdata(dai->dev, dai_data);
-
- pr_debug("%s : probe done for dai->id %d\n", __func__, dai->id);
- return rc;
-}
-
static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai)
{
- struct msm_dai_q6_dai_data *dai_data;
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data;
+ struct afe_clk_cfg *lpass_pcm_src_clk = NULL;
int rc;
- unsigned int rx_port = 0;
- unsigned int tx_port = 0;
- dai_data = dev_get_drvdata(dai->dev);
+ aux_dai_data = dev_get_drvdata(dai->dev);
- mutex_lock(&aux_pcm_mutex);
+ dev_dbg(dai->dev, "%s(): dai->id %d closing afe\n",
+ __func__, dai->id);
- if (aux_pcm_count == 0) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. clean up and return\n",
- __func__, dai->id);
- goto done;
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+ rc = afe_close(aux_dai_data->rx_pid); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUXPCM RX AFE port\n");
+ rc = afe_close(aux_dai_data->tx_pid);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUXPCM TX AFE port\n");
+ clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status);
+ clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status);
}
- aux_pcm_count--;
-
- if (aux_pcm_count > 0) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d\n",
- __func__, dai->id, aux_pcm_count);
- goto done;
- } else if (aux_pcm_count < 0) {
- dev_err(dai->dev, "%s(): ERROR: dai->id %d aux_pcm_count = %d < 0\n",
- __func__, dai->id, aux_pcm_count);
- goto done;
- }
-
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d.closing afe\n",
- __func__, dai->id, aux_pcm_count);
-
- if (dai->id == AFE_PORT_ID_PRIMARY_PCM_RX ||
- dai->id == AFE_PORT_ID_PRIMARY_PCM_TX) {
- rx_port = AFE_PORT_ID_PRIMARY_PCM_RX;
- tx_port = AFE_PORT_ID_PRIMARY_PCM_TX;
- } else if (dai->id == AFE_PORT_ID_SECONDARY_PCM_RX ||
- dai->id == AFE_PORT_ID_SECONDARY_PCM_TX) {
- rx_port = AFE_PORT_ID_SECONDARY_PCM_RX;
- tx_port = AFE_PORT_ID_SECONDARY_PCM_TX;
- }
- rc = afe_close(rx_port); /* can block */
- if (IS_ERR_VALUE(rc))
- dev_err(dai->dev, "fail to close AUX PCM RX AFE port\n");
-
- rc = afe_close(tx_port);
- if (IS_ERR_VALUE(rc))
- dev_err(dai->dev, "fail to close AUX PCM TX AFE port\n");
-done:
- kfree(dai_data);
- snd_soc_unregister_dai(dai->dev);
-
- mutex_unlock(&aux_pcm_mutex);
+ lpass_pcm_src_clk = (struct afe_clk_cfg *) &aux_dai_data->clk_cfg;
+ lpass_pcm_src_clk->clk_val1 = 0;
+ afe_set_lpass_clock(aux_dai_data->rx_pid, lpass_pcm_src_clk);
+ afe_set_lpass_clock(aux_dai_data->tx_pid, lpass_pcm_src_clk);
return 0;
}
@@ -448,31 +425,24 @@
.shutdown = msm_dai_q6_auxpcm_shutdown,
};
-static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_rx_dai = {
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_dai = {
.playback = {
- .rates = SNDRV_PCM_RATE_8000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 1,
- .rate_max = 8000,
+ .rate_max = 16000,
.rate_min = 8000,
},
- .ops = &msm_dai_q6_auxpcm_ops,
- .probe = msm_dai_q6_dai_auxpcm_probe,
- .remove = msm_dai_q6_dai_auxpcm_remove,
-};
-
-static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_tx_dai = {
.capture = {
- .rates = SNDRV_PCM_RATE_8000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 1,
- .rate_max = 8000,
+ .rate_max = 16000,
.rate_min = 8000,
},
.ops = &msm_dai_q6_auxpcm_ops,
- .probe = msm_dai_q6_dai_auxpcm_probe,
.remove = msm_dai_q6_dai_auxpcm_remove,
};
@@ -749,6 +719,7 @@
rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai);
break;
case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
case VOICE_RECORD_RX:
case VOICE_RECORD_TX:
rc = msm_dai_q6_psuedo_port_hw_params(params,
@@ -1090,72 +1061,38 @@
static int __devinit msm_auxpcm_dev_probe(struct platform_device *pdev)
{
- int id;
- void *plat_data;
- int rc = 0;
-
- if (pdev->dev.parent == NULL)
- return -ENODEV;
-
- plat_data = dev_get_drvdata(pdev->dev.parent);
-
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-auxpcm-dev-id", &id);
- if (rc) {
- dev_err(&pdev->dev, "%s: qcom,msm-auxpcm-dev-id missing in DT node\n",
- __func__);
- return rc;
- }
-
- pdev->id = id;
- dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6", id);
- dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
-
- dev_set_drvdata(&pdev->dev, plat_data);
- pdev->dev.id = id;
-
- switch (id) {
- case AFE_PORT_ID_PRIMARY_PCM_RX:
- case AFE_PORT_ID_SECONDARY_PCM_RX:
- rc = snd_soc_register_dai(&pdev->dev,
- &msm_dai_q6_aux_pcm_rx_dai);
- break;
- case AFE_PORT_ID_PRIMARY_PCM_TX:
- case AFE_PORT_ID_SECONDARY_PCM_TX:
- rc = snd_soc_register_dai(&pdev->dev,
- &msm_dai_q6_aux_pcm_tx_dai);
- break;
- default:
- rc = -ENODEV;
- break;
- }
-
- return rc;
-}
-
-static int __devinit msm_auxpcm_resource_probe(
- struct platform_device *pdev)
-{
- int rc = 0;
- struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
- struct afe_clk_cfg *clk_cfg = NULL;
+ struct msm_dai_q6_auxpcm_dai_data *dai_data;
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata;
uint32_t val_array[RATE_MAX_NUM_OF_AUX_PCM_RATES];
+ const char *intf_name;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_auxpcm_dai_data),
+ GFP_KERNEL);
+ if (!dai_data) {
+ dev_err(&pdev->dev,
+ "Failed to allocate memory for auxpcm DAI data\n");
+ return -ENOMEM;
+ }
auxpcm_pdata = kzalloc(sizeof(struct msm_dai_auxpcm_pdata),
GFP_KERNEL);
if (!auxpcm_pdata) {
dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
- return -ENOMEM;
+ goto fail_pdata_nomem;
}
+ dev_dbg(&pdev->dev, "%s: dev %p, dai_data %p, auxpcm_pdata %p\n",
+ __func__, &pdev->dev, dai_data, auxpcm_pdata);
+
rc = of_property_read_u32_array(pdev->dev.of_node,
"qcom,msm-cpudai-auxpcm-mode",
val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-mode missing in DT node\n",
__func__);
- goto fail_free_plat;
+ goto fail_invalid_dt;
}
auxpcm_pdata->mode_8k.mode = (u16)val_array[RATE_8KHZ];
auxpcm_pdata->mode_16k.mode = (u16)val_array[RATE_16KHZ];
@@ -1166,7 +1103,7 @@
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-sync missing in DT node\n",
__func__);
- goto fail_free_plat;
+ goto fail_invalid_dt;
}
auxpcm_pdata->mode_8k.sync = (u16)val_array[RATE_8KHZ];
auxpcm_pdata->mode_16k.sync = (u16)val_array[RATE_16KHZ];
@@ -1178,7 +1115,7 @@
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-frame missing in DT node\n",
__func__);
- goto fail_free_plat;
+ goto fail_invalid_dt;
}
auxpcm_pdata->mode_8k.frame = (u16)val_array[RATE_8KHZ];
auxpcm_pdata->mode_16k.frame = (u16)val_array[RATE_16KHZ];
@@ -1189,7 +1126,7 @@
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-quant missing in DT node\n",
__func__);
- goto fail_free_plat;
+ goto fail_invalid_dt;
}
auxpcm_pdata->mode_8k.quant = (u16)val_array[RATE_8KHZ];
auxpcm_pdata->mode_16k.quant = (u16)val_array[RATE_16KHZ];
@@ -1200,7 +1137,7 @@
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-slot missing in DT node\n",
__func__);
- goto fail_free_plat;
+ goto fail_invalid_dt;
}
auxpcm_pdata->mode_8k.slot = (u16)val_array[RATE_8KHZ];
auxpcm_pdata->mode_16k.slot = (u16)val_array[RATE_16KHZ];
@@ -1211,7 +1148,7 @@
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-data missing in DT node\n",
__func__);
- goto fail_free_plat;
+ goto fail_invalid_dt;
}
auxpcm_pdata->mode_8k.data = (u16)val_array[RATE_8KHZ];
auxpcm_pdata->mode_16k.data = (u16)val_array[RATE_16KHZ];
@@ -1219,60 +1156,79 @@
rc = of_property_read_u32_array(pdev->dev.of_node,
"qcom,msm-cpudai-auxpcm-pcm-clk-rate",
val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
-
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: qcom,msm-cpudai-auxpcm-pcm-clk-rate missing in DT\n",
+ __func__);
+ goto fail_invalid_dt;
+ }
auxpcm_pdata->mode_8k.pcm_clk_rate = (int)val_array[RATE_8KHZ];
auxpcm_pdata->mode_16k.pcm_clk_rate = (int)val_array[RATE_16KHZ];
- clk_cfg = kzalloc(sizeof(struct afe_clk_cfg), GFP_KERNEL);
- if (clk_cfg == NULL) {
- pr_err("%s: Failed to allocate memory for clk cfg\n", __func__);
- goto fail_free_plat;
- }
- auxpcm_pdata->clk_cfg = clk_cfg;
-
- platform_set_drvdata(pdev, auxpcm_pdata);
-
- rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ rc = of_property_read_string(pdev->dev.of_node,
+ "qcom,msm-auxpcm-interface", &intf_name);
if (rc) {
- dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+ dev_err(&pdev->dev,
+ "%s: qcom,msm-auxpcm-interface missing in DT node\n",
+ __func__);
+ goto fail_nodev_intf;
+ }
+
+ if (!strncmp(intf_name, "primary", sizeof("primary"))) {
+ dai_data->rx_pid = AFE_PORT_ID_PRIMARY_PCM_RX;
+ dai_data->tx_pid = AFE_PORT_ID_PRIMARY_PCM_TX;
+ pdev->id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID;
+ } else if (!strncmp(intf_name, "secondary", sizeof("secondary"))) {
+ dai_data->rx_pid = AFE_PORT_ID_SECONDARY_PCM_RX;
+ dai_data->tx_pid = AFE_PORT_ID_SECONDARY_PCM_TX;
+ pdev->id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID;
+ } else {
+ dev_err(&pdev->dev, "%s: invalid DT intf name %s\n",
+ __func__, intf_name);
+ goto fail_invalid_intf;
+ }
+
+ mutex_init(&dai_data->rlock);
+ dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-auxpcm", pdev->id);
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+ dev_set_drvdata(&pdev->dev, dai_data);
+ pdev->dev.platform_data = (void *) auxpcm_pdata;
+
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_aux_pcm_dai);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: auxpcm dai reg failed, rc=%d\n",
__func__, rc);
- goto fail_free_plat1;
+ goto fail_reg_dai;
}
return rc;
-fail_free_plat1:
- kfree(clk_cfg);
-fail_free_plat:
+fail_reg_dai:
+fail_invalid_intf:
+fail_nodev_intf:
+fail_invalid_dt:
kfree(auxpcm_pdata);
+fail_pdata_nomem:
+ kfree(dai_data);
return rc;
}
static int __devexit msm_auxpcm_dev_remove(struct platform_device *pdev)
{
+ struct msm_dai_q6_auxpcm_dai_data *dai_data;
+
+ dai_data = dev_get_drvdata(&pdev->dev);
+
snd_soc_unregister_dai(&pdev->dev);
- return 0;
-}
-static int __devexit msm_auxpcm_resource_remove(
- struct platform_device *pdev)
-{
- struct msm_dai_auxpcm_pdata *auxpcm_pdata;
- struct afe_clk_cfg *clk_cfg;
-
- auxpcm_pdata = dev_get_drvdata(&pdev->dev);
- clk_cfg = (struct afe_clk_cfg *)auxpcm_pdata->clk_cfg;
- kfree(clk_cfg);
- kfree(auxpcm_pdata);
+ mutex_destroy(&dai_data->rlock);
+ kfree(dai_data);
+ kfree(pdev->dev.platform_data);
return 0;
}
-static struct of_device_id msm_auxpcm_resource_dt_match[] = {
- { .compatible = "qcom,msm-auxpcm-resource", },
- {}
-};
-
static struct of_device_id msm_auxpcm_dev_dt_match[] = {
{ .compatible = "qcom,msm-auxpcm-dev", },
{}
@@ -1289,16 +1245,6 @@
},
};
-static struct platform_driver msm_auxpcm_resource_driver = {
- .probe = msm_auxpcm_resource_probe,
- .remove = __devexit_p(msm_auxpcm_resource_remove),
- .driver = {
- .name = "msm-auxpcm-resource",
- .owner = THIS_MODULE,
- .of_match_table = msm_auxpcm_resource_dt_match,
- },
-};
-
static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai = {
.playback = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
@@ -1638,8 +1584,21 @@
goto error_invalid_data;
}
dai_data->rate = params_rate(params);
- dai_data->port_config.i2s.bit_width = 16;
- dai_data->bitwidth = 16;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_SPECIAL:
+ dai_data->port_config.i2s.bit_width = 16;
+ dai_data->bitwidth = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dai_data->port_config.i2s.bit_width = 24;
+ dai_data->bitwidth = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
dai_data->port_config.i2s.i2s_cfg_minor_version =
AFE_API_VERSION_I2S_CONFIG;
dai_data->port_config.i2s.sample_rate = dai_data->rate;
@@ -1759,7 +1718,7 @@
.capture = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -2080,6 +2039,7 @@
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_tx_dai);
break;
case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_voice_playback_tx_dai);
break;
@@ -2210,14 +2170,9 @@
int rc;
rc = platform_driver_register(&msm_auxpcm_dev_driver);
- if (rc)
- goto fail;
-
- rc = platform_driver_register(&msm_auxpcm_resource_driver);
-
if (rc) {
- pr_err("%s: fail to register cpu dai driver\n", __func__);
- goto aux_pcm_resource_fail;
+ pr_err("%s: fail to register auxpcm dev driver", __func__);
+ goto fail;
}
rc = platform_driver_register(&msm_dai_q6);
@@ -2252,8 +2207,6 @@
dai_q6_dev_fail:
platform_driver_unregister(&msm_dai_q6);
dai_q6_fail:
- platform_driver_unregister(&msm_auxpcm_resource_driver);
-aux_pcm_resource_fail:
platform_driver_unregister(&msm_auxpcm_dev_driver);
fail:
return rc;
@@ -2265,7 +2218,6 @@
platform_driver_unregister(&msm_dai_q6_dev);
platform_driver_unregister(&msm_dai_q6);
platform_driver_unregister(&msm_auxpcm_dev_driver);
- platform_driver_unregister(&msm_auxpcm_resource_driver);
}
module_exit(msm_dai_q6_exit);
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
index 9ace410..ca2afaf 100644
--- a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
@@ -231,7 +231,7 @@
static int map_device_to_dolby_endpoint(int device)
{
- int i, dolby_dap_device = DOLBY_ENDP_INT_SPEAKERS;
+ int i, dolby_dap_device = DOLBY_ENDP_EXT_SPEAKERS;
for (i = 0; i < NUM_DOLBY_ENDP_DEVICE; i++) {
if (dolby_dap_endp_params[i].device == device) {
dolby_dap_device = dolby_dap_endp_params[i].dap_device;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index eeb1745..caf77ee 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -41,12 +41,15 @@
#include "msm-pcm-routing-v2.h"
#include "audio_ocmem.h"
#include <sound/pcm.h>
+#include <sound/tlv.h>
+#define LPA_LR_VOL_MAX_STEPS 0x20002000
+
+const DECLARE_TLV_DB_LINEAR(lpa_rx_vol_gain, 0,
+ LPA_LR_VOL_MAX_STEPS);
static struct audio_locks the_locks;
struct snd_msm {
- struct msm_audio *prtd;
- unsigned volume;
atomic_t audio_ocmem_req;
};
static struct snd_msm lpa_audio;
@@ -414,7 +417,6 @@
atomic_set(&prtd->stop, 1);
atomic_set(&lpa_audio.audio_ocmem_req, 0);
runtime->private_data = prtd;
- lpa_audio.prtd = prtd;
if (!atomic_cmpxchg(&lpa_audio.audio_ocmem_req, 0, 1))
audio_ocmem_process_req(AUDIO, true);
else
@@ -424,19 +426,19 @@
return 0;
}
-int lpa_set_volume(unsigned volume)
+static int lpa_set_volume(struct msm_audio *prtd, uint32_t volume)
{
int rc = 0;
- if (lpa_audio.prtd && lpa_audio.prtd->audio_client) {
- rc = q6asm_set_lrgain(lpa_audio.prtd->audio_client,
- (volume >> 16) & 0xFFFF,
- volume & 0xFFFF);
+ if (prtd && prtd->audio_client) {
+ rc = q6asm_set_lrgain(prtd->audio_client,
+ (volume >> 16) & 0xFFFF, volume & 0xFFFF);
if (rc < 0) {
pr_err("%s: Send Volume command failed rc=%d\n",
- __func__, rc);
+ __func__, rc);
+ } else {
+ prtd->volume = volume;
}
}
- lpa_audio.volume = volume;
return rc;
}
@@ -476,10 +478,8 @@
atomic_dec(&lpa_audio.audio_ocmem_req);
else if (atomic_cmpxchg(&lpa_audio.audio_ocmem_req, 1, 0))
audio_ocmem_process_req(AUDIO, false);
-
pr_debug("%s: req: %d\n", __func__,
atomic_read(&lpa_audio.audio_ocmem_req));
- lpa_audio.prtd = NULL;
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
@@ -594,12 +594,12 @@
prtd->audio_client->perf_mode,
prtd->session_id, substream->stream);
- lpa_set_volume(0);
- ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
+ lpa_set_volume(prtd, 0);
+ ret = q6asm_set_softpause(prtd->audio_client, &softpause);
if (ret < 0)
pr_err("%s: Send SoftPause Param failed ret=%d\n",
__func__, ret);
- ret = q6asm_set_softvolume(lpa_audio.prtd->audio_client, &softvol);
+ ret = q6asm_set_softvolume(prtd->audio_client, &softvol);
if (ret < 0)
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
__func__, ret);
@@ -695,6 +695,67 @@
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
+static int msm_lpa_volume_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_substream *substream =
+ vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct msm_audio *prtd;
+ int volume = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: volume : %x\n", __func__, volume);
+ if (!substream)
+ return -ENODEV;
+ if (!substream->runtime)
+ return 0;
+ prtd = substream->runtime->private_data;
+ if (prtd)
+ rc = lpa_set_volume(prtd, volume);
+
+ return rc;
+}
+
+static int msm_lpa_volume_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_substream *substream =
+ vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct msm_audio *prtd;
+
+ pr_debug("%s\n", __func__);
+ if (!substream)
+ return -ENODEV;
+ if (!substream->runtime)
+ return 0;
+ prtd = substream->runtime->private_data;
+ if (prtd)
+ ucontrol->value.integer.value[0] = prtd->volume;
+ return 0;
+}
+
+static int msm_lpa_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+ struct snd_pcm_volume *volume_info;
+ struct snd_kcontrol *kctl;
+
+ dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
+ ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1, rtd->dai_link->be_id,
+ &volume_info);
+ if (ret < 0)
+ return ret;
+ kctl = volume_info->kctl;
+ kctl->put = msm_lpa_volume_ctl_put;
+ kctl->get = msm_lpa_volume_ctl_get;
+ kctl->tlv.p = lpa_rx_vol_gain;
+ return 0;
+}
+
static struct snd_pcm_ops msm_pcm_ops = {
.open = msm_pcm_open,
.hw_params = msm_pcm_hw_params,
@@ -714,6 +775,10 @@
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_lpa_add_controls(rtd);
+ if (ret)
+ pr_err("%s, kctl add failed\n", __func__);
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 77f3a07..49bc488 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -42,12 +42,6 @@
struct snd_pcm *pcm;
};
-struct snd_msm_volume {
- struct msm_audio *prtd;
- unsigned volume;
-};
-static struct snd_msm_volume pcm_audio = {NULL, 0x2000};
-
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 8
#define PLAYBACK_MAX_PERIOD_SIZE 12288
@@ -63,7 +57,8 @@
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
@@ -260,6 +255,8 @@
struct msm_audio *prtd = runtime->private_data;
int ret = 0;
int i = 0;
+ uint16_t bits_per_sample = 16;
+
pr_debug("%s\n", __func__);
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
@@ -272,10 +269,19 @@
if (prtd->enabled)
return 0;
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bits_per_sample = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits_per_sample = 24;
+ break;
+ }
pr_debug("Samp_rate = %d\n", prtd->samp_rate);
pr_debug("Channel = %d\n", prtd->channel_mode);
- ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
- prtd->channel_mode);
+ ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client,
+ prtd->samp_rate, prtd->channel_mode,
+ bits_per_sample);
if (ret < 0)
pr_debug("%s: cmd cfg pcm was block failed", __func__);
@@ -390,7 +396,6 @@
prtd->dsp_cnt = 0;
prtd->set_channel_map = false;
runtime->private_data = prtd;
- pcm_audio.prtd = prtd;
return 0;
}
@@ -479,7 +484,6 @@
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
- pcm_audio.prtd = NULL;
q6asm_audio_client_free(prtd->audio_client);
}
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
@@ -702,10 +706,13 @@
/* Capture Path */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE)
+ bits_per_sample = 24;
pr_debug("%s Opening %d-ch PCM read stream\n",
__func__, params_channels(params));
- ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
+ ret = q6asm_open_read_v2(prtd->audio_client, FORMAT_LINEAR_PCM,
+ bits_per_sample);
if (ret < 0) {
pr_err("%s: q6asm_open_read failed\n", __func__);
q6asm_audio_client_free(prtd->audio_client);
@@ -760,30 +767,71 @@
.mmap = msm_pcm_mmap,
};
-static int pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int i;
- char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
pr_debug("%s", __func__);
- for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
- channel_mapping[i] = (char)(ucontrol->value.integer.value[i]);
- if (pcm_audio.prtd) {
- pcm_audio.prtd->set_channel_map = true;
- memcpy(pcm_audio.prtd->channel_map, channel_mapping,
- PCM_FORMAT_MAX_NUM_CHANNEL);
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ if (!substream->runtime)
+ return 0;
+
+ prtd = substream->runtime->private_data;
+ if (prtd) {
+ prtd->set_channel_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ prtd->channel_map[i] =
+ (char)(ucontrol->value.integer.value[i]);
}
return 0;
}
+static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+
+ pr_debug("%s", __func__);
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ memset(ucontrol->value.integer.value, 0,
+ sizeof(ucontrol->value.integer.value));
+ if (!substream->runtime)
+ return 0; /* no channels set */
+
+ prtd = substream->runtime->private_data;
+
+ if (prtd && prtd->set_channel_map == true) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ ucontrol->value.integer.value[i] =
+ (int)prtd->channel_map[i];
+ } else {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ ucontrol->value.integer.value[i] = 0;
+ }
+
+ return 0;
+}
+
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+ struct snd_pcm *pcm = rtd->pcm;
struct snd_pcm_chmap *chmap_info;
struct snd_kcontrol *kctl;
- char device_num[3];
+ char device_num[12];
int i, ret = 0;
if (!card->dev->coherent_dma_mask)
@@ -791,8 +839,9 @@
pr_debug("%s, Channel map cntrl add\n", __func__);
ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- NULL, PCM_FORMAT_MAX_NUM_CHANNEL, 0,
- &chmap_info);
+ snd_pcm_std_chmaps,
+ PCM_FORMAT_MAX_NUM_CHANNEL, 0,
+ &chmap_info);
if (ret < 0)
return ret;
kctl = chmap_info->kctl;
@@ -802,7 +851,8 @@
strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
pr_debug("%s, Overwriting channel map control name to: %s",
__func__, kctl->id.name);
- kctl->put = pcm_chmap_ctl_put;
+ kctl->put = msm_pcm_chmap_ctl_put;
+ kctl->get = msm_pcm_chmap_ctl_get;
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
index 6ded0d9..f7719ed 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
@@ -86,6 +86,7 @@
char channel_map[8];
int cmd_interrupt;
bool meta_data_mode;
+ uint32_t volume;
};
struct output_meta_data_st {
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 97803b3..76a525d 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -40,7 +40,10 @@
u16 port_id; /* AFE port ID */
u8 active; /* track if this backend is enabled */
unsigned long fe_sessions; /* Front-end sessions */
- unsigned long port_sessions; /* track Tx BE ports -> Rx BE */
+ u64 port_sessions; /* track Tx BE ports -> Rx BE
+ * number of BE should not exceed
+ * the size of this field
+ */
unsigned int sample_rate;
unsigned int channel;
unsigned int format;
@@ -82,23 +85,14 @@
#define INT_RX_VOL_MAX_STEPS 0x2000
#define INT_RX_VOL_GAIN 0x2000
-#define INT_RX_LR_VOL_MAX_STEPS 0x20002000
static int msm_route_fm_vol_control;
static const DECLARE_TLV_DB_LINEAR(fm_rx_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
-static int msm_route_lpa_vol_control;
-static const DECLARE_TLV_DB_LINEAR(lpa_rx_vol_gain, 0,
- INT_RX_LR_VOL_MAX_STEPS);
-
static int msm_route_multimedia2_vol_control;
static const DECLARE_TLV_DB_LINEAR(multimedia2_rx_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
-static int msm_route_compressed_vol_control;
-static const DECLARE_TLV_DB_LINEAR(compressed_rx_vol_gain, 0,
- INT_RX_LR_VOL_MAX_STEPS);
-
static int msm_route_multimedia5_vol_control;
static const DECLARE_TLV_DB_LINEAR(multimedia5_rx_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
@@ -217,6 +211,7 @@
{ AFE_PORT_ID_PRIMARY_PCM_RX, 0, 0, 0, 0, 0},
{ AFE_PORT_ID_PRIMARY_PCM_TX, 0, 0, 0, 0, 0},
{ VOICE_PLAYBACK_TX, 0, 0, 0, 0, 0},
+ { VOICE2_PLAYBACK_TX, 0, 0, 0, 0, 0},
{ VOICE_RECORD_RX, 0, 0, 0, 0, 0},
{ VOICE_RECORD_TX, 0, 0, 0, 0, 0},
{ MI2S_RX, 0, 0, 0, 0, 0},
@@ -264,6 +259,8 @@
{INVALID_SESSION, INVALID_SESSION},
/* MULTIMEDIA8 */
{INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA9 */
+ {INVALID_SESSION, INVALID_SESSION},
};
static uint8_t is_be_dai_extproc(int be_dai)
@@ -511,8 +508,9 @@
topology = get_topology(path_type);
if (set) {
if (!test_bit(val, &msm_bedais[reg].fe_sessions) &&
- (msm_bedais[reg].port_id == VOICE_PLAYBACK_TX))
- voc_start_playback(set);
+ ((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) ||
+ (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX)))
+ voc_start_playback(set, msm_bedais[reg].port_id);
set_bit(val, &msm_bedais[reg].fe_sessions);
if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
INVALID_SESSION) {
@@ -555,8 +553,9 @@
}
} else {
if (test_bit(val, &msm_bedais[reg].fe_sessions) &&
- (msm_bedais[reg].port_id == VOICE_PLAYBACK_TX))
- voc_start_playback(set);
+ ((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) ||
+ (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX)))
+ voc_start_playback(set, msm_bedais[reg].port_id);
clear_bit(val, &msm_bedais[reg].fe_sessions);
if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
INVALID_SESSION) {
@@ -970,7 +969,8 @@
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- if (test_bit(mc->shift, &msm_bedais[mc->reg].port_sessions))
+ if (test_bit(mc->shift,
+ (unsigned long *)&msm_bedais[mc->reg].port_sessions))
ucontrol->value.integer.value[0] = 1;
else
ucontrol->value.integer.value[0] = 0;
@@ -987,19 +987,19 @@
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
+ pr_debug("%s: reg 0x%x shift 0x%x val %ld\n", __func__, mc->reg,
mc->shift, ucontrol->value.integer.value[0]);
if (ucontrol->value.integer.value[0]) {
afe_loopback(1, msm_bedais[mc->reg].port_id,
msm_bedais[mc->shift].port_id);
set_bit(mc->shift,
- &msm_bedais[mc->reg].port_sessions);
+ (unsigned long *)&msm_bedais[mc->reg].port_sessions);
} else {
afe_loopback(0, msm_bedais[mc->reg].port_id,
msm_bedais[mc->shift].port_id);
clear_bit(mc->shift,
- &msm_bedais[mc->reg].port_sessions);
+ (unsigned long *)&msm_bedais[mc->reg].port_sessions);
}
return 1;
@@ -1022,23 +1022,6 @@
return 0;
}
-static int msm_routing_get_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = msm_route_lpa_vol_control;
- return 0;
-}
-
-static int msm_routing_set_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- if (!lpa_set_volume(ucontrol->value.integer.value[0]))
- msm_route_lpa_vol_control =
- ucontrol->value.integer.value[0];
-
- return 0;
-}
-
static int msm_routing_get_multimedia2_vol_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1077,24 +1060,6 @@
return 0;
}
-static int msm_routing_get_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
-
- ucontrol->value.integer.value[0] = msm_route_compressed_vol_control;
- return 0;
-}
-
-static int msm_routing_set_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- if (!compressed_set_volume(ucontrol->value.integer.value[0]))
- msm_route_compressed_vol_control =
- ucontrol->value.integer.value[0];
-
- return 0;
-}
-
static int msm_routing_get_channel_map_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1418,6 +1383,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = {
@@ -1445,6 +1413,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
@@ -1472,6 +1443,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_0_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = {
@@ -1499,6 +1473,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = {
@@ -1526,6 +1503,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = {
@@ -1568,6 +1548,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new mi2s_hl_mixer_controls[] = {
@@ -1604,6 +1587,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
@@ -1631,6 +1617,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
/* incall music delivery mixer */
static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
@@ -1640,6 +1629,21 @@
SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new incall_music2_delivery_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new slimbus_4_rx_mixer_controls[] = {
@@ -1676,6 +1680,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_BT_SCO_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
@@ -1703,6 +1710,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_FM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
@@ -1730,6 +1740,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
@@ -1757,6 +1770,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
@@ -1784,6 +1800,9 @@
SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
@@ -2141,6 +2160,9 @@
SOC_SINGLE_EXT("AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_AUXPCM_TX,
MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX_Voice2", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new tx_volte_mixer_controls[] = {
@@ -2381,12 +2403,6 @@
msm_routing_set_fm_vol_mixer, fm_rx_vol_gain),
};
-static const struct snd_kcontrol_new lpa_vol_mixer_controls[] = {
- SOC_SINGLE_EXT_TLV("LPA RX Volume", SND_SOC_NOPM, 0,
- INT_RX_VOL_GAIN, 0, msm_routing_get_lpa_vol_mixer,
- msm_routing_set_lpa_vol_mixer, lpa_rx_vol_gain),
-};
-
static const struct snd_kcontrol_new multimedia2_vol_mixer_controls[] = {
SOC_SINGLE_EXT_TLV("HIFI2 RX Volume", SND_SOC_NOPM, 0,
INT_RX_VOL_GAIN, 0, msm_routing_get_multimedia2_vol_mixer,
@@ -2399,12 +2415,6 @@
msm_routing_set_multimedia5_vol_mixer, multimedia5_rx_vol_gain),
};
-static const struct snd_kcontrol_new compressed_vol_mixer_controls[] = {
- SOC_SINGLE_EXT_TLV("COMPRESSED RX Volume", SND_SOC_NOPM, 0,
- INT_RX_VOL_GAIN, 0, msm_routing_get_compressed_vol_mixer,
- msm_routing_set_compressed_vol_mixer, compressed_rx_vol_gain),
-};
-
static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
SOC_SINGLE_MULTI_EXT("Playback Channel Map", SND_SOC_NOPM, 0, 16,
0, 8, msm_routing_get_channel_map_mixer,
@@ -2763,11 +2773,13 @@
SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL9", "MultiMedia9 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL9", "MultiMedia9 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VOICE2_DL", "Voice2 Playback", 0, 0, 0, 0),
@@ -2862,6 +2874,8 @@
/* incall */
SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback",
0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("VOICE2_PLAYBACK_TX", "Voice2 Farend Playback",
+ 0, 0, 0 , 0),
SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback",
0, 0, 0 , 0),
SND_SOC_DAPM_AIF_IN("INCALL_RECORD_TX", "Voice Uplink Capture",
@@ -2945,11 +2959,14 @@
sec_auxpcm_rx_mixer_controls, ARRAY_SIZE(sec_auxpcm_rx_mixer_controls)),
/* incall */
SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0,
- incall_music_delivery_mixer_controls,
- ARRAY_SIZE(incall_music_delivery_mixer_controls)),
+ incall_music_delivery_mixer_controls,
+ ARRAY_SIZE(incall_music_delivery_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Incall_Music_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ incall_music2_delivery_mixer_controls,
+ ARRAY_SIZE(incall_music2_delivery_mixer_controls)),
SND_SOC_DAPM_MIXER("SLIMBUS_4_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
- slimbus_4_rx_mixer_controls,
- ARRAY_SIZE(slimbus_4_rx_mixer_controls)),
+ slimbus_4_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_4_rx_mixer_controls)),
/* Voice Mixer */
SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
@@ -3066,6 +3083,7 @@
{"PRI_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"PRI_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"PRI_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
{"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -3076,6 +3094,7 @@
{"SEC_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"SEC_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"SEC_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -3086,6 +3105,7 @@
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
{"HDMI Mixer", "MultiMedia1", "MM_DL1"},
@@ -3096,12 +3116,18 @@
{"HDMI Mixer", "MultiMedia6", "MM_DL6"},
{"HDMI Mixer", "MultiMedia7", "MM_DL7"},
{"HDMI Mixer", "MultiMedia8", "MM_DL8"},
+ {"HDMI Mixer", "MultiMedia9", "MM_DL9"},
{"HDMI", NULL, "HDMI Mixer"},
/* incall */
{"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"},
{"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"Incall_Music Audio Mixer", "MultiMedia9", "MM_DL9"},
{"VOICE_PLAYBACK_TX", NULL, "Incall_Music Audio Mixer"},
+ {"Incall_Music_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"Incall_Music_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"Incall_Music_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"VOICE2_PLAYBACK_TX", NULL, "Incall_Music_2 Audio Mixer"},
{"SLIMBUS_4_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"SLIMBUS_4_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"SLIMBUS_4_RX", NULL, "SLIMBUS_4_RX Audio Mixer"},
@@ -3119,6 +3145,7 @@
{"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"MI2S_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -3174,6 +3201,7 @@
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -3184,6 +3212,7 @@
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -3194,6 +3223,7 @@
{"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
{"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
@@ -3217,6 +3247,7 @@
{"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -3227,6 +3258,7 @@
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"},
{"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
@@ -3320,6 +3352,7 @@
{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
{"Voice2_Tx Mixer", "PRI_TX_Voice2", "PRI_I2S_TX"},
+ {"Voice2_Tx Mixer", "PRI_MI2S_TX_Voice2", "PRI_MI2S_TX"},
{"Voice2_Tx Mixer", "MI2S_TX_Voice2", "MI2S_TX"},
{"Voice2_Tx Mixer", "SLIM_0_TX_Voice2", "SLIMBUS_0_TX"},
{"Voice2_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice2", "INT_BT_SCO_TX"},
@@ -3466,6 +3499,7 @@
{"BE_OUT", NULL, "AUX_PCM_RX"},
{"BE_OUT", NULL, "SEC_AUX_PCM_RX"},
{"BE_OUT", NULL, "VOICE_PLAYBACK_TX"},
+ {"BE_OUT", NULL, "VOICE2_PLAYBACK_TX"},
{"PRI_I2S_TX", NULL, "BE_IN"},
{"MI2S_TX", NULL, "BE_IN"},
@@ -3674,10 +3708,6 @@
ARRAY_SIZE(int_fm_vol_mixer_controls));
snd_soc_add_platform_controls(platform,
- lpa_vol_mixer_controls,
- ARRAY_SIZE(lpa_vol_mixer_controls));
-
- snd_soc_add_platform_controls(platform,
eq_enable_mixer_controls,
ARRAY_SIZE(eq_enable_mixer_controls));
@@ -3698,10 +3728,6 @@
ARRAY_SIZE(multimedia5_vol_mixer_controls));
snd_soc_add_platform_controls(platform,
- compressed_vol_mixer_controls,
- ARRAY_SIZE(compressed_vol_mixer_controls));
-
- snd_soc_add_platform_controls(platform,
lpa_SRS_trumedia_controls,
ARRAY_SIZE(lpa_SRS_trumedia_controls));
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index 9750756..4ce0db5 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -29,6 +29,7 @@
#define LPASS_BE_SEC_AUXPCM_RX "SEC_AUX_PCM_RX"
#define LPASS_BE_SEC_AUXPCM_TX "SEC_AUX_PCM_TX"
#define LPASS_BE_VOICE_PLAYBACK_TX "VOICE_PLAYBACK_TX"
+#define LPASS_BE_VOICE2_PLAYBACK_TX "VOICE2_PLAYBACK_TX"
#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_TX"
#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_RX"
#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
@@ -70,6 +71,7 @@
MSM_FRONTEND_DAI_MULTIMEDIA6,
MSM_FRONTEND_DAI_MULTIMEDIA7,
MSM_FRONTEND_DAI_MULTIMEDIA8,
+ MSM_FRONTEND_DAI_MULTIMEDIA9,
MSM_FRONTEND_DAI_CS_VOICE,
MSM_FRONTEND_DAI_VOIP,
MSM_FRONTEND_DAI_AFE_RX,
@@ -82,8 +84,8 @@
MSM_FRONTEND_DAI_MAX,
};
-#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA8 + 1)
-#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA8
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA9 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA9
enum {
MSM_BACKEND_DAI_PRI_I2S_RX = 0,
@@ -100,6 +102,7 @@
MSM_BACKEND_DAI_AUXPCM_RX,
MSM_BACKEND_DAI_AUXPCM_TX,
MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
MSM_BACKEND_DAI_INCALL_RECORD_RX,
MSM_BACKEND_DAI_INCALL_RECORD_TX,
MSM_BACKEND_DAI_MI2S_RX,
@@ -140,14 +143,10 @@
void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
-int lpa_set_volume(unsigned volume);
-
int msm_routing_check_backend_enabled(int fedai_id);
int multi_ch_pcm_set_volume(unsigned volume);
-int compressed_set_volume(unsigned volume);
-
uint32_t get_adm_rx_topology(void);
uint32_t get_adm_tx_topology(void);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index 5c420ed..071db4e 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -617,12 +617,36 @@
static __devinit int msm_pcm_probe(struct platform_device *pdev)
{
+ int rc;
+
+ if (!is_voc_initialized()) {
+ pr_debug("%s: voice module not initialized yet, deferring probe()\n",
+ __func__);
+
+ rc = -EPROBE_DEFER;
+ goto done;
+ }
+
+ rc = voc_alloc_cal_shared_memory();
+ if (rc == -EPROBE_DEFER) {
+ pr_debug("%s: memory allocation for calibration deferred %d\n",
+ __func__, rc);
+
+ goto done;
+ } else if (rc < 0) {
+ pr_err("%s: memory allocation for calibration failed %d\n",
+ __func__, rc);
+ }
+
if (pdev->dev.of_node)
dev_set_name(&pdev->dev, "%s", "msm-pcm-voice");
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
- return snd_soc_register_platform(&pdev->dev,
- &msm_soc_platform);
+ rc = snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+
+done:
+ return rc;
}
static int msm_pcm_remove(struct platform_device *pdev)
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
index f17fe5b..ae454a8 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
@@ -817,8 +817,15 @@
}
voc_register_mvs_cb(voip_process_ul_pkt,
voip_process_dl_pkt, prtd);
- voc_start_voice_call(voc_get_session_id(VOIP_SESSION_NAME));
+ ret = voc_start_voice_call(
+ voc_get_session_id(VOIP_SESSION_NAME));
+ if (ret < 0) {
+ pr_err("%s: voc_start_voice_call() failed err %d",
+ __func__, ret);
+
+ goto done;
+ }
prtd->state = VOIP_STARTED;
}
done:
@@ -1132,12 +1139,42 @@
static __devinit int msm_pcm_probe(struct platform_device *pdev)
{
+ int rc;
+
+ if (!is_voc_initialized()) {
+ pr_debug("%s: voice module not initialized yet, deferring probe()\n",
+ __func__);
+
+ rc = -EPROBE_DEFER;
+ goto done;
+ }
+
+ rc = voc_alloc_cal_shared_memory();
+ if (rc == -EPROBE_DEFER) {
+ pr_debug("%s: memory allocation for calibration deferred %d\n",
+ __func__, rc);
+
+ goto done;
+ } else if (rc < 0) {
+ pr_err("%s: memory allocation for calibration failed %d\n",
+ __func__, rc);
+ }
+
+ rc = voc_alloc_voip_shared_memory();
+ if (rc < 0) {
+ pr_err("%s: error allocating shared mem err %d\n",
+ __func__, rc);
+ }
+
if (pdev->dev.of_node)
dev_set_name(&pdev->dev, "%s", "msm-voip-dsp");
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
- return snd_soc_register_platform(&pdev->dev,
- &msm_soc_platform);
+ rc = snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+
+done:
+ return rc;
}
static int msm_pcm_remove(struct platform_device *pdev)
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 9882ec7..0eb13d4 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -234,6 +234,7 @@
case INT_BT_A2DP_RX:
case INT_FM_RX:
case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
case RT_PROXY_PORT_001_RX:
case AUDIO_PORT_ID_I2S_RX:
case AFE_PORT_ID_PRIMARY_MI2S_RX:
@@ -303,6 +304,7 @@
ret_size = SIZEOF_CFG_CMD(afe_param_id_slimbus_cfg);
break;
case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
case VOICE_RECORD_RX:
case VOICE_RECORD_TX:
ret_size = SIZEOF_CFG_CMD(afe_param_id_pseudo_port_cfg);
@@ -1263,6 +1265,7 @@
cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
break;
case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
case VOICE_RECORD_RX:
case VOICE_RECORD_TX:
cfg_type = AFE_PARAM_ID_PSEUDO_PORT_CONFIG;
@@ -1356,6 +1359,7 @@
case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
+ case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX;
case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
@@ -2663,6 +2667,7 @@
case VOICE_RECORD_RX:
case VOICE_RECORD_TX:
case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
case SLIMBUS_0_RX:
case SLIMBUS_0_TX:
case SLIMBUS_1_RX:
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index c65222b..869d642 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -1383,15 +1383,12 @@
hdr->pkt_size = pkt_size;
return;
}
-int q6asm_open_read(struct audio_client *ac,
- uint32_t format)
+static int __q6asm_open_read(struct audio_client *ac,
+ uint32_t format, uint16_t bits_per_sample)
{
int rc = 0x00;
struct asm_stream_cmd_open_read_v3 open;
- uint16_t bits_per_sample = 16;
-
-
config_debug_fs_reset_index();
if ((ac == NULL) || (ac->apr == NULL)) {
@@ -1466,6 +1463,18 @@
return -EINVAL;
}
+int q6asm_open_read(struct audio_client *ac,
+ uint32_t format)
+{
+ return __q6asm_open_read(ac, format, 16);
+}
+
+int q6asm_open_read_v2(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_read(ac, format, bits_per_sample);
+}
+
static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample)
{
@@ -1820,8 +1829,8 @@
return rc;
}
-int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
- uint32_t rate, uint32_t channels)
+static int __q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels, uint16_t bits_per_sample)
{
struct asm_multi_channel_pcm_enc_cfg_v2 enc_cfg;
u8 *channel_mapping;
@@ -1842,7 +1851,7 @@
sizeof(struct asm_enc_cfg_blk_param_v2);
enc_cfg.num_channels = channels;
- enc_cfg.bits_per_sample = 16;
+ enc_cfg.bits_per_sample = bits_per_sample;
enc_cfg.sample_rate = rate;
enc_cfg.is_signed = 1;
channel_mapping = enc_cfg.channel_mapping;
@@ -1869,6 +1878,18 @@
return -EINVAL;
}
+int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, 16);
+}
+
+int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
+ uint32_t rate, uint32_t channels, uint16_t bits_per_sample)
+{
+ return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, bits_per_sample);
+}
+
int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac,
uint32_t rate, uint32_t channels)
{
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index bc7ad4d..57ea2a7 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -43,6 +43,7 @@
case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
+ case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX;
case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
@@ -103,6 +104,7 @@
case VOICE_RECORD_RX: return AFE_PORT_ID_VOICE_RECORD_RX;
case VOICE_RECORD_TX: return AFE_PORT_ID_VOICE_RECORD_TX;
case VOICE_PLAYBACK_TX: return AFE_PORT_ID_VOICE_PLAYBACK_TX;
+ case VOICE2_PLAYBACK_TX: return AFE_PORT_ID_VOICE2_PLAYBACK_TX;
case SLIMBUS_0_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX;
case SLIMBUS_0_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX;
case SLIMBUS_1_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX;
@@ -213,6 +215,7 @@
case VOICE_RECORD_RX:
case VOICE_RECORD_TX:
case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
case SLIMBUS_0_RX:
case SLIMBUS_0_TX:
case SLIMBUS_1_RX:
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 87bee75..be62727 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -48,6 +48,7 @@
};
static struct common_data common;
+static bool module_initialized;
static int voice_send_enable_vocproc_cmd(struct voice_data *v);
static int voice_send_netid_timing_cmd(struct voice_data *v);
@@ -84,9 +85,16 @@
static int voice_send_set_pp_enable_cmd(struct voice_data *v,
uint32_t module_id, int enable);
+static int is_cal_memory_allocated(void);
+static int is_voip_memory_allocated(void);
+static int voice_alloc_cal_mem_map_table(void);
+static int voice_alloc_oob_shared_mem(void);
+static int voice_free_oob_shared_mem(void);
+static int voice_alloc_oob_mem_table(void);
+static int voice_alloc_and_map_cal_mem(struct voice_data *v);
+static int voice_alloc_and_map_oob_mem(struct voice_data *v);
static struct voice_data *voice_get_session_by_idx(int idx);
-static int voice_get_idx_for_session(u32 session_id);
static u16 voice_get_mvm_handle(struct voice_data *v)
{
@@ -231,7 +239,7 @@
return v;
}
-static int voice_get_idx_for_session(u32 session_id)
+int voice_get_idx_for_session(u32 session_id)
{
int idx = 0;
@@ -291,6 +299,16 @@
return (session_id == common.voice[VOC_PATH_VOICE2_PASSIVE].session_id);
}
+static bool is_voc_state_active(int voc_state)
+{
+ if ((voc_state == VOC_RUN) ||
+ (voc_state == VOC_CHANGE) ||
+ (voc_state == VOC_STANDBY))
+ return true;
+
+ return false;
+}
+
static bool is_other_session_active(u32 session_id)
{
int i;
@@ -301,9 +319,7 @@
if (common.voice[i].session_id == session_id)
continue;
- if ((common.voice[i].voc_state == VOC_RUN) ||
- (common.voice[i].voc_state == VOC_CHANGE) ||
- (common.voice[i].voc_state == VOC_STANDBY)) {
+ if (is_voc_state_active(common.voice[i].voc_state)) {
ret = true;
break;
}
@@ -1204,9 +1220,8 @@
for (i = 0; i < MAX_VOC_SESSIONS; i++) {
v = &common.voice[i];
if ((v->dtmf_rx_detect_en) &&
- ((v->voc_state == VOC_RUN) ||
- (v->voc_state == VOC_CHANGE) ||
- (v->voc_state == VOC_STANDBY))) {
+ is_voc_state_active(v->voc_state)) {
+
pr_debug("disable dtmf det on ses_id=%d\n",
v->session_id);
voice_send_dtmf_rx_detection_cmd(v, 0);
@@ -1227,9 +1242,7 @@
mutex_lock(&v->lock);
v->dtmf_rx_detect_en = enable;
- if ((v->voc_state == VOC_RUN) ||
- (v->voc_state == VOC_CHANGE) ||
- (v->voc_state == VOC_STANDBY))
+ if (is_voc_state_active(v->voc_state))
ret = voice_send_dtmf_rx_detection_cmd(v,
v->dtmf_rx_detect_en);
@@ -1238,6 +1251,89 @@
return ret;
}
+int voc_alloc_cal_shared_memory(void)
+{
+ int rc = 0;
+
+ mutex_lock(&common.common_lock);
+ if (is_cal_memory_allocated()) {
+ pr_debug("%s: Calibration shared buffer already allocated",
+ __func__);
+ } else {
+ /* Allocate memory for calibration memory map table. */
+ rc = voice_alloc_cal_mem_map_table();
+ if (rc < 0) {
+ pr_err("%s: Failed to allocate cal memory, err=%d",
+ __func__, rc);
+ }
+ }
+ mutex_unlock(&common.common_lock);
+
+ return rc;
+}
+
+int voc_alloc_voip_shared_memory(void)
+{
+ int rc = 0;
+
+ /* Allocate shared memory for OOB Voip */
+ rc = voice_alloc_oob_shared_mem();
+ if (rc < 0) {
+ pr_err("%s: Failed to alloc shared memory for OOB rc:%d\n",
+ __func__, rc);
+ } else {
+ /* Allocate mem map table for OOB */
+ rc = voice_alloc_oob_mem_table();
+ if (rc < 0) {
+ pr_err("%s: Failed to alloc mem map talbe rc:%d\n",
+ __func__, rc);
+
+ voice_free_oob_shared_mem();
+ }
+ }
+
+ return rc;
+}
+
+static int is_cal_memory_allocated(void)
+{
+ bool ret;
+
+ if (common.cal_mem_map_table.client != NULL &&
+ common.cal_mem_map_table.handle != NULL)
+ ret = true;
+ else
+ ret = false;
+
+ return ret;
+}
+
+static int is_voip_memory_allocated(void)
+{
+ bool ret;
+ struct voice_data *v = voice_get_session(
+ common.voice[VOC_PATH_FULL].session_id);
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL, session_id:%d\n", __func__,
+ common.voice[VOC_PATH_FULL].session_id);
+
+ ret = false;
+ goto done;
+ }
+
+ mutex_lock(&common.common_lock);
+ if (v->shmem_info.sh_buf.client != NULL &&
+ v->shmem_info.sh_buf.handle != NULL)
+ ret = true;
+ else
+ ret = false;
+ mutex_unlock(&common.common_lock);
+
+done:
+ return ret;
+}
+
static int voice_config_cvs_vocoder(struct voice_data *v)
{
int ret = 0;
@@ -1691,26 +1787,28 @@
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.apr_q6_cvs) {
pr_err("%s: apr_cvs is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.cal_mem_handle) {
- pr_err("%s: Cal mem handle is NULL\n", __func__);
+ pr_debug("%s: Cal mem handle is NULL\n", __func__);
- goto fail;
+ goto done;
}
get_vocstrm_cal(&cal_block);
if (cal_block.cal_size == 0) {
pr_err("%s: CVS cal size is 0\n", __func__);
- goto fail;
+ goto done;
}
cvs_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -1739,7 +1837,7 @@
if (ret < 0) {
pr_err("%s: Error %d registering CVS cal\n", __func__, ret);
- goto fail;
+ goto done;
}
ret = wait_event_timeout(v->cvs_wait,
(v->cvs_state == CMD_STATUS_SUCCESS),
@@ -1747,13 +1845,11 @@
if (!ret) {
pr_err("%s: Command timeout\n", __func__);
- goto fail;
+ goto done;
}
- return 0;
-
-fail:
- return -EINVAL;
+done:
+ return ret;
}
static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v)
@@ -1766,18 +1862,26 @@
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.apr_q6_cvs) {
pr_err("%s: apr_cvs is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.cal_mem_handle) {
+ pr_debug("%s: Cal mem handle is NULL\n", __func__);
+
+ goto done;
}
get_vocstrm_cal(&cal_block);
if (cal_block.cal_size == 0)
- return 0;
+ goto done;
cvs_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
@@ -1795,7 +1899,7 @@
if (ret < 0) {
pr_err("%s: Error %d de-registering CVS cal\n", __func__, ret);
- goto fail;
+ goto done;
}
ret = wait_event_timeout(v->cvs_wait,
(v->cvs_state == CMD_STATUS_SUCCESS),
@@ -1803,13 +1907,11 @@
if (!ret) {
pr_err("%s: Command timeout\n", __func__);
- goto fail;
+ goto done;
}
- return 0;
-
-fail:
- return -EINVAL;
+done:
+ return ret;
}
@@ -1823,26 +1925,28 @@
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.apr_q6_cvp) {
pr_err("%s: apr_cvp is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.cal_mem_handle) {
- pr_err("%s: Cal mem handle is NULL\n", __func__);
+ pr_debug("%s: Cal mem handle is NULL\n", __func__);
- goto fail;
+ goto done;
}
get_vocproc_dev_cfg_cal(&cal_block);
if (cal_block.cal_size == 0) {
pr_err("%s: CVP cal size is 0\n", __func__);
- goto fail;
+ goto done;
}
cvp_reg_dev_cfg_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -1867,7 +1971,7 @@
pr_err("%s: Error %d registering CVP dev cfg cal\n",
__func__, ret);
- goto fail;
+ goto done;
}
ret = wait_event_timeout(v->cvp_wait,
(v->cvp_state == CMD_STATUS_SUCCESS),
@@ -1875,13 +1979,11 @@
if (!ret) {
pr_err("%s: Command timeout\n", __func__);
- goto fail;
+ goto done;
}
- return 0;
-
-fail:
- return -EINVAL;
+done:
+ return ret;
}
static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v)
@@ -1894,18 +1996,26 @@
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.apr_q6_cvp) {
- pr_err("%s: apr_cvp is NULL.\n", __func__);
+ pr_err("%s: apr_cvp is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.cal_mem_handle) {
+ pr_debug("%s: Cal mem handle is NULL\n", __func__);
+
+ goto done;
}
get_vocproc_dev_cfg_cal(&cal_block);
if (cal_block.cal_size == 0)
- return 0;
+ goto done;
cvp_dereg_dev_cfg_cmd.hdr.hdr_field =
APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -1926,7 +2036,7 @@
pr_err("%s: Error %d de-registering CVP dev cfg cal\n",
__func__, ret);
- goto fail;
+ goto done;
}
ret = wait_event_timeout(v->cvp_wait,
(v->cvp_state == CMD_STATUS_SUCCESS),
@@ -1934,13 +2044,11 @@
if (!ret) {
pr_err("%s: Command timeout\n", __func__);
- goto fail;
+ goto done;
}
- return 0;
-
-fail:
- return -EINVAL;
+done:
+ return ret;
}
static int voice_send_cvp_register_cal_cmd(struct voice_data *v)
@@ -1953,26 +2061,28 @@
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.apr_q6_cvp) {
pr_err("%s: apr_cvp is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.cal_mem_handle) {
- pr_err("%s: Cal mem handle is NULL\n", __func__);
+ pr_debug("%s: Cal mem handle is NULL\n", __func__);
- goto fail;
+ goto done;
}
get_vocproc_cal(&cal_block);
if (cal_block.cal_size == 0) {
pr_err("%s: CVP cal size is 0\n", __func__);
- goto fail;
+ goto done;
}
cvp_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -2001,7 +2111,7 @@
if (ret < 0) {
pr_err("%s: Error %d registering CVP cal\n", __func__, ret);
- goto fail;
+ goto done;
}
ret = wait_event_timeout(v->cvp_wait,
(v->cvp_state == CMD_STATUS_SUCCESS),
@@ -2009,13 +2119,11 @@
if (!ret) {
pr_err("%s: Command timeout\n", __func__);
- goto fail;
+ goto done;
}
- return 0;
-
-fail:
- return -EINVAL;
+done:
+ return ret;
}
static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v)
@@ -2028,18 +2136,26 @@
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.apr_q6_cvp) {
pr_err("%s: apr_cvp is NULL.\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.cal_mem_handle) {
+ pr_debug("%s: Cal mem handle is NULL\n", __func__);
+
+ goto done;
}
get_vocproc_cal(&cal_block);
if (cal_block.cal_size == 0)
- return 0;
+ goto done;
cvp_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
@@ -2057,7 +2173,7 @@
if (ret < 0) {
pr_err("%s: Error %d de-registering CVP cal\n", __func__, ret);
- goto fail;
+ goto done;
}
ret = wait_event_timeout(v->cvp_wait,
(v->cvp_state == CMD_STATUS_SUCCESS),
@@ -2065,12 +2181,10 @@
if (!ret) {
pr_err("%s: Command timeout\n", __func__);
- goto fail;
+ goto done;
}
- return 0;
-
-fail:
+done:
return -EINVAL;
}
@@ -2084,26 +2198,28 @@
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.apr_q6_cvp) {
- pr_err("%s: apr_cvp is NULL.\n", __func__);
+ pr_err("%s: apr_cvp is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.cal_mem_handle) {
- pr_err("%s: Cal mem handle is NULL\n", __func__);
+ pr_debug("%s: Cal mem handle is NULL\n", __func__);
- goto fail;
+ goto done;
}
get_vocvol_cal(&cal_block);
if (cal_block.cal_size == 0) {
pr_err("%s: CVP vol cal size is 0\n", __func__);
- goto fail;
+ goto done;
}
cvp_reg_vol_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -2135,7 +2251,7 @@
if (ret < 0) {
pr_err("%s: Error %d registering CVP vol cal\n", __func__, ret);
- goto fail;
+ goto done;
}
ret = wait_event_timeout(v->cvp_wait,
(v->cvp_state == CMD_STATUS_SUCCESS),
@@ -2143,13 +2259,11 @@
if (!ret) {
pr_err("%s: Command timeout\n", __func__);
- goto fail;
+ goto done;
}
- return 0;
-
-fail:
- return -EINVAL;
+done:
+ return ret;
}
static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v)
@@ -2162,18 +2276,26 @@
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
}
if (!common.apr_q6_cvp) {
pr_err("%s: apr_cvp is NULL\n", __func__);
- goto fail;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.cal_mem_handle) {
+ pr_debug("%s: Cal mem handle is NULL\n", __func__);
+
+ goto done;
}
get_vocvol_cal(&cal_block);
if (cal_block.cal_size == 0)
- return 0;
+ goto done;
cvp_dereg_vol_cal_cmd.hdr.hdr_field =
APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -2194,7 +2316,7 @@
pr_err("%s: Error %d de-registering CVP vol cal\n",
__func__, ret);
- goto fail;
+ goto done;
}
ret = wait_event_timeout(v->cvp_wait,
(v->cvp_state == CMD_STATUS_SUCCESS),
@@ -2202,13 +2324,67 @@
if (!ret) {
pr_err("%s: Command timeout\n", __func__);
- goto fail;
+ goto done;
}
- return 0;
+done:
+ return ret;
+}
-fail:
- return -EINVAL;
+int voc_register_vocproc_vol_table(void)
+{
+ int result = 0;
+ int i;
+ struct voice_data *v = NULL;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&common.common_lock);
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ v = &common.voice[i];
+
+ mutex_lock(&v->lock);
+ if (is_voc_state_active(v->voc_state)) {
+ result = voice_send_cvp_register_vol_cal_cmd(v);
+ if (result) {
+ pr_err("%s: Failed to register vocvol table for session 0x%x!\n",
+ __func__, v->session_id);
+ mutex_unlock(&v->lock);
+ goto done;
+ }
+ }
+ mutex_unlock(&v->lock);
+ }
+done:
+ mutex_unlock(&common.common_lock);
+ return result;
+}
+
+int voc_deregister_vocproc_vol_table(void)
+{
+ int result = 0;
+ int i;
+ struct voice_data *v = NULL;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&common.common_lock);
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ v = &common.voice[i];
+
+ mutex_lock(&v->lock);
+ if (is_voc_state_active(v->voc_state)) {
+ result = voice_send_cvp_deregister_vol_cal_cmd(v);
+ if (result) {
+ pr_err("%s: Failed to deregister vocvol table for session 0x%x!\n",
+ __func__, v->session_id);
+ mutex_unlock(&v->lock);
+ goto done;
+ }
+ }
+ mutex_unlock(&v->lock);
+ }
+done:
+ mutex_unlock(&common.common_lock);
+ return result;
}
static int voice_map_memory_physical_cmd(struct voice_data *v,
@@ -2320,10 +2496,11 @@
return -EINVAL;
}
+ mutex_lock(&common.common_lock);
if (common.cal_mem_handle != 0) {
pr_debug("%s: Cal block already mem mapped\n", __func__);
- return ret;
+ goto done;
}
/* Get the physical address of calibration memory block from ACDB. */
@@ -2332,7 +2509,8 @@
if (!cal_block.cal_paddr) {
pr_err("%s: Cal block not allocated\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
}
ret = voice_map_memory_physical_cmd(v,
@@ -2341,6 +2519,8 @@
cal_block.cal_size,
VOC_CAL_MEM_MAP_TOKEN);
+done:
+ mutex_unlock(&common.common_lock);
return ret;
}
@@ -2414,7 +2594,6 @@
}
voice_send_cvs_register_cal_cmd(v);
-
voice_send_cvp_register_dev_cfg_cmd(v);
voice_send_cvp_register_cal_cmd(v);
voice_send_cvp_register_vol_cal_cmd(v);
@@ -3457,7 +3636,8 @@
cvs_start_playback.hdr.token = 0;
cvs_start_playback.hdr.opcode = VSS_IPLAYBACK_CMD_START;
cvs_start_playback.playback_mode.port_id =
- VSS_IPLAYBACK_PORT_ID_DEFAULT;
+ v->music_info.port_id;
+
v->cvs_state = CMD_STATUS_FAIL;
ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_playback);
@@ -3555,17 +3735,21 @@
return ret;
}
-int voc_start_playback(uint32_t set)
+int voc_start_playback(uint32_t set, uint16_t port_id)
{
int ret = 0;
u16 cvs_handle;
- int i;
+ struct voice_data *v = NULL;
- for (i = 0; i < MAX_VOC_SESSIONS; i++) {
- struct voice_data *v = &common.voice[i];
+ if (port_id == VOICE_PLAYBACK_TX)
+ v = voice_get_session(voc_get_session_id(VOICE_SESSION_NAME));
+ else if (port_id == VOICE2_PLAYBACK_TX)
+ v = voice_get_session(voc_get_session_id(VOICE2_SESSION_NAME));
+ if (v != NULL) {
mutex_lock(&v->lock);
+ v->music_info.port_id = port_id;
v->music_info.play_enable = set;
if (set)
v->music_info.count++;
@@ -3583,6 +3767,8 @@
}
mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: Invalid port_id 0x%x", __func__, port_id);
}
return ret;
@@ -3744,9 +3930,7 @@
v->stream_tx.stream_mute = mute;
- if ((v->voc_state == VOC_RUN) ||
- (v->voc_state == VOC_CHANGE) ||
- (v->voc_state == VOC_STANDBY))
+ if (is_voc_state_active(v->voc_state))
ret = voice_send_stream_mute_cmd(v);
mutex_unlock(&v->lock);
@@ -3900,9 +4084,7 @@
v->dev_rx.volume = vol_idx;
- if ((v->voc_state == VOC_RUN) ||
- (v->voc_state == VOC_CHANGE) ||
- (v->voc_state == VOC_STANDBY))
+ if (is_voc_state_active(v->voc_state))
ret = voice_send_vol_index_cmd(v);
mutex_unlock(&v->lock);
@@ -4150,31 +4332,34 @@
goto fail;
}
- /* Memory map the calibration memory block. */
- ret = voice_mem_map_cal_block(v);
+ /* Allocate cal mem if not already allocated and memory map
+ * the calibration memory block.
+ */
+ ret = voice_alloc_and_map_cal_mem(v);
if (ret < 0) {
- pr_err("%s: Memory map of cal block failed %d\n",
- __func__, ret);
- /* Allow call to continue, call quality will be bad. */
+ pr_debug("%s: Continue without calibration %d\n",
+ __func__, ret);
}
if (is_voip_session(session_id)) {
- ret = voice_map_memory_physical_cmd(v,
- &v->shmem_info.memtbl,
- v->shmem_info.sh_buf.buf[0].phys,
- v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS,
- VOIP_MEM_MAP_TOKEN);
- if (ret) {
- pr_err("%s: mvm_map_memory_phy failed %d\n",
- __func__, ret);
+ /* Allocate oob mem if not already allocated and
+ * memory map the oob memory block.
+ */
+ ret = voice_alloc_and_map_oob_mem(v);
+ if (ret < 0) {
+ pr_err("%s: voice_alloc_and_map_oob_mem() failed, ret:%d\n",
+ __func__, ret);
+
goto fail;
}
+
ret = voice_set_packet_exchange_mode_and_config(
session_id,
VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND);
if (ret) {
pr_err("%s: Err: exchange_mode_and_config %d\n",
__func__, ret);
+
goto fail;
}
}
@@ -4284,9 +4469,14 @@
apr_reset(c->apr_q6_mvm);
c->apr_q6_mvm = NULL;
+ /* clean up memory handle */
+ c->cal_mem_handle = 0;
+
/* Sub-system restart is applicable to all sessions. */
- for (i = 0; i < MAX_VOC_SESSIONS; i++)
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
c->voice[i].mvm_handle = 0;
+ c->voice[i].shmem_info.mem_handle = 0;
+ }
}
return 0;
}
@@ -4787,6 +4977,45 @@
return 0;
}
+static int voice_free_oob_shared_mem(void)
+{
+ int rc = 0;
+ int cnt = 0;
+ int bufcnt = NUM_OF_BUFFERS;
+ struct voice_data *v = voice_get_session(
+ common.voice[VOC_PATH_FULL].session_id);
+
+ mutex_lock(&common.common_lock);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = msm_audio_ion_free(v->shmem_info.sh_buf.client,
+ v->shmem_info.sh_buf.handle);
+ if (rc < 0) {
+ pr_err("%s: Error:%d freeing memory\n", __func__, rc);
+
+ goto done;
+ }
+
+
+ while (cnt < bufcnt) {
+ v->shmem_info.sh_buf.buf[cnt].data = NULL;
+ v->shmem_info.sh_buf.buf[cnt].phys = 0;
+ cnt++;
+ }
+
+ v->shmem_info.sh_buf.client = NULL;
+ v->shmem_info.sh_buf.handle = NULL;
+
+done:
+ mutex_unlock(&common.common_lock);
+ return rc;
+}
+
static int voice_alloc_oob_shared_mem(void)
{
int cnt = 0;
@@ -4799,9 +5028,12 @@
struct voice_data *v = voice_get_session(
common.voice[VOC_PATH_FULL].session_id);
+ mutex_lock(&common.common_lock);
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- return -EINVAL;
+
+ rc = -EINVAL;
+ goto done;
}
rc = msm_audio_ion_alloc("voip_client", &(v->shmem_info.sh_buf.client),
@@ -4809,10 +5041,11 @@
bufsz*bufcnt,
(ion_phys_addr_t *)&phys, (size_t *)&len,
&mem_addr);
- if (rc) {
+ if (rc < 0) {
pr_err("%s: audio ION alloc failed, rc = %d\n",
__func__, rc);
- return -EINVAL;
+
+ goto done;
}
while (cnt < bufcnt) {
@@ -4835,7 +5068,9 @@
memset((void *)v->shmem_info.sh_buf.buf[0].data, 0, (bufsz * bufcnt));
- return 0;
+done:
+ mutex_unlock(&common.common_lock);
+ return rc;
}
static int voice_alloc_oob_mem_table(void)
@@ -4845,9 +5080,12 @@
struct voice_data *v = voice_get_session(
common.voice[VOC_PATH_FULL].session_id);
+ mutex_lock(&common.common_lock);
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- return -EINVAL;
+
+ rc = -EINVAL;
+ goto done;
}
rc = msm_audio_ion_alloc("voip_client", &(v->shmem_info.memtbl.client),
@@ -4856,21 +5094,22 @@
(ion_phys_addr_t *)&v->shmem_info.memtbl.phys,
(size_t *)&len,
&(v->shmem_info.memtbl.data));
- if (rc) {
+ if (rc < 0) {
pr_err("%s: audio ION alloc failed, rc = %d\n",
__func__, rc);
- return -EINVAL;
+
+ goto done;
}
v->shmem_info.memtbl.size = sizeof(struct vss_imemory_table_t);
-
pr_debug("%s data[%p]phys[%p][%p]\n", __func__,
(void *)v->shmem_info.memtbl.data,
(void *)v->shmem_info.memtbl.phys,
(void *)&v->shmem_info.memtbl.phys);
- return 0;
-
+done:
+ mutex_unlock(&common.common_lock);
+ return rc;
}
static int voice_alloc_cal_mem_map_table(void)
@@ -4878,17 +5117,17 @@
int ret = 0;
int len;
- ret = msm_audio_ion_alloc("voip_client",
+ ret = msm_audio_ion_alloc("voc_cal",
&(common.cal_mem_map_table.client),
&(common.cal_mem_map_table.handle),
sizeof(struct vss_imemory_table_t),
(ion_phys_addr_t *)&common.cal_mem_map_table.phys,
(size_t *) &len,
&(common.cal_mem_map_table.data));
- if (ret) {
+ if (ret < 0) {
pr_err("%s: audio ION alloc failed, rc = %d\n",
__func__, ret);
- return -EINVAL;
+ goto done;
}
common.cal_mem_map_table.size = sizeof(struct vss_imemory_table_t);
@@ -4896,7 +5135,78 @@
(unsigned int) common.cal_mem_map_table.data,
common.cal_mem_map_table.phys);
- return 0;
+done:
+ return ret;
+}
+
+static int voice_alloc_and_map_cal_mem(struct voice_data *v)
+{
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ return -EINVAL;
+ }
+
+ ret = voc_alloc_cal_shared_memory();
+ if (ret < 0) {
+ pr_err("%s: Memory allocation of cal block failed %d\n",
+ __func__, ret);
+
+ goto done;
+ }
+
+ /* Memory map the calibration memory block. */
+ ret = voice_mem_map_cal_block(v);
+ if (ret < 0) {
+ pr_err("%s: Memory map of cal block failed %d\n",
+ __func__, ret);
+ }
+
+done:
+ return ret;
+}
+
+static int voice_alloc_and_map_oob_mem(struct voice_data *v)
+{
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ return -EINVAL;
+ }
+
+ if (!is_voip_memory_allocated()) {
+ ret = voc_alloc_voip_shared_memory();
+ if (ret < 0) {
+ pr_err("%s: Failed to create voip oob memory %d\n",
+ __func__, ret);
+
+ goto done;
+ }
+ }
+
+ ret = voice_map_memory_physical_cmd(v,
+ &v->shmem_info.memtbl,
+ v->shmem_info.sh_buf.buf[0].phys,
+ v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS,
+ VOIP_MEM_MAP_TOKEN);
+ if (ret) {
+ pr_err("%s: mvm_map_memory_phy failed %d\n",
+ __func__, ret);
+
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+int is_voc_initialized(void)
+{
+ return module_initialized;
}
static int __init voice_init(void)
@@ -4942,21 +5252,11 @@
mutex_init(&common.voice[i].lock);
}
- /* Allocate shared memory for OOB Voip */
- rc = voice_alloc_oob_shared_mem();
- if (rc < 0)
- pr_err("failed to alloc shared memory for OOB %d\n", rc);
- else {
- /* Allocate mem map table for OOB */
- rc = voice_alloc_oob_mem_table();
- if (rc < 0)
- pr_err("failed to alloc mem map talbe %d\n", rc);
- }
+ if (rc == 0)
+ module_initialized = true;
- /* Allocate memory for calibration memory map table. */
- rc = voice_alloc_cal_mem_map_table();
-
+ pr_debug("%s: rc=%d\n", __func__, rc);
return rc;
}
-late_initcall(voice_init);
+device_initcall(voice_init);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
index 5a16115..42b1800 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -486,6 +486,12 @@
#define VSS_IPLAYBACK_PORT_ID_DEFAULT 0x0000FFFF
/* Default AFE port ID. */
+#define VSS_IPLAYBACK_PORT_ID_VOICE 0x00008005
+/* AFE port ID for VOICE 1. */
+
+#define VSS_IPLAYBACK_PORT_ID_VOICE2 0x00008002
+/* AFE port ID for VOICE 2. */
+
#define VSS_IRECORD_CMD_START 0x000112BE
/* Start in-call conversation recording. */
#define VSS_IRECORD_CMD_STOP 0x00011237
@@ -1209,6 +1215,7 @@
uint32_t playing;
int count;
int force;
+ uint16_t port_id;
};
struct share_memory_info {
@@ -1367,9 +1374,16 @@
uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir);
int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable);
void voc_disable_dtmf_det_on_active_sessions(void);
+int voc_alloc_cal_shared_memory(void);
+int voc_alloc_voip_shared_memory(void);
+int is_voc_initialized(void);
+int voc_register_vocproc_vol_table(void);
+int voc_deregister_vocproc_vol_table(void);
uint32_t voc_get_session_id(char *name);
-int voc_start_playback(uint32_t set);
+int voc_start_playback(uint32_t set, uint16_t port_id);
int voc_start_record(uint32_t port_id, uint32_t set);
+int voice_get_idx_for_session(u32 session_id);
+
#endif
diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c
index 1f2a487..a4983d3 100644
--- a/sound/soc/msm/qdsp6v2/rtac.c
+++ b/sound/soc/msm/qdsp6v2/rtac.c
@@ -24,7 +24,7 @@
#include <sound/q6asm-v2.h>
#include <sound/q6afe-v2.h>
#include <sound/apr_audio-v2.h>
-
+#include <q6voice.h>
#include "audio_acdb.h"
@@ -352,13 +352,13 @@
return;
}
-static int get_voice_index_cvs(u32 cvs_handle)
+static u32 get_voice_session_id_cvs(u32 cvs_handle)
{
u32 i;
for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
if (rtac_voice_data.voice[i].cvs_handle == cvs_handle)
- return i;
+ return voice_session_id[i];
}
pr_err("%s: No voice index for CVS handle %d found returning 0\n",
@@ -366,13 +366,13 @@
return 0;
}
-static int get_voice_index_cvp(u32 cvp_handle)
+static u32 get_voice_session_id_cvp(u32 cvp_handle)
{
u32 i;
for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
if (rtac_voice_data.voice[i].cvp_handle == cvp_handle)
- return i;
+ return voice_session_id[i];
}
pr_err("%s: No voice index for CVP handle %d found returning 0\n",
@@ -383,9 +383,11 @@
static int get_voice_index(u32 mode, u32 handle)
{
if (mode == RTAC_CVP)
- return get_voice_index_cvp(handle);
+ return voice_get_idx_for_session(
+ get_voice_session_id_cvp(handle));
if (mode == RTAC_CVS)
- return get_voice_index_cvs(handle);
+ return voice_get_idx_for_session(
+ get_voice_session_id_cvs(handle));
pr_err("%s: Invalid mode %d, returning 0\n",
__func__, mode);
@@ -847,8 +849,7 @@
payload_size);
voice_params.src_svc = 0;
voice_params.src_domain = APR_DOMAIN_APPS;
- voice_params.src_port = voice_session_id[
- get_voice_index(mode, dest_port)];
+ voice_params.src_port = get_voice_index(mode, dest_port);
voice_params.dest_svc = 0;
voice_params.dest_domain = APR_DOMAIN_MODEM;
voice_params.dest_port = (u16)dest_port;